Update copyright notices for 2013.
[bpt/emacs.git] / lisp / gnus / gnus-salt.el
CommitLineData
eec82323 1;;; gnus-salt.el --- alternate summary mode interfaces for Gnus
16409b0b 2
ab422c4d 3;; Copyright (C) 1996-1999, 2001-2013 Free Software Foundation, Inc.
eec82323 4
6748645f
LMI
5;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6;; Keywords: news
eec82323
LMI
7
8;; This file is part of GNU Emacs.
9
5e809f55 10;; GNU Emacs is free software: you can redistribute it and/or modify
eec82323 11;; it under the terms of the GNU General Public License as published by
5e809f55
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
eec82323
LMI
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
5e809f55 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
eec82323
LMI
22
23;;; Commentary:
24
25;;; Code:
26
5ab7173c 27(eval-when-compile (require 'cl))
d029b5d2
KY
28(eval-when-compile
29 (when (featurep 'xemacs)
30 (require 'easy-mmode))) ; for `define-minor-mode'
5ab7173c 31
eec82323
LMI
32(require 'gnus)
33(require 'gnus-sum)
23f87bed 34(require 'gnus-win)
eec82323
LMI
35
36;;;
37;;; gnus-pick-mode
38;;;
39
a8151ef7
LMI
40(defcustom gnus-pick-display-summary nil
41 "*Display summary while reading."
42 :type 'boolean
43 :group 'gnus-summary-pick)
44
45(defcustom gnus-pick-mode-hook nil
46 "Hook run in summary pick mode buffers."
47 :type 'hook
48 :group 'gnus-summary-pick)
49
23f87bed
MB
50(when (featurep 'xemacs)
51 (add-hook 'gnus-pick-mode-hook 'gnus-xmas-pick-menu-add))
52
a8151ef7
LMI
53(defcustom gnus-mark-unpicked-articles-as-read nil
54 "*If non-nil, mark all unpicked articles as read."
55 :type 'boolean
56 :group 'gnus-summary-pick)
57
58(defcustom gnus-pick-elegant-flow t
23f87bed
MB
59 "If non-nil, `gnus-pick-start-reading' runs
60 `gnus-summary-next-group' when no articles have been picked."
a8151ef7
LMI
61 :type 'boolean
62 :group 'gnus-summary-pick)
63
64(defcustom gnus-summary-pick-line-format
23f87bed 65 "%-5P %U\%R\%z\%I\%(%[%4L: %-23,23n%]%) %s\n"
eec82323 66 "*The format specification of the lines in pick buffers.
a8151ef7
LMI
67It accepts the same format specs that `gnus-summary-line-format' does."
68 :type 'string
69 :group 'gnus-summary-pick)
eec82323
LMI
70
71;;; Internal variables.
72
bbf52f1e
SM
73(defvar gnus-pick-mode-map
74 (let ((map (make-sparse-keymap)))
75 (gnus-define-keys map
76 " " gnus-pick-next-page
77 "u" gnus-pick-unmark-article-or-thread
78 "." gnus-pick-article-or-thread
79 gnus-down-mouse-2 gnus-pick-mouse-pick-region
80 "\r" gnus-pick-start-reading)
81 map))
eec82323
LMI
82
83(defun gnus-pick-make-menu-bar ()
84 (unless (boundp 'gnus-pick-menu)
85 (easy-menu-define
23f87bed
MB
86 gnus-pick-menu gnus-pick-mode-map ""
87 '("Pick"
88 ("Pick"
89 ["Article" gnus-summary-mark-as-processable t]
90 ["Thread" gnus-uu-mark-thread t]
91 ["Region" gnus-uu-mark-region t]
92 ["Regexp" gnus-uu-mark-by-regexp t]
93 ["Buffer" gnus-uu-mark-buffer t])
94 ("Unpick"
95 ["Article" gnus-summary-unmark-as-processable t]
96 ["Thread" gnus-uu-unmark-thread t]
97 ["Region" gnus-uu-unmark-region t]
98 ["Regexp" gnus-uu-unmark-by-regexp t]
99 ["Buffer" gnus-summary-unmark-all-processable t])
100 ["Start reading" gnus-pick-start-reading t]
101 ["Switch pick mode off" gnus-pick-mode gnus-pick-mode]))))
eec82323 102
765d4319
KY
103(eval-when-compile
104 (when (featurep 'xemacs)
105 (defvar gnus-pick-mode-on-hook)
106 (defvar gnus-pick-mode-off-hook)))
107
bbf52f1e 108(define-minor-mode gnus-pick-mode
eec82323
LMI
109 "Minor mode for providing a pick-and-read interface in Gnus summary buffers.
110
111\\{gnus-pick-mode-map}"
bbf52f1e
SM
112 :lighter " Pick" :keymap gnus-pick-mode-map
113 (cond
114 ((not (derived-mode-p 'gnus-summary-mode)) (setq gnus-pick-mode nil))
115 ((not gnus-pick-mode)
116 ;; FIXME: a buffer-local minor mode removing globally from a hook??
117 (remove-hook 'gnus-message-setup-hook 'gnus-pick-setup-message))
118 (t
119 ;; Make sure that we don't select any articles upon group entry.
120 (set (make-local-variable 'gnus-auto-select-first) nil)
121 ;; Change line format.
122 (setq gnus-summary-line-format gnus-summary-pick-line-format)
123 (setq gnus-summary-line-format-spec nil)
124 (gnus-update-format-specifications nil 'summary)
125 (gnus-update-summary-mark-positions)
126 ;; FIXME: a buffer-local minor mode adding globally to a hook??
127 (add-hook 'gnus-message-setup-hook 'gnus-pick-setup-message)
128 (set (make-local-variable 'gnus-summary-goto-unread) 'never)
129 ;; Set up the menu.
130 (when (gnus-visual-p 'pick-menu 'menu)
131 (gnus-pick-make-menu-bar)))))
eec82323
LMI
132
133(defun gnus-pick-setup-message ()
134 "Make Message do the right thing on exit."
135 (when (and (gnus-buffer-live-p gnus-summary-buffer)
67d0660b 136 (with-current-buffer gnus-summary-buffer
eec82323
LMI
137 gnus-pick-mode))
138 (message-add-action
67d0660b 139 `(gnus-configure-windows ,gnus-current-window-configuration t)
16409b0b 140 'send 'exit 'postpone 'kill)))
eec82323
LMI
141
142(defvar gnus-pick-line-number 1)
143(defun gnus-pick-line-number ()
144 "Return the current line number."
145 (if (bobp)
146 (setq gnus-pick-line-number 1)
147 (incf gnus-pick-line-number)))
148
149(defun gnus-pick-start-reading (&optional catch-up)
150 "Start reading the picked articles.
151If given a prefix, mark all unpicked articles as read."
152 (interactive "P")
153 (if gnus-newsgroup-processable
154 (progn
23f87bed
MB
155 (gnus-summary-limit-to-articles nil)
156 (when (or catch-up gnus-mark-unpicked-articles-as-read)
eec82323 157 (gnus-summary-limit-mark-excluded-as-read))
23f87bed
MB
158 (gnus-summary-first-article)
159 (gnus-configure-windows
eec82323
LMI
160 (if gnus-pick-display-summary 'article 'pick) t))
161 (if gnus-pick-elegant-flow
162 (progn
163 (when (or catch-up gnus-mark-unpicked-articles-as-read)
a8151ef7 164 (gnus-summary-catchup nil t))
eec82323
LMI
165 (if (gnus-group-quit-config gnus-newsgroup-name)
166 (gnus-summary-exit)
167 (gnus-summary-next-group)))
168 (error "No articles have been picked"))))
169
6748645f 170(defun gnus-pick-goto-article (arg)
16409b0b
GM
171 "Go to the article number indicated by ARG.
172If ARG is an invalid article number, then stay on current line."
6748645f
LMI
173 (let (pos)
174 (save-excursion
175 (goto-char (point-min))
176 (when (zerop (forward-line (1- (prefix-numeric-value arg))))
177 (setq pos (point))))
178 (if (not pos)
179 (gnus-error 2 "No such line: %s" arg)
180 (goto-char pos))))
16409b0b 181
eec82323 182(defun gnus-pick-article (&optional arg)
16409b0b 183 "Pick the article on the current line.
eec82323
LMI
184If ARG, pick the article on that line instead."
185 (interactive "P")
186 (when arg
6748645f 187 (gnus-pick-goto-article arg))
eec82323
LMI
188 (gnus-summary-mark-as-processable 1))
189
6748645f 190(defun gnus-pick-article-or-thread (&optional arg)
16409b0b 191 "If `gnus-thread-hide-subtree' is t, then pick the thread on the current line.
6748645f
LMI
192Otherwise pick the article on the current line.
193If ARG, pick the article/thread on that line instead."
194 (interactive "P")
195 (when arg
196 (gnus-pick-goto-article arg))
197 (if gnus-thread-hide-subtree
16409b0b
GM
198 (progn
199 (save-excursion
200 (gnus-uu-mark-thread))
201 (forward-line 1))
6748645f 202 (gnus-summary-mark-as-processable 1)))
16409b0b 203
6748645f 204(defun gnus-pick-unmark-article-or-thread (&optional arg)
16409b0b 205 "If `gnus-thread-hide-subtree' is t, then unmark the thread on current line.
6748645f
LMI
206Otherwise unmark the article on current line.
207If ARG, unmark thread/article on that line instead."
208 (interactive "P")
209 (when arg
210 (gnus-pick-goto-article arg))
211 (if gnus-thread-hide-subtree
16409b0b
GM
212 (save-excursion
213 (gnus-uu-unmark-thread))
6748645f 214 (gnus-summary-unmark-as-processable 1)))
16409b0b 215
eec82323
LMI
216(defun gnus-pick-mouse-pick (e)
217 (interactive "e")
218 (mouse-set-point e)
219 (save-excursion
220 (gnus-summary-mark-as-processable 1)))
221
222(defun gnus-pick-mouse-pick-region (start-event)
223 "Pick articles that the mouse is dragged over.
224This must be bound to a button-down mouse event."
225 (interactive "e")
226 (mouse-minibuffer-check start-event)
227 (let* ((echo-keystrokes 0)
228 (start-posn (event-start start-event))
229 (start-point (posn-point start-posn))
9b40100a 230 (start-line (1+ (count-lines (point-min) start-point)))
eec82323 231 (start-window (posn-window start-posn))
6748645f 232 (bounds (gnus-window-edges start-window))
eec82323
LMI
233 (top (nth 1 bounds))
234 (bottom (if (window-minibuffer-p start-window)
235 (nth 3 bounds)
236 ;; Don't count the mode line.
237 (1- (nth 3 bounds))))
238 (click-count (1- (event-click-count start-event))))
239 (setq mouse-selection-click-count click-count)
240 (setq mouse-selection-click-count-buffer (current-buffer))
241 (mouse-set-point start-event)
23f87bed 242 ;; In case the down click is in the middle of some intangible text,
eec82323
LMI
243 ;; use the end of that text, and put it in START-POINT.
244 (when (< (point) start-point)
245 (goto-char start-point))
246 (gnus-pick-article)
247 (setq start-point (point))
248 ;; end-of-range is used only in the single-click case.
249 ;; It is the place where the drag has reached so far
250 ;; (but not outside the window where the drag started).
6748645f 251 (let (event end end-point (end-of-range (point)))
eec82323 252 (track-mouse
23f87bed
MB
253 (while (progn
254 (setq event (cdr (gnus-read-event-char)))
255 (or (mouse-movement-p event)
256 (eq (car-safe event) 'switch-frame)))
257 (if (eq (car-safe event) 'switch-frame)
258 nil
259 (setq end (event-end event)
260 end-point (posn-point end))
261
262 (cond
263 ;; Are we moving within the original window?
264 ((and (eq (posn-window end) start-window)
265 (integer-or-marker-p end-point))
9b40100a 266 ;; Go to START-POINT first, so that when we move to END-POINT,
23f87bed
MB
267 ;; if it's in the middle of intangible text,
268 ;; point jumps in the direction away from START-POINT.
269 (goto-char start-point)
270 (goto-char end-point)
271 (gnus-pick-article)
272 ;; In case the user moved his mouse really fast, pick
9b40100a
SM
273 ;; articles on the line between this one and the last one.
274 (let* ((this-line (1+ (count-lines (point-min) end-point)))
23f87bed
MB
275 (min-line (min this-line start-line))
276 (max-line (max this-line start-line)))
277 (while (< min-line max-line)
7a4abdd6
GM
278 (goto-char (point-min))
279 (forward-line (1- min-line))
23f87bed
MB
280 (gnus-pick-article)
281 (setq min-line (1+ min-line)))
282 (setq start-line this-line))
283 (when (zerop (% click-count 3))
284 (setq end-of-range (point))))
285 (t
286 (let ((mouse-row (cdr (cdr (mouse-position)))))
287 (cond
288 ((null mouse-row))
289 ((< mouse-row top)
290 (mouse-scroll-subr start-window (- mouse-row top)))
291 ((>= mouse-row bottom)
292 (mouse-scroll-subr start-window
293 (1+ (- mouse-row bottom)))))))))))
eec82323
LMI
294 (when (consp event)
295 (let ((fun (key-binding (vector (car event)))))
296 ;; Run the binding of the terminating up-event, if possible.
23f87bed 297 ;; In the case of a multiple click, it gives the wrong results,
eec82323
LMI
298 ;; because it would fail to set up a region.
299 (when nil
23f87bed
MB
300 ;; (and (= (mod mouse-selection-click-count 3) 0) (fboundp fun))
301 ;; In this case, we can just let the up-event execute normally.
eec82323
LMI
302 (let ((end (event-end event)))
303 ;; Set the position in the event before we replay it,
304 ;; because otherwise it may have a position in the wrong
305 ;; buffer.
306 (setcar (cdr end) end-of-range)
307 ;; Delete the overlay before calling the function,
23f87bed 308 ;; because delete-overlay increases buffer-modified-tick.
eec82323
LMI
309 (push event unread-command-events))))))))
310
311(defun gnus-pick-next-page ()
312 "Go to the next page. If at the end of the buffer, start reading articles."
313 (interactive)
314 (let ((scroll-in-place nil))
315 (condition-case nil
316 (scroll-up)
317 (end-of-buffer (gnus-pick-start-reading)))))
318
319;;;
320;;; gnus-binary-mode
321;;;
322
eec82323
LMI
323(defvar gnus-binary-mode-hook nil
324 "Hook run in summary binary mode buffers.")
325
bbf52f1e
SM
326(defvar gnus-binary-mode-map
327 (let ((map (make-sparse-keymap)))
328 (gnus-define-keys map
329 "g" gnus-binary-show-article)
330 map))
eec82323
LMI
331
332(defun gnus-binary-make-menu-bar ()
333 (unless (boundp 'gnus-binary-menu)
334 (easy-menu-define
23f87bed
MB
335 gnus-binary-menu gnus-binary-mode-map ""
336 '("Pick"
337 ["Switch binary mode off" gnus-binary-mode t]))))
eec82323 338
765d4319
KY
339(eval-when-compile
340 (when (featurep 'xemacs)
341 (defvar gnus-binary-mode-on-hook)
342 (defvar gnus-binary-mode-off-hook)))
343
bbf52f1e 344(define-minor-mode gnus-binary-mode
eec82323 345 "Minor mode for providing a binary group interface in Gnus summary buffers."
bbf52f1e
SM
346 :lighter " Binary" :keymap gnus-binary-mode-map
347 (cond
348 ((not (derived-mode-p 'gnus-summary-mode)) (setq gnus-binary-mode nil))
349 (gnus-binary-mode
350 ;; Make sure that we don't select any articles upon group entry.
351 (make-local-variable 'gnus-auto-select-first)
352 (setq gnus-auto-select-first nil)
353 (make-local-variable 'gnus-summary-display-article-function)
354 (setq gnus-summary-display-article-function 'gnus-binary-display-article)
355 ;; Set up the menu.
356 (when (gnus-visual-p 'binary-menu 'menu)
357 (gnus-binary-make-menu-bar)))))
eec82323
LMI
358
359(defun gnus-binary-display-article (article &optional all-header)
360 "Run ARTICLE through the binary decode functions."
361 (when (gnus-summary-goto-subject article)
23f87bed 362 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
eec82323
LMI
363 (gnus-uu-decode-uu))))
364
365(defun gnus-binary-show-article (&optional arg)
366 "Bypass the binary functions and show the article."
367 (interactive "P")
368 (let (gnus-summary-display-article-function)
369 (gnus-summary-show-article arg)))
370
371;;;
372;;; gnus-tree-mode
373;;;
374
a8151ef7
LMI
375(defcustom gnus-tree-line-format "%(%[%3,3n%]%)"
376 "Format of tree elements."
377 :type 'string
378 :group 'gnus-summary-tree)
eec82323 379
a8151ef7 380(defcustom gnus-tree-minimize-window t
eec82323
LMI
381 "If non-nil, minimize the tree buffer window.
382If a number, never let the tree buffer grow taller than that number of
a8151ef7 383lines."
6748645f
LMI
384 :type '(choice boolean
385 integer)
a8151ef7 386 :group 'gnus-summary-tree)
eec82323 387
a8151ef7
LMI
388(defcustom gnus-selected-tree-face 'modeline
389 "*Face used for highlighting selected articles in the thread tree."
390 :type 'face
391 :group 'gnus-summary-tree)
eec82323
LMI
392
393(defvar gnus-tree-brackets '((?\[ . ?\]) (?\( . ?\))
394 (?\{ . ?\}) (?< . ?>))
395 "Brackets used in tree nodes.")
396
397(defvar gnus-tree-parent-child-edges '(?- ?\\ ?|)
398 "Characters used to connect parents with children.")
399
a8151ef7
LMI
400(defcustom gnus-tree-mode-line-format "Gnus: %%b %S %Z"
401 "*The format specification for the tree mode line."
402 :type 'string
403 :group 'gnus-summary-tree)
eec82323 404
a8151ef7 405(defcustom gnus-generate-tree-function 'gnus-generate-vertical-tree
eec82323
LMI
406 "*Function for generating a thread tree.
407Two predefined functions are available:
a8151ef7
LMI
408`gnus-generate-horizontal-tree' and `gnus-generate-vertical-tree'."
409 :type '(radio (function-item gnus-generate-vertical-tree)
410 (function-item gnus-generate-horizontal-tree)
411 (function :tag "Other" nil))
412 :group 'gnus-summary-tree)
eec82323 413
a8151ef7
LMI
414(defcustom gnus-tree-mode-hook nil
415 "*Hook run in tree mode buffers."
416 :type 'hook
417 :group 'gnus-summary-tree)
eec82323 418
23f87bed
MB
419(when (featurep 'xemacs)
420 (add-hook 'gnus-tree-mode-hook 'gnus-xmas-tree-menu-add)
421 (add-hook 'gnus-tree-mode-hook 'gnus-xmas-switch-horizontal-scrollbar-off))
422
423
eec82323
LMI
424;;; Internal variables.
425
426(defvar gnus-tree-line-format-alist
427 `((?n gnus-tmp-name ?s)
428 (?f gnus-tmp-from ?s)
429 (?N gnus-tmp-number ?d)
430 (?\[ gnus-tmp-open-bracket ?c)
431 (?\] gnus-tmp-close-bracket ?c)
432 (?s gnus-tmp-subject ?s)))
433
434(defvar gnus-tree-mode-line-format-alist gnus-summary-mode-line-format-alist)
435
436(defvar gnus-tree-mode-line-format-spec nil)
437(defvar gnus-tree-line-format-spec nil)
438
439(defvar gnus-tree-node-length nil)
440(defvar gnus-selected-tree-overlay nil)
441
442(defvar gnus-tree-displayed-thread nil)
16409b0b 443(defvar gnus-tree-inhibit nil)
eec82323
LMI
444
445(defvar gnus-tree-mode-map nil)
446(put 'gnus-tree-mode 'mode-class 'special)
447
448(unless gnus-tree-mode-map
449 (setq gnus-tree-mode-map (make-keymap))
450 (suppress-keymap gnus-tree-mode-map)
451 (gnus-define-keys
16409b0b
GM
452 gnus-tree-mode-map
453 "\r" gnus-tree-select-article
454 gnus-mouse-2 gnus-tree-pick-article
455 "\C-?" gnus-tree-read-summary-keys
456 "h" gnus-tree-show-summary
eec82323 457
16409b0b 458 "\C-c\C-i" gnus-info-find-node)
eec82323
LMI
459
460 (substitute-key-definition
461 'undefined 'gnus-tree-read-summary-keys gnus-tree-mode-map))
462
463(defun gnus-tree-make-menu-bar ()
464 (unless (boundp 'gnus-tree-menu)
465 (easy-menu-define
23f87bed
MB
466 gnus-tree-menu gnus-tree-mode-map ""
467 '("Tree"
468 ["Select article" gnus-tree-select-article t]))))
eec82323
LMI
469
470(defun gnus-tree-mode ()
471 "Major mode for displaying thread trees."
472 (interactive)
6748645f
LMI
473 (gnus-set-format 'tree-mode)
474 (gnus-set-format 'tree t)
eec82323
LMI
475 (when (gnus-visual-p 'tree-menu 'menu)
476 (gnus-tree-make-menu-bar))
477 (kill-all-local-variables)
478 (gnus-simplify-mode-line)
479 (setq mode-name "Tree")
480 (setq major-mode 'gnus-tree-mode)
481 (use-local-map gnus-tree-mode-map)
16409b0b 482 (buffer-disable-undo)
eec82323
LMI
483 (setq buffer-read-only t)
484 (setq truncate-lines t)
485 (save-excursion
486 (gnus-set-work-buffer)
487 (gnus-tree-node-insert (make-mail-header "") nil)
488 (setq gnus-tree-node-length (1- (point))))
cfcd5c91 489 (gnus-run-mode-hooks 'gnus-tree-mode-hook))
eec82323
LMI
490
491(defun gnus-tree-read-summary-keys (&optional arg)
492 "Read a summary buffer key sequence and execute it."
493 (interactive "P")
16409b0b
GM
494 (unless gnus-tree-inhibit
495 (let ((buf (current-buffer))
496 (gnus-tree-inhibit t)
497 win)
498 (set-buffer gnus-article-buffer)
499 (gnus-article-read-summary-keys arg nil t)
500 (when (setq win (get-buffer-window buf))
501 (select-window win)
502 (when gnus-selected-tree-overlay
503 (goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
504 (gnus-tree-minimize)))))
eec82323 505
a8151ef7
LMI
506(defun gnus-tree-show-summary ()
507 "Reconfigure windows to show summary buffer."
508 (interactive)
509 (if (not (gnus-buffer-live-p gnus-summary-buffer))
510 (error "There is no summary buffer for this tree buffer")
511 (gnus-configure-windows 'article)
512 (gnus-summary-goto-subject gnus-current-article)))
513
eec82323
LMI
514(defun gnus-tree-select-article (article)
515 "Select the article under point, if any."
516 (interactive (list (gnus-tree-article-number)))
517 (let ((buf (current-buffer)))
518 (when article
67d0660b 519 (with-current-buffer gnus-summary-buffer
eec82323
LMI
520 (gnus-summary-goto-article article))
521 (select-window (get-buffer-window buf)))))
522
523(defun gnus-tree-pick-article (e)
524 "Select the article under the mouse pointer."
525 (interactive "e")
526 (mouse-set-point e)
527 (gnus-tree-select-article (gnus-tree-article-number)))
528
529(defun gnus-tree-article-number ()
530 (get-text-property (point) 'gnus-number))
531
532(defun gnus-tree-article-region (article)
533 "Return a cons with BEG and END of the article region."
16409b0b
GM
534 (let ((pos (text-property-any
535 (point-min) (point-max) 'gnus-number article)))
eec82323
LMI
536 (when pos
537 (cons pos (next-single-property-change pos 'gnus-number)))))
538
eec82323
LMI
539(defun gnus-tree-recenter ()
540 "Center point in the tree window."
541 (let ((selected (selected-window))
23f87bed 542 (tree-window (gnus-get-buffer-window gnus-tree-buffer t)))
eec82323
LMI
543 (when tree-window
544 (select-window tree-window)
545 (when gnus-selected-tree-overlay
546 (goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
547 (let* ((top (cond ((< (window-height) 4) 0)
548 ((< (window-height) 7) 1)
549 (t 2)))
550 (height (1- (window-height)))
551 (bottom (save-excursion (goto-char (point-max))
552 (forward-line (- height))
553 (point))))
23f87bed 554 ;; Set the window start to either `bottom', which is the biggest
eec82323
LMI
555 ;; possible valid number, or the second line from the top,
556 ;; whichever is the least.
557 (set-window-start
558 tree-window (min bottom (save-excursion
559 (forward-line (- top)) (point)))))
560 (select-window selected))))
561
562(defun gnus-get-tree-buffer ()
563 "Return the tree buffer properly initialized."
67d0660b 564 (with-current-buffer (gnus-get-buffer-create gnus-tree-buffer)
eec82323 565 (unless (eq major-mode 'gnus-tree-mode)
eec82323
LMI
566 (gnus-tree-mode))
567 (current-buffer)))
568
569(defun gnus-tree-minimize ()
570 (when (and gnus-tree-minimize-window
571 (not (one-window-p)))
572 (let ((windows 0)
573 tot-win-height)
574 (walk-windows (lambda (window) (incf windows)))
575 (setq tot-win-height
576 (- (frame-height)
577 (* window-min-height (1- windows))
578 2))
579 (let* ((window-min-height 2)
580 (height (count-lines (point-min) (point-max)))
581 (min (max (1- window-min-height) height))
582 (tot (if (numberp gnus-tree-minimize-window)
583 (min gnus-tree-minimize-window min)
584 min))
585 (win (get-buffer-window (current-buffer)))
586 (wh (and win (1- (window-height win)))))
587 (setq tot (min tot tot-win-height))
588 (when (and win
589 (not (eq tot wh)))
590 (let ((selected (selected-window)))
591 (when (ignore-errors (select-window win))
592 (enlarge-window (- tot wh))
593 (select-window selected))))))))
594
595;;; Generating the tree.
596
597(defun gnus-tree-node-insert (header sparse &optional adopted)
598 (let* ((dummy (stringp header))
599 (header (if (vectorp header) header
600 (progn
601 (setq header (make-mail-header "*****"))
602 (mail-header-set-number header 0)
603 (mail-header-set-lines header 0)
604 (mail-header-set-chars header 0)
605 header)))
606 (gnus-tmp-from (mail-header-from header))
607 (gnus-tmp-subject (mail-header-subject header))
608 (gnus-tmp-number (mail-header-number header))
609 (gnus-tmp-name
610 (cond
611 ((string-match "(.+)" gnus-tmp-from)
612 (substring gnus-tmp-from
613 (1+ (match-beginning 0)) (1- (match-end 0))))
614 ((string-match "<[^>]+> *$" gnus-tmp-from)
615 (let ((beg (match-beginning 0)))
616 (or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
617 (substring gnus-tmp-from (1+ (match-beginning 0))
618 (1- (match-end 0))))
619 (substring gnus-tmp-from 0 beg))))
620 ((memq gnus-tmp-number sparse)
621 "***")
622 (t gnus-tmp-from)))
623 (gnus-tmp-open-bracket
624 (cond ((memq gnus-tmp-number sparse)
625 (caadr gnus-tree-brackets))
626 (dummy (caaddr gnus-tree-brackets))
627 (adopted (car (nth 3 gnus-tree-brackets)))
628 (t (caar gnus-tree-brackets))))
629 (gnus-tmp-close-bracket
630 (cond ((memq gnus-tmp-number sparse)
631 (cdadr gnus-tree-brackets))
632 (adopted (cdr (nth 3 gnus-tree-brackets)))
633 (dummy
634 (cdaddr gnus-tree-brackets))
635 (t (cdar gnus-tree-brackets))))
636 (buffer-read-only nil)
637 beg end)
638 (gnus-add-text-properties
639 (setq beg (point))
640 (setq end (progn (eval gnus-tree-line-format-spec) (point)))
641 (list 'gnus-number gnus-tmp-number))
642 (when (or t (gnus-visual-p 'tree-highlight 'highlight))
643 (gnus-tree-highlight-node gnus-tmp-number beg end))))
644
645(defun gnus-tree-highlight-node (article beg end)
646 "Highlight current line according to `gnus-summary-highlight'."
647 (let ((list gnus-summary-highlight)
648 face)
67d0660b 649 (with-current-buffer gnus-summary-buffer
eec82323
LMI
650 (let* ((score (or (cdr (assq article gnus-newsgroup-scored))
651 gnus-summary-default-score 0))
652 (default gnus-summary-default-score)
23f87bed
MB
653 (default-high gnus-summary-default-high-score)
654 (default-low gnus-summary-default-low-score)
655 (uncached (memq article gnus-newsgroup-undownloaded))
656 (downloaded (not uncached))
eec82323
LMI
657 (mark (or (gnus-summary-article-mark article) gnus-unread-mark)))
658 ;; Eval the cars of the lists until we find a match.
659 (while (and list
660 (not (eval (caar list))))
661 (setq list (cdr list)))))
662 (unless (eq (setq face (cdar list)) (get-text-property beg 'face))
1d43a35f
CY
663 (gnus-put-text-property-excluding-characters-with-faces
664 beg end 'face
665 (if (boundp face) (symbol-value face) face)))))
eec82323
LMI
666
667(defun gnus-tree-indent (level)
668 (insert (make-string (1- (* (1+ gnus-tree-node-length) level)) ? )))
669
670(defvar gnus-tmp-limit)
671(defvar gnus-tmp-sparse)
672(defvar gnus-tmp-indent)
673
674(defun gnus-generate-tree (thread)
675 "Generate a thread tree for THREAD."
67d0660b 676 (with-current-buffer (gnus-get-tree-buffer)
eec82323
LMI
677 (let ((buffer-read-only nil)
678 (gnus-tmp-indent 0))
679 (erase-buffer)
680 (funcall gnus-generate-tree-function thread 0)
681 (gnus-set-mode-line 'tree)
682 (goto-char (point-min))
683 (gnus-tree-minimize)
684 (gnus-tree-recenter)
685 (let ((selected (selected-window)))
23f87bed
MB
686 (when (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t)
687 (select-window (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t))
eec82323
LMI
688 (gnus-horizontal-recenter)
689 (select-window selected))))))
690
691(defun gnus-generate-horizontal-tree (thread level &optional dummyp adopted)
692 "Generate a horizontal tree."
693 (let* ((dummy (stringp (car thread)))
694 (do (or dummy
a8151ef7
LMI
695 (and (car thread)
696 (memq (mail-header-number (car thread))
697 gnus-tmp-limit))))
eec82323
LMI
698 col beg)
699 (if (not do)
700 ;; We don't want this article.
701 (setq thread (cdr thread))
702 (if (not (bolp))
703 ;; Not the first article on the line, so we insert a "-".
704 (insert (car gnus-tree-parent-child-edges))
705 ;; If the level isn't zero, then we insert some indentation.
706 (unless (zerop level)
707 (gnus-tree-indent level)
708 (insert (cadr gnus-tree-parent-child-edges))
01c52d31 709 (setq col (- (setq beg (point)) (point-at-bol) 1))
eec82323
LMI
710 ;; Draw "|" lines upwards.
711 (while (progn
712 (forward-line -1)
713 (forward-char col)
16409b0b 714 (eq (char-after) ? ))
eec82323
LMI
715 (delete-char 1)
716 (insert (caddr gnus-tree-parent-child-edges)))
717 (goto-char beg)))
718 (setq dummyp nil)
719 ;; Insert the article node.
720 (gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted))
721 (if (null thread)
722 ;; End of the thread, so we go to the next line.
723 (unless (bolp)
724 (insert "\n"))
725 ;; Recurse downwards in all children of this article.
726 (while thread
727 (gnus-generate-horizontal-tree
728 (pop thread) (if do (1+ level) level)
729 (or dummyp dummy) dummy)))))
730
731(defsubst gnus-tree-indent-vertical ()
732 (let ((len (- (* (1+ gnus-tree-node-length) gnus-tmp-indent)
01c52d31 733 (- (point) (point-at-bol)))))
eec82323
LMI
734 (when (> len 0)
735 (insert (make-string len ? )))))
736
737(defsubst gnus-tree-forward-line (n)
738 (while (>= (decf n) 0)
739 (unless (zerop (forward-line 1))
740 (end-of-line)
741 (insert "\n")))
742 (end-of-line))
743
744(defun gnus-generate-vertical-tree (thread level &optional dummyp adopted)
745 "Generate a vertical tree."
746 (let* ((dummy (stringp (car thread)))
747 (do (or dummy
748 (and (car thread)
749 (memq (mail-header-number (car thread))
750 gnus-tmp-limit))))
751 beg)
752 (if (not do)
753 ;; We don't want this article.
754 (setq thread (cdr thread))
755 (if (not (save-excursion (beginning-of-line) (bobp)))
756 ;; Not the first article on the line, so we insert a "-".
757 (progn
758 (gnus-tree-indent-vertical)
759 (insert (make-string (/ gnus-tree-node-length 2) ? ))
760 (insert (caddr gnus-tree-parent-child-edges))
761 (gnus-tree-forward-line 1))
762 ;; If the level isn't zero, then we insert some indentation.
763 (unless (zerop gnus-tmp-indent)
764 (gnus-tree-forward-line (1- (* 2 level)))
765 (gnus-tree-indent-vertical)
766 (delete-char -1)
767 (insert (cadr gnus-tree-parent-child-edges))
768 (setq beg (point))
a8151ef7 769 (forward-char -1)
eec82323 770 ;; Draw "-" lines leftwards.
9b40100a 771 (while (and (not (bobp))
16409b0b 772 (eq (char-after (1- (point))) ? ))
a8151ef7
LMI
773 (delete-char -1)
774 (insert (car gnus-tree-parent-child-edges))
775 (forward-char -1))
eec82323
LMI
776 (goto-char beg)
777 (gnus-tree-forward-line 1)))
778 (setq dummyp nil)
779 ;; Insert the article node.
780 (gnus-tree-indent-vertical)
781 (gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted)
782 (gnus-tree-forward-line 1))
783 (if (null thread)
784 ;; End of the thread, so we go to the next line.
785 (progn
786 (goto-char (point-min))
787 (end-of-line)
788 (incf gnus-tmp-indent))
789 ;; Recurse downwards in all children of this article.
790 (while thread
791 (gnus-generate-vertical-tree
792 (pop thread) (if do (1+ level) level)
793 (or dummyp dummy) dummy)))))
794
795;;; Interface functions.
796
797(defun gnus-possibly-generate-tree (article &optional force)
798 "Generate the thread tree for ARTICLE if it isn't displayed already."
67d0660b 799 (when (with-current-buffer gnus-summary-buffer
eec82323
LMI
800 (and gnus-use-trees
801 gnus-show-threads
802 (vectorp (gnus-summary-article-header article))))
803 (save-excursion
67d0660b 804 (let ((top (with-current-buffer gnus-summary-buffer
eec82323
LMI
805 (gnus-cut-thread
806 (gnus-remove-thread
807 (mail-header-id
808 (gnus-summary-article-header article))
809 t))))
810 (gnus-tmp-limit gnus-newsgroup-limit)
811 (gnus-tmp-sparse gnus-newsgroup-sparse))
812 (when (or force
813 (not (eq top gnus-tree-displayed-thread)))
814 (gnus-generate-tree top)
815 (setq gnus-tree-displayed-thread top))))))
816
817(defun gnus-tree-open (group)
818 (gnus-get-tree-buffer))
819
820(defun gnus-tree-close (group)
6748645f 821 (gnus-kill-buffer gnus-tree-buffer))
eec82323 822
23f87bed
MB
823(defun gnus-tree-perhaps-minimize ()
824 (when (and gnus-tree-minimize-window
825 (get-buffer gnus-tree-buffer))
67d0660b 826 (with-current-buffer gnus-tree-buffer
23f87bed
MB
827 (gnus-tree-minimize))))
828
eec82323
LMI
829(defun gnus-highlight-selected-tree (article)
830 "Highlight the selected article in the tree."
831 (let ((buf (current-buffer))
832 region)
833 (set-buffer gnus-tree-buffer)
834 (when (setq region (gnus-tree-article-region article))
835 (when (or (not gnus-selected-tree-overlay)
836 (gnus-extent-detached-p gnus-selected-tree-overlay))
837 ;; Create a new overlay.
838 (gnus-overlay-put
9b40100a
SM
839 (setq gnus-selected-tree-overlay
840 (gnus-make-overlay (point-min) (1+ (point-min))))
eec82323
LMI
841 'face gnus-selected-tree-face))
842 ;; Move the overlay to the article.
843 (gnus-move-overlay
844 gnus-selected-tree-overlay (goto-char (car region)) (cdr region))
845 (gnus-tree-minimize)
846 (gnus-tree-recenter)
847 (let ((selected (selected-window)))
23f87bed
MB
848 (when (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t)
849 (select-window (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t))
eec82323
LMI
850 (gnus-horizontal-recenter)
851 (select-window selected))))
23f87bed 852;; If we remove this save-excursion, it updates the wrong mode lines?!?
67d0660b 853 (with-current-buffer gnus-tree-buffer
eec82323
LMI
854 (gnus-set-mode-line 'tree))
855 (set-buffer buf)))
856
857(defun gnus-tree-highlight-article (article face)
67d0660b 858 (with-current-buffer (gnus-get-tree-buffer)
eec82323
LMI
859 (let (region)
860 (when (setq region (gnus-tree-article-region article))
861 (gnus-put-text-property (car region) (cdr region) 'face face)
862 (set-window-point
23f87bed 863 (gnus-get-buffer-window (current-buffer) t) (cdr region))))))
eec82323 864
eec82323
LMI
865;;; Allow redefinition of functions.
866(gnus-ems-redefine)
867
868(provide 'gnus-salt)
869
870;;; gnus-salt.el ends here