X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=lisp%2Fgnus-art.el;h=442a1fc1d632e772a7985eb48e93343f501c197a;hb=3c6a96d019e0fcdf0d35f9d4873f62c1962995ad;hp=fc6b49bdca256a5f2d76361675cac9cd2cc2c2ab;hpb=b008f17a2c9cff5c7c0b0c669d54aba93c561a23;p=elisp%2Fgnus.git- diff --git a/lisp/gnus-art.el b/lisp/gnus-art.el index fc6b49b..442a1fc 100644 --- a/lisp/gnus-art.el +++ b/lisp/gnus-art.el @@ -1,8 +1,10 @@ -;;; gnus-art.el --- article mode commands for Gnus -;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc. +;;; gnus-art.el --- article mode commands for Semi-gnus +;; Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen -;; Keywords: news +;; MORIOKA Tomohiko +;; Katsumi Yamaoka +;; Keywords: mail, news, MIME ;; This file is part of GNU Emacs. @@ -26,19 +28,29 @@ ;;; Code: (eval-when-compile (require 'cl)) +(eval-when-compile (require 'static)) +(require 'path-util) (require 'custom) (require 'gnus) (require 'gnus-sum) (require 'gnus-spec) (require 'gnus-int) (require 'browse-url) -(require 'mm-bodies) -(require 'mail-parse) -(require 'mm-decode) -(require 'mm-view) -(require 'wid-edit) -(require 'mm-uu) +(require 'alist) +(require 'mime-view) + +;; Avoid byte-compile warnings. +(defvar gnus-article-decoded-p) +(defvar gnus-article-mime-handles) +(eval-when-compile + (require 'mm-bodies) + (require 'mail-parse) + (require 'mm-decode) + (require 'mm-view) + (require 'wid-edit) + (require 'mm-uu) + ) (defgroup gnus-article nil "Article display." @@ -116,11 +128,19 @@ "^Originator:" "^X-Problems-To:" "^X-Auth-User:" "^X-Post-Time:" "^X-Admin:" "^X-UID:" "^Resent-[-A-Za-z]+:" "^X-Mailing-List:" "^Precedence:" "^Original-[-A-Za-z]+:" "^X-filename:" "^X-Orcpt:" - "^Old-Received:" "^X-Pgp-Fingerprint:" "^X-Pgp-Key-Id:" - "^X-Pgp-Public-Key-Url:" "^X-Auth:" "^X-From-Line:" + "^Old-Received:" "^X-Pgp" "^X-Auth:" "^X-From-Line:" "^X-Gnus-Article-Number:" "^X-Majordomo:" "^X-Url:" "^X-Sender:" - "^X-Mailing-List:" "^MBOX-Line" "^Priority:" "^X-Pgp" "^X400-[-A-Za-z]+:" - "^Status:") + "^MBOX-Line" "^Priority:" "^X-Pgp" "^X400-[-A-Za-z]+:" + "^Status:" "^X-Gnus-Mail-Source:" "^Cancel-Lock:" + "^X-FTN" "^X-EXP32-SerialNo:" "^Encoding:" "^Importance:" + "^Autoforwarded:" "^Original-Encoded-Information-Types:" "^X-Ya-Pop3:" + "^X-Face-Version:" "^X-Vms-To:" "^X-ML-NAME:" "^X-ML-COUNT:" + "^Mailing-List:" "^X-finfo:" "^X-md5sum:" "^X-md5sum-Origin:" + "^X-Sun-Charset:" "^X-Accept-Language:" "^X-Envelope-Sender:" + "^List-[A-Za-z]+:" "^X-Listprocessor-Version:" + "^X-Received:" "^X-Distribute:" "^X-Sequence:" "^X-Juno-Line-Breaks:" + "^X-Notes-Item:" "^X-MS-TNEF-Correlator:" "^x-uunet-gateway:" + "^X-Received:" "^Content-length:" "X-precedence:") "*All headers that start with this regexp will be hidden. This variable can also be a list of regexps of headers to be ignored. If `gnus-visible-headers' is non-nil, this variable will be ignored." @@ -161,8 +181,8 @@ Possible values in this list are `empty', `newsgroups', `followup-to', (const :tag "Followup-to identical to newsgroups." followup-to) (const :tag "Reply-to identical to from." reply-to) (const :tag "Date less than four days old." date) - (const :tag "Very long To header." long-to) - (const :tag "Multiple To headers." many-to)) + (const :tag "Very long To and/or Cc header." long-to) + (const :tag "Multiple To and/or Cc headers." many-to)) :group 'gnus-article-hiding) (defcustom gnus-signature-separator '("^-- $" "^-- *$") @@ -174,7 +194,7 @@ the end of the buffer." :group 'gnus-article-signature) (defcustom gnus-signature-limit nil - "Provide a limit to what is considered a signature. + "Provide a limit to what is considered a signature. If it is a number, no signature may not be longer (in characters) than that number. If it is a floating point number, no signature may be longer (in lines) than that number. If it is a function, the function @@ -193,7 +213,12 @@ regexp. If it matches, the text in question is not a signature." :group 'gnus-article-hiding) (defcustom gnus-article-x-face-command - "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | xv -quit -" + (if (and (not gnus-xemacs) + window-system + (module-installed-p 'x-face-mule)) + 'x-face-mule-gnus-article-display-x-face + "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | display -" + ) "*String or function to be executed to display an X-Face header. If it is a string, the command will be executed in a sub-shell asynchronously. The compressed face will be piped to this command." @@ -207,7 +232,7 @@ asynchronously. The compressed face will be piped to this command." (defcustom gnus-emphasis-alist (let ((format - "\\(\\s-\\|^\\|[-\"]\\|\\s(\\|\\s)\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\\(\\s-\\|[-?!.,;:\"]\\|\\s(\\|\\s)\\)") + "\\(\\s-\\|^\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\\(\\s-\\|[-,;:\"]\\s-\\|[?!.]+\\s-\\|\\s)\\)") (types '(("_" "_" underline) ("/" "/" italic) @@ -221,7 +246,7 @@ asynchronously. The compressed face will be piped to this command." ,@(mapcar (lambda (spec) (list - (format format (car spec) (cadr spec)) + (format format (car spec) (car (cdr spec))) 2 3 (intern (format "gnus-emphasis-%s" (nth 2 spec))))) types))) "*Alist that says how to fontify certain phrases. @@ -258,7 +283,7 @@ is the face used for highlighting." :group 'gnus-article-emphasis) (defface gnus-emphasis-underline-italic '((t (:italic t :underline t))) - "Face used for displaying underlined italic emphasized text (_*word*_)." + "Face used for displaying underlined italic emphasized text (_/word/_)." :group 'gnus-article-emphasis) (defface gnus-emphasis-bold-italic '((t (:bold t :italic t))) @@ -271,6 +296,11 @@ is the face used for highlighting." Esample: (_/*word*/_)." :group 'gnus-article-emphasis) +(defface gnus-emphasis-highlight-words + '((t (:background "black" :foreground "yellow"))) + "Face used for displaying highlighted words." + :group 'gnus-article-emphasis) + (defcustom gnus-article-time-format "%a, %b %d %Y %T %Z" "Format for display of Date headers in article bodies. See `format-time-string' for the possible values. @@ -384,6 +414,20 @@ be used as possible file names." (cons :value ("" "") regexp (repeat string)) (sexp :value nil)))) +(defcustom gnus-article-display-method-for-mime + 'gnus-article-display-mime-message + "Function to display a MIME message. +The function is called from the article buffer." + :group 'gnus-article-mime + :type 'function) + +(defcustom gnus-article-display-method-for-traditional + 'gnus-article-display-traditional-message + "*Function to display a traditional message. +The function is called from the article buffer." + :group 'gnus-article-mime + :type 'function) + (defcustom gnus-page-delimiter "^\^L" "*Regexp describing what to use as article page delimiters. The default value is \"^\^L\", which is a form linefeed at the @@ -391,7 +435,7 @@ beginning of a line." :type 'regexp :group 'gnus-article-various) -(defcustom gnus-article-mode-line-format "Gnus: %g %S%m" +(defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m" "*The format specification for the article mode line. See `gnus-summary-mode-line-format' for a closer description. @@ -446,8 +490,7 @@ Obsolete; use the face `gnus-signature-face' for customizations instead." :group 'gnus-article-signature) (defface gnus-signature-face - '((((type x)) - (:italic t))) + '((t (:italic t))) "Face used for highlighting a signature in the article buffer." :group 'gnus-article-highlight :group 'gnus-article-signature) @@ -542,8 +585,7 @@ displayed by the first non-nil matching CONTENT face." (item :tag "skip" nil) (face :value default))))) -(defcustom gnus-article-decode-hook - '(article-decode-charset article-decode-encoded-words) +(defcustom gnus-article-decode-hook nil "*Hook run to decode charsets in articles." :group 'gnus-article-headers :type 'hook) @@ -566,9 +608,9 @@ displayed by the first non-nil matching CONTENT face." ("\221" "`") ("\222" "'") ("\223" "``") - ("\224" "''") + ("\224" "\"") ("\225" "*") - ("\226" "-") + ("\226" "---") ("\227" "-") ("\231" "(TM)") ("\233" ">") @@ -582,7 +624,7 @@ displayed by the first non-nil matching CONTENT face." :type '(repeat regexp)) (defcustom gnus-unbuttonized-mime-types '(".*/.*") - "List of MIME types that should not be given buttons when rendered." + "List of MIME types that should not be given buttons when rendered inline." :group 'gnus-article-mime :type '(repeat regexp)) @@ -593,6 +635,33 @@ on parts -- for instance, adding Vcard info to a database." :group 'gnus-article-mime :type 'function) +(defcustom gnus-mime-multipart-functions nil + "An alist of MIME types to functions to display them.") + +(defcustom gnus-article-date-lapsed-new-header nil + "Whether the X-Sent and Date headers can coexist. +When using `gnus-treat-date-lapsed', the \"X-Sent:\" header will +either replace the old \"Date:\" header (if this variable is nil), or +be added below it (otherwise)." + :group 'gnus-article-headers + :type 'boolean) + +(defcustom gnus-article-mime-match-handle-function 'undisplayed-alternative + "Function called with a MIME handle as the argument. +This is meant for people who want to view first matched part. +For `undisplayed-alternative' (default), the first undisplayed +part or alternative part is used. For `undisplayed', the first +undisplayed part is used. For a function, the first part which +the function return `t' is used. For `nil', the first part is +used." + :group 'gnus-article-mime + :type '(choice + (item :tag "first" :value nil) + (item :tag "undisplayed" :value undisplayed) + (item :tag "undisplayed or alternative" + :value undisplayed-alternative) + (function))) + ;;; ;;; The treatment variables ;;; @@ -605,9 +674,15 @@ on parts -- for instance, adding Vcard info to a database." (const :tag "On" t) (const :tag "Header" head) (const :tag "Last" last) + (const :tag "Mime" mime) (integer :tag "Less") + (repeat :tag "Groups" regexp) (sexp :tag "Predicate"))) +(defvar gnus-article-treat-head-custom + '(choice (const :tag "Off" nil) + (const :tag "Header" head))) + (defvar gnus-article-treat-types '("text/plain") "Parts to treat.") @@ -615,174 +690,322 @@ on parts -- for instance, adding Vcard info to a database." "Whether to inhibit treatment.") (defcustom gnus-treat-highlight-signature '(or last (typep "text/x-vcard")) - "Highlight the signature." + "Highlight the signature. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(put 'gnus-treat-highlight-signature 'highlight t) -(defcustom gnus-treat-buttonize t - "Add buttons." +(defcustom gnus-treat-buttonize 100000 + "Add buttons. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(put 'gnus-treat-buttonize 'highlight t) (defcustom gnus-treat-buttonize-head 'head - "Add buttons to the head." + "Add buttons to the head. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) +(put 'gnus-treat-buttonize-head 'highlight t) -(defcustom gnus-treat-emphasize t - "Emphasize text." +(defcustom gnus-treat-emphasize nil + "Emphasize text. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(put 'gnus-treat-emphasize 'highlight t) (defcustom gnus-treat-strip-cr nil - "Remove carriage returns." + "Remove carriage returns. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-hide-headers 'head - "Hide headers." + "Hide headers. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) (defcustom gnus-treat-hide-boring-headers nil - "Hide boring headers." + "Hide boring headers. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) (defcustom gnus-treat-hide-signature nil - "Hide the signature." + "Hide the signature. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-fill-article nil - "Fill the article." + "Fill the article. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-hide-citation nil - "Hide cited text." + "Hide cited text. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-custom) + +(defcustom gnus-treat-strip-list-identifiers 'head + "Strip list identifiers from `gnus-list-identifiers`. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-strip-pgp t - "Strip PGP signatures." + "Strip PGP signatures. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-strip-pem nil - "Strip PEM signatures." + "Strip PEM signatures. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) -(defcustom gnus-treat-highlight-headers 'head - "Highlight the headers." +(defcustom gnus-treat-strip-banner t + "Strip banners from articles. +The banner to be stripped is specified in the `banner' group parameter. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(defcustom gnus-treat-highlight-headers 'head + "Highlight the headers. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) +(put 'gnus-treat-highlight-headers 'highlight t) + (defcustom gnus-treat-highlight-citation t - "Highlight cited text." + "Highlight cited text. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(put 'gnus-treat-highlight-citation 'highlight t) (defcustom gnus-treat-date-ut nil - "Display the Date in UT (GMT)." + "Display the Date in UT (GMT). +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) (defcustom gnus-treat-date-local nil - "Display the Date in the local timezone." + "Display the Date in the local timezone. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) (defcustom gnus-treat-date-lapsed nil - "Display the Date header in a way that says how much time has elapsed." + "Display the Date header in a way that says how much time has elapsed. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat - :type gnus-article-treat-custom) + :type gnus-article-treat-head-custom) (defcustom gnus-treat-date-original nil - "Display the date in the original timezone." + "Display the date in the original timezone. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) + +(defcustom gnus-treat-date-iso8601 nil + "Display the date in the ISO8601 format. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) + +(defcustom gnus-treat-date-user-defined nil + "Display the date in a user-defined format. +The format is defined by the `gnus-article-time-format' variable. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) + +(defcustom gnus-treat-strip-headers-in-body t + "Strip the X-No-Archive header line from the beginning of the body. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-strip-trailing-blank-lines nil - "Strip trailing blank lines." + "Strip trailing blank lines. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-strip-leading-blank-lines nil - "Strip leading blank lines." + "Strip leading blank lines. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) (defcustom gnus-treat-strip-multiple-blank-lines nil - "Strip multiple blank lines." + "Strip multiple blank lines. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) -(defcustom gnus-treat-strip-blank-lines nil - "Strip all blank lines." +(defcustom gnus-treat-overstrike t + "Treat overstrike highlighting. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) +(put 'gnus-treat-overstrike 'highlight t) -(defcustom gnus-treat-overstrike t - "Treat overstrike highlighting." +(defcustom gnus-treat-display-xface + (if (or (and gnus-xemacs (featurep 'xface)) + (eq 'x-face-mule-gnus-article-display-x-face + gnus-article-x-face-command)) + 'head + nil) + "Display X-Face headers. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) +(put 'gnus-treat-display-xface 'highlight t) + +(defcustom gnus-treat-display-smileys + (if (or (and gnus-xemacs (featurep 'xpm)) + (and (not gnus-xemacs) + window-system + (module-installed-p 'gnus-bitmap))) + t + nil) + "Display smileys. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-custom) +(put 'gnus-treat-display-smileys 'highlight t) + +(defcustom gnus-treat-display-picons (if gnus-xemacs 'head nil) + "Display picons. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." + :group 'gnus-article-treat + :type gnus-article-treat-head-custom) +(put 'gnus-treat-display-picons 'highlight t) + +(defcustom gnus-treat-capitalize-sentences nil + "Capitalize sentence-starting words. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) -(defcustom gnus-treat-display-xface (if (and gnus-xemacs (featurep 'xface)) - 'head nil) - "Display X-Face headers." +(defcustom gnus-treat-fill-long-lines nil + "Fill long lines. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) -(defcustom gnus-treat-display-smileys (if (and gnus-xemacs - (featurep 'xpm)) - t nil) - "Display smileys." +(defcustom gnus-treat-play-sounds nil + "Play sounds. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) -(defcustom gnus-treat-display-picons (if gnus-xemacs 'head nil) - "Display picons." +(defcustom gnus-treat-decode-article-as-default-mime-charset nil + "Decode an article as `default-mime-charset'. For instance, if you want to +attempt to decode an article even if the value of `gnus-show-mime' is nil, +you could set this variable to something like: nil for don't decode, t for +decode the body, '(or header t) for the whole article, etc." + :group 'gnus-article-treat + :type '(radio (const :tag "Off" nil) + (const :tag "Decode body" t) + (const :tag "Decode all" (or head t)))) + +(defcustom gnus-treat-translate nil + "Translate articles from one language to another. +Valid values are nil, t, `head', `last', an integer or a predicate. +See the manual for details." :group 'gnus-article-treat :type gnus-article-treat-custom) ;;; Internal variables (defvar article-goto-body-goes-to-point-min-p nil) +(defvar gnus-article-wash-types nil) +(defvar gnus-article-emphasis-alist nil) (defvar gnus-article-mime-handle-alist-1 nil) (defvar gnus-treatment-function-alist - '((gnus-treat-highlight-signature gnus-article-highlight-signature) + '((gnus-treat-decode-article-as-default-mime-charset + gnus-article-decode-article-as-default-mime-charset) + (gnus-treat-strip-banner gnus-article-strip-banner) + (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body) (gnus-treat-buttonize gnus-article-add-buttons) - (gnus-treat-buttonize-head gnus-article-add-buttons-to-head) (gnus-treat-fill-article gnus-article-fill-cited-article) + (gnus-treat-fill-long-lines gnus-article-fill-long-lines) (gnus-treat-strip-cr gnus-article-remove-cr) - (gnus-treat-hide-headers gnus-article-hide-headers) + (gnus-treat-display-xface gnus-article-display-x-face) + (gnus-treat-hide-headers gnus-article-maybe-hide-headers) (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers) (gnus-treat-hide-signature gnus-article-hide-signature) (gnus-treat-hide-citation gnus-article-hide-citation) + (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers) (gnus-treat-strip-pgp gnus-article-hide-pgp) (gnus-treat-strip-pem gnus-article-hide-pem) (gnus-treat-highlight-headers gnus-article-highlight-headers) + (gnus-treat-emphasize gnus-article-emphasize) (gnus-treat-highlight-citation gnus-article-highlight-citation) (gnus-treat-highlight-signature gnus-article-highlight-signature) - (gnus-treat-emphasize gnus-article-emphasize) (gnus-treat-date-ut gnus-article-date-ut) (gnus-treat-date-local gnus-article-date-local) (gnus-treat-date-lapsed gnus-article-date-lapsed) (gnus-treat-date-original gnus-article-date-original) + (gnus-treat-date-user-defined gnus-article-date-user) + (gnus-treat-date-iso8601 gnus-article-date-iso8601) (gnus-treat-strip-trailing-blank-lines gnus-article-remove-trailing-blank-lines) (gnus-treat-strip-leading-blank-lines gnus-article-strip-leading-blank-lines) (gnus-treat-strip-multiple-blank-lines gnus-article-strip-multiple-blank-lines) - (gnus-treat-strip-blank-lines gnus-article-strip-blank-lines) (gnus-treat-overstrike gnus-article-treat-overstrike) - (gnus-treat-display-xface gnus-article-display-x-face) - (gnus-treat-display-smileys gnus-smiley-display) - (gnus-treat-display-picons gnus-article-display-picons))) + (gnus-treat-buttonize-head gnus-article-add-buttons-to-head) + (gnus-treat-display-smileys gnus-article-smiley-display) + (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences) + (gnus-treat-display-picons gnus-article-display-picons) + (gnus-treat-play-sounds gnus-earcon-display))) (defvar gnus-article-mime-handle-alist nil) (defvar article-lapsed-timer nil) @@ -815,34 +1038,6 @@ Initialized from `text-mode-syntax-table.") (put-text-property (max (1- b) (point-min)) b 'intangible (cddr (memq 'intangible props))))) - -(defmacro gnus-with-article (article &rest forms) - "Select ARTICLE and perform FORMS in the original article buffer. -Then replace the article with the result." - `(progn - ;; We don't want the article to be marked as read. - (let (gnus-mark-article-hook) - (gnus-summary-select-article t t nil ,article)) - (set-buffer gnus-original-article-buffer) - ,@forms - (if (not (gnus-check-backend-function - 'request-replace-article (car gnus-article-current))) - (gnus-message 5 "Read-only group; not replacing") - (unless (gnus-request-replace-article - ,article (car gnus-article-current) - (current-buffer) t) - (error "Couldn't replace article"))) - ;; The cache and backlog have to be flushed somewhat. - (when gnus-keep-backlog - (gnus-backlog-remove-article - (car gnus-article-current) (cdr gnus-article-current))) - (when gnus-use-cache - (gnus-cache-update-article - (car gnus-article-current) (cdr gnus-article-current))))) - -(put 'gnus-with-article 'lisp-indent-function 1) -(put 'gnus-with-article 'edebug-form-spec '(form body)) - (defsubst gnus-article-unhide-text (b e) "Remove hidden text properties from region between B and E." (remove-text-properties b e gnus-hidden-properties) @@ -852,11 +1047,14 @@ Then replace the article with the result." (defun gnus-article-hide-text-type (b e type) "Hide text of TYPE between B and E." + (push type gnus-article-wash-types) (gnus-article-hide-text b e (cons 'article-type (cons type gnus-hidden-properties)))) (defun gnus-article-unhide-text-type (b e type) "Unhide text of TYPE between B and E." + (setq gnus-article-wash-types + (delq type gnus-article-wash-types)) (remove-text-properties b e (cons 'article-type (cons type gnus-hidden-properties))) (when (memq 'intangible gnus-hidden-properties) @@ -905,79 +1103,76 @@ Then replace the article with the result." i)) (defun article-hide-headers (&optional arg delete) - "Toggle whether to hide unwanted headers and possibly sort them as well. -If given a negative prefix, always show; if given a positive prefix, -always hide." + "Hide unwanted headers and possibly sort them as well." (interactive (gnus-article-hidden-arg)) - (current-buffer) + ;; Lars said that this function might be inhibited. (if (gnus-article-check-hidden-text 'headers arg) - ;; Show boring headers as well. - (gnus-article-show-hidden-text 'boring-headers) - ;; This function might be inhibited. - (unless gnus-inhibit-hiding - (save-excursion - (save-restriction - (let ((buffer-read-only nil) - (case-fold-search t) - (props (nconc (list 'article-type 'headers) - gnus-hidden-properties)) - (max (1+ (length gnus-sorted-header-list))) - (ignored (when (not gnus-visible-headers) - (cond ((stringp gnus-ignored-headers) - gnus-ignored-headers) - ((listp gnus-ignored-headers) - (mapconcat 'identity gnus-ignored-headers - "\\|"))))) - (visible - (cond ((stringp gnus-visible-headers) - gnus-visible-headers) - ((and gnus-visible-headers - (listp gnus-visible-headers)) - (mapconcat 'identity gnus-visible-headers "\\|")))) - (inhibit-point-motion-hooks t) - beg) - ;; First we narrow to just the headers. - (widen) - (goto-char (point-min)) - ;; Hide any "From " lines at the beginning of (mail) articles. - (while (looking-at "From ") - (forward-line 1)) - (unless (bobp) - (if delete - (delete-region (point-min) (point)) - (gnus-article-hide-text (point-min) (point) props))) - ;; Then treat the rest of the header lines. - (narrow-to-region - (point) - (if (search-forward "\n\n" nil t) ; if there's a body - (progn (forward-line -1) (point)) - (point-max))) - ;; Then we use the two regular expressions - ;; `gnus-ignored-headers' and `gnus-visible-headers' to - ;; select which header lines is to remain visible in the - ;; article buffer. - (goto-char (point-min)) - (while (re-search-forward "^[^ \t]*:" nil t) - (beginning-of-line) - ;; Mark the rank of the header. - (put-text-property - (point) (1+ (point)) 'message-rank - (if (or (and visible (looking-at visible)) - (and ignored - (not (looking-at ignored)))) - (gnus-article-header-rank) - (+ 2 max))) - (forward-line 1)) - (message-sort-headers-1) - (when (setq beg (text-property-any - (point-min) (point-max) 'message-rank (+ 2 max))) - ;; We make the unwanted headers invisible. - (if delete - (delete-region beg (point-max)) - ;; Suggested by Sudish Joseph . - (gnus-article-hide-text-type beg (point-max) 'headers)) - ;; Work around XEmacs lossage. - (put-text-property (point-min) beg 'invisible nil)))))))) + (progn + ;; Show boring headers as well. + (gnus-article-show-hidden-text 'boring-headers) + (when (eq 1 (point-min)) + (set-window-start (get-buffer-window (current-buffer)) 1))) + (unless gnus-inhibit-hiding + (save-excursion + (save-restriction + (let ((buffer-read-only nil) + (inhibit-read-only t) + (case-fold-search t) + (max (1+ (length gnus-sorted-header-list))) + (ignored (when (not gnus-visible-headers) + (cond ((stringp gnus-ignored-headers) + gnus-ignored-headers) + ((listp gnus-ignored-headers) + (mapconcat 'identity gnus-ignored-headers + "\\|"))))) + (visible + (cond ((stringp gnus-visible-headers) + gnus-visible-headers) + ((and gnus-visible-headers + (listp gnus-visible-headers)) + (mapconcat 'identity gnus-visible-headers "\\|")))) + (inhibit-point-motion-hooks t) + beg) + ;; First we narrow to just the headers. + (article-narrow-to-head) + ;; Hide any "From " lines at the beginning of (mail) articles. + (while (looking-at "From ") + (forward-line 1)) + (unless (bobp) + (if delete + (delete-region (point-min) (point)) + (gnus-article-hide-text (point-min) (point) + (nconc (list 'article-type 'headers) + gnus-hidden-properties)))) + ;; Then treat the rest of the header lines. + ;; Then we use the two regular expressions + ;; `gnus-ignored-headers' and `gnus-visible-headers' to + ;; select which header lines is to remain visible in the + ;; article buffer. + (while (re-search-forward "^[^ \t]*:" nil t) + (beginning-of-line) + ;; Mark the rank of the header. + (put-text-property + (point) (1+ (point)) 'message-rank + (if (or (and visible (looking-at visible)) + (and ignored + (not (looking-at ignored)))) + (gnus-article-header-rank) + (+ 2 max))) + (forward-line 1)) + (message-sort-headers-1) + (when (setq beg (text-property-any + (point-min) (point-max) 'message-rank (+ 2 max))) + ;; We delete or make invisible the unwanted headers. + (push 'headers gnus-article-wash-types) + (if delete + (progn + (add-text-properties + (point-min) (+ 5 (point-min)) + '(article-type headers dummy-invisible t)) + (delete-region beg (point-max))) + (gnus-article-hide-text-type beg (point-max) 'headers)))))))) + ) (defun article-hide-boring-headers (&optional arg) "Toggle hiding of headers that aren't very interesting. @@ -992,14 +1187,14 @@ always hide." (list gnus-boring-article-headers) (inhibit-point-motion-hooks t) elem) - (nnheader-narrow-to-headers) + (article-narrow-to-head) (while list (setq elem (pop list)) (goto-char (point-min)) (cond ;; Hide empty headers. ((eq elem 'empty) - (while (re-search-forward "^[^:]+:[ \t]*\n[^ \t]" nil t) + (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t) (forward-line -1) (gnus-article-hide-text-type (progn (beginning-of-line) (point)) @@ -1028,8 +1223,8 @@ always hide." from reply-to (ignore-errors (equal - (nth 1 (mail-extract-address-components from)) - (nth 1 (mail-extract-address-components reply-to))))) + (nth 1 (funcall gnus-extract-address-components from)) + (nth 1 (funcall gnus-extract-address-components reply-to))))) (gnus-article-hide-header "reply-to")))) ((eq elem 'date) (let ((date (message-fetch-field "date"))) @@ -1038,11 +1233,15 @@ always hide." 4)) (gnus-article-hide-header "date")))) ((eq elem 'long-to) - (let ((to (message-fetch-field "to"))) + (let ((to (message-fetch-field "to")) + (cc (message-fetch-field "cc"))) (when (> (length to) 1024) - (gnus-article-hide-header "to")))) + (gnus-article-hide-header "to")) + (when (> (length cc) 1024) + (gnus-article-hide-header "cc")))) ((eq elem 'many-to) - (let ((to-count 0)) + (let ((to-count 0) + (cc-count 0)) (goto-char (point-min)) (while (re-search-forward "^to:" nil t) (setq to-count (1+ to-count))) @@ -1054,7 +1253,19 @@ always hide." (forward-line -1) (narrow-to-region (point) (point-max)) (gnus-article-hide-header "to")) - (setq to-count (1- to-count))))))))))))) + (setq to-count (1- to-count)))) + (goto-char (point-min)) + (while (re-search-forward "^cc:" nil t) + (setq cc-count (1+ cc-count))) + (when (> cc-count 1) + (while (> cc-count 0) + (goto-char (point-min)) + (save-restriction + (re-search-forward "^cc:" nil nil cc-count) + (forward-line -1) + (narrow-to-region (point) (point-max)) + (gnus-article-hide-header "cc")) + (setq cc-count (1- cc-count))))))))))))) (defun gnus-article-hide-header (header) (save-excursion @@ -1069,6 +1280,87 @@ always hide." (point-max))) 'boring-headers)))) +(defun article-toggle-headers (&optional arg) + "Toggle hiding of headers. If given a negative prefix, always show; +if given a positive prefix, always hide." + (interactive (gnus-article-hidden-arg)) + (let ((force (when (numberp arg) + (cond ((> arg 0) 'always-hide) + ((< arg 0) 'always-show)))) + (window (get-buffer-window gnus-article-buffer)) + (header-end (point-min)) + header-start field-end field-start + (inhibit-point-motion-hooks t) + (inhibit-read-only t) + buffer-read-only) + (save-restriction + (widen) + (while (and (setq header-start + (text-property-any header-end (point-max) + 'article-treated-header t)) + (setq header-end + (text-property-not-all header-start (point-max) + 'article-treated-header t))) + (setq field-end header-start) + (cond + (;; Hide exposed invisible fields. + (and (not (eq 'always-show force)) + (setq field-start + (text-property-any field-end header-end + 'exposed-invisible-field t))) + (while (and field-start + (setq field-end (text-property-not-all + field-start header-end + 'exposed-invisible-field t))) + (add-text-properties field-start field-end gnus-hidden-properties) + (setq field-start (text-property-any field-end header-end + 'exposed-invisible-field t))) + (put-text-property header-start header-end + 'exposed-invisible-field nil)) + (;; Expose invisible fields. + (and (not (eq 'always-hide force)) + (setq field-start + (text-property-any field-end header-end 'invisible t))) + (while (and field-start + (setq field-end (text-property-not-all + field-start header-end + 'invisible t))) + ;; If the invisible text is not terminated with newline, we + ;; won't expose it. Because it may be created by x-face-mule. + ;; BTW, XEmacs sometimes fail in putting a invisible text + ;; property with `gnus-article-hide-text' (really?). In that + ;; case, the invisible text might be started from the middle of + ;; a line so we will expose the sort of thing. + (when (or (not (or (eq header-start field-start) + (eq ?\n (char-before field-start)))) + (eq ?\n (char-before field-end))) + (remove-text-properties field-start field-end + gnus-hidden-properties) + (put-text-property field-start field-end + 'exposed-invisible-field t)) + (setq field-start (text-property-any field-end header-end + 'invisible t)))) + (;; Hide fields. + (not (eq 'always-show force)) + (narrow-to-region header-start header-end) + (article-hide-headers) + ;; Re-display X-Face image under XEmacs. + (when (and gnus-xemacs + (gnus-functionp gnus-article-x-face-command)) + (let ((func (cadr (assq 'gnus-treat-display-xface + gnus-treatment-function-alist))) + (condition 'head)) + (when (and (not gnus-inhibit-treatment) + func + (gnus-treat-predicate gnus-treat-display-xface)) + (funcall func) + (put-text-property header-start header-end 'read-only nil)))) + (widen)) + )) + (goto-char (point-min)) + (when window + (set-window-start window (point-min)))))) + (defvar gnus-article-normalized-header-length 40 "Length of normalized headers.") @@ -1079,7 +1371,7 @@ always hide." column) (save-excursion (save-restriction - (message-narrow-to-head) + (article-narrow-to-head) (while (not (eobp)) (cond ((< (setq column (- (gnus-point-at-eol) (point))) @@ -1144,23 +1436,29 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (let ((buffer-read-only nil)) (while (search-forward "\b" nil t) (let ((next (char-after)) - (previous (char-after (- (point) 2)))) + start end previous) + (backward-char 2) + (setq start (point) + previous (char-after)) + (forward-char 3) + (setq end (point)) + (backward-char) ;; We do the boldification/underlining by hiding the ;; overstrikes and putting the proper text property ;; on the letters. (cond ((eq next previous) - (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike) - (put-text-property (point) (1+ (point)) 'face 'bold)) + (gnus-article-hide-text-type start (point) 'overstrike) + (put-text-property (point) end 'face 'bold)) ((eq next ?_) (gnus-article-hide-text-type (1- (point)) (1+ (point)) 'overstrike) (put-text-property - (- (point) 2) (1- (point)) 'face 'underline)) + start (1- (point)) 'face 'underline)) ((eq previous ?_) - (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike) + (gnus-article-hide-text-type start (point) 'overstrike) (put-text-property - (point) (1+ (point)) 'face 'underline))))))))) + (point) end 'face 'underline))))))))) (defun article-fill-long-lines () "Fill lines that are wider than the window width." @@ -1169,7 +1467,6 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (let ((buffer-read-only nil) (width (window-width (get-buffer-window (current-buffer))))) (save-restriction - (widen) (article-goto-body) (let ((adaptive-fill-mode nil)) (while (not (eobp)) @@ -1181,13 +1478,24 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (widen)) (forward-line 1))))))) +(defun article-capitalize-sentences () + "Capitalize the first word in each sentence." + (interactive) + (save-excursion + (let ((buffer-read-only nil) + (paragraph-start "^[\n\^L]")) + (article-goto-body) + (while (not (eobp)) + (capitalize-word 1) + (forward-sentence))))) + (defun article-remove-cr () - "Translate CRLF pairs into LF, and then CR into LF.." + "Remove trailing CRs and then translate remaining CRs into LFs." (interactive) (save-excursion (let ((buffer-read-only nil)) (goto-char (point-min)) - (while (search-forward "\r$" nil t) + (while (re-search-forward "\r+$" nil t) (replace-match "" t t)) (goto-char (point-min)) (while (search-forward "\r" nil t) @@ -1221,7 +1529,8 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (case-fold-search t) from last) (save-restriction - (nnheader-narrow-to-headers) + (article-narrow-to-head) + (goto-char (point-min)) (setq from (message-fetch-field "from")) (goto-char (point-min)) (while (and gnus-article-x-face-command @@ -1233,7 +1542,7 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (not (string-match gnus-article-x-face-too-ugly from)))) ;; Has to be present. - (re-search-forward "^X-Face: " nil t)) + (re-search-forward "^X-Face:[ \t]*" nil t)) ;; This used to try to do multiple faces (`while' instead of ;; `when' above), but (a) sending multiple EOFs to xv doesn't ;; work (b) it can crash some versions of Emacs (c) are @@ -1267,50 +1576,60 @@ MAP is an alist where the elements are on the form (\"from\" \"to\")." (set-buffer gnus-article-buffer) (let ((inhibit-point-motion-hooks t) buffer-read-only - (mail-parse-charset gnus-newsgroup-charset)) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) (mail-decode-encoded-word-region (point-min) (point-max))))) (defun article-decode-charset (&optional prompt) "Decode charset-encoded text in the article. If PROMPT (the prefix), prompt for a coding system to use." (interactive "P") + (let ((inhibit-point-motion-hooks t) (case-fold-search t) + buffer-read-only + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (condition-case nil + (set-buffer gnus-summary-buffer) + (error)) + gnus-newsgroup-ignored-charsets)) + ct cte ctl charset) (save-excursion (save-restriction - (message-narrow-to-head) - (let* ((inhibit-point-motion-hooks t) - (case-fold-search t) - (ct (message-fetch-field "Content-Type" t)) - (cte (message-fetch-field "Content-Transfer-Encoding" t)) - (ctl (and ct (condition-case () - (mail-header-parse-content-type ct) - (error nil)))) - (charset (cond - (prompt - (mm-read-coding-system "Charset to decode: ")) - (ctl - (mail-content-type-get ctl 'charset)))) - (mail-parse-charset gnus-newsgroup-charset) - buffer-read-only) - (goto-char (point-max)) - (widen) - (forward-line 1) - (narrow-to-region (point) (point-max)) - (when (and (or (not ctl) - (equal (car ctl) "text/plain")) - (not (mm-uu-test))) - (mm-decode-body - charset (and cte (intern (downcase - (gnus-strip-whitespace cte)))) - (car ctl))))))) + (article-narrow-to-head) + (setq ct (message-fetch-field "Content-Type" t) + cte (message-fetch-field "Content-Transfer-Encoding" t) + ctl (and ct (ignore-errors + (mail-header-parse-content-type ct))) + charset (cond + (prompt + (mm-read-coding-system "Charset to decode: ")) + (ctl + (mail-content-type-get ctl 'charset)))) + (when cte + (setq cte (mail-header-strip cte))) + (if (and ctl (not (string-match "/" (car ctl)))) + (setq ctl nil)) + (goto-char (point-max))) + (forward-line 1) + (save-restriction + (narrow-to-region (point) (point-max)) + (when (and (or (not ctl) + (equal (car ctl) "text/plain"))) + (mm-decode-body + charset (and cte (intern (downcase + (gnus-strip-whitespace cte)))) + (car ctl))))))) (defun article-decode-encoded-words () "Remove encoded-word encoding from headers." - (let ((inhibit-point-motion-hooks t) - (mail-parse-charset gnus-newsgroup-charset) - buffer-read-only) - (save-restriction - (message-narrow-to-head) - (funcall gnus-decode-header-function (point-min) (point-max))))) + (let (buffer-read-only) + (let ((charset (save-excursion + (set-buffer gnus-summary-buffer) + default-mime-charset))) + (mime-decode-header-in-buffer charset) + ))) (defun article-de-quoted-unreadable (&optional force) "Translate a quoted-printable-encoded article. @@ -1324,29 +1643,52 @@ or not." (when (or force (and type (string-match "quoted-printable" (downcase type)))) (article-goto-body) - (save-restriction - (narrow-to-region (point) (point-max)) - (quoted-printable-decode-region (point-min) (point-max)) - (when charset - (mm-decode-body charset))))))) + (quoted-printable-decode-region (point) (point-max) charset))))) -(defun article-hide-pgp (&optional arg) - "Toggle hiding of any PGP headers and signatures in the current article. -If given a negative prefix, always show; if given a positive prefix, -always hide." - (interactive (gnus-article-hidden-arg)) - (unless (gnus-article-check-hidden-text 'pgp arg) - (save-excursion +(eval-when-compile + (require 'rfc1843)) + +(defun article-decode-HZ () + "Translate a HZ-encoded article." + (interactive) + (require 'rfc1843) + (save-excursion + (let ((buffer-read-only nil)) + (rfc1843-decode-region (point-min) (point-max))))) + +(defun article-hide-list-identifiers () + "Remove list identifies from the Subject header. +The `gnus-list-identifiers' variable specifies what to do." + (interactive) + (save-excursion + (save-restriction + (let ((inhibit-point-motion-hooks t) + buffer-read-only) + (article-narrow-to-head) + (let ((regexp (if (stringp gnus-list-identifiers) gnus-list-identifiers + (mapconcat 'identity gnus-list-identifiers " *\\|")))) + (when regexp + (goto-char (point-min)) + (when (re-search-forward + (concat "^Subject: +\\(Re: +\\)?\\(" regexp " *\\)") + nil t) + (delete-region (match-beginning 2) (match-end 0))))))))) + +(defun article-hide-pgp () + "Remove any PGP headers and signatures in the current article." + (interactive) + (save-excursion + (save-restriction (let ((inhibit-point-motion-hooks t) buffer-read-only beg end) - (widen) - (goto-char (point-min)) + (article-goto-body) ;; Hide the "header". - (when (search-forward "\n-----BEGIN PGP SIGNED MESSAGE-----\n" nil t) - (delete-region (1+ (match-beginning 0)) (match-end 0)) - ;; PGP 5 and GNU PG add a `Hash: <>' comment, hide that too - (when (looking-at "Hash:.*$") - (delete-region (point) (1+ (gnus-point-at-eol)))) + (when (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\n" nil t) + (push 'pgp gnus-article-wash-types) + (delete-region (match-beginning 0) (match-end 0)) + ;; Remove armor headers (rfc2440 6.2) + (delete-region (point) (or (re-search-forward "^[ \t]*\n" nil t) + (point))) (setq beg (point)) ;; Hide the actual signature. (and (search-forward "\n-----BEGIN PGP SIGNATURE-----\n" nil t) @@ -1376,38 +1718,99 @@ always hide." (unless (gnus-article-check-hidden-text 'pem arg) (save-excursion (let (buffer-read-only end) - (widen) (goto-char (point-min)) - ;; hide the horrendously ugly "header". - (and (search-forward "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n" - nil - t) - (setq end (1+ (match-beginning 0))) - (gnus-article-hide-text-type - end - (if (search-forward "\n\n" nil t) - (match-end 0) - (point-max)) - 'pem)) - ;; hide the trailer as well - (and (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n" - nil - t) - (gnus-article-hide-text-type - (match-beginning 0) (match-end 0) 'pem)))))) + ;; Hide the horrendously ugly "header". + (when (and (search-forward + "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n" + nil t) + (setq end (1+ (match-beginning 0)))) + (push 'pem gnus-article-wash-types) + (gnus-article-hide-text-type + end + (if (search-forward "\n\n" nil t) + (match-end 0) + (point-max)) + 'pem) + ;; Hide the trailer as well + (when (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n" + nil t) + (gnus-article-hide-text-type + (match-beginning 0) (match-end 0) 'pem))))))) + +(defun article-strip-banner () + "Strip the banner specified by the `banner' group parameter." + (interactive) + (save-excursion + (save-restriction + (let ((inhibit-point-motion-hooks t) + (banner (gnus-group-get-parameter gnus-newsgroup-name 'banner)) + (gnus-signature-limit nil) + buffer-read-only beg end) + (when banner + (article-goto-body) + (cond + ((eq banner 'signature) + (when (gnus-article-narrow-to-signature) + (widen) + (forward-line -1) + (delete-region (point) (point-max)))) + ((stringp banner) + (while (re-search-forward banner nil t) + (delete-region (match-beginning 0) (match-end 0)))))))))) + +(defun article-babel () + "Translate article using an online translation service." + (interactive) + (require 'babel) + (save-excursion + (set-buffer gnus-article-buffer) + (when (article-goto-body) + (let* ((buffer-read-only nil) + (start (point)) + (end (point-max)) + (orig (buffer-substring start end)) + (trans (babel-as-string orig))) + (save-restriction + (narrow-to-region start end) + (delete-region start end) + (insert trans)))))) (defun article-hide-signature (&optional arg) "Hide the signature in the current article. If given a negative prefix, always show; if given a positive prefix, always hide." (interactive (gnus-article-hidden-arg)) - (unless (gnus-article-check-hidden-text 'signature arg) - (save-excursion - (save-restriction - (let ((buffer-read-only nil)) - (when (gnus-article-narrow-to-signature) - (gnus-article-hide-text-type - (point-min) (point-max) 'signature))))))) + (save-excursion + (save-restriction + (if (interactive-p) + (progn + (widen) + (article-goto-body)) + (goto-char (point-min))) + (unless (gnus-article-check-hidden-text 'signature arg) + (let ((buffer-read-only nil) + (button (point))) + (while (setq button (text-property-any button (point-max) + 'gnus-callback + 'gnus-signature-toggle)) + (setq button (text-property-not-all button (point-max) + 'gnus-callback + 'gnus-signature-toggle)) + (when (and button (not (eobp))) + (gnus-article-hide-text-type + (1+ button) + (next-single-property-change (1+ button) 'mime-view-entity + nil (point-max)) + 'signature)))))))) + +(defun article-strip-headers-in-body () + "Strip offensive headers from bodies." + (interactive) + (save-excursion + (article-goto-body) + (let ((case-fold-search t)) + (when (looking-at "x-no-archive:") + (gnus-delete-line))))) (defun article-strip-leading-blank-lines () "Remove all blank lines from the beginning of the article." @@ -1420,10 +1823,22 @@ always hide." (looking-at "[ \t]*$")) (gnus-delete-line)))))) +(defun article-narrow-to-head () + "Narrow the buffer to the head of the message. +Point is left at the beginning of the narrowed-to region." + (narrow-to-region + (goto-char (point-min)) + (if (search-forward "\n\n" nil 1) + (1- (point)) + (point-max))) + (goto-char (point-min))) + (defun article-goto-body () "Place point at the start of the body." (goto-char (point-min)) (cond + ;; This variable is only bound when dealing with separate + ;; MIME body parts. (article-goto-body-goes-to-point-min-p t) ((search-forward "\n\n" nil t) @@ -1490,7 +1905,6 @@ always hide." (defun gnus-article-narrow-to-signature () "Narrow to the signature; return t if a signature is found, else nil." - (widen) (let ((inhibit-point-motion-hooks t)) (when (gnus-article-search-signature) (forward-line 1) @@ -1531,38 +1945,6 @@ Put point at the beginning of the signature separator." (goto-char cur) nil))) -(eval-and-compile - (autoload 'w3-display "w3-parse") - (autoload 'w3-do-setup "w3" "" t) - (autoload 'w3-region "w3-display" "" t)) - -(defun gnus-article-treat-html () - "Render HTML." - (interactive) - (let ((cbuf (current-buffer))) - (set-buffer gnus-article-buffer) - (let (buf buffer-read-only b e) - (w3-do-setup) - (goto-char (point-min)) - (narrow-to-region - (if (search-forward "\n\n" nil t) - (setq b (point)) - (point-max)) - (setq e (point-max))) - (with-temp-buffer - (insert-buffer-substring gnus-article-buffer b e) - (require 'url) - (save-window-excursion - (w3-region (point-min) (point-max)) - (setq buf (buffer-substring-no-properties (point-min) (point-max))))) - (when buf - (delete-region (point-min) (point-max)) - (insert buf)) - (widen) - (goto-char (point-min)) - (set-window-start (get-buffer-window (current-buffer)) (point-min)) - (set-buffer cbuf)))) - (defun gnus-article-hidden-arg () "Return the current prefix arg as a number, or 0 if no prefix." (list (if current-prefix-arg @@ -1575,7 +1957,6 @@ Arg can be nil or a number. Nil and positive means hide, negative means show, 0 means toggle." (save-excursion (save-restriction - (widen) (let ((hide (gnus-article-hidden-text-p type))) (cond ((or (null arg) @@ -1592,7 +1973,8 @@ means show, 0 means toggle." "Say whether the current buffer contains hidden text of type TYPE." (let ((pos (text-property-any (point-min) (point-max) 'article-type type))) (while (and pos - (not (get-text-property pos 'invisible))) + (not (get-text-property pos 'invisible)) + (not (get-text-property pos 'dummy-invisible))) (setq pos (text-property-any (1+ pos) (point-max) 'article-type type))) (if pos @@ -1630,46 +2012,80 @@ If HIDE, hide the text instead." (defun article-date-ut (&optional type highlight header) "Convert DATE date to universal time in the current article. If TYPE is `local', convert to local time; if it is `lapsed', output -how much time has lapsed since DATE." +how much time has lapsed since DATE. For `lapsed', the value of +`gnus-article-date-lapsed-new-header' says whether the \"X-Sent:\" header +should replace the \"Date:\" one, or should be added below it." (interactive (list 'ut t)) (let* ((header (or header - (mail-header-date (save-excursion - (set-buffer gnus-summary-buffer) - gnus-current-headers)) + (and (eq 1 (point-min)) + (mail-header-date (save-excursion + (set-buffer gnus-summary-buffer) + gnus-current-headers))) (message-fetch-field "date") "")) (date (if (vectorp header) (mail-header-date header) header)) - (date-regexp "^Date:[ \t]\\|^X-Sent:[ \t]") (inhibit-point-motion-hooks t) - bface eface newline) + bface eface date-pos) (when (and date (not (string= date ""))) (save-excursion (save-restriction - (nnheader-narrow-to-headers) + (article-narrow-to-head) + (when (or (and (eq type 'lapsed) + gnus-article-date-lapsed-new-header + ;; Attempt to get the face of X-Sent first. + (re-search-forward "^X-Sent:[ \t]" nil t)) + (re-search-forward "^Date:[ \t]" nil t) + ;; If Date is missing, try again for X-Sent. + (re-search-forward "^X-Sent:[ \t]" nil t)) + (setq bface (get-text-property (gnus-point-at-bol) 'face) + date (or (get-text-property (gnus-point-at-bol) + 'original-date) + date) + eface (get-text-property (1- (gnus-point-at-eol)) + 'face))) (let ((buffer-read-only nil)) + ;; Delete any old X-Sent headers. + (when (setq date-pos + (text-property-any (point-min) (point-max) + 'article-date-lapsed t)) + (goto-char (setq date-pos (set-marker (make-marker) date-pos))) + (delete-region (match-beginning 0) + (progn (forward-line 1) (point)))) + (goto-char (point-min)) ;; Delete any old Date headers. - (if (re-search-forward date-regexp nil t) + (while (re-search-forward "^Date:[ \t]" nil t) + (unless date-pos + (setq date-pos (match-beginning 0))) + (unless (and (eq type 'lapsed) + gnus-article-date-lapsed-new-header) + (delete-region (match-beginning 0) + (progn (message-next-header) (point))))) + (if date-pos (progn - (setq bface (get-text-property (gnus-point-at-bol) 'face) - eface (get-text-property (1- (gnus-point-at-eol)) - 'face)) - (delete-region (progn (beginning-of-line) (point)) - (progn (end-of-line) (point))) - (beginning-of-line)) - (goto-char (point-max)) - (setq newline t)) + (goto-char date-pos) + (unless (bolp) + ;; Possibly, Date has been deleted. + (insert "\n")) + (when (and (eq type 'lapsed) + gnus-article-date-lapsed-new-header + (looking-at "Date:")) + (forward-line 1))) + (goto-char (point-min))) (insert (article-make-date-line date type)) + (when (eq type 'lapsed) + (put-text-property (gnus-point-at-bol) (point) + 'article-date-lapsed t)) + (insert "\n") + (forward-line -1) ;; Do highlighting. - (beginning-of-line) (when (looking-at "\\([^:]+\\): *\\(.*\\)$") (put-text-property (match-beginning 1) (1+ (match-end 1)) + 'original-date date) + (put-text-property (match-beginning 1) (1+ (match-end 1)) 'face bface) (put-text-property (match-beginning 2) (match-end 2) - 'face eface)) - (when newline - (end-of-line) - (insert "\n")))))))) + 'face eface)))))))) (defun article-make-date-line (date type) "Return a DATE line of TYPE." @@ -1682,17 +2098,18 @@ how much time has lapsed since DATE." ;; functions since they aren't particularly resistant to ;; buggy dates. ((eq type 'local) - (let ((tz (car (current-time-zone)))) - (format "Date: %s %s%04d" (current-time-string time) - (if (> tz 0) "+" "-") (abs (/ tz 36))))) + (let ((tz (car (current-time-zone time)))) + (format "Date: %s %s%02d%02d" (current-time-string time) + (if (> tz 0) "+" "-") (/ (abs tz) 3600) + (/ (% (abs tz) 3600) 60)))) ;; Convert to Universal Time. ((eq type 'ut) (concat "Date: " (current-time-string (let* ((e (parse-time-string date)) - (tm (apply 'encode-time e)) - (ms (car tm)) - (ls (- (cadr tm) (car (current-time-zone))))) + (tm (apply 'encode-time e)) + (ms (car tm)) + (ls (- (cadr tm) (car (current-time-zone time))))) (cond ((< ls 0) (list (1- ms) (+ ls 65536))) ((> ls 65535) (list (1+ ms) (- ls 65536))) (t (list ms ls))))) @@ -1711,9 +2128,13 @@ how much time has lapsed since DATE." (format-time-string gnus-article-time-format time)))) ;; ISO 8601. ((eq type 'iso8601) - (concat - "Date: " - (format-time-string "%Y%m%dT%H%M%S" time))) + (let ((tz (car (current-time-zone time)))) + (concat + "Date: " + (format-time-string "%Y%m%dT%H%M%S" time) + (format "%s%02d%02d" + (if (> tz 0) "+" "-") (/ (abs tz) 3600) + (/ (% (abs tz) 3600) 60))))) ;; Do an X-Sent lapsed format. ((eq type 'lapsed) ;; If the date is seriously mangled, the timezone functions are @@ -1824,15 +2245,33 @@ This format is defined by the `gnus-article-time-format' variable." "Show all hidden text in the article buffer." (interactive) (save-excursion + (widen) (let ((buffer-read-only nil)) - (gnus-article-unhide-text (point-min) (point-max))))) + (gnus-article-unhide-text (point-min) (point-max)) + (gnus-remove-text-with-property 'gnus-prev) + (gnus-remove-text-with-property 'gnus-next)))) + +(defun article-show-all-headers () + "Show all hidden headers in the article buffer." + (interactive) + (save-excursion + (save-restriction + (widen) + (article-narrow-to-head) + (let ((buffer-read-only nil)) + (gnus-article-unhide-text (point-min) (point-max)))))) (defun article-emphasize (&optional arg) "Emphasize text according to `gnus-emphasis-alist'." (interactive (gnus-article-hidden-arg)) (unless (gnus-article-check-hidden-text 'emphasis arg) (save-excursion - (let ((alist gnus-emphasis-alist) + (let ((alist (or + (condition-case nil + (with-current-buffer gnus-summary-buffer + gnus-article-emphasis-alist) + (error)) + gnus-emphasis-alist)) (buffer-read-only nil) (props (append '(article-type emphasis) gnus-hidden-properties)) @@ -1847,6 +2286,7 @@ This format is defined by the `gnus-article-time-format' variable." face (nth 3 elem)) (while (re-search-forward regexp nil t) (when (and (match-beginning visible) (match-beginning invisible)) + (push 'emphasis gnus-article-wash-types) (gnus-article-hide-text (match-beginning invisible) (match-end invisible) props) (gnus-article-unhide-text-type @@ -1855,6 +2295,26 @@ This format is defined by the `gnus-article-time-format' variable." (match-beginning visible) (match-end visible) 'face face) (goto-char (match-end invisible))))))))) +(defun gnus-article-setup-highlight-words (&optional highlight-words) + "Setup newsgroup emphasis alist." + (unless gnus-article-emphasis-alist + (let ((name (and gnus-newsgroup-name + (gnus-group-real-name gnus-newsgroup-name)))) + (make-local-variable 'gnus-article-emphasis-alist) + (setq gnus-article-emphasis-alist + (nconc + (let ((alist gnus-group-highlight-words-alist) elem highlight) + (while (setq elem (pop alist)) + (when (and name (string-match (car elem) name)) + (setq alist nil + highlight (copy-sequence (cdr elem))))) + highlight) + (copy-sequence highlight-words) + (if gnus-newsgroup-name + (copy-sequence (gnus-group-find-parameter + gnus-newsgroup-name 'highlight-words t))) + gnus-emphasis-alist))))) + (defvar gnus-summary-article-menu) (defvar gnus-summary-post-menu) @@ -2165,15 +2625,21 @@ If variable `gnus-use-long-file-name' is non-nil, it is (apply ',afunc args)))))))) '(article-hide-headers article-hide-boring-headers + article-toggle-headers article-treat-overstrike article-fill-long-lines + article-capitalize-sentences article-remove-cr article-display-x-face article-de-quoted-unreadable - article-mime-decode-quoted-printable + article-decode-HZ + article-hide-list-identifiers article-hide-pgp + article-strip-banner + article-babel article-hide-pem article-hide-signature + article-strip-headers-in-body article-remove-trailing-blank-lines article-strip-leading-blank-lines article-strip-multiple-blank-lines @@ -2193,7 +2659,8 @@ If variable `gnus-use-long-file-name' is non-nil, it is article-emphasize article-treat-dumbquotes article-normalize-headers - (article-show-all . gnus-article-show-all-headers)))) + (article-show-all-headers . gnus-article-show-all-headers) + (article-show-all . gnus-article-show-all)))) ;;; ;;; Gnus article mode @@ -2201,8 +2668,6 @@ If variable `gnus-use-long-file-name' is non-nil, it is (put 'gnus-article-mode 'mode-class 'special) -(set-keymap-parent gnus-article-mode-map widget-keymap) - (gnus-define-keys gnus-article-mode-map " " gnus-article-goto-next-page "\177" gnus-article-goto-prev-page @@ -2213,7 +2678,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is "s" gnus-article-show-summary "\C-c\C-m" gnus-article-mail "?" gnus-article-describe-briefly - "e" gnus-article-edit + "e" gnus-summary-edit-article "<" beginning-of-buffer ">" end-of-buffer "\C-c\C-i" gnus-info-find-node @@ -2225,8 +2690,28 @@ If variable `gnus-use-long-file-name' is non-nil, it is "\M-^" gnus-article-read-summary-keys "\M-g" gnus-article-read-summary-keys) -(substitute-key-definition - 'undefined 'gnus-article-read-summary-keys gnus-article-mode-map) +;; Define almost undefined keys to `gnus-article-read-summary-keys'. +(mapcar + (lambda (key) + (unless (lookup-key gnus-article-mode-map key) + (define-key gnus-article-mode-map key + 'gnus-article-read-summary-keys))) + (delq nil + (append + (mapcar + (lambda (elt) + (let ((key (car elt))) + (and (> (length key) 0) + (not (eq 'menu-bar (aref key 0))) + (symbolp (lookup-key gnus-summary-mode-map key)) + key))) + (accessible-keymaps gnus-summary-mode-map)) + (let ((c 127) + keys) + (while (>= c 32) + (push (char-to-string c) keys) + (decf c)) + keys)))) (defun gnus-article-make-menu-bar () (gnus-turn-off-edit-menu 'article) @@ -2244,12 +2729,12 @@ If variable `gnus-use-long-file-name' is non-nil, it is (easy-menu-define gnus-article-treatment-menu gnus-article-mode-map "" '("Treatment" - ["Hide headers" gnus-article-hide-headers t] + ["Hide headers" gnus-article-toggle-headers t] ["Hide signature" gnus-article-hide-signature t] ["Hide citation" gnus-article-hide-citation t] ["Treat overstrike" gnus-article-treat-overstrike t] ["Remove carriage return" gnus-article-remove-cr t] - ["Remove quoted-unreadable" gnus-article-de-quoted-unreadable t])) + ["Decode HZ" gnus-article-decode-HZ t])) ;; Note "Commands" menu is defined in gnus-sum.el for consistency @@ -2281,6 +2766,8 @@ commands: (setq mode-name "Article") (setq major-mode 'gnus-article-mode) (make-local-variable 'minor-mode-alist) + (unless (assq 'gnus-show-mime minor-mode-alist) + (push (list 'gnus-show-mime " MIME") minor-mode-alist)) (use-local-map gnus-article-mode-map) (gnus-update-format-specifications nil 'article-mode) (set (make-local-variable 'page-delimiter) gnus-page-delimiter) @@ -2290,11 +2777,11 @@ commands: (make-local-variable 'gnus-article-mime-handles) (make-local-variable 'gnus-article-decoded-p) (make-local-variable 'gnus-article-mime-handle-alist) + (make-local-variable 'gnus-article-wash-types) (gnus-set-default-directory) (buffer-disable-undo) (setq buffer-read-only t) (set-syntax-table gnus-article-mode-syntax-table) - (mm-enable-multibyte) (gnus-run-hooks 'gnus-article-mode-hook)) (defun gnus-article-setup-buffer () @@ -2315,10 +2802,10 @@ commands: (setq gnus-article-buffer name) (setq gnus-original-article-buffer original) (gnus-set-global-variables))) + (gnus-article-setup-highlight-words) ;; Init original article buffer. (save-excursion (set-buffer (gnus-get-buffer-create gnus-original-article-buffer)) - (mm-enable-multibyte) (setq major-mode 'gnus-original-article-mode) (make-local-variable 'gnus-original-article)) (if (get-buffer name) @@ -2351,6 +2838,71 @@ commands: (forward-line line) (point))))) +;;; @@ article filters +;;; + +(defun gnus-article-display-mime-message () + "Article display method for MIME message." + ;; called from `gnus-original-article-buffer'. + (let (charset all-headers) + (with-current-buffer gnus-summary-buffer + (setq charset default-mime-charset + all-headers gnus-have-all-headers)) + (make-local-variable 'default-mime-charset) + (setq default-mime-charset charset) + (with-current-buffer (get-buffer-create gnus-article-buffer) + (make-local-variable 'default-mime-charset) + (setq default-mime-charset charset)) + (mime-display-message mime-message-structure + gnus-article-buffer nil gnus-article-mode-map) + (when all-headers + (gnus-article-hide-headers nil -1)) + ) + ;; `mime-display-message' changes current buffer to `gnus-article-buffer'. + (make-local-variable 'mime-button-mother-dispatcher) + (setq mime-button-mother-dispatcher + (function gnus-article-push-button)) + (run-hooks 'gnus-mime-article-prepare-hook)) + +(defun gnus-article-display-traditional-message () + "Article display method for traditional message." + (set-buffer gnus-article-buffer) + (let (buffer-read-only) + (erase-buffer) + (insert-buffer-substring gnus-original-article-buffer))) + +(defun gnus-article-make-full-mail-header (&optional number charset) + "Create a new mail header structure in a raw article buffer." + (unless (and number charset) + (save-current-buffer + (set-buffer gnus-summary-buffer) + (unless number + (setq number (or (cdr gnus-article-current) 0))) + (unless charset + (setq charset (or default-mime-charset 'x-ctext))))) + (goto-char (point-min)) + (let ((header-end (if (search-forward "\n\n" nil t) + (1- (point)) + (goto-char (point-max)))) + (chars (- (point-max) (point))) + (lines (count-lines (point) (point-max))) + (default-mime-charset charset) + xref) + (narrow-to-region (point-min) header-end) + (setq xref (std11-fetch-field "xref")) + (prog1 + (make-full-mail-header + number + (std11-fetch-field "subject") + (std11-fetch-field "from") + (std11-fetch-field "date") + (std11-fetch-field "message-id") + (std11-fetch-field "references") + chars + lines + (when xref (concat "Xref: " xref))) + (widen)))) + (defun gnus-article-prepare (article &optional all-headers header) "Prepare ARTICLE in article mode buffer. ARTICLE should either be an article number or a Message-ID. @@ -2368,7 +2920,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." result) (save-excursion (gnus-article-setup-buffer) - (set-buffer gnus-article-buffer) + (set-buffer gnus-original-article-buffer) ;; Deactivate active regions. (when (and (boundp 'transient-mark-mode) transient-mark-mode) @@ -2390,8 +2942,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (message "Message marked for downloading")) (gnus-summary-mark-article article gnus-canceled-mark) (unless (memq article gnus-newsgroup-sparse) - (gnus-error 1 - "No such article (may have expired or been canceled)"))))) + (gnus-error 1 "No such article (may have expired or been canceled)"))))) (if (or (eq result 'pseudo) (eq result 'nneething)) (progn @@ -2439,8 +2990,6 @@ If ALL-HEADERS is non-nil, no headers are hidden." (when (gnus-visual-p 'article-highlight 'highlight) (gnus-run-hooks 'gnus-visual-mark-article-hook)) ;; Set the global newsgroup variables here. - ;; Suggested by Jim Sisolak - ;; . (gnus-set-global-variables) (setq gnus-have-all-headers (or all-headers gnus-show-all-headers)))) @@ -2456,25 +3005,130 @@ If ALL-HEADERS is non-nil, no headers are hidden." (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist)) (gnus-set-mode-line 'article)) - (gnus-configure-windows 'article) (article-goto-body) (set-window-point (get-buffer-window (current-buffer)) (point)) + (gnus-configure-windows 'article) t)))))) +(defun gnus-article-prepare-mime-display (&optional number) + (goto-char (point-min)) + (when (re-search-forward "^[^\t ]+:" nil t) + (goto-char (match-beginning 0))) + (let ((entity (if (eq 1 (point-min)) + (get-text-property 1 'mime-view-entity) + (get-text-property (point) 'mime-view-entity))) + last-entity child-entity next type) + (setq child-entity (mime-entity-children entity)) + (if child-entity + (setq last-entity (nth (1- (length child-entity)) + child-entity)) + (setq last-entity entity)) + (save-restriction + (narrow-to-region (point) + (if (search-forward "\n\n" nil t) + (point) + (point-max))) + (gnus-treat-article 'head) + (put-text-property (point-min) (point-max) 'article-treated-header t) + (goto-char (point-max))) + (while (and (not (eobp)) entity) + (setq next (set-marker + (make-marker) + (next-single-property-change (point) 'mime-view-entity + nil (point-max)))) + (let ((types (mime-entity-content-type entity))) + (while (eq 'multipart (mime-content-type-primary-type types)) + (setq entity (car (mime-entity-children entity)) + types (mime-entity-content-type entity))) + (when types + (setq type (format "%s/%s" + (mime-content-type-primary-type types) + (mime-content-type-subtype types))))) + (if (string-equal type "message/rfc822") + (progn + (setq next (point)) + (let ((children (mime-entity-children entity)) + last-children) + (when children + (setq last-children (nth (1- (length children)) children)) + (while + (and + (not (eq last-children + (get-text-property next 'mime-view-entity))) + (setq next + (next-single-property-change next + 'mime-view-entity + nil (point-max))))))) + (setq next (next-single-property-change next 'mime-view-entity + nil (point-max))) + (save-restriction + (narrow-to-region (point) next) + (gnus-article-prepare-mime-display) + (goto-char (point-max))) + (setq entity (get-text-property (point) 'mime-view-entity))) + (save-restriction + (narrow-to-region (point) next) + ;; Kludge. We have to count true number, but for now, + ;; part number is here only to achieve `last'. + (gnus-treat-article nil 1 + (if (eq entity last-entity) + 1 2) + type) + (goto-char (point-max))) + (setq entity (get-text-property next 'mime-view-entity)))))) + ;;;###autoload (defun gnus-article-prepare-display () "Make the current buffer look like a nice article." - ;; Hooks for getting information from the article. - ;; This hook must be called before being narrowed. - (let ((gnus-article-buffer (current-buffer)) + (setq gnus-article-wash-types nil) + (gnus-run-hooks 'gnus-tmp-internal-hook) + (gnus-run-hooks 'gnus-article-prepare-hook) + ;; Display message. + (let (mime-display-header-hook mime-display-text/plain-hook) + (funcall (if gnus-show-mime + (progn + (setq mime-message-structure gnus-current-headers) + (mime-buffer-entity-set-buffer-internal + mime-message-structure + gnus-original-article-buffer) + (mime-entity-set-representation-type-internal + mime-message-structure 'mime-buffer-entity) + (luna-send mime-message-structure + 'initialize-instance + mime-message-structure) + gnus-article-display-method-for-mime) + gnus-article-display-method-for-traditional))) + ;; Associate this article with the current summary buffer. + (setq gnus-article-current-summary gnus-summary-buffer) + ;; Call the treatment functions. + (let ((inhibit-read-only t) buffer-read-only) - (unless (eq major-mode 'gnus-article-mode) - (gnus-article-mode)) - (setq buffer-read-only nil) - (gnus-run-hooks 'gnus-tmp-internal-hook) - (gnus-run-hooks 'gnus-article-prepare-hook) - (when gnus-display-mime-function - (funcall gnus-display-mime-function)))) + (save-restriction + (widen) + (if gnus-show-mime + (gnus-article-prepare-mime-display) + (narrow-to-region (goto-char (point-min)) + (if (search-forward "\n\n" nil t) + (point) + (point-max))) + (gnus-treat-article 'head) + (put-text-property (point-min) (point-max) 'article-treated-header t) + (goto-char (point-max)) + (widen) + (narrow-to-region (point) (point-max)) + (gnus-treat-article nil)) + (put-text-property (point-min) (point-max) 'read-only nil))) + ;; Perform the article display hooks. Incidentally, this hook is + ;; an obsolete variable by now. + (gnus-run-hooks 'gnus-article-display-hook)) + +(defun gnus-article-decode-article-as-default-mime-charset () + "Decode an article as `default-mime-charset'. It won't work if the +value of the variable `gnus-show-mime' is non-nil." + (unless gnus-show-mime + (decode-mime-charset-region (point-min) (point-max) + (with-current-buffer gnus-summary-buffer + default-mime-charset)))) ;;; ;;; Gnus MIME viewing functions @@ -2500,19 +3154,22 @@ If ALL-HEADERS is non-nil, no headers are hidden." (?e gnus-tmp-dots ?s))) (defvar gnus-mime-button-commands - '((gnus-article-press-button "\r" "Toggle Display") - (gnus-mime-view-part "v" "View Interactively...") - (gnus-mime-save-part "o" "Save...") - (gnus-mime-copy-part "c" "View As Text, In Other Buffer") - (gnus-mime-inline-part "i" "View As Text, In This Buffer") - (gnus-mime-internalize-part "E" "View Internally") - (gnus-mime-externalize-part "e" "View Externally") - (gnus-mime-pipe-part "|" "Pipe To Command..."))) + '((gnus-article-press-button "\r" "Toggle Display") + (gnus-mime-view-part "v" "View Interactively...") + (gnus-mime-view-part-as-type "t" "View As Type...") + (gnus-mime-save-part "o" "Save...") + (gnus-mime-copy-part "c" "View As Text, In Other Buffer") + (gnus-mime-inline-part "i" "View As Text, In This Buffer") + (gnus-mime-internalize-part "E" "View Internally") + (gnus-mime-externalize-part "e" "View Externally") + (gnus-mime-pipe-part "|" "Pipe To Command..."))) (defun gnus-article-mime-part-status () - (if gnus-article-mime-handle-alist-1 - (format " (%d parts)" (length gnus-article-mime-handle-alist-1)) - "")) + (with-current-buffer gnus-article-buffer + (let ((entity (get-text-property (point-min) 'mime-view-entity))) + (if (and entity (mime-entity-children entity)) + (format " (%d parts)" (length (mime-entity-children entity))) + "")))) (defvar gnus-mime-button-map nil) (unless gnus-mime-button-map @@ -2546,7 +3203,10 @@ If ALL-HEADERS is non-nil, no headers are hidden." (save-current-buffer (set-buffer gnus-article-buffer) (let ((handles (or handles gnus-article-mime-handles)) - (mail-parse-charset gnus-newsgroup-charset)) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) (if (stringp (car handles)) (gnus-mime-view-all-parts (cdr handles)) (mapcar 'mm-display-part handles))))) @@ -2566,12 +3226,40 @@ If ALL-HEADERS is non-nil, no headers are hidden." (mm-pipe-part data))) (defun gnus-mime-view-part () - "Interactively choose a view method for the MIME part under point." + "Interactively choose a viewing method for the MIME part under point." (interactive) (gnus-article-check-buffer) (let ((data (get-text-property (point) 'gnus-data))) (mm-interactively-view-part data))) +(defun gnus-mime-view-part-as-type-internal () + (gnus-article-check-buffer) + (let* ((name (mail-content-type-get + (mm-handle-type (get-text-property (point) 'gnus-data)) + 'name)) + (def-type (and name (mm-default-file-encoding name)))) + (and def-type (cons def-type 0)))) + +(defun gnus-mime-view-part-as-type (mime-type) + "Choose a MIME media type, and view the part as such." + (interactive + (list (completing-read + "View as MIME type: " + (mapcar (lambda (i) (list i i)) (mailcap-mime-types)) + nil nil + (gnus-mime-view-part-as-type-internal)))) + (gnus-article-check-buffer) + (let ((handle (get-text-property (point) 'gnus-data))) + (gnus-mm-display-part + (mm-make-handle (mm-handle-buffer handle) + (cons mime-type (cdr (mm-handle-type handle))) + (mm-handle-encoding handle) + (mm-handle-undisplayer handle) + (mm-handle-disposition handle) + (mm-handle-description handle) + (mm-handle-cache handle) + (mm-handle-id handle))))) + (defun gnus-mime-copy-part (&optional handle) "Put the the MIME part under point into a new buffer." (interactive) @@ -2595,23 +3283,19 @@ If ALL-HEADERS is non-nil, no headers are hidden." (setq buffer-file-name nil)) (goto-char (point-min)))) -(defun gnus-mime-inline-part (&optional charset) +(defun gnus-mime-inline-part (&optional handle) "Insert the MIME part under point into the current buffer." - (interactive "P") ; For compatibility reasons we are not using "z". + (interactive) (gnus-article-check-buffer) - (let* ((data (get-text-property (point) 'gnus-data)) + (let* ((handle (or handle (get-text-property (point) 'gnus-data))) contents (b (point)) buffer-read-only) - (if (mm-handle-undisplayer data) - (mm-remove-part data) - (setq contents (mm-get-part data)) + (if (mm-handle-undisplayer handle) + (mm-remove-part handle) + (setq contents (mm-get-part handle)) (forward-line 2) - (when charset - (unless (symbolp charset) - (setq charset (mm-read-coding-system "Charset: "))) - (setq contents (mm-decode-coding-string contents charset))) - (mm-insert-inline data contents) + (mm-insert-inline handle contents) (goto-char b)))) (defun gnus-mime-externalize-part (&optional handle) @@ -2620,20 +3304,27 @@ If ALL-HEADERS is non-nil, no headers are hidden." (gnus-article-check-buffer) (let* ((handle (or handle (get-text-property (point) 'gnus-data))) (mm-user-display-methods nil) - (mm-all-images-fit t) - (mail-parse-charset gnus-newsgroup-charset)) + (mm-inline-large-images nil) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) (if (mm-handle-undisplayer handle) (mm-remove-part handle) (mm-display-part handle)))) (defun gnus-mime-internalize-part (&optional handle) - "View the MIME part under point with an internal viewer." + "View the MIME part under point with an internal viewer. +In no internal viewer is available, use an external viewer." (interactive) (gnus-article-check-buffer) (let* ((handle (or handle (get-text-property (point) 'gnus-data))) - (mm-user-display-methods '((".*" . inline))) - (mm-all-images-fit t) - (mail-parse-charset gnus-newsgroup-charset)) + (mm-inlined-types '(".*")) + (mm-inline-large-images t) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) (if (mm-handle-undisplayer handle) (mm-remove-part handle) (mm-display-part handle)))) @@ -2643,6 +3334,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (set-buffer gnus-article-buffer) (when (> n (length gnus-article-mime-handle-alist)) (error "No such part")) + (gnus-article-goto-part n) (let ((handle (cdr (assq n gnus-article-mime-handle-alist)))) (funcall function handle)))) @@ -2657,25 +3349,52 @@ If ALL-HEADERS is non-nil, no headers are hidden." (gnus-article-part-wrapper n 'mm-save-part)) (defun gnus-article-interactively-view-part (n) - "Pipe MIME part N, which is the numerical prefix." + "View MIME part N interactively, which is the numerical prefix." (interactive "p") (gnus-article-part-wrapper n 'mm-interactively-view-part)) (defun gnus-article-copy-part (n) - "Pipe MIME part N, which is the numerical prefix." + "Copy MIME part N, which is the numerical prefix." (interactive "p") (gnus-article-part-wrapper n 'gnus-mime-copy-part)) (defun gnus-article-externalize-part (n) - "Pipe MIME part N, which is the numerical prefix." + "View MIME part N externally, which is the numerical prefix." (interactive "p") (gnus-article-part-wrapper n 'gnus-mime-externalize-part)) -(defun gnus-article-view-part (n) - "View MIME part N, which is the numerical prefix." +(defun gnus-article-inline-part (n) + "Inline MIME part N, which is the numerical prefix." (interactive "p") + (gnus-article-part-wrapper n 'gnus-mime-inline-part)) + +(defun gnus-article-mime-match-handle-first (condition) + (if condition + (let ((alist gnus-article-mime-handle-alist) ihandle n) + (while (setq ihandle (pop alist)) + (if (and (cond + ((functionp condition) + (funcall condition (cdr ihandle))) + ((eq condition 'undisplayed) + (not (or (mm-handle-undisplayer (cdr ihandle)) + (equal (mm-handle-media-type (cdr ihandle)) + "multipart/alternative")))) + ((eq condition 'undisplayed-alternative) + (not (mm-handle-undisplayer (cdr ihandle)))) + (t t)) + (gnus-article-goto-part (car ihandle)) + (or (not n) (< (car ihandle) n))) + (setq n (car ihandle)))) + (or n 1)) + 1)) + +(defun gnus-article-view-part (&optional n) + "View MIME part N, which is the numerical prefix." + (interactive "P") (save-current-buffer (set-buffer gnus-article-buffer) + (or (numberp n) (setq n (gnus-article-mime-match-handle-first + gnus-article-mime-match-handle-function))) (when (> n (length gnus-article-mime-handle-alist)) (error "No such part")) (let ((handle (cdr (assq n gnus-article-mime-handle-alist)))) @@ -2693,7 +3412,10 @@ If ALL-HEADERS is non-nil, no headers are hidden." (forward-line 1) (prog1 (let ((window (selected-window)) - (mail-parse-charset gnus-newsgroup-charset)) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) (save-excursion (unwind-protect (let ((win (get-buffer-window (current-buffer) t)) @@ -2708,10 +3430,15 @@ If ALL-HEADERS is non-nil, no headers are hidden." (save-restriction (narrow-to-region (point) (1+ (point))) (mm-display-part handle) + ;; We narrow to the part itself and + ;; then call the treatment functions. + (goto-char (point-min)) + (forward-line 1) + (narrow-to-region (point) (point-max)) (gnus-treat-article nil id (1- (length gnus-article-mime-handles)) - (car (mm-handle-type handle)))))) + (mm-handle-media-type handle))))) (select-window window)))) (goto-char point) (delete-region (gnus-point-at-bol) (progn (forward-line 1) (point))) @@ -2732,7 +3459,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (mail-content-type-get (mm-handle-disposition handle) 'filename) "")) - (gnus-tmp-type (car (mm-handle-type handle))) + (gnus-tmp-type (mm-handle-media-type handle)) (gnus-tmp-description (mail-decode-encoded-word-string (or (mm-handle-description handle) ""))) @@ -2782,6 +3509,8 @@ If ALL-HEADERS is non-nil, no headers are hidden." (goto-char (widget-get elems :from)) (gnus-article-press-button)) +(defvar gnus-displaying-mime nil) + (defun gnus-display-mime (&optional ihandles) "Display the MIME parts." (save-excursion @@ -2794,12 +3523,14 @@ If ALL-HEADERS is non-nil, no headers are hidden." ;; may change the point. So we set the window point. (set-window-point window point))) (let* ((handles (or ihandles (mm-dissect-buffer) (mm-uu-dissect))) - handle name type b e display) - (unless ihandles + buffer-read-only handle name type b e display) + (when (and (not ihandles) + (not gnus-displaying-mime)) ;; Top-level call; we clean up. - (mm-destroy-parts gnus-article-mime-handles) - (setq gnus-article-mime-handles handles - gnus-article-mime-handle-alist nil) + (when gnus-article-mime-handles + (mm-destroy-parts gnus-article-mime-handles) + (setq gnus-article-mime-handle-alist nil));; A trick. + (setq gnus-article-mime-handles handles) ;; We allow users to glean info from the handles. (when gnus-article-mime-part-function (gnus-mime-part-function handles))) @@ -2807,21 +3538,25 @@ If ALL-HEADERS is non-nil, no headers are hidden." (or (not (stringp (car handles))) (cdr handles))) (progn - (unless ihandles + (when (and (not ihandles) + (not gnus-displaying-mime)) ;; Clean up for mime parts. (article-goto-body) (delete-region (point) (point-max))) - (gnus-mime-display-part handles)) + (let ((gnus-displaying-mime t)) + (gnus-mime-display-part handles))) (save-restriction (article-goto-body) (narrow-to-region (point) (point-max)) - (gnus-treat-article nil 1 1))) - ;; Highlight the headers. - (save-excursion - (save-restriction - (article-goto-body) - (narrow-to-region (point-min) (point)) - (gnus-treat-article 'head))))))) + (gnus-treat-article nil 1 1) + (widen))) + (unless ihandles + ;; Highlight the headers. + (save-excursion + (save-restriction + (article-goto-body) + (narrow-to-region (point-min) (point)) + (gnus-treat-article 'head)))))))) (defvar gnus-mime-display-multipart-as-mixed nil) @@ -2830,6 +3565,10 @@ If ALL-HEADERS is non-nil, no headers are hidden." ;; Single part. ((not (stringp (car handle))) (gnus-mime-display-single handle)) + ;; User-defined multipart + ((cdr (assoc (car handle) gnus-mime-multipart-functions)) + (funcall (cdr (assoc (car handle) gnus-mime-multipart-functions)) + handle)) ;; multipart/alternative ((and (equal (car handle) "multipart/alternative") (not gnus-mime-display-multipart-as-mixed)) @@ -2855,7 +3594,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (mapcar 'gnus-mime-display-part handles)) (defun gnus-mime-display-single (handle) - (let ((type (car (mm-handle-type handle))) + (let ((type (mm-handle-media-type handle)) (ignored gnus-ignored-mime-types) (not-attachment t) (move nil) @@ -2866,38 +3605,45 @@ If ALL-HEADERS is non-nil, no headers are hidden." (when (string-match (pop ignored) type) (throw 'ignored nil))) (if (and (setq not-attachment - (or (not (mm-handle-disposition handle)) - (equal (car (mm-handle-disposition handle)) - "inline") - (mm-attachment-override-p type))) - (mm-automatic-display-p type) - (or (mm-inlinable-part-p type) + (and (not (mm-inline-override-p handle)) + (or (not (mm-handle-disposition handle)) + (equal (car (mm-handle-disposition handle)) + "inline") + (mm-attachment-override-p handle)))) + (mm-automatic-display-p handle) + (or (mm-inlined-p handle) (mm-automatic-external-display-p type))) (setq display t) - (when (equal (car (split-string type "/")) - "text") + (when (equal (mm-handle-media-supertype handle) "text") (setq text t))) (let ((id (1+ (length gnus-article-mime-handle-alist)))) (push (cons id handle) gnus-article-mime-handle-alist) (when (or (not display) (not (gnus-unbuttonized-mime-type-p type))) - (gnus-article-insert-newline) + ;(gnus-article-insert-newline) (gnus-insert-mime-button handle id (list (or display (and not-attachment text)))) (gnus-article-insert-newline) - (gnus-article-insert-newline) + ;(gnus-article-insert-newline) (setq move t))) (let ((beg (point))) (cond (display (when move - (forward-line -2)) - (let ((mail-parse-charset gnus-newsgroup-charset)) + (forward-line -2) + (setq beg (point))) + (let ((mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (condition-case () + (set-buffer gnus-summary-buffer) + (error)) + gnus-newsgroup-ignored-charsets))) (mm-display-part handle t)) (goto-char (point-max))) ((and text not-attachment) (when move - (forward-line -2)) + (forward-line -2) + (setq beg (point))) (gnus-article-insert-newline) (mm-insert-inline handle (mm-get-part handle)) (goto-char (point-max)))) @@ -2908,7 +3654,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (gnus-treat-article nil (length gnus-article-mime-handle-alist) (1- (length gnus-article-mime-handles)) - (car (mm-handle-type handle)))))))))) + (mm-handle-media-type handle))))))))) (defun gnus-unbuttonized-mime-type-p (type) "Say whether TYPE is to be unbuttonized." @@ -2975,9 +3721,7 @@ If ALL-HEADERS is non-nil, no headers are hidden." (progn (insert (format "(%c) %-18s" (if (equal handle preferred) ?* ? ) - (if (stringp (car handle)) - (car handle) - (car (mm-handle-type handle))))) + (mm-handle-media-type handle))) (point)) `(gnus-callback (lambda (handles) @@ -3000,8 +3744,19 @@ If ALL-HEADERS is non-nil, no headers are hidden." (when preferred (if (stringp (car preferred)) (gnus-display-mime preferred) - (let ((mail-parse-charset gnus-newsgroup-charset)) - (mm-display-part preferred))) + (let ((mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets + (save-excursion (set-buffer gnus-summary-buffer) + gnus-newsgroup-ignored-charsets))) + (mm-display-part preferred) + ;; Do highlighting. + (save-excursion + (save-restriction + (narrow-to-region (car begend) (point-max)) + (gnus-treat-article + nil (length gnus-article-mime-handle-alist) + (1- (length gnus-article-mime-handles)) + (mm-handle-media-type handle)))))) (goto-char (point-max)) (setcdr begend (point-marker))))) (when ibegend @@ -3011,20 +3766,21 @@ If ALL-HEADERS is non-nil, no headers are hidden." "Return a string which display status of article washing." (save-excursion (set-buffer gnus-article-buffer) - (let ((cite (gnus-article-hidden-text-p 'cite)) - (headers (gnus-article-hidden-text-p 'headers)) - (boring (gnus-article-hidden-text-p 'boring-headers)) - (pgp (gnus-article-hidden-text-p 'pgp)) - (pem (gnus-article-hidden-text-p 'pem)) - (signature (gnus-article-hidden-text-p 'signature)) - (overstrike (gnus-article-hidden-text-p 'overstrike)) - (emphasis (gnus-article-hidden-text-p 'emphasis))) - (format "%c%c%c%c%c%c" + (let ((cite (memq 'cite gnus-article-wash-types)) + (headers (memq 'headers gnus-article-wash-types)) + (boring (memq 'boring-headers gnus-article-wash-types)) + (pgp (memq 'pgp gnus-article-wash-types)) + (pem (memq 'pem gnus-article-wash-types)) + (signature (memq 'signature gnus-article-wash-types)) + (overstrike (memq 'overstrike gnus-article-wash-types)) + (emphasis (memq 'emphasis gnus-article-wash-types))) + (format "%c%c%c%c%c%c%c" (if cite ?c ? ) (if (or headers boring) ?h ? ) (if (or pgp pem) ?p ? ) (if signature ?s ? ) (if overstrike ?o ? ) + (if gnus-show-mime ?m ? ) (if emphasis ?e ? ))))) (fset 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers) @@ -3049,7 +3805,7 @@ Provided for backwards compatibility." ;; save it to file. (goto-char (point-max)) (insert "\n") - (append-to-file (point-min) (point-max) file-name) + (write-region-as-binary (point-min) (point-max) file-name 'append) t))) (defun gnus-narrow-to-page (&optional arg) @@ -3108,48 +3864,56 @@ If given a numerical ARG, move forward ARG pages." If end of article, return non-nil. Otherwise return nil. Argument LINES specifies lines to be scrolled up." (interactive "p") - (move-to-window-line -1) - (if (save-excursion - (end-of-line) - (and (pos-visible-in-window-p) ;Not continuation line. - (eobp))) - ;; Nothing in this page. - (if (or (not gnus-page-broken) - (save-excursion - (save-restriction - (widen) (forward-line 1) (eobp)))) ;Real end-of-buffer? - t ;Nothing more. - (gnus-narrow-to-page 1) ;Go to next page. - nil) - ;; More in this page. - (let ((scroll-in-place nil)) - (condition-case () - (scroll-up lines) - (end-of-buffer - ;; Long lines may cause an end-of-buffer error. - (goto-char (point-max))))) - (move-to-window-line 0) - nil)) + (let ((start (window-start)) + end-of-buffer end-of-page) + (save-excursion + (move-to-window-line -1) + (if (<= (point) start) + (progn + (forward-line 2) + (setq start (point))) + (forward-line 1) + (setq start nil)) + (unless (or (cond ((eq (1+ (buffer-size)) (point)) + (and (pos-visible-in-window-p) + (setq end-of-buffer t))) + ((eobp) + (setq end-of-page t))) + (not lines)) + (move-to-window-line lines) + (unless (search-backward "\n\n" nil t) + (setq start (point))))) + (cond (end-of-buffer t) + (end-of-page + (gnus-narrow-to-page 1) + nil) + (t + (if start + (set-window-start (selected-window) start) + (let (window-pixel-scroll-increment) + (scroll-up lines))) + nil)))) (defun gnus-article-prev-page (&optional lines) "Show previous page of current article. Argument LINES specifies lines to be scrolled down." (interactive "p") - (move-to-window-line 0) - (if (and gnus-page-broken - (bobp) - (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer? - (progn - (gnus-narrow-to-page -1) ;Go to previous page. - (goto-char (point-max)) - (recenter -1)) - (let ((scroll-in-place nil)) - (prog1 - (condition-case () - (scroll-down lines) - (beginning-of-buffer - (goto-char (point-min)))) - (move-to-window-line 0))))) + (let (beginning-of-buffer beginning-of-page) + (save-excursion + (move-to-window-line 0) + (cond ((eq 1 (point)) + (setq beginning-of-buffer t)) + ((bobp) + (setq beginning-of-page t)))) + (cond (beginning-of-buffer) + (beginning-of-page + (gnus-narrow-to-page -1)) + (t + (condition-case nil + (let (window-pixel-scroll-increment) + (scroll-down lines)) + (beginning-of-buffer + (goto-char (point-min)))))))) (defun gnus-article-refer-article () "Read article specified by message-id around point." @@ -3202,7 +3966,7 @@ Argument LINES specifies lines to be scrolled down." (defun gnus-article-check-buffer () "Beep if not in an article buffer." - (unless (equal major-mode 'gnus-article-mode) + (unless (eq (get-buffer gnus-article-buffer) (current-buffer)) (error "Command invoked outside of a Gnus article buffer"))) (defun gnus-article-read-summary-keys (&optional arg key not-restore-window) @@ -3211,7 +3975,7 @@ Argument LINES specifies lines to be scrolled down." (gnus-article-check-buffer) (let ((nosaves '("q" "Q" "c" "r" "R" "\C-c\C-f" "m" "a" "f" "F" - "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP" + "Zc" "ZC" "ZE" "ZJ" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP" "=" "^" "\M-^" "|")) (nosave-but-article '("A\r")) @@ -3224,7 +3988,9 @@ Argument LINES specifies lines to be scrolled down." (set-buffer gnus-article-current-summary) (let (gnus-pick-mode) (push (or key last-command-event) unread-command-events) - (setq keys (read-key-sequence nil)))) + (setq keys (static-if (featurep 'xemacs) + (events-to-keys (read-key-sequence nil)) + (read-key-sequence nil))))) (message "") (if (or (member keys nosaves) @@ -3236,7 +4002,8 @@ Argument LINES specifies lines to be scrolled down." ;; We disable the pick minor mode commands. (let (gnus-pick-mode) (setq func (lookup-key (current-local-map) keys)))) - (if (not func) + (if (or (not func) + (numberp func)) (ding) (unless (member keys nosave-in-article) (set-buffer gnus-article-current-summary)) @@ -3266,9 +4033,12 @@ Argument LINES specifies lines to be scrolled down." (set-buffer obuf) (unless not-restore-window (set-window-configuration owin)) - (unless (or (not (eq selected 'old)) (member keys up-to-top)) + (when (eq selected 'old) + (article-goto-body) + (set-window-start (get-buffer-window (current-buffer)) + 1) (set-window-point (get-buffer-window (current-buffer)) - opoint)) + (point))) (let ((win (get-buffer-window gnus-article-current-summary))) (when win (set-window-point win new-sum-point)))))))) @@ -3280,6 +4050,7 @@ headers will be hidden. If given a prefix, show the hidden text instead." (interactive (append (gnus-article-hidden-arg) (list 'force))) (gnus-article-hide-headers arg) + (gnus-article-hide-list-identifiers arg) (gnus-article-hide-pgp arg) (gnus-article-hide-citation-maybe arg force) (gnus-article-hide-signature arg)) @@ -3312,8 +4083,7 @@ If given a prefix, show the hidden text instead." ;; We only request an article by message-id when we do not have the ;; headers for it, so we'll have to get those. (when (stringp article) - (let ((gnus-override-method gnus-refer-article-method)) - (gnus-read-header article))) + (gnus-read-header article)) ;; If the article number is negative, that means that this article ;; doesn't belong in this newsgroup (possibly), so we find its @@ -3331,8 +4101,7 @@ If given a prefix, show the hidden text instead." ;; This is a sparse gap article. (setq do-update-line article) (setq article (mail-header-id header)) - (let ((gnus-override-method gnus-refer-article-method)) - (setq sparse-header (gnus-read-header article))) + (setq sparse-header (gnus-read-header article)) (setq gnus-newsgroup-sparse (delq article gnus-newsgroup-sparse))) ((vectorp header) @@ -3367,15 +4136,6 @@ If given a prefix, show the hidden text instead." (assq article gnus-newsgroup-reads))) gnus-canceled-mark)) nil) - ;; We first check `gnus-original-article-buffer'. - ((and (get-buffer gnus-original-article-buffer) - (numberp article) - (save-excursion - (set-buffer gnus-original-article-buffer) - (and (equal (car gnus-original-article) group) - (eq (cdr gnus-original-article) article)))) - (insert-buffer-substring gnus-original-article-buffer) - 'article) ;; Check the backlog. ((and gnus-keep-backlog (gnus-backlog-request-article group article (current-buffer))) @@ -3392,20 +4152,40 @@ If given a prefix, show the hidden text instead." (gnus-cache-request-article article group)) 'article) ;; Get the article and put into the article buffer. - ((or (stringp article) (numberp article)) - (let ((gnus-override-method - (and (stringp article) gnus-refer-article-method)) + ((or (stringp article) + (numberp article)) + (let ((gnus-override-method gnus-override-method) + (methods (and (stringp article) + gnus-refer-article-method)) + result (buffer-read-only nil)) - (erase-buffer) - (gnus-kill-all-overlays) - (gnus-check-group-server) - (when (gnus-request-article article group (current-buffer)) - (when (numberp article) - (gnus-async-prefetch-next group article gnus-summary-buffer) - (when gnus-keep-backlog - (gnus-backlog-enter-article - group article (current-buffer)))) - 'article))) + (setq methods + (if (listp methods) + methods + (list methods))) + (when (and (null gnus-override-method) + methods) + (setq gnus-override-method (pop methods))) + (while (not result) + (when (eq gnus-override-method 'current) + (setq gnus-override-method gnus-current-select-method)) + (erase-buffer) + (gnus-kill-all-overlays) + (let ((gnus-newsgroup-name group)) + (gnus-check-group-server)) + (when (gnus-request-article article group (current-buffer)) + (when (numberp article) + (gnus-async-prefetch-next group article + gnus-summary-buffer) + (when gnus-keep-backlog + (gnus-backlog-enter-article + group article (current-buffer)))) + (setq result 'article)) + (if (not result) + (if methods + (setq gnus-override-method (pop methods)) + (setq result 'done)))) + (and (eq result 'article) 'article))) ;; It was a pseudo. (t article))) @@ -3455,6 +4235,12 @@ If given a prefix, show the hidden text instead." :group 'gnus-article-various :type 'hook) +(defcustom gnus-article-edit-article-setup-function + 'gnus-article-mime-edit-article-setup + "Function called to setup an editing article buffer." + :group 'gnus-article-various + :type 'function) + (defvar gnus-article-edit-done-function nil) (defvar gnus-article-edit-mode-map nil) @@ -3499,21 +4285,24 @@ groups." (error "The current newsgroup does not support article editing")) (gnus-article-date-original) (gnus-article-edit-article + 'ignore `(lambda (no-highlight) + 'ignore (gnus-summary-edit-article-done ,(or (mail-header-references gnus-current-headers) "") ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight)))) -(defun gnus-article-edit-article (exit-func) +(defun gnus-article-edit-article (start-func exit-func) "Start editing the contents of the current article buffer." (let ((winconf (current-window-configuration))) (set-buffer gnus-article-buffer) (gnus-article-edit-mode) - (gnus-article-delete-text-of-type 'annotation) - (gnus-set-text-properties (point-min) (point-max) nil) + (funcall start-func) (gnus-configure-windows 'edit-article) (setq gnus-article-edit-done-function exit-func) (setq gnus-prev-winconf winconf) + (when gnus-article-edit-article-setup-function + (funcall gnus-article-edit-article-setup-function)) (gnus-message 6 "C-c C-c to end edits"))) (defun gnus-article-edit-done (&optional arg) @@ -3543,6 +4332,8 @@ groups." (let ((func gnus-article-edit-done-function) (buf (current-buffer)) (start (window-start))) + (remove-hook 'gnus-article-mode-hook + 'gnus-article-mime-edit-article-unwind) (gnus-article-edit-exit) (save-excursion (set-buffer buf) @@ -3593,6 +4384,88 @@ groups." (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2")))) ;;; +;;; Article editing with MIME-Edit +;;; + +(defcustom gnus-article-mime-edit-article-setup-hook nil + "Hook run after setting up a MIME editing article buffer." + :group 'gnus-article-various + :type 'hook) + +(defun gnus-article-mime-edit-article-unwind () + "Unwind `gnus-article-buffer' if article editing was given up." + (remove-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind) + (when mime-edit-mode-flag + (mime-edit-exit 'nomime 'no-error) + (message "")) + (when (featurep 'font-lock) + (setq font-lock-defaults nil) + (font-lock-mode 0))) + +(defun gnus-article-mime-edit-article-setup () + "Convert current buffer to MIME-Edit buffer and turn on MIME-Edit mode +after replacing with the original article." + (setq gnus-show-mime t) + (setq gnus-article-edit-done-function + `(lambda (&rest args) + (when mime-edit-mode-flag + (mime-edit-exit) + (message "")) + (goto-char (point-min)) + (let (case-fold-search) + (when (re-search-forward + (format "^%s$" (regexp-quote mail-header-separator)) + nil t) + (replace-match ""))) + (when (featurep 'font-lock) + (setq font-lock-defaults nil) + (font-lock-mode 0)) + (apply ,gnus-article-edit-done-function args) + (set-buffer gnus-original-article-buffer) + (erase-buffer) + (insert-buffer gnus-article-buffer) + (setq gnus-current-headers (gnus-article-make-full-mail-header)) + (gnus-article-prepare-display))) + (substitute-key-definition + 'gnus-article-edit-exit 'gnus-article-mime-edit-exit + gnus-article-edit-mode-map) + (erase-buffer) + (insert-buffer gnus-original-article-buffer) + (mime-edit-again) + (when (featurep 'font-lock) + (set (make-local-variable 'font-lock-defaults) + '(message-font-lock-keywords t)) + (font-lock-set-defaults) + (turn-on-font-lock)) + (add-hook 'gnus-article-mode-hook 'gnus-article-mime-edit-article-unwind) + (gnus-run-hooks 'gnus-article-mime-edit-article-setup-hook)) + +(defun gnus-article-mime-edit-exit () + "Exit the article MIME editing without updating." + (interactive) + (let ((winconf gnus-prev-winconf) + buf) + (when mime-edit-mode-flag + (mime-edit-exit) + (message "")) + (goto-char (point-min)) + (let (case-fold-search) + (when (re-search-forward + (format "^%s$" (regexp-quote mail-header-separator)) nil t) + (replace-match ""))) + (when (featurep 'font-lock) + (setq font-lock-defaults nil) + (font-lock-mode 0)) + ;; We remove all text props from the article buffer. + (setq buf (format "%s" (buffer-string))) + (set-buffer (get-buffer-create gnus-original-article-buffer)) + (erase-buffer) + (insert buf) + (setq gnus-current-headers (gnus-article-make-full-mail-header)) + (gnus-article-prepare-display) + (set-window-configuration winconf))) + +;;; ;;; Article highlights ;;; @@ -3600,7 +4473,7 @@ groups." ;;; Internal Variables: -(defcustom gnus-button-url-regexp "\\b\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\):\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?\\([-a-zA-Z0-9_=!?#$@~`%&*+|\\/:;.,]\\|\\w\\)+\\([-a-zA-Z0-9_=#$@~`%&*+|\\/]\\|\\w\\)" +(defcustom gnus-button-url-regexp "\\b\\(\\(s?https?\\|ftp\\|file\\|gopher\\|news\\|telnet\\|wais\\|mailto\\):\\(//[-a-zA-Z0-9_.]+:[0-9]*\\)?\\([-a-zA-Z0-9_=!?#$@~`%&*+|\\/:;.,]\\|\\w\\)+\\([-a-zA-Z0-9_=#$@~`%&*+|\\/]\\|\\w\\)\\)\\|[-a-zA-Z0-9_]+\\.[-a-zA-Z0-9_]+\\(\\.[-a-zA-Z0-9_]+[-a-zA-Z0-9_/]+\\)+" "Regular expression that matches URLs." :group 'gnus-article-buttons :type 'regexp) @@ -3619,9 +4492,9 @@ groups." ("mailto:\\([-a-zA-Z.@_+0-9%]+\\)" 0 t gnus-url-mailto 1) ("\\bmailto:\\([^ \n\t]+\\)" 0 t gnus-url-mailto 1) ;; This is how URLs _should_ be embedded in text... - ("]*\\)>" 0 t gnus-button-embedded-url 1) + ("]*\\)>" 0 t gnus-button-embedded-url 1) ;; Raw URLs. - (,gnus-button-url-regexp 0 t gnus-button-url 0)) + (,gnus-button-url-regexp 0 t browse-url 0)) "*Alist of regexps matching buttons in article bodies. Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where @@ -3649,9 +4522,9 @@ variable it the real callback function." ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$" 1 t gnus-button-reply 1) ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+" 0 t gnus-button-mailto 0) - ("^X-[Uu][Rr][Ll]:" ,gnus-button-url-regexp 0 t gnus-button-url 0) - ("^Subject:" ,gnus-button-url-regexp 0 t gnus-button-url 0) - ("^[^:]+:" ,gnus-button-url-regexp 0 t gnus-button-url 0) + ("^X-[Uu][Rr][Ll]:" ,gnus-button-url-regexp 0 t browse-url 0) + ("^Subject:" ,gnus-button-url-regexp 0 t browse-url 0) + ("^[^:]+:" ,gnus-button-url-regexp 0 t browse-url 0) ("^[^:]+:" "\\(<\\(url: \\)?news:\\([^>\n ]*\\)>\\)" 1 t gnus-button-message-id 3)) "*Alist of headers and regexps to match buttons in article heads. @@ -3706,6 +4579,40 @@ call it with the value of the `gnus-data' text property." (when fun (funcall fun data)))) +(defun gnus-article-prev-button (n) + "Move point to N buttons backward. +If N is negative, move forward instead." + (interactive "p") + (gnus-article-next-button (- n))) + +(defun gnus-article-next-button (n) + "Move point to N buttons forward. +If N is negative, move backward instead." + (interactive "p") + (let ((function (if (< n 0) 'previous-single-property-change + 'next-single-property-change)) + (inhibit-point-motion-hooks t) + (backward (< n 0)) + (limit (if (< n 0) (point-min) (point-max)))) + (setq n (abs n)) + (while (and (not (= limit (point))) + (> n 0)) + ;; Skip past the current button. + (when (get-text-property (point) 'gnus-callback) + (goto-char (funcall function (point) 'gnus-callback nil limit))) + ;; Go to the next (or previous) button. + (gnus-goto-char (funcall function (point) 'gnus-callback nil limit)) + ;; Put point at the start of the button. + (when (and backward (not (get-text-property (point) 'gnus-callback))) + (goto-char (funcall function (point) 'gnus-callback nil limit))) + ;; Skip past intangible buttons. + (when (get-text-property (point) 'intangible) + (incf n)) + (decf n)) + (unless (zerop n) + (gnus-message 5 "No more buttons")) + n)) + (defun gnus-article-highlight (&optional force) "Highlight current article. This function calls `gnus-article-highlight-headers', @@ -3740,7 +4647,7 @@ do the highlighting. See the documentation for those functions." (case-fold-search t) (inhibit-point-motion-hooks t) entry regexp header-face field-face from hpoints fpoints) - (message-narrow-to-head) + (article-narrow-to-head) (while (setq entry (pop alist)) (goto-char (point-min)) (setq regexp (concat "^\\(" @@ -3773,21 +4680,28 @@ do the highlighting. See the documentation for those functions." It does this by highlighting everything after `gnus-signature-separator' using `gnus-signature-face'." (interactive) + (when gnus-signature-face + (save-excursion + (set-buffer gnus-article-buffer) + (let ((buffer-read-only nil) + (inhibit-point-motion-hooks t)) + (save-restriction + (when (gnus-article-narrow-to-signature) + (gnus-overlay-put (gnus-make-overlay (point-min) (point-max)) + 'face gnus-signature-face))))))) + +(defun gnus-article-buttonize-signature () + "Add button to the signature." + (interactive) (save-excursion (set-buffer gnus-article-buffer) (let ((buffer-read-only nil) (inhibit-point-motion-hooks t)) - (save-restriction - (when (and gnus-signature-face - (gnus-article-narrow-to-signature)) - (gnus-overlay-put (gnus-make-overlay (point-min) (point-max)) - 'face gnus-signature-face) - (widen) - (gnus-article-search-signature) - (let ((start (match-beginning 0)) - (end (set-marker (make-marker) (1+ (match-end 0))))) - (gnus-article-add-button start (1- end) 'gnus-signature-toggle - end))))))) + (when (gnus-article-search-signature) + (gnus-article-add-button (match-beginning 0) (match-end 0) + 'gnus-signature-toggle + (set-marker (make-marker) + (1+ (match-end 0)))))))) (defun gnus-button-in-region-p (b e prop) "Say whether PROP exists in the region." @@ -3806,14 +4720,17 @@ specified by `gnus-button-alist'." (alist gnus-button-alist) beg entry regexp) ;; Remove all old markers. - (let (marker entry) + (let (marker entry new-list) (while (setq marker (pop gnus-button-marker-list)) - (goto-char marker) - (when (setq entry (gnus-button-entry)) - (put-text-property (match-beginning (nth 1 entry)) - (match-end (nth 1 entry)) - 'gnus-callback nil)) - (set-marker marker nil))) + (if (or (< marker (point-min)) (>= marker (point-max))) + (push marker new-list) + (goto-char marker) + (when (setq entry (gnus-button-entry)) + (put-text-property (match-beginning (nth 1 entry)) + (match-end (nth 1 entry)) + 'gnus-callback nil)) + (set-marker marker nil))) + (setq gnus-button-marker-list new-list)) ;; We skip the headers. (article-goto-body) (setq beg (point)) @@ -3841,38 +4758,38 @@ specified by `gnus-button-alist'." (interactive) (save-excursion (set-buffer gnus-article-buffer) - (let ((buffer-read-only nil) - (inhibit-point-motion-hooks t) - (case-fold-search t) - (alist gnus-header-button-alist) - entry beg end) - (nnheader-narrow-to-headers) - (while alist - ;; Each alist entry. - (setq entry (car alist) - alist (cdr alist)) - (goto-char (point-min)) - (while (re-search-forward (car entry) nil t) - ;; Each header matching the entry. - (setq beg (match-beginning 0)) - (setq end (or (and (re-search-forward "^[^ \t]" nil t) - (match-beginning 0)) - (point-max))) - (goto-char beg) - (while (re-search-forward (nth 1 entry) end t) - ;; Each match within a header. - (let* ((entry (cdr entry)) - (start (match-beginning (nth 1 entry))) - (end (match-end (nth 1 entry))) - (form (nth 2 entry))) - (goto-char (match-end 0)) - (when (eval form) - (gnus-article-add-button - start end (nth 3 entry) - (buffer-substring (match-beginning (nth 4 entry)) - (match-end (nth 4 entry))))))) - (goto-char end)))) - (widen))) + (save-restriction + (let ((buffer-read-only nil) + (inhibit-point-motion-hooks t) + (case-fold-search t) + (alist gnus-header-button-alist) + entry beg end) + (article-narrow-to-head) + (while alist + ;; Each alist entry. + (setq entry (car alist) + alist (cdr alist)) + (goto-char (point-min)) + (while (re-search-forward (car entry) nil t) + ;; Each header matching the entry. + (setq beg (match-beginning 0)) + (setq end (or (and (re-search-forward "^[^ \t]" nil t) + (match-beginning 0)) + (point-max))) + (goto-char beg) + (while (re-search-forward (nth 1 entry) end t) + ;; Each match within a header. + (let* ((entry (cdr entry)) + (start (match-beginning (nth 1 entry))) + (end (match-end (nth 1 entry))) + (form (nth 2 entry))) + (goto-char (match-end 0)) + (when (eval form) + (gnus-article-add-button + start end (nth 3 entry) + (buffer-substring (match-beginning (nth 4 entry)) + (match-end (nth 4 entry))))))) + (goto-char end))))))) ;;; External functions: @@ -3886,9 +4803,7 @@ specified by `gnus-button-alist'." (nconc (and gnus-article-mouse-face (list gnus-mouse-face-prop gnus-article-mouse-face)) (list 'gnus-callback fun) - (and data (list 'gnus-data data)))) - (widget-convert-button 'link from to :action 'gnus-widget-press-button - :button-keymap gnus-widget-button-keymap)) + (and data (list 'gnus-data data))))) ;;; Internal functions: @@ -3901,10 +4816,12 @@ specified by `gnus-button-alist'." (save-excursion (set-buffer gnus-article-buffer) (let ((buffer-read-only nil) - (inhibit-point-motion-hooks t)) + (inhibit-point-motion-hooks t) + (limit (next-single-property-change end 'mime-view-entity + nil (point-max)))) (if (get-text-property end 'invisible) - (gnus-article-unhide-text end (point-max)) - (gnus-article-hide-text end (point-max) gnus-hidden-properties))))) + (gnus-article-unhide-text end limit) + (gnus-article-hide-text end limit gnus-hidden-properties))))) (defun gnus-button-entry () ;; Return the first entry in `gnus-button-alist' matching this place. @@ -4024,40 +4941,37 @@ forbidden in URL encoding." (setq to (gnus-url-unhex-string url))) (setq args (cons (list "to" to) args) subject (cdr-safe (assoc "subject" args))) - (message-mail) - (while args - (setq func (intern-soft (concat "message-goto-" (downcase (caar args))))) - (if (fboundp func) - (funcall func) - (message-position-on-field (caar args))) - (insert (mapconcat 'identity (cdar args) ", ")) - (setq args (cdr args))) - (if subject - (message-goto-body) - (message-goto-subject)))) + (gnus-setup-message 'reply + (message-mail) + (while args + (setq func (intern-soft (concat "message-goto-" (downcase (caar args))))) + (if (fboundp func) + (funcall func) + (message-position-on-field (caar args))) + (insert (mapconcat 'identity (cdar args) ", ")) + (setq args (cdr args))) + (if subject + (message-goto-body) + (message-goto-subject))))) (defun gnus-button-mailto (address) ;; Mail to ADDRESS. (set-buffer (gnus-copy-article-buffer)) - (message-reply address)) + (gnus-setup-message 'reply + (message-reply address))) (defun gnus-button-reply (address) ;; Reply to ADDRESS. - (message-reply address)) - -(defun gnus-button-url (address) - "Browse ADDRESS." - ;; In Emacs 20, `browse-url-browser-function' may be an alist. - (if (listp browse-url-browser-function) - (browse-url address) - (funcall browse-url-browser-function address))) + (gnus-setup-message 'reply + (message-reply address))) (defun gnus-button-embedded-url (address) "Browse ADDRESS." - ;; In Emacs 20, `browse-url-browser-function' may be an alist. - (if (listp browse-url-browser-function) - (browse-url (gnus-strip-whitespace address)) - (funcall browse-url-browser-function (gnus-strip-whitespace address)))) + (browse-url (gnus-strip-whitespace address))) + +(defun gnus-article-smiley-display () + "Display \"smileys\" as small graphical icons." + (smiley-toggle-buffer 1 (current-buffer) (point-min) (point-max))) ;;; Next/prev buttons in the article buffer. @@ -4070,18 +4984,29 @@ forbidden in URL encoding." (define-key gnus-prev-page-map gnus-mouse-2 'gnus-button-prev-page) (define-key gnus-prev-page-map "\r" 'gnus-button-prev-page)) -(defun gnus-insert-prev-page-button () - (let ((buffer-read-only nil)) - (gnus-eval-format - gnus-prev-page-line-format nil - `(gnus-prev t local-map ,gnus-prev-page-map - gnus-callback gnus-article-button-prev-page - article-type annotation)))) +(static-if (featurep 'xemacs) + (defun gnus-insert-prev-page-button () + (let ((buffer-read-only nil)) + (gnus-eval-format + gnus-prev-page-line-format nil + `(gnus-prev t local-map ,gnus-prev-page-map + gnus-callback gnus-article-button-prev-page + article-type annotation)))) + (defun gnus-insert-prev-page-button () + (let ((buffer-read-only nil) + (situation (get-text-property (point-min) 'mime-view-situation))) + (set-keymap-parent gnus-prev-page-map (current-local-map)) + (gnus-eval-format + gnus-prev-page-line-format nil + `(gnus-prev t local-map ,gnus-prev-page-map + gnus-callback gnus-article-button-prev-page + article-type annotation + mime-view-situation ,situation)))) + ) (defvar gnus-next-page-map nil) (unless gnus-next-page-map - (setq gnus-next-page-map (make-keymap)) - (suppress-keymap gnus-prev-page-map) + (setq gnus-next-page-map (make-sparse-keymap)) (define-key gnus-next-page-map gnus-mouse-2 'gnus-button-next-page) (define-key gnus-next-page-map "\r" 'gnus-button-next-page)) @@ -4101,13 +5026,25 @@ forbidden in URL encoding." (gnus-article-prev-page) (select-window win))) -(defun gnus-insert-next-page-button () - (let ((buffer-read-only nil)) - (gnus-eval-format gnus-next-page-line-format nil - `(gnus-next - t local-map ,gnus-next-page-map - gnus-callback gnus-article-button-next-page - article-type annotation)))) +(static-if (featurep 'xemacs) + (defun gnus-insert-next-page-button () + (let ((buffer-read-only nil)) + (gnus-eval-format gnus-next-page-line-format nil + `(gnus-next + t local-map ,gnus-next-page-map + gnus-callback gnus-article-button-next-page + article-type annotation)))) + (defun gnus-insert-next-page-button () + (let ((buffer-read-only nil) + (situation (get-text-property (point-min) 'mime-view-situation))) + (set-keymap-parent gnus-next-page-map (current-local-map)) + (gnus-eval-format gnus-next-page-line-format nil + `(gnus-next + t local-map ,gnus-next-page-map + gnus-callback gnus-article-button-next-page + article-type annotation + mime-view-situation ,situation)))) + ) (defun gnus-article-button-next-page (arg) "Go to the next page." @@ -4129,8 +5066,8 @@ forbidden in URL encoding." '(mail-decode-encoded-word-region) "List of methods used to decode headers. -This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item is -FUNCTION, FUNCTION will be apply to all newsgroups. If item is a +This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item +is FUNCTION, FUNCTION will be apply to all newsgroups. If item is a (REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups whose names match REGEXP. @@ -4148,13 +5085,13 @@ For example: (eq gnus-newsgroup-name (car gnus-decode-header-methods-cache))) (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name)) - (mapc '(lambda (x) - (if (symbolp x) - (nconc gnus-decode-header-methods-cache (list x)) - (if (and gnus-newsgroup-name - (string-match (car x) gnus-newsgroup-name)) - (nconc gnus-decode-header-methods-cache - (list (cdr x)))))) + (mapcar (lambda (x) + (if (symbolp x) + (nconc gnus-decode-header-methods-cache (list x)) + (if (and gnus-newsgroup-name + (string-match (car x) gnus-newsgroup-name)) + (nconc gnus-decode-header-methods-cache + (list (cdr x)))))) gnus-decode-header-methods)) (let ((xlist gnus-decode-header-methods-cache)) (pop xlist) @@ -4178,15 +5115,35 @@ For example: (while list (when (string-match (pop list) type) (throw 'found t))))))) - val elem) - (when (gnus-visual-p 'article-highlight 'highlight) - (gnus-run-hooks 'gnus-part-display-hook) + (highlightp (gnus-visual-p 'article-highlight 'highlight)) + (entity (static-unless (featurep 'xemacs) + (when (eq 'head condition) + (get-text-property (point-min) 'mime-view-entity)))) + val elem buttonized) + (gnus-run-hooks 'gnus-part-display-hook) + (unless gnus-inhibit-treatment (while (setq elem (pop alist)) - (setq val (symbol-value (car elem))) + (with-current-buffer gnus-summary-buffer + (setq val (symbol-value (car elem)))) (when (and (or (consp val) treated-type) - (gnus-treat-predicate val)) - (funcall (cadr elem))))))) + (gnus-treat-predicate val) + (or (not (get (car elem) 'highlight)) + highlightp)) + (when (and (not buttonized) + (memq (car elem) + '(gnus-treat-hide-signature + gnus-treat-highlight-signature))) + (gnus-article-buttonize-signature) + (setq buttonized t)) + (save-restriction + (funcall (cadr elem))))) + ;; FSF Emacsen does not inherit the existing text properties + ;; in the new text, so we should do it for `mime-view-entity'. + (static-unless (featurep 'xemacs) + (when entity + (put-text-property (point-min) (point-max) + 'mime-view-entity entity)))))) ;; Dynamic variables. (defvar part-number) @@ -4196,34 +5153,85 @@ For example: (defvar length) (defun gnus-treat-predicate (val) (cond - (condition - (eq condition val)) ((null val) nil) - ((eq val t) - t) - ((eq val 'head) - nil) - ((eq val 'last) - (eq part-number total-parts)) - ((numberp val) - (< length val)) + ((and (listp val) + (stringp (car val))) + (apply 'gnus-or (mapcar `(lambda (s) + (string-match s ,(or gnus-newsgroup-name ""))) + val))) ((listp val) (let ((pred (pop val))) (cond ((eq pred 'or) (apply 'gnus-or (mapcar 'gnus-treat-predicate val))) ((eq pred 'and) - (apply 'gnus-and (mapcar 'gnus-tread-predicate val))) + (apply 'gnus-and (mapcar 'gnus-treat-predicate val))) ((eq pred 'not) - (not (gnus-treat-predicate val))) + (not (gnus-treat-predicate (car val)))) ((eq pred 'typep) - (equal (cadr val) type)) + (equal (car val) type)) (t (error "%S is not a valid predicate" pred))))) + ((eq val 'mime) + gnus-show-mime) + (condition + (eq condition val)) + ((eq val t) + t) + ((eq val 'head) + nil) + ((eq val 'last) + (eq part-number total-parts)) + ((numberp val) + (< length val)) (t (error "%S is not a valid value" val)))) + +;;; @ for mime-view +;;; + +(defun gnus-article-header-presentation-method (entity situation) + (mime-insert-header entity) + ) + +(set-alist 'mime-header-presentation-method-alist + 'gnus-original-article-mode + #'gnus-article-header-presentation-method) + +(defun gnus-mime-preview-quitting-method () + (mime-preview-kill-buffer) + (delete-other-windows) + (gnus-article-show-summary) + (gnus-summary-select-article gnus-show-all-headers t)) + +(set-alist 'mime-preview-quitting-method-alist + 'gnus-original-article-mode #'gnus-mime-preview-quitting-method) + +(set-alist 'mime-preview-following-method-alist + 'gnus-original-article-mode #'gnus-following-method) + +(set-alist 'mime-preview-over-to-previous-method-alist + 'gnus-original-article-mode + (lambda () + (if (> (point-min) 1) + (gnus-article-prev-page) + (gnus-article-read-summary-keys + nil (gnus-character-to-event ?P))))) + +(set-alist 'mime-preview-over-to-next-method-alist + 'gnus-original-article-mode' + (lambda () + (if (< (point-max) (buffer-size)) + (gnus-article-next-page) + (gnus-article-read-summary-keys + nil (gnus-character-to-event ?N))))) + + +;;; @ end +;;; + (gnus-ems-redefine) (provide 'gnus-art)