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