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