* progmodes/cperl-mode.el (cperl-mode-unload-function): New function.
[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,
114f9c96 4;; 2006, 2007, 2008, 2009, 2010 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
bbf52f1e 104(define-minor-mode gnus-pick-mode
eec82323
LMI
105 "Minor mode for providing a pick-and-read interface in Gnus summary buffers.
106
107\\{gnus-pick-mode-map}"
bbf52f1e
SM
108 :lighter " Pick" :keymap gnus-pick-mode-map
109 (cond
110 ((not (derived-mode-p 'gnus-summary-mode)) (setq gnus-pick-mode nil))
111 ((not gnus-pick-mode)
112 ;; FIXME: a buffer-local minor mode removing globally from a hook??
113 (remove-hook 'gnus-message-setup-hook 'gnus-pick-setup-message))
114 (t
115 ;; Make sure that we don't select any articles upon group entry.
116 (set (make-local-variable 'gnus-auto-select-first) nil)
117 ;; Change line format.
118 (setq gnus-summary-line-format gnus-summary-pick-line-format)
119 (setq gnus-summary-line-format-spec nil)
120 (gnus-update-format-specifications nil 'summary)
121 (gnus-update-summary-mark-positions)
122 ;; FIXME: a buffer-local minor mode adding globally to a hook??
123 (add-hook 'gnus-message-setup-hook 'gnus-pick-setup-message)
124 (set (make-local-variable 'gnus-summary-goto-unread) 'never)
125 ;; Set up the menu.
126 (when (gnus-visual-p 'pick-menu 'menu)
127 (gnus-pick-make-menu-bar)))))
eec82323
LMI
128
129(defun gnus-pick-setup-message ()
130 "Make Message do the right thing on exit."
131 (when (and (gnus-buffer-live-p gnus-summary-buffer)
67d0660b 132 (with-current-buffer gnus-summary-buffer
eec82323
LMI
133 gnus-pick-mode))
134 (message-add-action
67d0660b 135 `(gnus-configure-windows ,gnus-current-window-configuration t)
16409b0b 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
23f87bed
MB
151 (gnus-summary-limit-to-articles nil)
152 (when (or catch-up gnus-mark-unpicked-articles-as-read)
eec82323 153 (gnus-summary-limit-mark-excluded-as-read))
23f87bed
MB
154 (gnus-summary-first-article)
155 (gnus-configure-windows
eec82323
LMI
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))
9b40100a 226 (start-line (1+ (count-lines (point-min) start-point)))
eec82323 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)
23f87bed 238 ;; In case the down click is in the middle of some intangible text,
eec82323
LMI
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
23f87bed
MB
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))
9b40100a 262 ;; Go to START-POINT first, so that when we move to END-POINT,
23f87bed
MB
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
9b40100a
SM
269 ;; articles on the line between this one and the last one.
270 (let* ((this-line (1+ (count-lines (point-min) end-point)))
23f87bed
MB
271 (min-line (min this-line start-line))
272 (max-line (max this-line start-line)))
273 (while (< min-line max-line)
7a4abdd6
GM
274 (goto-char (point-min))
275 (forward-line (1- min-line))
23f87bed
MB
276 (gnus-pick-article)
277 (setq min-line (1+ min-line)))
278 (setq start-line this-line))
279 (when (zerop (% click-count 3))
280 (setq end-of-range (point))))
281 (t
282 (let ((mouse-row (cdr (cdr (mouse-position)))))
283 (cond
284 ((null mouse-row))
285 ((< mouse-row top)
286 (mouse-scroll-subr start-window (- mouse-row top)))
287 ((>= mouse-row bottom)
288 (mouse-scroll-subr start-window
289 (1+ (- mouse-row bottom)))))))))))
eec82323
LMI
290 (when (consp event)
291 (let ((fun (key-binding (vector (car event)))))
292 ;; Run the binding of the terminating up-event, if possible.
23f87bed 293 ;; In the case of a multiple click, it gives the wrong results,
eec82323
LMI
294 ;; because it would fail to set up a region.
295 (when nil
23f87bed
MB
296 ;; (and (= (mod mouse-selection-click-count 3) 0) (fboundp fun))
297 ;; In this case, we can just let the up-event execute normally.
eec82323
LMI
298 (let ((end (event-end event)))
299 ;; Set the position in the event before we replay it,
300 ;; because otherwise it may have a position in the wrong
301 ;; buffer.
302 (setcar (cdr end) end-of-range)
303 ;; Delete the overlay before calling the function,
23f87bed 304 ;; because delete-overlay increases buffer-modified-tick.
eec82323
LMI
305 (push event unread-command-events))))))))
306
307(defun gnus-pick-next-page ()
308 "Go to the next page. If at the end of the buffer, start reading articles."
309 (interactive)
310 (let ((scroll-in-place nil))
311 (condition-case nil
312 (scroll-up)
313 (end-of-buffer (gnus-pick-start-reading)))))
314
315;;;
316;;; gnus-binary-mode
317;;;
318
eec82323
LMI
319(defvar gnus-binary-mode-hook nil
320 "Hook run in summary binary mode buffers.")
321
bbf52f1e
SM
322(defvar gnus-binary-mode-map
323 (let ((map (make-sparse-keymap)))
324 (gnus-define-keys map
325 "g" gnus-binary-show-article)
326 map))
eec82323
LMI
327
328(defun gnus-binary-make-menu-bar ()
329 (unless (boundp 'gnus-binary-menu)
330 (easy-menu-define
23f87bed
MB
331 gnus-binary-menu gnus-binary-mode-map ""
332 '("Pick"
333 ["Switch binary mode off" gnus-binary-mode t]))))
eec82323 334
bbf52f1e 335(define-minor-mode gnus-binary-mode
eec82323 336 "Minor mode for providing a binary group interface in Gnus summary buffers."
bbf52f1e
SM
337 :lighter " Binary" :keymap gnus-binary-mode-map
338 (cond
339 ((not (derived-mode-p 'gnus-summary-mode)) (setq gnus-binary-mode nil))
340 (gnus-binary-mode
341 ;; Make sure that we don't select any articles upon group entry.
342 (make-local-variable 'gnus-auto-select-first)
343 (setq gnus-auto-select-first nil)
344 (make-local-variable 'gnus-summary-display-article-function)
345 (setq gnus-summary-display-article-function 'gnus-binary-display-article)
346 ;; Set up the menu.
347 (when (gnus-visual-p 'binary-menu 'menu)
348 (gnus-binary-make-menu-bar)))))
eec82323
LMI
349
350(defun gnus-binary-display-article (article &optional all-header)
351 "Run ARTICLE through the binary decode functions."
352 (when (gnus-summary-goto-subject article)
23f87bed 353 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
eec82323
LMI
354 (gnus-uu-decode-uu))))
355
356(defun gnus-binary-show-article (&optional arg)
357 "Bypass the binary functions and show the article."
358 (interactive "P")
359 (let (gnus-summary-display-article-function)
360 (gnus-summary-show-article arg)))
361
362;;;
363;;; gnus-tree-mode
364;;;
365
a8151ef7
LMI
366(defcustom gnus-tree-line-format "%(%[%3,3n%]%)"
367 "Format of tree elements."
368 :type 'string
369 :group 'gnus-summary-tree)
eec82323 370
a8151ef7 371(defcustom gnus-tree-minimize-window t
eec82323
LMI
372 "If non-nil, minimize the tree buffer window.
373If a number, never let the tree buffer grow taller than that number of
a8151ef7 374lines."
6748645f
LMI
375 :type '(choice boolean
376 integer)
a8151ef7 377 :group 'gnus-summary-tree)
eec82323 378
a8151ef7
LMI
379(defcustom gnus-selected-tree-face 'modeline
380 "*Face used for highlighting selected articles in the thread tree."
381 :type 'face
382 :group 'gnus-summary-tree)
eec82323
LMI
383
384(defvar gnus-tree-brackets '((?\[ . ?\]) (?\( . ?\))
385 (?\{ . ?\}) (?< . ?>))
386 "Brackets used in tree nodes.")
387
388(defvar gnus-tree-parent-child-edges '(?- ?\\ ?|)
389 "Characters used to connect parents with children.")
390
a8151ef7
LMI
391(defcustom gnus-tree-mode-line-format "Gnus: %%b %S %Z"
392 "*The format specification for the tree mode line."
393 :type 'string
394 :group 'gnus-summary-tree)
eec82323 395
a8151ef7 396(defcustom gnus-generate-tree-function 'gnus-generate-vertical-tree
eec82323
LMI
397 "*Function for generating a thread tree.
398Two predefined functions are available:
a8151ef7
LMI
399`gnus-generate-horizontal-tree' and `gnus-generate-vertical-tree'."
400 :type '(radio (function-item gnus-generate-vertical-tree)
401 (function-item gnus-generate-horizontal-tree)
402 (function :tag "Other" nil))
403 :group 'gnus-summary-tree)
eec82323 404
a8151ef7
LMI
405(defcustom gnus-tree-mode-hook nil
406 "*Hook run in tree mode buffers."
407 :type 'hook
408 :group 'gnus-summary-tree)
eec82323 409
23f87bed
MB
410(when (featurep 'xemacs)
411 (add-hook 'gnus-tree-mode-hook 'gnus-xmas-tree-menu-add)
412 (add-hook 'gnus-tree-mode-hook 'gnus-xmas-switch-horizontal-scrollbar-off))
413
414
eec82323
LMI
415;;; Internal variables.
416
417(defvar gnus-tree-line-format-alist
418 `((?n gnus-tmp-name ?s)
419 (?f gnus-tmp-from ?s)
420 (?N gnus-tmp-number ?d)
421 (?\[ gnus-tmp-open-bracket ?c)
422 (?\] gnus-tmp-close-bracket ?c)
423 (?s gnus-tmp-subject ?s)))
424
425(defvar gnus-tree-mode-line-format-alist gnus-summary-mode-line-format-alist)
426
427(defvar gnus-tree-mode-line-format-spec nil)
428(defvar gnus-tree-line-format-spec nil)
429
430(defvar gnus-tree-node-length nil)
431(defvar gnus-selected-tree-overlay nil)
432
433(defvar gnus-tree-displayed-thread nil)
16409b0b 434(defvar gnus-tree-inhibit nil)
eec82323
LMI
435
436(defvar gnus-tree-mode-map nil)
437(put 'gnus-tree-mode 'mode-class 'special)
438
439(unless gnus-tree-mode-map
440 (setq gnus-tree-mode-map (make-keymap))
441 (suppress-keymap gnus-tree-mode-map)
442 (gnus-define-keys
16409b0b
GM
443 gnus-tree-mode-map
444 "\r" gnus-tree-select-article
445 gnus-mouse-2 gnus-tree-pick-article
446 "\C-?" gnus-tree-read-summary-keys
447 "h" gnus-tree-show-summary
eec82323 448
16409b0b 449 "\C-c\C-i" gnus-info-find-node)
eec82323
LMI
450
451 (substitute-key-definition
452 'undefined 'gnus-tree-read-summary-keys gnus-tree-mode-map))
453
454(defun gnus-tree-make-menu-bar ()
455 (unless (boundp 'gnus-tree-menu)
456 (easy-menu-define
23f87bed
MB
457 gnus-tree-menu gnus-tree-mode-map ""
458 '("Tree"
459 ["Select article" gnus-tree-select-article t]))))
eec82323
LMI
460
461(defun gnus-tree-mode ()
462 "Major mode for displaying thread trees."
463 (interactive)
6748645f
LMI
464 (gnus-set-format 'tree-mode)
465 (gnus-set-format 'tree t)
eec82323
LMI
466 (when (gnus-visual-p 'tree-menu 'menu)
467 (gnus-tree-make-menu-bar))
468 (kill-all-local-variables)
469 (gnus-simplify-mode-line)
470 (setq mode-name "Tree")
471 (setq major-mode 'gnus-tree-mode)
472 (use-local-map gnus-tree-mode-map)
16409b0b 473 (buffer-disable-undo)
eec82323
LMI
474 (setq buffer-read-only t)
475 (setq truncate-lines t)
476 (save-excursion
477 (gnus-set-work-buffer)
478 (gnus-tree-node-insert (make-mail-header "") nil)
479 (setq gnus-tree-node-length (1- (point))))
cfcd5c91 480 (gnus-run-mode-hooks 'gnus-tree-mode-hook))
eec82323
LMI
481
482(defun gnus-tree-read-summary-keys (&optional arg)
483 "Read a summary buffer key sequence and execute it."
484 (interactive "P")
16409b0b
GM
485 (unless gnus-tree-inhibit
486 (let ((buf (current-buffer))
487 (gnus-tree-inhibit t)
488 win)
489 (set-buffer gnus-article-buffer)
490 (gnus-article-read-summary-keys arg nil t)
491 (when (setq win (get-buffer-window buf))
492 (select-window win)
493 (when gnus-selected-tree-overlay
494 (goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
495 (gnus-tree-minimize)))))
eec82323 496
a8151ef7
LMI
497(defun gnus-tree-show-summary ()
498 "Reconfigure windows to show summary buffer."
499 (interactive)
500 (if (not (gnus-buffer-live-p gnus-summary-buffer))
501 (error "There is no summary buffer for this tree buffer")
502 (gnus-configure-windows 'article)
503 (gnus-summary-goto-subject gnus-current-article)))
504
eec82323
LMI
505(defun gnus-tree-select-article (article)
506 "Select the article under point, if any."
507 (interactive (list (gnus-tree-article-number)))
508 (let ((buf (current-buffer)))
509 (when article
67d0660b 510 (with-current-buffer gnus-summary-buffer
eec82323
LMI
511 (gnus-summary-goto-article article))
512 (select-window (get-buffer-window buf)))))
513
514(defun gnus-tree-pick-article (e)
515 "Select the article under the mouse pointer."
516 (interactive "e")
517 (mouse-set-point e)
518 (gnus-tree-select-article (gnus-tree-article-number)))
519
520(defun gnus-tree-article-number ()
521 (get-text-property (point) 'gnus-number))
522
523(defun gnus-tree-article-region (article)
524 "Return a cons with BEG and END of the article region."
16409b0b
GM
525 (let ((pos (text-property-any
526 (point-min) (point-max) 'gnus-number article)))
eec82323
LMI
527 (when pos
528 (cons pos (next-single-property-change pos 'gnus-number)))))
529
530(defun gnus-tree-goto-article (article)
16409b0b
GM
531 (let ((pos (text-property-any
532 (point-min) (point-max) 'gnus-number article)))
eec82323
LMI
533 (when pos
534 (goto-char pos))))
535
536(defun gnus-tree-recenter ()
537 "Center point in the tree window."
538 (let ((selected (selected-window))
23f87bed 539 (tree-window (gnus-get-buffer-window gnus-tree-buffer t)))
eec82323
LMI
540 (when tree-window
541 (select-window tree-window)
542 (when gnus-selected-tree-overlay
543 (goto-char (or (gnus-overlay-end gnus-selected-tree-overlay) 1)))
544 (let* ((top (cond ((< (window-height) 4) 0)
545 ((< (window-height) 7) 1)
546 (t 2)))
547 (height (1- (window-height)))
548 (bottom (save-excursion (goto-char (point-max))
549 (forward-line (- height))
550 (point))))
23f87bed 551 ;; Set the window start to either `bottom', which is the biggest
eec82323
LMI
552 ;; possible valid number, or the second line from the top,
553 ;; whichever is the least.
554 (set-window-start
555 tree-window (min bottom (save-excursion
556 (forward-line (- top)) (point)))))
557 (select-window selected))))
558
559(defun gnus-get-tree-buffer ()
560 "Return the tree buffer properly initialized."
67d0660b 561 (with-current-buffer (gnus-get-buffer-create gnus-tree-buffer)
eec82323 562 (unless (eq major-mode 'gnus-tree-mode)
eec82323
LMI
563 (gnus-tree-mode))
564 (current-buffer)))
565
566(defun gnus-tree-minimize ()
567 (when (and gnus-tree-minimize-window
568 (not (one-window-p)))
569 (let ((windows 0)
570 tot-win-height)
571 (walk-windows (lambda (window) (incf windows)))
572 (setq tot-win-height
573 (- (frame-height)
574 (* window-min-height (1- windows))
575 2))
576 (let* ((window-min-height 2)
577 (height (count-lines (point-min) (point-max)))
578 (min (max (1- window-min-height) height))
579 (tot (if (numberp gnus-tree-minimize-window)
580 (min gnus-tree-minimize-window min)
581 min))
582 (win (get-buffer-window (current-buffer)))
583 (wh (and win (1- (window-height win)))))
584 (setq tot (min tot tot-win-height))
585 (when (and win
586 (not (eq tot wh)))
587 (let ((selected (selected-window)))
588 (when (ignore-errors (select-window win))
589 (enlarge-window (- tot wh))
590 (select-window selected))))))))
591
592;;; Generating the tree.
593
594(defun gnus-tree-node-insert (header sparse &optional adopted)
595 (let* ((dummy (stringp header))
596 (header (if (vectorp header) header
597 (progn
598 (setq header (make-mail-header "*****"))
599 (mail-header-set-number header 0)
600 (mail-header-set-lines header 0)
601 (mail-header-set-chars header 0)
602 header)))
603 (gnus-tmp-from (mail-header-from header))
604 (gnus-tmp-subject (mail-header-subject header))
605 (gnus-tmp-number (mail-header-number header))
606 (gnus-tmp-name
607 (cond
608 ((string-match "(.+)" gnus-tmp-from)
609 (substring gnus-tmp-from
610 (1+ (match-beginning 0)) (1- (match-end 0))))
611 ((string-match "<[^>]+> *$" gnus-tmp-from)
612 (let ((beg (match-beginning 0)))
613 (or (and (string-match "^\"[^\"]*\"" gnus-tmp-from)
614 (substring gnus-tmp-from (1+ (match-beginning 0))
615 (1- (match-end 0))))
616 (substring gnus-tmp-from 0 beg))))
617 ((memq gnus-tmp-number sparse)
618 "***")
619 (t gnus-tmp-from)))
620 (gnus-tmp-open-bracket
621 (cond ((memq gnus-tmp-number sparse)
622 (caadr gnus-tree-brackets))
623 (dummy (caaddr gnus-tree-brackets))
624 (adopted (car (nth 3 gnus-tree-brackets)))
625 (t (caar gnus-tree-brackets))))
626 (gnus-tmp-close-bracket
627 (cond ((memq gnus-tmp-number sparse)
628 (cdadr gnus-tree-brackets))
629 (adopted (cdr (nth 3 gnus-tree-brackets)))
630 (dummy
631 (cdaddr gnus-tree-brackets))
632 (t (cdar gnus-tree-brackets))))
633 (buffer-read-only nil)
634 beg end)
635 (gnus-add-text-properties
636 (setq beg (point))
637 (setq end (progn (eval gnus-tree-line-format-spec) (point)))
638 (list 'gnus-number gnus-tmp-number))
639 (when (or t (gnus-visual-p 'tree-highlight 'highlight))
640 (gnus-tree-highlight-node gnus-tmp-number beg end))))
641
642(defun gnus-tree-highlight-node (article beg end)
643 "Highlight current line according to `gnus-summary-highlight'."
644 (let ((list gnus-summary-highlight)
645 face)
67d0660b 646 (with-current-buffer gnus-summary-buffer
eec82323
LMI
647 (let* ((score (or (cdr (assq article gnus-newsgroup-scored))
648 gnus-summary-default-score 0))
649 (default gnus-summary-default-score)
23f87bed
MB
650 (default-high gnus-summary-default-high-score)
651 (default-low gnus-summary-default-low-score)
652 (uncached (memq article gnus-newsgroup-undownloaded))
653 (downloaded (not uncached))
eec82323
LMI
654 (mark (or (gnus-summary-article-mark article) gnus-unread-mark)))
655 ;; Eval the cars of the lists until we find a match.
656 (while (and list
657 (not (eval (caar list))))
658 (setq list (cdr list)))))
659 (unless (eq (setq face (cdar list)) (get-text-property beg 'face))
6748645f 660 (gnus-put-text-property-excluding-characters-with-faces
eec82323
LMI
661 beg end 'face
662 (if (boundp face) (symbol-value face) face)))))
663
664(defun gnus-tree-indent (level)
665 (insert (make-string (1- (* (1+ gnus-tree-node-length) level)) ? )))
666
667(defvar gnus-tmp-limit)
668(defvar gnus-tmp-sparse)
669(defvar gnus-tmp-indent)
670
671(defun gnus-generate-tree (thread)
672 "Generate a thread tree for THREAD."
67d0660b 673 (with-current-buffer (gnus-get-tree-buffer)
eec82323
LMI
674 (let ((buffer-read-only nil)
675 (gnus-tmp-indent 0))
676 (erase-buffer)
677 (funcall gnus-generate-tree-function thread 0)
678 (gnus-set-mode-line 'tree)
679 (goto-char (point-min))
680 (gnus-tree-minimize)
681 (gnus-tree-recenter)
682 (let ((selected (selected-window)))
23f87bed
MB
683 (when (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t)
684 (select-window (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t))
eec82323
LMI
685 (gnus-horizontal-recenter)
686 (select-window selected))))))
687
688(defun gnus-generate-horizontal-tree (thread level &optional dummyp adopted)
689 "Generate a horizontal tree."
690 (let* ((dummy (stringp (car thread)))
691 (do (or dummy
a8151ef7
LMI
692 (and (car thread)
693 (memq (mail-header-number (car thread))
694 gnus-tmp-limit))))
eec82323
LMI
695 col beg)
696 (if (not do)
697 ;; We don't want this article.
698 (setq thread (cdr thread))
699 (if (not (bolp))
700 ;; Not the first article on the line, so we insert a "-".
701 (insert (car gnus-tree-parent-child-edges))
702 ;; If the level isn't zero, then we insert some indentation.
703 (unless (zerop level)
704 (gnus-tree-indent level)
705 (insert (cadr gnus-tree-parent-child-edges))
01c52d31 706 (setq col (- (setq beg (point)) (point-at-bol) 1))
eec82323
LMI
707 ;; Draw "|" lines upwards.
708 (while (progn
709 (forward-line -1)
710 (forward-char col)
16409b0b 711 (eq (char-after) ? ))
eec82323
LMI
712 (delete-char 1)
713 (insert (caddr gnus-tree-parent-child-edges)))
714 (goto-char beg)))
715 (setq dummyp nil)
716 ;; Insert the article node.
717 (gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted))
718 (if (null thread)
719 ;; End of the thread, so we go to the next line.
720 (unless (bolp)
721 (insert "\n"))
722 ;; Recurse downwards in all children of this article.
723 (while thread
724 (gnus-generate-horizontal-tree
725 (pop thread) (if do (1+ level) level)
726 (or dummyp dummy) dummy)))))
727
728(defsubst gnus-tree-indent-vertical ()
729 (let ((len (- (* (1+ gnus-tree-node-length) gnus-tmp-indent)
01c52d31 730 (- (point) (point-at-bol)))))
eec82323
LMI
731 (when (> len 0)
732 (insert (make-string len ? )))))
733
734(defsubst gnus-tree-forward-line (n)
735 (while (>= (decf n) 0)
736 (unless (zerop (forward-line 1))
737 (end-of-line)
738 (insert "\n")))
739 (end-of-line))
740
741(defun gnus-generate-vertical-tree (thread level &optional dummyp adopted)
742 "Generate a vertical tree."
743 (let* ((dummy (stringp (car thread)))
744 (do (or dummy
745 (and (car thread)
746 (memq (mail-header-number (car thread))
747 gnus-tmp-limit))))
748 beg)
749 (if (not do)
750 ;; We don't want this article.
751 (setq thread (cdr thread))
752 (if (not (save-excursion (beginning-of-line) (bobp)))
753 ;; Not the first article on the line, so we insert a "-".
754 (progn
755 (gnus-tree-indent-vertical)
756 (insert (make-string (/ gnus-tree-node-length 2) ? ))
757 (insert (caddr gnus-tree-parent-child-edges))
758 (gnus-tree-forward-line 1))
759 ;; If the level isn't zero, then we insert some indentation.
760 (unless (zerop gnus-tmp-indent)
761 (gnus-tree-forward-line (1- (* 2 level)))
762 (gnus-tree-indent-vertical)
763 (delete-char -1)
764 (insert (cadr gnus-tree-parent-child-edges))
765 (setq beg (point))
a8151ef7 766 (forward-char -1)
eec82323 767 ;; Draw "-" lines leftwards.
9b40100a 768 (while (and (not (bobp))
16409b0b 769 (eq (char-after (1- (point))) ? ))
a8151ef7
LMI
770 (delete-char -1)
771 (insert (car gnus-tree-parent-child-edges))
772 (forward-char -1))
eec82323
LMI
773 (goto-char beg)
774 (gnus-tree-forward-line 1)))
775 (setq dummyp nil)
776 ;; Insert the article node.
777 (gnus-tree-indent-vertical)
778 (gnus-tree-node-insert (pop thread) gnus-tmp-sparse adopted)
779 (gnus-tree-forward-line 1))
780 (if (null thread)
781 ;; End of the thread, so we go to the next line.
782 (progn
783 (goto-char (point-min))
784 (end-of-line)
785 (incf gnus-tmp-indent))
786 ;; Recurse downwards in all children of this article.
787 (while thread
788 (gnus-generate-vertical-tree
789 (pop thread) (if do (1+ level) level)
790 (or dummyp dummy) dummy)))))
791
792;;; Interface functions.
793
794(defun gnus-possibly-generate-tree (article &optional force)
795 "Generate the thread tree for ARTICLE if it isn't displayed already."
67d0660b 796 (when (with-current-buffer gnus-summary-buffer
eec82323
LMI
797 (and gnus-use-trees
798 gnus-show-threads
799 (vectorp (gnus-summary-article-header article))))
800 (save-excursion
67d0660b 801 (let ((top (with-current-buffer gnus-summary-buffer
eec82323
LMI
802 (gnus-cut-thread
803 (gnus-remove-thread
804 (mail-header-id
805 (gnus-summary-article-header article))
806 t))))
807 (gnus-tmp-limit gnus-newsgroup-limit)
808 (gnus-tmp-sparse gnus-newsgroup-sparse))
809 (when (or force
810 (not (eq top gnus-tree-displayed-thread)))
811 (gnus-generate-tree top)
812 (setq gnus-tree-displayed-thread top))))))
813
814(defun gnus-tree-open (group)
815 (gnus-get-tree-buffer))
816
817(defun gnus-tree-close (group)
6748645f 818 (gnus-kill-buffer gnus-tree-buffer))
eec82323 819
23f87bed
MB
820(defun gnus-tree-perhaps-minimize ()
821 (when (and gnus-tree-minimize-window
822 (get-buffer gnus-tree-buffer))
67d0660b 823 (with-current-buffer gnus-tree-buffer
23f87bed
MB
824 (gnus-tree-minimize))))
825
eec82323
LMI
826(defun gnus-highlight-selected-tree (article)
827 "Highlight the selected article in the tree."
828 (let ((buf (current-buffer))
829 region)
830 (set-buffer gnus-tree-buffer)
831 (when (setq region (gnus-tree-article-region article))
832 (when (or (not gnus-selected-tree-overlay)
833 (gnus-extent-detached-p gnus-selected-tree-overlay))
834 ;; Create a new overlay.
835 (gnus-overlay-put
9b40100a
SM
836 (setq gnus-selected-tree-overlay
837 (gnus-make-overlay (point-min) (1+ (point-min))))
eec82323
LMI
838 'face gnus-selected-tree-face))
839 ;; Move the overlay to the article.
840 (gnus-move-overlay
841 gnus-selected-tree-overlay (goto-char (car region)) (cdr region))
842 (gnus-tree-minimize)
843 (gnus-tree-recenter)
844 (let ((selected (selected-window)))
23f87bed
MB
845 (when (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t)
846 (select-window (gnus-get-buffer-window (set-buffer gnus-tree-buffer) t))
eec82323
LMI
847 (gnus-horizontal-recenter)
848 (select-window selected))))
23f87bed 849;; If we remove this save-excursion, it updates the wrong mode lines?!?
67d0660b 850 (with-current-buffer gnus-tree-buffer
eec82323
LMI
851 (gnus-set-mode-line 'tree))
852 (set-buffer buf)))
853
854(defun gnus-tree-highlight-article (article face)
67d0660b 855 (with-current-buffer (gnus-get-tree-buffer)
eec82323
LMI
856 (let (region)
857 (when (setq region (gnus-tree-article-region article))
858 (gnus-put-text-property (car region) (cdr region) 'face face)
859 (set-window-point
23f87bed 860 (gnus-get-buffer-window (current-buffer) t) (cdr region))))))
eec82323
LMI
861
862;;;
863;;; gnus-carpal
864;;;
865
866(defvar gnus-carpal-group-buffer-buttons
867 '(("next" . gnus-group-next-unread-group)
868 ("prev" . gnus-group-prev-unread-group)
869 ("read" . gnus-group-read-group)
870 ("select" . gnus-group-select-group)
871 ("catch-up" . gnus-group-catchup-current)
872 ("new-news" . gnus-group-get-new-news-this-group)
873 ("toggle-sub" . gnus-group-unsubscribe-current-group)
874 ("subscribe" . gnus-group-unsubscribe-group)
875 ("kill" . gnus-group-kill-group)
876 ("yank" . gnus-group-yank-group)
877 ("describe" . gnus-group-describe-group)
878 "list"
879 ("subscribed" . gnus-group-list-groups)
880 ("all" . gnus-group-list-all-groups)
881 ("killed" . gnus-group-list-killed)
882 ("zombies" . gnus-group-list-zombies)
883 ("matching" . gnus-group-list-matching)
884 ("post" . gnus-group-post-news)
885 ("mail" . gnus-group-mail)
23f87bed 886 ("local" . (lambda () (interactive) (gnus-group-news 0)))
eec82323
LMI
887 ("rescan" . gnus-group-get-new-news)
888 ("browse-foreign" . gnus-group-browse-foreign)
889 ("exit" . gnus-group-exit)))
890
891(defvar gnus-carpal-summary-buffer-buttons
892 '("mark"
893 ("read" . gnus-summary-mark-as-read-forward)
894 ("tick" . gnus-summary-tick-article-forward)
895 ("clear" . gnus-summary-clear-mark-forward)
896 ("expirable" . gnus-summary-mark-as-expirable)
897 "move"
898 ("scroll" . gnus-summary-next-page)
899 ("next-unread" . gnus-summary-next-unread-article)
900 ("prev-unread" . gnus-summary-prev-unread-article)
901 ("first" . gnus-summary-first-unread-article)
902 ("best" . gnus-summary-best-unread-article)
903 "article"
904 ("headers" . gnus-summary-toggle-header)
905 ("uudecode" . gnus-uu-decode-uu)
906 ("enter-digest" . gnus-summary-enter-digest-group)
907 ("fetch-parent" . gnus-summary-refer-parent-article)
908 "mail"
909 ("move" . gnus-summary-move-article)
910 ("copy" . gnus-summary-copy-article)
911 ("respool" . gnus-summary-respool-article)
912 "threads"
913 ("lower" . gnus-summary-lower-thread)
914 ("kill" . gnus-summary-kill-thread)
915 "post"
916 ("post" . gnus-summary-post-news)
23f87bed
MB
917 ("local" . gnus-summary-news-other-window)
918 ("mail" . gnus-summary-mail-other-window)
eec82323
LMI
919 ("followup" . gnus-summary-followup-with-original)
920 ("reply" . gnus-summary-reply-with-original)
921 ("cancel" . gnus-summary-cancel-article)
922 "misc"
923 ("exit" . gnus-summary-exit)
924 ("fed-up" . gnus-summary-catchup-and-goto-next-group)))
925
926(defvar gnus-carpal-server-buffer-buttons
927 '(("add" . gnus-server-add-server)
928 ("browse" . gnus-server-browse-server)
929 ("list" . gnus-server-list-servers)
930 ("kill" . gnus-server-kill-server)
931 ("yank" . gnus-server-yank-server)
932 ("copy" . gnus-server-copy-server)
933 ("exit" . gnus-server-exit)))
934
935(defvar gnus-carpal-browse-buffer-buttons
936 '(("subscribe" . gnus-browse-unsubscribe-current-group)
937 ("exit" . gnus-browse-exit)))
938
939(defvar gnus-carpal-group-buffer "*Carpal Group*")
940(defvar gnus-carpal-summary-buffer "*Carpal Summary*")
941(defvar gnus-carpal-server-buffer "*Carpal Server*")
942(defvar gnus-carpal-browse-buffer "*Carpal Browse*")
943
944(defvar gnus-carpal-attached-buffer nil)
945
946(defvar gnus-carpal-mode-hook nil
947 "*Hook run in carpal mode buffers.")
948
949(defvar gnus-carpal-button-face 'bold
950 "*Face used on carpal buttons.")
951
952(defvar gnus-carpal-header-face 'bold-italic
953 "*Face used on carpal buffer headers.")
954
955(defvar gnus-carpal-mode-map nil)
956(put 'gnus-carpal-mode 'mode-class 'special)
957
958(if gnus-carpal-mode-map
959 nil
960 (setq gnus-carpal-mode-map (make-keymap))
961 (suppress-keymap gnus-carpal-mode-map)
962 (define-key gnus-carpal-mode-map " " 'gnus-carpal-select)
963 (define-key gnus-carpal-mode-map "\r" 'gnus-carpal-select)
964 (define-key gnus-carpal-mode-map gnus-mouse-2 'gnus-carpal-mouse-select))
965
966(defun gnus-carpal-mode ()
967 "Major mode for clicking buttons.
968
969All normal editing commands are switched off.
970\\<gnus-carpal-mode-map>
971The following commands are available:
972
973\\{gnus-carpal-mode-map}"
974 (interactive)
975 (kill-all-local-variables)
a8151ef7 976 (setq mode-line-modified (cdr gnus-mode-line-modified))
eec82323
LMI
977 (setq major-mode 'gnus-carpal-mode)
978 (setq mode-name "Gnus Carpal")
979 (setq mode-line-process nil)
980 (use-local-map gnus-carpal-mode-map)
16409b0b 981 (buffer-disable-undo)
eec82323
LMI
982 (setq buffer-read-only t)
983 (make-local-variable 'gnus-carpal-attached-buffer)
cfcd5c91 984 (gnus-run-mode-hooks 'gnus-carpal-mode-hook))
eec82323
LMI
985
986(defun gnus-carpal-setup-buffer (type)
987 (let ((buffer (symbol-value (intern (format "gnus-carpal-%s-buffer" type)))))
988 (if (get-buffer buffer)
989 ()
67d0660b 990 (with-current-buffer (gnus-get-buffer-create buffer)
eec82323
LMI
991 (gnus-carpal-mode)
992 (setq gnus-carpal-attached-buffer
993 (intern (format "gnus-%s-buffer" type)))
eec82323
LMI
994 (let ((buttons (symbol-value
995 (intern (format "gnus-carpal-%s-buffer-buttons"
996 type))))
997 (buffer-read-only nil)
998 button)
999 (while buttons
1000 (setq button (car buttons)
1001 buttons (cdr buttons))
1002 (if (stringp button)
01c52d31 1003 (set-text-properties
eec82323
LMI
1004 (point)
1005 (prog2 (insert button) (point) (insert " "))
1006 (list 'face gnus-carpal-header-face))
01c52d31 1007 (set-text-properties
eec82323
LMI
1008 (point)
1009 (prog2 (insert (car button)) (point) (insert " "))
1010 (list 'gnus-callback (cdr button)
1011 'face gnus-carpal-button-face
1012 gnus-mouse-face-prop 'highlight))))
1013 (let ((fill-column (- (window-width) 2)))
1014 (fill-region (point-min) (point-max)))
1015 (set-window-point (get-buffer-window (current-buffer))
1016 (point-min)))))))
1017
1018(defun gnus-carpal-select ()
1019 "Select the button under point."
1020 (interactive)
1021 (let ((func (get-text-property (point) 'gnus-callback)))
1022 (if (null func)
1023 ()
1024 (pop-to-buffer (symbol-value gnus-carpal-attached-buffer))
1025 (call-interactively func))))
1026
1027(defun gnus-carpal-mouse-select (event)
1028 "Select the button under the mouse pointer."
1029 (interactive "e")
1030 (mouse-set-point event)
1031 (gnus-carpal-select))
1032
1033;;; Allow redefinition of functions.
1034(gnus-ems-redefine)
1035
1036(provide 'gnus-salt)
1037
9b40100a 1038;; arch-tag: 35449164-77b3-4398-bcbd-a2e3e998f810
eec82323 1039;;; gnus-salt.el ends here