*** empty log message ***
[gnus] / lisp / gnus-salt.el
1 ;;; gnus-salt.el --- alternate summary mode interfaces for Gnus
2 ;; Copyright (C) 1996 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5
6 ;; This file is part of GNU Emacs.
7
8 ;; GNU Emacs is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2, or (at your option)
11 ;; any later version.
12
13 ;; GNU Emacs is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with GNU Emacs; see the file COPYING.  If not, write to
20 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 ;;; Commentary:
23
24 ;;; Code:
25
26 (require 'gnus)
27
28 ;;;
29 ;;; gnus-pick-mode
30 ;;;
31
32 (defvar gnus-pick-mode nil
33   "Minor mode for providing a pick-and-read interface in Gnus summary buffers.")
34
35 (defvar gnus-pick-display-summary nil
36   "*Display summary while reading.")
37
38 (defvar gnus-pick-mode-hook nil
39   "Hook run in summary pick mode buffers.")
40
41 ;;; Internal variables.
42
43 (defvar gnus-pick-mode-map nil)
44
45 (unless gnus-pick-mode-map
46   (setq gnus-pick-mode-map (make-sparse-keymap))
47
48   (gnus-define-keys
49    gnus-pick-mode-map
50    "t" gnus-uu-mark-thread
51    "T" gnus-uu-unmark-thread
52    " " gnus-summary-mark-as-processable
53    "u" gnus-summary-unmark-as-processable
54    "U" gnus-summary-unmark-all-processable
55    "v" gnus-uu-mark-over
56    "r" gnus-uu-mark-region
57    "R" gnus-uu-unmark-region
58    "e" gnus-uu-mark-by-regexp
59    "E" gnus-uu-mark-by-regexp
60    "b" gnus-uu-mark-buffer
61    "B" gnus-uu-unmark-buffer
62    "\r" gnus-pick-start-reading))
63
64 (defun gnus-pick-make-menu-bar ()
65   (unless (boundp 'gnus-pick-menu)
66     (easy-menu-define
67      gnus-pick-menu gnus-pick-mode-map ""
68      '("Pick"
69        ("Pick"
70         ["Article" gnus-summary-mark-as-processable t]
71         ["Thread" gnus-uu-mark-thread t]
72         ["Region" gnus-uu-mark-region t]
73         ["Regexp" gnus-uu-mark-regexp t]
74         ["Buffer" gnus-uu-mark-buffer t])
75        ("Unpick"
76         ["Article" gnus-summary-unmark-as-processable t]
77         ["Thread" gnus-uu-unmark-thread t]
78         ["Region" gnus-uu-unmark-region t]
79         ["Regexp" gnus-uu-unmark-regexp t]
80         ["Buffer" gnus-uu-unmark-buffer t])
81        ["Start reading" gnus-pick-start-reading t]
82        ["Switch pick mode off" gnus-pick-mode gnus-pick-mode]))))
83
84 (defun gnus-pick-mode (&optional arg)
85   "Minor mode for provind a pick-and-read interface in Gnus summary buffers."
86   (interactive "P")
87   (when (eq major-mode 'gnus-summary-mode)
88     (make-local-variable 'gnus-pick-mode)
89     (setq gnus-pick-mode 
90           (if (null arg) (not gnus-pick-mode)
91             (> (prefix-numeric-value arg) 0)))
92     (when gnus-pick-mode
93       ;; Make sure that we don't select any articles upon group entry.
94       (make-local-variable 'gnus-auto-select-first)
95       (setq gnus-auto-select-first nil)
96       ;; Set up the menu.
97       (when (and menu-bar-mode
98                  (gnus-visual-p 'pick-menu 'menu))
99         (gnus-pick-make-menu-bar))
100       (unless (assq 'gnus-pick-mode minor-mode-alist)
101         (push '(gnus-pick-mode " Pick") minor-mode-alist))
102       (unless (assq 'gnus-topic-mode minor-mode-map-alist)
103         (push (cons 'gnus-topic-mode gnus-pick-mode-map)
104               minor-mode-map-alist))
105       (run-hooks 'gnus-pick-mode-hook))))
106
107 (defun gnus-pick-start-reading (&optional catch-up)
108   "Start reading the picked articles.
109 If given a prefix, mark all unpicked articles as read."
110   (interactive "P")
111   (unless gnus-newsgroup-processable
112     (error "No articles have been picked"))
113   (gnus-summary-limit-to-articles nil)
114   (when catch-up
115     (gnus-summary-limit-mark-excluded-as-read))
116   (gnus-configure-windows (if gnus-pick-display-summary 'summary 'pick) t))
117
118
119 ;;;
120 ;;; gnus-binary-mode
121 ;;;
122
123 (defvar gnus-binary-mode nil
124   "Minor mode for provind a binary group interface in Gnus summary buffers.")
125
126 (defvar gnus-binary-mode-hook nil
127   "Hook run in summary binary mode buffers.")
128
129 (defvar gnus-binary-mode-map nil)
130
131 (unless gnus-binary-mode-map
132   (setq gnus-binary-mode-map (make-sparse-keymap))
133
134   (gnus-define-keys
135    gnus-binary-mode-map
136    "g" gnus-binary-show-article))
137
138 (defun gnus-binary-make-menu-bar ()
139   (unless (boundp 'gnus-binary-menu)
140     (easy-menu-define
141      gnus-binary-menu gnus-binary-mode-map ""
142      '("Pick"
143        ["Switch binary mode off" gnus-binary-mode t]))))
144
145 (defun gnus-binary-mode (&optional arg)
146   "Minor mode for providing a binary group interface in Gnus summary buffers."
147   (interactive "P")
148   (when (eq major-mode 'gnus-summary-mode)
149     (make-local-variable 'gnus-binary-mode)
150     (setq gnus-binary-mode 
151           (if (null arg) (not gnus-binary-mode)
152             (> (prefix-numeric-value arg) 0)))
153     (when gnus-binary-mode
154       ;; Make sure that we don't select any articles upon group entry.
155       (make-local-variable 'gnus-auto-select-first)
156       (setq gnus-auto-select-first nil)
157       (make-local-variable 'gnus-summary-display-article-function)
158       (setq gnus-summary-display-article-function 'gnus-binary-display-article)
159       ;; Set up the menu.
160       (when (and menu-bar-mode
161                  (gnus-visual-p 'binary-menu 'menu))
162         (gnus-binary-make-menu-bar))
163       (unless (assq 'gnus-binary-mode minor-mode-alist)
164         (push '(gnus-binary-mode " Binary") minor-mode-alist))
165       (unless (assq 'gnus-topic-mode minor-mode-map-alist)
166         (push (cons 'gnus-topic-mode gnus-binary-mode-map)
167               minor-mode-map-alist))
168       (run-hooks 'gnus-binary-mode-hook))))
169
170 (defun gnus-binary-display-article (article &optional all-header)
171   "Run ARTICLE through the binary decode functions."
172   (when (gnus-summary-goto-subject article)
173     (let ((gnus-view-pseudos 'automatic))
174       (gnus-uu-decode-uu))))
175
176 (defun gnus-binary-show-article (&optional arg)
177   "Bypass the binary functions and show the article."
178   (interactive "P")
179   (let (gnus-summary-display-article-function)
180     (gnus-summary-show-article arg)))
181
182 ;;;
183 ;;; gnus-tree-mode
184 ;;;
185
186 (defvar gnus-tree-line-format "%(%[%3,3n%]%)"
187   "Format of tree elements.")
188
189 (defvar gnus-tree-minimize-window t
190   "If non-nil, minimize the tree buffer window.
191 If a number, never let the tree buffer grow taller than that number of
192 lines.")
193
194 (defvar gnus-selected-tree-face 'modeline
195   "*Face used for highlighting selected articles in the thread tree.")
196
197 (defvar gnus-tree-brackets '((?\[ . ?\]) (?\( . ?\)) (?\{ . ?\}))
198   "Brackets used in tree nodes.")
199
200 (defvar gnus-tree-parent-child-edges '(?- ?\\ ?|)
201   "Charaters used to connect parents with children.")
202
203 (defvar gnus-tree-mode-line-format "Gnus: %%b %S %Z"
204   "*The format specification for the tree mode line.")
205
206 (defvar gnus-generate-tree-function 'gnus-generate-vertical-tree
207   "*Function for generating a thread tree.
208 Two predefined functions are available:
209 `gnus-generate-horizontal-tree' and `gnus-generate-vertical-tree'.")
210
211 (defvar gnus-tree-mode-hook nil
212   "*Hook run in tree mode buffers.")
213
214 (defvar gnus-tree-buffer "*Tree*"
215   "Buffer where Gnus thread trees are displayed.")
216
217 ;;; Internal variables.
218
219 (defvar gnus-tree-line-format-alist 
220   `((?n gnus-tmp-name ?s)
221     (?f gnus-tmp-from ?s)
222     (?N gnus-tmp-number ?d)
223     (?\[ gnus-tmp-open-bracket ?c)
224     (?\] gnus-tmp-close-bracket ?c)
225     (?s gnus-tmp-subject ?s)))
226
227 (defvar gnus-tree-mode-line-format-alist gnus-summary-mode-line-format-alist)
228
229 (defvar gnus-tree-mode-line-format-spec nil)
230 (defvar gnus-tree-line-format-spec nil)
231
232 (defvar gnus-tree-node-length nil)
233 (defvar gnus-selected-tree-overlay nil)
234
235 (defvar gnus-tree-displayed-thread nil)
236
237 (defvar gnus-tree-mode-map nil)
238 (put 'gnus-tree-mode 'mode-class 'special)
239
240 (unless gnus-tree-mode-map
241   (setq gnus-tree-mode-map (make-keymap))
242   (suppress-keymap gnus-tree-mode-map)
243   (gnus-define-keys
244    gnus-tree-mode-map
245    "\r" gnus-tree-select-article
246    gnus-mouse-2 gnus-tree-pick-article
247    "\C-?" gnus-tree-read-summary-keys)
248
249   (substitute-key-definition
250    'undefined 'gnus-tree-read-summary-keys gnus-tree-mode-map))
251
252 (defun gnus-tree-make-menu-bar ()
253   )
254
255 (defun gnus-tree-mode ()
256   "Major mode for displaying thread trees."
257   (interactive)
258   (setq gnus-tree-mode-line-format-spec 
259         (gnus-parse-format gnus-tree-mode-line-format 
260                            gnus-summary-mode-line-format-alist))
261   (setq gnus-tree-line-format-spec 
262         (gnus-parse-format gnus-tree-line-format 
263                            gnus-tree-line-format-alist t))
264   (when (and menu-bar-mode
265              (gnus-visual-p 'tree-menu 'menu))
266     (gnus-tree-make-menu-bar))
267   (kill-all-local-variables)
268   (gnus-simplify-mode-line)
269   (setq mode-name "Tree")
270   (setq major-mode 'gnus-tree-mode)
271   (use-local-map gnus-tree-mode-map)
272   (buffer-disable-undo (current-buffer))
273   (setq buffer-read-only t)
274   (setq truncate-lines t)
275   (save-excursion
276     (gnus-set-work-buffer)
277     (gnus-tree-node-insert (make-mail-header "") nil)
278     (setq gnus-tree-node-length (1- (point))))
279   (run-hooks 'gnus-tree-mode-hook))
280
281 (defun gnus-tree-read-summary-keys (&optional arg)
282   "Read a summary buffer key sequence and execute it."
283   (interactive "P")
284   (let ((buf (current-buffer))
285         win)
286     (gnus-article-read-summary-keys arg nil t)
287     (when (setq win (get-buffer-window buf))
288       (select-window win)
289       (when gnus-selected-tree-overlay
290         (goto-char (overlay-end gnus-selected-tree-overlay)))
291       (gnus-tree-minimize))))
292
293 (defun gnus-tree-select-article (article)
294   "Select the article under point, if any."
295   (interactive (list (gnus-tree-article-number)))
296   (let ((buf (current-buffer)))
297     (when article
298       (save-excursion
299         (set-buffer gnus-summary-buffer)
300         (gnus-summary-goto-article article))
301       (select-window (get-buffer-window buf)))))
302
303 (defun gnus-tree-pick-article (e)
304   "Select the article under the mouse pointer."
305   (interactive "e")
306   (mouse-set-point e)
307   (gnus-tree-select-article (gnus-tree-article-number)))
308
309 (defun gnus-tree-article-number ()
310   (get-text-property (point) 'gnus-number))
311
312 (defun gnus-tree-article-region (article)
313   "Return a cons with BEG and END of the article region."
314   (let ((pos (text-property-any (point-min) (point-max) 'gnus-number article)))
315     (when pos
316       (cons pos (next-single-property-change pos 'gnus-number)))))
317
318 (defun gnus-tree-goto-article (article)
319   (let ((pos (text-property-any (point-min) (point-max) 'gnus-number article)))
320     (when pos
321       (goto-char pos))))
322
323 (defun gnus-tree-recenter ()
324   "Center point in the tree window."
325   (when (get-buffer-window (current-buffer))
326     (save-selected-window
327       (select-window (get-buffer-window (current-buffer)))
328       (let* ((top (cond ((< (window-height) 4) 0)
329                         ((< (window-height) 7) 1)
330                         (t 2)))
331              (height (1- (window-height)))
332              (bottom (save-excursion (goto-char (point-max))
333                                      (forward-line (- height))
334                                      (point)))
335              (window (get-buffer-window (current-buffer))))
336         ;; Set the window start to either `bottom', which is the biggest
337         ;; possible valid number, or the second line from the top,
338         ;; whichever is the least.
339         (set-window-start
340          window (min bottom (save-excursion 
341                               (forward-line (- top)) (point))))))))
342
343 (defun gnus-get-tree-buffer ()
344   "Return the tree buffer properly initialized."
345   (save-excursion
346     (set-buffer (get-buffer-create gnus-tree-buffer))
347     (unless (eq major-mode 'gnus-tree-mode)
348       (gnus-add-current-to-buffer-list)
349       (gnus-tree-mode))
350     (current-buffer)))
351
352 (defun gnus-tree-minimize ()
353   (when (and gnus-tree-minimize-window
354              (not (one-window-p)))
355     (let* ((window-min-height 2)
356            (height (count-lines (point-min) (point-max)))
357            (min (max (1- window-min-height) height))
358            (tot (if (numberp gnus-tree-minimize-window)
359                     (min gnus-tree-minimize-window min)
360                   min))
361            (win (get-buffer-window (current-buffer)))
362            (wh (and win (1- (window-height win)))))
363       (when (and win
364                  (not (eq tot wh)))
365         (save-selected-window
366           (select-window win)
367           (enlarge-window (- tot wh)))))))
368
369 ;;; Generating the tree.
370
371 (defun gnus-tree-node-insert (header sparse)
372   (let* ((dummy (stringp header))
373          (header (if (vectorp header) header
374                    (progn
375                      (setq header (make-mail-header "*****"))
376                      (mail-header-set-number header 0)
377                      (mail-header-set-lines header 0)
378                      (mail-header-set-chars header 0)
379                      header)))
380          (gnus-tmp-from (mail-header-from header))
381          (gnus-tmp-subject (mail-header-subject header))
382          (gnus-tmp-number (mail-header-number header))
383          (gnus-tmp-name
384           (cond
385            ((string-match "(.+)" gnus-tmp-from)
386             (substring gnus-tmp-from
387                        (1+ (match-beginning 0)) (1- (match-end 0))))
388            ((string-match "<[^>]+> *$" gnus-tmp-from)
389             (let ((beg (match-beginning 0)))
390               (or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
391                        (substring gnus-tmp-from (1+ (match-beginning 0))
392                                   (1- (match-end 0))))
393                   (substring gnus-tmp-from 0 beg))))
394            ((memq gnus-tmp-number sparse)
395             "***")
396            (t gnus-tmp-from)))
397          (gnus-tmp-open-bracket
398           (cond ((memq gnus-tmp-number sparse) 
399                  (caadr gnus-tree-brackets))
400                 (dummy (caaddr gnus-tree-brackets))
401                 (t (caar gnus-tree-brackets))))
402          (gnus-tmp-close-bracket
403           (cond ((memq gnus-tmp-number sparse)
404                  (cdadr gnus-tree-brackets))
405                 (dummy
406                  (cdaddr gnus-tree-brackets))
407                 (t (cdar gnus-tree-brackets))))
408          (buffer-read-only nil)
409          beg end)
410     (add-text-properties
411      (setq beg (point))
412      (setq end (progn (eval gnus-tree-line-format-spec) (point)))
413      (list 'gnus-number gnus-tmp-number))
414     (when (or t (gnus-visual-p 'tree-highlight 'highlight))
415       (gnus-tree-highlight-node gnus-tmp-number beg end))))
416
417 (defun gnus-tree-highlight-node (article beg end)
418   "Highlight current line according to `gnus-summary-highlight'."
419   (let ((list gnus-summary-highlight)
420         face)
421     (save-excursion
422       (set-buffer gnus-summary-buffer)
423       (let* ((score (or (cdr (assq article gnus-newsgroup-scored))
424                         gnus-summary-default-score 0))
425              (default gnus-summary-default-score)
426              (mark (or (gnus-summary-article-mark article) gnus-unread-mark)))
427         ;; Eval the cars of the lists until we find a match.
428         (while (and list
429                     (not (eval (caar list))))
430           (setq list (cdr list)))))
431     (unless (eq (setq face (cdar list)) (get-text-property beg 'face))
432       (put-text-property 
433        beg end 'face 
434        (if (boundp face) (symbol-value face) face)))))
435
436 (defun gnus-tree-indent (level)
437   (insert (make-string (1- (* (1+ gnus-tree-node-length) level)) ? )))
438
439 (defvar gnus-tmp-limit)
440 (defvar gnus-tmp-sparse)
441 (defvar gnus-tmp-indent)
442
443 (defun gnus-generate-tree (thread)
444   "Generate a thread tree for THREAD."
445   (save-excursion
446     (set-buffer (gnus-get-tree-buffer))
447     (let ((buffer-read-only nil)
448           (gnus-tmp-indent 0))
449       (erase-buffer)
450       (funcall gnus-generate-tree-function thread 0)
451       (gnus-set-mode-line 'tree)
452       (goto-char (point-min))
453       (gnus-tree-minimize)
454       (gnus-tree-recenter)
455       (gnus-horizontal-recenter))))
456
457 (defun gnus-generate-horizontal-tree (thread level &optional dummyp)
458   "Generate a horizontal tree."
459   (let* ((dummy (stringp (car thread)))
460          (do (or dummy
461                  (memq (mail-header-number (car thread)) gnus-tmp-limit)))
462          col beg)
463     (if (not do)
464         ;; We don't want this article.
465         (setq thread (cdr thread))
466       (if (not (bolp))
467           ;; Not the first article on the line, so we insert a "-".
468           (insert (car gnus-tree-parent-child-edges))
469         ;; If the level isn't zero, then we insert some indentation.
470         (unless (zerop level)
471           (gnus-tree-indent level)
472           (insert (cadr gnus-tree-parent-child-edges))
473           (setq col (- (setq beg (point)) (gnus-point-at-bol) 1))
474           ;; Draw "|" lines upwards.
475           (while (progn
476                    (forward-line -1)
477                    (forward-char col)
478                    (= (following-char) ? ))
479             (delete-char 1)
480             (insert (caddr gnus-tree-parent-child-edges)))
481           (goto-char beg)))
482       (setq dummyp nil)
483       ;; Insert the article node.
484       (gnus-tree-node-insert (pop thread) gnus-tmp-sparse))
485     (if (null thread)
486         ;; End of the thread, so we go to the next line.
487         (unless (bolp)
488           (insert "\n"))
489       ;; Recurse downwards in all children of this article.
490       (while thread
491         (gnus-generate-horizontal-tree
492          (pop thread) (if do (1+ level) level) 
493          (or dummyp dummy))))))
494
495 (defsubst gnus-tree-indent-vertical ()
496   (let ((len (- (* (1+ gnus-tree-node-length) gnus-tmp-indent) 
497                 (- (point) (gnus-point-at-bol)))))
498     (when (> len 0)
499       (insert (make-string len ? )))))
500
501 (defsubst gnus-tree-forward-line (n)
502   (while (>= (decf n) 0)
503     (unless (zerop (forward-line 1))
504       (end-of-line)
505       (insert "\n")))
506   (end-of-line))
507
508 (defun gnus-generate-vertical-tree (thread level &optional dummyp)
509   "Generate a vertical tree."
510   (let* ((dummy (stringp (car thread)))
511          (do (or dummy
512                  (memq (mail-header-number (car thread)) gnus-tmp-limit)))
513          col beg)
514     (if (not do)
515         ;; We don't want this article.
516         (setq thread (cdr thread))
517       (if (not (save-excursion (beginning-of-line) (bobp)))
518           ;; Not the first article on the line, so we insert a "-".
519           (progn
520             (gnus-tree-indent-vertical)
521             (insert (make-string (/ gnus-tree-node-length 2) ? ))
522             (insert (caddr gnus-tree-parent-child-edges))
523             (gnus-tree-forward-line 1))
524         ;; If the level isn't zero, then we insert some indentation.
525         (unless (zerop gnus-tmp-indent)
526           (gnus-tree-forward-line (1- (* 2 level)))
527           (gnus-tree-indent-vertical)
528           (delete-char -1)
529           (insert (cadr gnus-tree-parent-child-edges))
530           (setq beg (point))
531           ;; Draw "-" lines leftwards.
532           (while (progn
533                    (forward-char -2)
534                    (= (following-char) ? ))
535             (delete-char 1)
536             (insert (car gnus-tree-parent-child-edges)))
537           (goto-char beg)
538           (gnus-tree-forward-line 1)))
539       (setq dummyp nil)
540       ;; Insert the article node.
541       (gnus-tree-indent-vertical)
542       (gnus-tree-node-insert (pop thread) gnus-tmp-sparse)
543       (gnus-tree-forward-line 1))
544     (if (null thread)
545         ;; End of the thread, so we go to the next line.
546         (progn
547           (goto-char (point-min))
548           (end-of-line)
549           (incf gnus-tmp-indent))
550       ;; Recurse downwards in all children of this article.
551       (while thread
552         (gnus-generate-vertical-tree
553          (pop thread) (if do (1+ level) level) 
554          (or dummyp dummy))))))
555
556 ;;; Interface functions.
557
558 (defun gnus-possibly-generate-tree (article &optional force)
559   "Generate the thread tree for ARTICLE if it isn't displayed already."
560   (save-excursion
561     (let ((top (save-excursion
562                  (set-buffer gnus-summary-buffer)
563                  (gnus-cut-thread
564                   (gnus-remove-thread 
565                    (mail-header-id (gnus-summary-article-header article)) t))))
566           (gnus-tmp-limit gnus-newsgroup-limit)
567           (gnus-tmp-sparse gnus-newsgroup-sparse))
568       (when (or force
569                 (not (eq top gnus-tree-displayed-thread)))
570         (gnus-generate-tree top)
571         (setq gnus-tree-displayed-thread top)))))
572
573 (defun gnus-tree-open (group)
574   (gnus-get-tree-buffer))
575
576 (defun gnus-tree-close (group)
577   (gnus-kill-buffer gnus-tree-buffer))
578
579 (defun gnus-highlight-selected-tree (article)
580   "Highlight the selected article in the tree."
581   (let ((buf (current-buffer))
582         region)
583     (set-buffer gnus-tree-buffer)
584     (when (setq region (gnus-tree-article-region article))
585       (unless gnus-selected-tree-overlay
586         ;; Create a new overlay.
587         (gnus-overlay-put
588          (setq gnus-selected-tree-overlay (gnus-make-overlay 1 1))
589          'face gnus-selected-tree-face))
590       ;; Move the overlay to the article.
591       (gnus-move-overlay 
592        gnus-selected-tree-overlay (goto-char (car region)) (cdr region))
593       (gnus-tree-minimize)
594       (gnus-tree-recenter)
595       (gnus-horizontal-recenter))
596     ;; If we remove this save-excursion, it updates the wrong mode lines?!?
597     (save-excursion
598       (set-buffer gnus-tree-buffer)
599       (gnus-set-mode-line 'tree))
600     (set-buffer buf)))
601
602 (defun gnus-tree-highlight-article (article face)
603   (save-excursion
604     (set-buffer (gnus-get-tree-buffer))
605     (let (region)
606       (when (setq region (gnus-tree-article-region article))
607         (put-text-property (car region) (cdr region) 'face face)))))
608
609 ;;; gnus-salt.el ends here