gnus-bookmark.el 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. ;;; gnus-bookmark.el --- Bookmarks in Gnus
  2. ;; Copyright (C) 2006-2012 Free Software Foundation, Inc.
  3. ;; Author: Bastien Guerry <bzg AT altern DOT org>
  4. ;; Keywords: news
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This file implements real bookmarks for Gnus, closely following the way
  18. ;; `bookmark.el' handles bookmarks. Most of the code comes from
  19. ;; `bookmark.el'.
  20. ;;
  21. ;; Set a Gnus bookmark:
  22. ;; M-x `gnus-bookmark-set' from the summary buffer.
  23. ;;
  24. ;; Jump to a Gnus bookmark:
  25. ;; M-x `gnus-bookmark-jump'.
  26. ;;
  27. ;; Display a list of bookmarks
  28. ;; M-x `gnus-bookmark-bmenu-list'.
  29. ;;
  30. ;;; Todo:
  31. ;; - add tags to bookmarks
  32. ;; - don't write file each time a bookmark is created
  33. ;; - better annotation interactive buffer
  34. ;; - edit annotation in gnus-bookmark-bmenu
  35. ;; - sort gnus-bookmark-buffer by author/subject/date/group/message-id
  36. ;; - auto-bmk-name customizable format
  37. ;; - renaming bookmarks in gnus-bookmark-bmenu-list
  38. ;; - better (formatted string) display in bmenu-list
  39. ;; - Integrate the `gnus-summary-*-bookmark' functionality
  40. ;; - Initialize defcustoms from corresponding `bookmark.el' variables?
  41. ;;; Code:
  42. (require 'gnus-sum)
  43. ;; FIXME: should avoid using C-c (no?)
  44. ;; (define-key gnus-summary-mode-map "\C-crm" 'gnus-bookmark-set)
  45. ;; (define-key global-map "\C-crb" 'gnus-bookmark-jump)
  46. ;; (define-key global-map "\C-crj" 'gnus-bookmark-jump)
  47. ;; (define-key global-map "\C-crl" 'gnus-bookmark-bmenu-list)
  48. ;; FIXME: Add keybindings, see
  49. ;; http://thread.gmane.org/gmane.emacs.gnus.general/63101/focus=63379
  50. ;; http://thread.gmane.org/v9fxx9fkm4.fsf@marauder.physik.uni-ulm.de
  51. ;; FIXME: Check if `gnus-bookmark.el' should use
  52. ;; `bookmark-make-cell-function'.
  53. ;; Cf. http://article.gmane.org/gmane.emacs.gnus.general/66076
  54. (defgroup gnus-bookmark nil
  55. "Setting, annotation and jumping to Gnus bookmarks."
  56. :group 'gnus)
  57. (defcustom gnus-bookmark-default-file
  58. (cond
  59. ;; Backward compatibility with previous versions:
  60. ((file-exists-p "~/.gnus.bmk") "~/.gnus.bmk")
  61. (t (nnheader-concat gnus-directory "bookmarks.el")))
  62. "The default Gnus bookmarks file."
  63. :type 'string
  64. :group 'gnus-bookmark)
  65. (defcustom gnus-bookmark-file-coding-system
  66. (if (mm-coding-system-p 'iso-2022-7bit)
  67. 'iso-2022-7bit)
  68. "Coding system used for writing Gnus bookmark files."
  69. :type '(symbol :tag "Coding system")
  70. :group 'gnus-bookmark)
  71. (defcustom gnus-bookmark-sort-flag t
  72. "Non-nil means Gnus bookmarks are sorted by bookmark names.
  73. Otherwise they will be displayed in LIFO order (that is,
  74. most recently set ones come first, oldest ones come last)."
  75. :type 'boolean
  76. :group 'gnus-bookmark)
  77. (defcustom gnus-bookmark-bmenu-toggle-infos t
  78. "Non-nil means show details when listing Gnus bookmarks.
  79. List of details is defined in `gnus-bookmark-bookmark-inline-details'.
  80. This may result in truncated bookmark names. To disable this, put the
  81. following in your `.emacs' file:
  82. \(setq gnus-bookmark-bmenu-toggle-infos nil\)"
  83. :type 'boolean
  84. :group 'gnus-bookmark)
  85. (defcustom gnus-bookmark-bmenu-file-column 30
  86. "Column at which to display details in a buffer listing Gnus bookmarks.
  87. You can toggle whether details are shown with \\<gnus-bookmark-bmenu-mode-map>\\[gnus-bookmark-bmenu-toggle-infos]."
  88. :type 'integer
  89. :group 'gnus-bookmark)
  90. (defcustom gnus-bookmark-use-annotations nil
  91. "If non-nil, ask for an annotation when setting a bookmark."
  92. :type 'boolean
  93. :group 'gnus-bookmark)
  94. (defcustom gnus-bookmark-bookmark-inline-details '(author)
  95. "Details to be shown with `gnus-bookmark-bmenu-toggle-infos'.
  96. The default value is \(subject\)."
  97. :type '(list :tag "Gnus bookmark details"
  98. (set :inline t
  99. (const :tag "Author" author)
  100. (const :tag "Subject" subject)
  101. (const :tag "Date" date)
  102. (const :tag "Group" group)
  103. (const :tag "Message-id" message-id)))
  104. :group 'gnus-bookmark)
  105. (defcustom gnus-bookmark-bookmark-details
  106. '(author subject date group annotation)
  107. "Details to be shown with `gnus-bookmark-bmenu-show-details'.
  108. The default value is \(author subject date group annotation\)."
  109. :type '(list :tag "Gnus bookmark details"
  110. (set :inline t
  111. (const :tag "Author" author)
  112. (const :tag "Subject" subject)
  113. (const :tag "Date" date)
  114. (const :tag "Group" group)
  115. (const :tag "Message-id" message-id)
  116. (const :tag "Annotation" annotation)))
  117. :group 'gnus-bookmark)
  118. (defface gnus-bookmark-menu-heading
  119. '((t (:inherit font-lock-type-face)))
  120. "Face used to highlight the heading in Gnus bookmark menu buffers."
  121. :version "23.1" ;; No Gnus
  122. :group 'gnus-bookmark)
  123. (defconst gnus-bookmark-end-of-version-stamp-marker
  124. "-*- End Of Bookmark File Format Version Stamp -*-\n"
  125. "This string marks the end of the version stamp in a Gnus bookmark file.")
  126. (defconst gnus-bookmark-file-format-version 0
  127. "The current version of the format used by bookmark files.
  128. You should never need to change this.")
  129. (defvar gnus-bookmark-alist ()
  130. "Association list of Gnus bookmarks and their records.
  131. The format of the alist is
  132. \(BMK1 BMK2 ...\)
  133. where each BMK is of the form
  134. \(NAME
  135. \(group . GROUP\)
  136. \(message-id . MESSAGE-ID\)
  137. \(author . AUTHOR\)
  138. \(date . DATE\)
  139. \(subject . SUBJECT\)
  140. \(annotation . ANNOTATION\)\)
  141. So the cdr of each bookmark is an alist too.")
  142. (defmacro gnus-bookmark-mouse-available-p ()
  143. "Return non-nil if a mouse is available."
  144. (if (featurep 'xemacs)
  145. '(device-on-window-system-p)
  146. '(display-mouse-p)))
  147. (defun gnus-bookmark-remove-properties (string)
  148. "Remove all text properties from STRING."
  149. (set-text-properties 0 (length string) nil string)
  150. string)
  151. ;;;###autoload
  152. (defun gnus-bookmark-set ()
  153. "Set a bookmark for this article."
  154. (interactive)
  155. (gnus-bookmark-maybe-load-default-file)
  156. (if (or (not (eq major-mode 'gnus-summary-mode))
  157. (not gnus-article-current))
  158. (error "Please select an article in the Gnus summary buffer")
  159. (let* ((group (car gnus-article-current))
  160. (article (cdr gnus-article-current))
  161. (header (gnus-summary-article-header article))
  162. (author (mail-header-from header))
  163. (message-id (mail-header-id header))
  164. (date (mail-header-date header))
  165. (subject (gnus-summary-subject-string))
  166. (bmk-name (gnus-bookmark-set-bookmark-name group author subject))
  167. ;; Maybe ask for annotation
  168. (annotation
  169. (if gnus-bookmark-use-annotations
  170. (read-from-minibuffer
  171. (format "Annotation for %s: " bmk-name)) "")))
  172. ;; Set the bookmark list
  173. (setq gnus-bookmark-alist
  174. (cons
  175. (list (gnus-bookmark-remove-properties bmk-name)
  176. (gnus-bookmark-make-record
  177. group message-id author date subject annotation))
  178. gnus-bookmark-alist))))
  179. (gnus-bookmark-bmenu-surreptitiously-rebuild-list)
  180. (gnus-bookmark-write-file))
  181. (defun gnus-bookmark-make-record
  182. (group message-id author date subject annotation)
  183. "Return the record part of a new bookmark, given GROUP MESSAGE-ID AUTHOR DATE SUBJECT and ANNOTATION."
  184. (let ((the-record
  185. `((group . ,(gnus-bookmark-remove-properties group))
  186. (message-id . ,(gnus-bookmark-remove-properties message-id))
  187. (author . ,(gnus-bookmark-remove-properties author))
  188. (date . ,(gnus-bookmark-remove-properties date))
  189. (subject . ,(gnus-bookmark-remove-properties subject))
  190. (annotation . ,(gnus-bookmark-remove-properties annotation)))))
  191. the-record))
  192. (defun gnus-bookmark-set-bookmark-name (group author subject)
  193. "Set bookmark name from GROUP AUTHOR and SUBJECT."
  194. (let* ((subject (split-string subject))
  195. (default-name-0 ;; Should be merged with -1?
  196. (concat (car (nreverse (delete "" (split-string group "[\\.:]"))))
  197. "-" (car (split-string author))
  198. "-" (car subject) "-" (cadr subject)))
  199. (default-name-1
  200. ;; Strip "[]" chars from the bookmark name:
  201. (gnus-replace-in-string default-name-0 "[]_[]" ""))
  202. (name (read-from-minibuffer
  203. (format "Set bookmark (%s): " default-name-1)
  204. nil nil nil nil
  205. default-name-1)))
  206. (if (string-equal name "")
  207. default-name-1
  208. name)))
  209. (defun gnus-bookmark-write-file ()
  210. "Write currently defined Gnus bookmarks into `gnus-bookmark-default-file'."
  211. (interactive)
  212. (save-excursion
  213. (save-window-excursion
  214. ;; Avoir warnings?
  215. ;; (message "Saving Gnus bookmarks to file %s..." gnus-bookmark-default-file)
  216. (set-buffer (get-buffer-create " *Gnus bookmarks*"))
  217. (erase-buffer)
  218. (gnus-bookmark-insert-file-format-version-stamp)
  219. (pp gnus-bookmark-alist (current-buffer))
  220. (condition-case nil
  221. (let ((coding-system-for-write gnus-bookmark-file-coding-system))
  222. (write-region (point-min) (point-max)
  223. gnus-bookmark-default-file))
  224. (file-error (message "Can't write %s"
  225. gnus-bookmark-default-file)))
  226. (kill-buffer (current-buffer))
  227. (message
  228. "Saving Gnus bookmarks to file %s...done"
  229. gnus-bookmark-default-file))))
  230. (defun gnus-bookmark-insert-file-format-version-stamp ()
  231. "Insert text indicating current version of Gnus bookmark file format."
  232. (insert
  233. (format ";;;; Gnus Bookmark Format Version %d %s;;;;\n"
  234. gnus-bookmark-file-format-version
  235. (if gnus-bookmark-file-coding-system
  236. (concat "-*- coding: "
  237. (symbol-name gnus-bookmark-file-coding-system)
  238. "; -*- ")
  239. "")))
  240. (insert ";;; This format is meant to be slightly human-readable;\n"
  241. ";;; nevertheless, you probably don't want to edit it.\n"
  242. ";;; "
  243. gnus-bookmark-end-of-version-stamp-marker))
  244. ;;;###autoload
  245. (defun gnus-bookmark-jump (&optional bmk-name)
  246. "Jump to a Gnus bookmark (BMK-NAME)."
  247. (interactive)
  248. (gnus-bookmark-maybe-load-default-file)
  249. (let* ((bookmark (or bmk-name
  250. (gnus-completing-read "Jump to bookmarked article"
  251. (mapcar 'car gnus-bookmark-alist))))
  252. (bmk-record (cadr (assoc bookmark gnus-bookmark-alist)))
  253. (group (cdr (assoc 'group bmk-record)))
  254. (message-id (cdr (assoc 'message-id bmk-record))))
  255. (when group
  256. (unless (get-buffer gnus-group-buffer)
  257. (gnus-no-server))
  258. (gnus-activate-group group)
  259. (gnus-group-quick-select-group 0 group))
  260. (if message-id
  261. (or (gnus-summary-goto-article message-id nil 'force)
  262. (if (fboundp 'gnus-summary-insert-cached-articles)
  263. (progn
  264. (gnus-summary-insert-cached-articles)
  265. (gnus-summary-goto-article message-id nil 'force))
  266. (message "Message could not be found."))))))
  267. (defvar gnus-bookmark-already-loaded nil)
  268. (defun gnus-bookmark-alist-from-buffer ()
  269. "Return a `gnus-bookmark-alist' from the current buffer.
  270. The buffer must of course contain Gnus bookmark format information.
  271. Does not care from where in the buffer it is called, and does not
  272. affect point."
  273. (save-excursion
  274. (goto-char (point-min))
  275. (if (search-forward
  276. gnus-bookmark-end-of-version-stamp-marker nil t)
  277. (read (current-buffer))
  278. ;; Else no hope of getting information here.
  279. (error "Not Gnus bookmark format"))))
  280. (defun gnus-bookmark-load (file)
  281. "Load Gnus bookmarks from FILE (which must be in bookmark format)."
  282. (interactive
  283. (list (read-file-name
  284. (format "Load Gnus bookmarks from: (%s) "
  285. gnus-bookmark-default-file)
  286. "~/" gnus-bookmark-default-file 'confirm)))
  287. (setq file (expand-file-name file))
  288. (if (file-readable-p file)
  289. (save-excursion
  290. (save-window-excursion
  291. (set-buffer (let ((enable-local-variables nil))
  292. (find-file-noselect file)))
  293. (goto-char (point-min))
  294. (let ((blist (gnus-bookmark-alist-from-buffer)))
  295. (if (listp blist)
  296. (progn (setq gnus-bookmark-already-loaded t)
  297. (setq gnus-bookmark-alist blist))
  298. (error "Not Gnus bookmark format")))))))
  299. (defun gnus-bookmark-maybe-load-default-file ()
  300. "Maybe load Gnus bookmarks in `gnus-bookmark-alist'."
  301. (and (not gnus-bookmark-already-loaded)
  302. (null gnus-bookmark-alist)
  303. (file-readable-p (expand-file-name gnus-bookmark-default-file))
  304. (gnus-bookmark-load gnus-bookmark-default-file)))
  305. (defun gnus-bookmark-maybe-sort-alist ()
  306. "Return the gnus-bookmark-alist for display.
  307. If the gnus-bookmark-sort-flag is non-nil, then return a sorted
  308. copy of the alist."
  309. (when gnus-bookmark-sort-flag
  310. (setq gnus-bookmark-alist
  311. (sort (copy-alist gnus-bookmark-alist)
  312. (function
  313. (lambda (x y) (string-lessp (car x) (car y))))))))
  314. ;;;###autoload
  315. (defun gnus-bookmark-bmenu-list ()
  316. "Display a list of existing Gnus bookmarks.
  317. The list is displayed in a buffer named `*Gnus Bookmark List*'.
  318. The leftmost column displays a D if the bookmark is flagged for
  319. deletion, or > if it is flagged for displaying."
  320. (interactive)
  321. (gnus-bookmark-maybe-load-default-file)
  322. (if (interactive-p)
  323. (switch-to-buffer (get-buffer-create "*Gnus Bookmark List*"))
  324. (set-buffer (get-buffer-create "*Gnus Bookmark List*")))
  325. (let ((inhibit-read-only t)
  326. alist name start end)
  327. (erase-buffer)
  328. (insert "% Gnus Bookmark\n- --------\n")
  329. (add-text-properties (point-min) (point)
  330. '(font-lock-face gnus-bookmark-menu-heading))
  331. ;; sort before displaying
  332. (gnus-bookmark-maybe-sort-alist)
  333. ;; Display gnus bookmarks
  334. (setq alist gnus-bookmark-alist)
  335. (while alist
  336. (setq name (gnus-bookmark-name-from-full-record (pop alist)))
  337. ;; if a Gnus bookmark has an annotation, prepend a "*"
  338. ;; in the list of bookmarks.
  339. (insert (if (member (gnus-bookmark-get-annotation name) (list nil ""))
  340. " "
  341. " *"))
  342. (if (gnus-bookmark-mouse-available-p)
  343. (add-text-properties
  344. (prog1
  345. (point)
  346. (insert name))
  347. (let ((end (point)))
  348. (prog2
  349. (re-search-backward "[^ \t]")
  350. (1+ (point))
  351. (goto-char end)
  352. (insert "\n")))
  353. `(mouse-face highlight follow-link t
  354. help-echo ,(format "%s: go to this article"
  355. (aref gnus-mouse-2 0))))
  356. (insert name "\n")))
  357. (goto-char (point-min))
  358. (forward-line 2)
  359. (gnus-bookmark-bmenu-mode)
  360. (if gnus-bookmark-bmenu-toggle-infos
  361. (gnus-bookmark-bmenu-toggle-infos t))))
  362. (defun gnus-bookmark-bmenu-surreptitiously-rebuild-list ()
  363. "Rebuild the Bookmark List if it exists.
  364. Don't affect the buffer ring order."
  365. (if (get-buffer "*Gnus Bookmark List*")
  366. (save-excursion
  367. (save-window-excursion
  368. (gnus-bookmark-bmenu-list)))))
  369. (defun gnus-bookmark-get-annotation (bookmark)
  370. "Return the annotation of Gnus BOOKMARK, or nil if none."
  371. (cdr (assq 'annotation (gnus-bookmark-get-bookmark-record bookmark))))
  372. (defun gnus-bookmark-get-bookmark (bookmark)
  373. "Return the full entry for Gnus BOOKMARK in `gnus-bookmark-alist'.
  374. If BOOKMARK is not a string, return nil."
  375. (when (stringp bookmark)
  376. (assoc bookmark gnus-bookmark-alist)))
  377. (defun gnus-bookmark-get-bookmark-record (bookmark)
  378. "Return the guts of the entry for Gnus BOOKMARK in `gnus-bookmark-alist'.
  379. That is, all information but the name."
  380. (car (cdr (gnus-bookmark-get-bookmark bookmark))))
  381. (defun gnus-bookmark-name-from-full-record (full-record)
  382. "Return name of FULL-RECORD \(an alist element instead of a string\)."
  383. (car full-record))
  384. (defvar gnus-bookmark-bmenu-bookmark-column nil)
  385. (defvar gnus-bookmark-bmenu-hidden-bookmarks ())
  386. (defvar gnus-bookmark-bmenu-mode-map nil)
  387. (if gnus-bookmark-bmenu-mode-map
  388. nil
  389. (setq gnus-bookmark-bmenu-mode-map (make-keymap))
  390. (suppress-keymap gnus-bookmark-bmenu-mode-map t)
  391. (define-key gnus-bookmark-bmenu-mode-map "q" (if (fboundp 'quit-window)
  392. 'quit-window
  393. 'bury-buffer))
  394. (define-key gnus-bookmark-bmenu-mode-map "\C-m" 'gnus-bookmark-bmenu-select)
  395. (define-key gnus-bookmark-bmenu-mode-map "v" 'gnus-bookmark-bmenu-select)
  396. (define-key gnus-bookmark-bmenu-mode-map "d" 'gnus-bookmark-bmenu-delete)
  397. (define-key gnus-bookmark-bmenu-mode-map "k" 'gnus-bookmark-bmenu-delete)
  398. (define-key gnus-bookmark-bmenu-mode-map "\C-d" 'gnus-bookmark-bmenu-delete-backwards)
  399. (define-key gnus-bookmark-bmenu-mode-map "x" 'gnus-bookmark-bmenu-execute-deletions)
  400. (define-key gnus-bookmark-bmenu-mode-map " " 'next-line)
  401. (define-key gnus-bookmark-bmenu-mode-map "n" 'next-line)
  402. (define-key gnus-bookmark-bmenu-mode-map "p" 'previous-line)
  403. (define-key gnus-bookmark-bmenu-mode-map "\177" 'gnus-bookmark-bmenu-backup-unmark)
  404. (define-key gnus-bookmark-bmenu-mode-map "?" 'describe-mode)
  405. (define-key gnus-bookmark-bmenu-mode-map "u" 'gnus-bookmark-bmenu-unmark)
  406. (define-key gnus-bookmark-bmenu-mode-map "m" 'gnus-bookmark-bmenu-mark)
  407. (define-key gnus-bookmark-bmenu-mode-map "l" 'gnus-bookmark-bmenu-load)
  408. (define-key gnus-bookmark-bmenu-mode-map "s" 'gnus-bookmark-bmenu-save)
  409. (define-key gnus-bookmark-bmenu-mode-map "t" 'gnus-bookmark-bmenu-toggle-infos)
  410. (define-key gnus-bookmark-bmenu-mode-map "a" 'gnus-bookmark-bmenu-show-details)
  411. (define-key gnus-bookmark-bmenu-mode-map gnus-mouse-2
  412. 'gnus-bookmark-bmenu-select-by-mouse))
  413. ;; Bookmark Buffer Menu mode is suitable only for specially formatted
  414. ;; data.
  415. (put 'gnus-bookmark-bmenu-mode 'mode-class 'special)
  416. ;; Been to lazy to use gnus-bookmark-save...
  417. (defalias 'gnus-bookmark-bmenu-save 'gnus-bookmark-write-file)
  418. (defun gnus-bookmark-bmenu-mode ()
  419. "Major mode for editing a list of Gnus bookmarks.
  420. Each line describes one of the bookmarks in Gnus.
  421. Letters do not insert themselves; instead, they are commands.
  422. Gnus bookmarks names preceded by a \"*\" have annotations.
  423. \\<gnus-bookmark-bmenu-mode-map>
  424. \\[gnus-bookmark-bmenu-mark] -- mark bookmark to be displayed.
  425. \\[gnus-bookmark-bmenu-select] -- select bookmark of line point is on.
  426. Also show bookmarks marked using m in other windows.
  427. \\[gnus-bookmark-bmenu-toggle-infos] -- toggle displaying of details (they may obscure long bookmark names).
  428. \\[gnus-bookmark-bmenu-locate] -- display (in minibuffer) location of this bookmark.
  429. \\[gnus-bookmark-bmenu-rename] -- rename this bookmark \(prompts for new name\).
  430. \\[gnus-bookmark-bmenu-delete] -- mark this bookmark to be deleted, and move down.
  431. \\[gnus-bookmark-bmenu-delete-backwards] -- mark this bookmark to be deleted, and move up.
  432. \\[gnus-bookmark-bmenu-execute-deletions] -- delete bookmarks marked with `\\[gnus-bookmark-bmenu-delete]'.
  433. \\[gnus-bookmark-bmenu-load] -- load in a file of bookmarks (prompts for file.)
  434. \\[gnus-bookmark-bmenu-save] -- load in a file of bookmarks (prompts for file.)
  435. \\[gnus-bookmark-bmenu-unmark] -- remove all kinds of marks from current line.
  436. With prefix argument, also move up one line.
  437. \\[gnus-bookmark-bmenu-backup-unmark] -- back up a line and remove marks.
  438. \\[gnus-bookmark-bmenu-show-details] -- show the annotation, if it exists, for the current bookmark
  439. in another buffer.
  440. \\[gnus-bookmark-bmenu-show-all-annotations] -- show the annotations of all bookmarks in another buffer.
  441. \\[gnus-bookmark-bmenu-edit-annotation] -- edit the annotation for the current bookmark."
  442. (kill-all-local-variables)
  443. (use-local-map gnus-bookmark-bmenu-mode-map)
  444. (setq truncate-lines t)
  445. (setq buffer-read-only t)
  446. (setq major-mode 'gnus-bookmark-bmenu-mode)
  447. (setq mode-name "Bookmark Menu")
  448. (gnus-run-mode-hooks 'gnus-bookmark-bmenu-mode-hook))
  449. ;; avoid compilation warnings
  450. (defvar gnus-bookmark-bmenu-toggle-infos nil)
  451. (defun gnus-bookmark-bmenu-toggle-infos (&optional show)
  452. "Toggle whether details are shown in the Gnus bookmark list.
  453. Optional argument SHOW means show them unconditionally."
  454. (interactive)
  455. (cond
  456. (show
  457. (setq gnus-bookmark-bmenu-toggle-infos nil)
  458. (gnus-bookmark-bmenu-show-infos)
  459. (setq gnus-bookmark-bmenu-toggle-infos t))
  460. (gnus-bookmark-bmenu-toggle-infos
  461. (gnus-bookmark-bmenu-hide-infos)
  462. (setq gnus-bookmark-bmenu-toggle-infos nil))
  463. (t
  464. (gnus-bookmark-bmenu-show-infos)
  465. (setq gnus-bookmark-bmenu-toggle-infos t))))
  466. (defun gnus-bookmark-bmenu-show-infos (&optional force)
  467. "Show infos in bmenu, maybe FORCE display of infos."
  468. (if (and (not force) gnus-bookmark-bmenu-toggle-infos)
  469. nil ;already shown, so do nothing
  470. (save-excursion
  471. (save-window-excursion
  472. (goto-char (point-min))
  473. (forward-line 2)
  474. (setq gnus-bookmark-bmenu-hidden-bookmarks ())
  475. (let ((inhibit-read-only t))
  476. (while (< (point) (point-max))
  477. (let ((bmrk (gnus-bookmark-bmenu-bookmark)))
  478. (setq gnus-bookmark-bmenu-hidden-bookmarks
  479. (cons bmrk gnus-bookmark-bmenu-hidden-bookmarks))
  480. (let ((start (point-at-eol)))
  481. (move-to-column gnus-bookmark-bmenu-file-column t)
  482. ;; Strip off `mouse-face' from the white spaces region.
  483. (if (gnus-bookmark-mouse-available-p)
  484. (remove-text-properties start (point)
  485. '(mouse-face nil help-echo nil))))
  486. (delete-region (point) (progn (end-of-line) (point)))
  487. (insert " ")
  488. ;; Pass the NO-HISTORY arg:
  489. (gnus-bookmark-insert-details bmrk)
  490. (forward-line 1))))))))
  491. (defun gnus-bookmark-insert-details (bmk-name)
  492. "Insert the details of the article associated with BMK-NAME."
  493. (let ((start (point)))
  494. (prog1
  495. (insert (gnus-bookmark-get-details
  496. bmk-name
  497. gnus-bookmark-bookmark-inline-details))
  498. (if (gnus-bookmark-mouse-available-p)
  499. (add-text-properties
  500. start
  501. (save-excursion (re-search-backward
  502. "[^ \t]")
  503. (1+ (point)))
  504. `(mouse-face highlight
  505. follow-link t
  506. help-echo ,(format "%s: go to this article"
  507. (aref gnus-mouse-2 0))))))))
  508. (defun gnus-bookmark-kill-line (&optional newline-too)
  509. "Kill from point to end of line.
  510. If optional arg NEWLINE-TOO is non-nil, delete the newline too.
  511. Does not affect the kill ring."
  512. (delete-region (point) (point-at-eol))
  513. (if (and newline-too (looking-at "\n"))
  514. (delete-char 1)))
  515. (defun gnus-bookmark-get-details (bmk-name details-list)
  516. "Get details for a Gnus BMK-NAME depending on DETAILS-LIST."
  517. (let ((details (cadr (assoc bmk-name gnus-bookmark-alist))))
  518. (mapconcat
  519. (lambda (info)
  520. (cdr (assoc info details)))
  521. details-list " | ")))
  522. (defun gnus-bookmark-bmenu-hide-infos (&optional force)
  523. "Hide infos in bmenu, maybe FORCE."
  524. (if (and (not force) gnus-bookmark-bmenu-toggle-infos)
  525. ;; nothing to hide if above is nil
  526. (save-excursion
  527. (save-window-excursion
  528. (goto-char (point-min))
  529. (forward-line 2)
  530. (setq gnus-bookmark-bmenu-hidden-bookmarks
  531. (nreverse gnus-bookmark-bmenu-hidden-bookmarks))
  532. (save-excursion
  533. (goto-char (point-min))
  534. (search-forward "Gnus Bookmark")
  535. (backward-word 2)
  536. (setq gnus-bookmark-bmenu-bookmark-column (current-column)))
  537. (save-excursion
  538. (let ((inhibit-read-only t))
  539. (while gnus-bookmark-bmenu-hidden-bookmarks
  540. (move-to-column gnus-bookmark-bmenu-bookmark-column t)
  541. (gnus-bookmark-kill-line)
  542. (let ((start (point)))
  543. (insert (car gnus-bookmark-bmenu-hidden-bookmarks))
  544. (if (gnus-bookmark-mouse-available-p)
  545. (add-text-properties
  546. start
  547. (save-excursion (re-search-backward
  548. "[^ \t]")
  549. (1+ (point)))
  550. `(mouse-face highlight
  551. follow-link t
  552. help-echo
  553. ,(format "%s: go to this bookmark in other window"
  554. (aref gnus-mouse-2 0))))))
  555. (setq gnus-bookmark-bmenu-hidden-bookmarks
  556. (cdr gnus-bookmark-bmenu-hidden-bookmarks))
  557. (forward-line 1))))))))
  558. (defun gnus-bookmark-bmenu-check-position ()
  559. "Return non-nil if on a line with a bookmark.
  560. The actual value returned is gnus-bookmark-alist. Else
  561. reposition and try again, else return nil."
  562. (cond ((< (count-lines (point-min) (point)) 2)
  563. (goto-char (point-min))
  564. (forward-line 2)
  565. gnus-bookmark-alist)
  566. ((and (bolp) (eobp))
  567. (beginning-of-line 0)
  568. gnus-bookmark-alist)
  569. (t
  570. gnus-bookmark-alist)))
  571. (defun gnus-bookmark-bmenu-bookmark ()
  572. "Return a string which is bookmark of this line."
  573. (if (gnus-bookmark-bmenu-check-position)
  574. (save-excursion
  575. (save-window-excursion
  576. (goto-char (point-min))
  577. (search-forward "Gnus Bookmark")
  578. (backward-word 2)
  579. (setq gnus-bookmark-bmenu-bookmark-column (current-column)))))
  580. (if gnus-bookmark-bmenu-toggle-infos
  581. (gnus-bookmark-bmenu-hide-infos))
  582. (save-excursion
  583. (save-window-excursion
  584. (beginning-of-line)
  585. (forward-char gnus-bookmark-bmenu-bookmark-column)
  586. (prog1
  587. (buffer-substring-no-properties (point)
  588. (progn
  589. (end-of-line)
  590. (point)))
  591. ;; well, this is certainly crystal-clear:
  592. (if gnus-bookmark-bmenu-toggle-infos
  593. (gnus-bookmark-bmenu-toggle-infos t))))))
  594. (defun gnus-bookmark-show-details (bookmark)
  595. "Display the annotation for BOOKMARK in a buffer."
  596. (let ((record (gnus-bookmark-get-bookmark-record bookmark))
  597. (old-buf (current-buffer))
  598. (details gnus-bookmark-bookmark-details)
  599. detail)
  600. (save-excursion
  601. (pop-to-buffer (get-buffer-create "*Gnus Bookmark Annotation*") t)
  602. (erase-buffer)
  603. (while details
  604. (setq detail (pop details))
  605. (unless (equal (cdr (assoc detail record)) "")
  606. (insert (symbol-name detail) ": " (cdr (assoc detail record)) "\n")))
  607. (goto-char (point-min))
  608. (pop-to-buffer old-buf))))
  609. (defun gnus-bookmark-bmenu-show-details ()
  610. "Show the annotation for the current bookmark in another window."
  611. (interactive)
  612. (let ((bookmark (gnus-bookmark-bmenu-bookmark)))
  613. (if (gnus-bookmark-bmenu-check-position)
  614. (gnus-bookmark-show-details bookmark))))
  615. (defun gnus-bookmark-bmenu-mark ()
  616. "Mark bookmark on this line to be displayed by \\<gnus-bookmark-bmenu-mode-map>\\[gnus-bookmark-bmenu-select]."
  617. (interactive)
  618. (beginning-of-line)
  619. (if (gnus-bookmark-bmenu-check-position)
  620. (let ((inhibit-read-only t))
  621. (delete-char 1)
  622. (insert ?>)
  623. (forward-line 1)
  624. (gnus-bookmark-bmenu-check-position))))
  625. (defun gnus-bookmark-bmenu-unmark (&optional backup)
  626. "Cancel all requested operations on bookmark on this line and move down.
  627. Optional BACKUP means move up."
  628. (interactive "P")
  629. (beginning-of-line)
  630. (if (gnus-bookmark-bmenu-check-position)
  631. (progn
  632. (let ((inhibit-read-only t))
  633. (delete-char 1)
  634. ;; any flags to reset according to circumstances? How about a
  635. ;; flag indicating whether this bookmark is being visited?
  636. ;; well, we don't have this now, so maybe later.
  637. (insert " "))
  638. (forward-line (if backup -1 1))
  639. (gnus-bookmark-bmenu-check-position))))
  640. (defun gnus-bookmark-bmenu-backup-unmark ()
  641. "Move up and cancel all requested operations on bookmark on line above."
  642. (interactive)
  643. (forward-line -1)
  644. (if (gnus-bookmark-bmenu-check-position)
  645. (progn
  646. (gnus-bookmark-bmenu-unmark)
  647. (forward-line -1)
  648. (gnus-bookmark-bmenu-check-position))))
  649. (defun gnus-bookmark-bmenu-delete ()
  650. "Mark Gnus bookmark on this line to be deleted.
  651. To carry out the deletions that you've marked, use
  652. \\<gnus-bookmark-bmenu-mode-map>\\[gnus-bookmark-bmenu-execute-deletions]."
  653. (interactive)
  654. (beginning-of-line)
  655. (if (gnus-bookmark-bmenu-check-position)
  656. (let ((inhibit-read-only t))
  657. (delete-char 1)
  658. (insert ?D)
  659. (forward-line 1)
  660. (gnus-bookmark-bmenu-check-position))))
  661. (defun gnus-bookmark-bmenu-delete-backwards ()
  662. "Mark bookmark on this line to be deleted, then move up one line.
  663. To carry out the deletions that you've marked, use
  664. \\<gnus-bookmark-bmenu-mode-map>\\[gnus-bookmark-bmenu-execute-deletions]."
  665. (interactive)
  666. (gnus-bookmark-bmenu-delete)
  667. (forward-line -2)
  668. (if (gnus-bookmark-bmenu-check-position)
  669. (forward-line 1))
  670. (gnus-bookmark-bmenu-check-position))
  671. (defun gnus-bookmark-bmenu-select ()
  672. "Select this line's bookmark; also display bookmarks marked with `>'.
  673. You can mark bookmarks with the
  674. \\<gnus-bookmark-bmenu-mode-map>\\[gnus-bookmark-bmenu-mark]
  675. command."
  676. (interactive)
  677. (if (gnus-bookmark-bmenu-check-position)
  678. (let ((bmrk (gnus-bookmark-bmenu-bookmark))
  679. (menu (current-buffer)))
  680. (goto-char (point-min))
  681. (delete-other-windows)
  682. (gnus-bookmark-jump bmrk)
  683. (bury-buffer menu))))
  684. (defun gnus-bookmark-bmenu-select-by-mouse (event)
  685. (interactive "e")
  686. (mouse-set-point event)
  687. (gnus-bookmark-bmenu-select))
  688. (defun gnus-bookmark-bmenu-load ()
  689. "Load the Gnus bookmark file and rebuild the bookmark menu-buffer."
  690. (interactive)
  691. (if (gnus-bookmark-bmenu-check-position)
  692. (save-excursion
  693. (save-window-excursion
  694. ;; This will call `gnus-bookmark-bmenu-list'
  695. (call-interactively 'gnus-bookmark-load)))))
  696. (defun gnus-bookmark-bmenu-execute-deletions ()
  697. "Delete Gnus bookmarks marked with \\<Buffer-menu-mode-map>\\[Buffer-menu-delete] commands."
  698. (interactive)
  699. (message "Deleting Gnus bookmarks...")
  700. (let ((hide-em gnus-bookmark-bmenu-toggle-infos)
  701. (o-point (point))
  702. (o-str (save-excursion
  703. (beginning-of-line)
  704. (if (looking-at "^D")
  705. nil
  706. (buffer-substring
  707. (point)
  708. (progn (end-of-line) (point))))))
  709. (o-col (current-column)))
  710. (if hide-em (gnus-bookmark-bmenu-hide-infos))
  711. (setq gnus-bookmark-bmenu-toggle-infos nil)
  712. (goto-char (point-min))
  713. (forward-line 1)
  714. (while (re-search-forward "^D" (point-max) t)
  715. (gnus-bookmark-delete (gnus-bookmark-bmenu-bookmark) t)) ; pass BATCH arg
  716. (gnus-bookmark-bmenu-list)
  717. (setq gnus-bookmark-bmenu-toggle-infos hide-em)
  718. (if gnus-bookmark-bmenu-toggle-infos
  719. (gnus-bookmark-bmenu-toggle-infos t))
  720. (if o-str
  721. (progn
  722. (goto-char (point-min))
  723. (search-forward o-str)
  724. (beginning-of-line)
  725. (forward-char o-col))
  726. (goto-char o-point))
  727. (beginning-of-line)
  728. (gnus-bookmark-write-file)
  729. (message "Deleting bookmarks...done")))
  730. (defun gnus-bookmark-delete (bookmark &optional batch)
  731. "Delete BOOKMARK from the bookmark list.
  732. Removes only the first instance of a bookmark with that name. If
  733. there are one or more other bookmarks with the same name, they will
  734. not be deleted. Defaults to the \"current\" bookmark \(that is, the
  735. one most recently used in this file, if any\).
  736. Optional second arg BATCH means don't update the bookmark list buffer,
  737. probably because we were called from there."
  738. (gnus-bookmark-maybe-load-default-file)
  739. (let ((will-go (gnus-bookmark-get-bookmark bookmark)))
  740. (setq gnus-bookmark-alist (delq will-go gnus-bookmark-alist)))
  741. ;; Don't rebuild the list
  742. (if batch
  743. nil
  744. (gnus-bookmark-bmenu-surreptitiously-rebuild-list)))
  745. (provide 'gnus-bookmark)
  746. ;;; gnus-bookmark.el ends here