initial commit
[emacs-init.git] / auto-install / bookmark+-1.el
1 ;;; bookmark+-1.el - First part of package Bookmark+.
2 ;; 
3 ;; Filename: bookmark+-1.el
4 ;; Description: First part of package Bookmark+.
5 ;; Author: Drew Adams, Thierry Volpiatto
6 ;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com")
7 ;; Copyright (C) 2000-2011, Drew Adams, all rights reserved.
8 ;; Copyright (C) 2009, Thierry Volpiatto, all rights reserved.
9 ;; Created: Mon Jul 12 13:43:55 2010 (-0700)
10 ;; Last-Updated: Tue Aug  9 10:57:27 2011 (-0700)
11 ;;           By: dradams
12 ;;     Update #: 2238
13 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/bookmark+-1.el
14 ;; Keywords: bookmarks, bookmark+, placeholders, annotations, search, info, url, w3m, gnus
15 ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
16 ;; 
17 ;; Features that might be required by this library:
18 ;;
19 ;;   `bookmark', `bookmark+-1', `bookmark+-mac', `dired',
20 ;;   `dired-aux', `dired-x', `ffap', `pp'.
21 ;;
22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;; 
24 ;;; Commentary: 
25 ;;
26 ;;    The Bookmark+ libraries are these:
27 ;;
28 ;;    `bookmark+.el'     - main (driver) library
29 ;;    `bookmark+-mac.el' - Lisp macros
30 ;;    `bookmark+-lit.el' - (optional) code for highlighting bookmarks
31 ;;    `bookmark+-bmu.el' - code for the `*Bookmark List*' (bmenu)
32 ;;    `bookmark+-1.el'   - other (non-bmenu) required code (this file)
33 ;;    `bookmark+-key.el' - key and menu bindings
34 ;;
35 ;;    `bookmark+-doc.el' - documentation (comment-only file)
36 ;;    `bookmark+-chg.el' - change log (comment-only file)
37 ;;
38 ;;    The documentation (in `bookmark+-doc.el') includes how to
39 ;;    byte-compile and install Bookmark+.  The documentation is also
40 ;;    available in these ways:
41 ;;
42 ;;    1. From the bookmark list (`C-x r l'):
43 ;;       Use `?' to show the current bookmark-list status and general
44 ;;       help, then click link `Doc in Commentary' or link `Doc on the
45 ;;       Web'.
46 ;;
47 ;;    2. From the Emacs-Wiki Web site:
48 ;;       http://www.emacswiki.org/cgi-bin/wiki/BookmarkPlus.
49 ;;    
50 ;;    3. From the Bookmark+ group customization buffer:
51 ;;       `M-x customize-group bookmark-plus', then click link
52 ;;       `Commentary'.
53 ;;
54 ;;    (The commentary links in #1 and #3 work only if you have library
55 ;;    `bookmark+-doc.el' in your `load-path'.)
56  
57 ;;(@> "Index")
58 ;;
59 ;;  Index
60 ;;  -----
61 ;;
62 ;;  If you have library `linkd.el' and Emacs 22 or later, load
63 ;;  `linkd.el' and turn on `linkd-mode' now.  It lets you easily
64 ;;  navigate around the sections of this doc.  Linkd mode will
65 ;;  highlight this Index, as well as the cross-references and section
66 ;;  headings throughout this file.  You can get `linkd.el' here:
67 ;;  http://dto.freeshell.org/notebook/Linkd.html.
68 ;;
69 ;;  (@> "Things Defined Here")
70 ;;  (@> "User Options (Customizable)")
71 ;;  (@> "Internal Variables")
72 ;;  (@> "Compatibility Code for Older Emacs Versions")
73 ;;  (@> "Core Replacements (`bookmark-*' except `bookmark-bmenu-*')")
74 ;;  (@> "Bookmark+ Functions (`bmkp-*')")
75 ;;    (@> "Search-and-Replace Locations of Marked Bookmarks")
76 ;;    (@> "Tags")
77 ;;    (@> "Bookmark Predicates")
78 ;;    (@> "Filter Functions")
79 ;;    (@> "General Utility Functions")
80 ;;    (@> "Bookmark Entry Access Functions")
81 ;;    (@> "Sorting - General Functions")
82 ;;    (@> "Sorting - Commands")
83 ;;    (@> "Sorting - General Predicates")
84 ;;    (@> "Sorting - File-Name Predicates")
85 ;;    (@> "Indirect Bookmarking Functions")
86 ;;    (@> "Other Bookmark+ Functions (`bmkp-*')")
87 ;;  (@> "Keymaps")
88  
89 ;;(@* "Things Defined Here")
90 ;;
91 ;;  Things Defined Here
92 ;;  -------------------
93 ;;
94 ;;  Commands defined here:
95 ;;
96 ;;    `bmkp-add-tags', `bmkp-all-tags-jump',
97 ;;    `bmkp-all-tags-jump-other-window', `bmkp-all-tags-regexp-jump',
98 ;;    `bmkp-all-tags-regexp-jump-other-window',
99 ;;    `bmkp-autofile-add-tags', `bmkp-autofile-jump',
100 ;;    `bmkp-autofile-jump-other-window', `bmkp-autofile-remove-tags',
101 ;;    `bmkp-autofile-set', `bmkp-autonamed-jump',
102 ;;    `bmkp-autonamed-jump-other-window',
103 ;;    `bmkp-autonamed-this-buffer-jump',
104 ;;    `bmkp-autonamed-this-buffer-jump-other-window',
105 ;;    `bmkp-bookmark-a-file' `bmkp-bookmark-file-jump',
106 ;;    `bmkp-bookmark-list-jump',
107 ;;    `bmkp-choose-navlist-from-bookmark-list',
108 ;;    `bmkp-choose-navlist-of-type', `bmkp-compilation-target-set',
109 ;;    `bmkp-compilation-target-set-all', `bmkp-copy-tags',
110 ;;    `bmkp-crosshairs-highlight', `bmkp-cycle',
111 ;;    `bmkp-cycle-autonamed', `bmkp-cycle-autonamed-other-window',
112 ;;    `bmkp-cycle-bookmark-list',
113 ;;    `bmkp-cycle-bookmark-list-other-window', `bmkp-cycle-desktop',
114 ;;    `bmkp-cycle-dired', `bmkp-cycle-dired-other-window',
115 ;;    `bmkp-cycle-file', `bmkp-cycle-file-other-window',
116 ;;    `bmkp-cycle-gnus', `bmkp-cycle-gnus-other-window',
117 ;;    `bmkp-cycle-info', `bmkp-cycle-info-other-window',
118 ;;    `bmkp-cycle-lighted', `bmkp-cycle-lighted-other-window',
119 ;;    `bmkp-cycle-local-file', `bmkp-cycle-local-file-other-window',
120 ;;    `bmkp-cycle-man', `bmkp-cycle-man-other-window',
121 ;;    `bmkp-cycle-non-file', `bmkp-cycle-non-file-other-window',
122 ;;    `bmkp-cycle-other-window', `bmkp-cycle-remote-file',
123 ;;    `bmkp-cycle-remote-file-other-window',
124 ;;    `bmkp-cycle-specific-buffers',
125 ;;    `bmkp-cycle-specific-buffers-other-window',
126 ;;    `bmkp-cycle-specific-files',
127 ;;    `bmkp-cycle-specific-files-other-window',
128 ;;    `bmkp-cycle-this-buffer', `bmkp-cycle-this-buffer-other-window',
129 ;;    `bmkp-cycle-variable-list', `bmkp-cycle-url',
130 ;;    `bmkp-cycle-url-other-window',
131 ;;    `bmkp-delete-all-autonamed-for-this-buffer',
132 ;;    `bmkp-delete-bookmarks', `bmkp-describe-bookmark',
133 ;;    `bmkp-describe-bookmark-internals', `bmkp-desktop-change-dir',
134 ;;    `bmkp-desktop-delete', `bmkp-desktop-jump', `bmkp-desktop-read',
135 ;;    `bmkp-dired-jump', `bmkp-dired-jump-other-window',
136 ;;    `bmkp-dired-this-dir-jump',
137 ;;    `bmkp-dired-this-dir-jump-other-window', `bmkp-edit-bookmark',
138 ;;    `bmkp-edit-bookmark-record', `bmkp-edit-bookmark-record-mode',
139 ;;    `bmkp-edit-bookmark-record-send', `bmkp-edit-tags',
140 ;;    `bmkp-edit-tags-send', `bmkp-empty-file',
141 ;;    `bmkp-file-target-set', `bmkp-file-all-tags-jump',
142 ;;    `bmkp-file-all-tags-jump-other-window',
143 ;;    `bmkp-file-all-tags-regexp-jump',
144 ;;    `bmkp-file-all-tags-regexp-jump-other-window', `bmkp-file-jump',
145 ;;    `bmkp-file-jump-other-window', `bmkp-file-some-tags-jump',
146 ;;    `bmkp-file-some-tags-jump-other-window',
147 ;;    `bmkp-file-some-tags-regexp-jump',
148 ;;    `bmkp-file-some-tags-regexp-jump-other-window',
149 ;;    `bmkp-file-this-dir-all-tags-jump',
150 ;;    `bmkp-file-this-dir-all-tags-jump-other-window',
151 ;;    `bmkp-file-this-dir-all-tags-regexp-jump',
152 ;;    `bmkp-file-this-dir-all-tags-regexp-jump-other-window',
153 ;;    `bmkp-file-this-dir-jump',
154 ;;    `bmkp-file-this-dir-jump-other-window',
155 ;;    `bmkp-file-this-dir-some-tags-jump',
156 ;;    `bmkp-file-this-dir-some-tags-jump-other-window',
157 ;;    `bmkp-file-this-dir-some-tags-regexp-jump',
158 ;;    `bmkp-file-this-dir-some-tags-regexp-jump-other-window',
159 ;;    `bmkp-find-file', `bmkp-find-file-other-window',
160 ;;    `bmkp-find-file-all-tags',
161 ;;    `bmkp-find-file-all-tags-other-window',
162 ;;    `bmkp-find-file-all-tags-regexp',
163 ;;    `bmkp-find-file-all-tags-regexp-other-window',
164 ;;    `bmkp-find-file-some-tags',
165 ;;    `bmkp-find-file-some-tags-other-window',
166 ;;    `bmkp-find-file-some-tags-regexp',
167 ;;    `bmkp-find-file-some-tags-regexp-other-window',
168 ;;    `bmkp-gnus-jump', `bmkp-gnus-jump-other-window',
169 ;;    `bmkp-info-jump', `bmkp-info-jump-other-window',
170 ;;    `bmkp-jump-in-navlist', `bmkp-jump-in-navlist-other-window',
171 ;;    `bmkp-jump-to-type', `bmkp-jump-to-type-other-window',
172 ;;    `bmkp-list-all-tags', `bmkp-list-defuns-in-commands-file',
173 ;;    `bmkp-local-file-jump', `bmkp-local-file-jump-other-window',
174 ;;    `bmkp-make-function-bookmark', `bmkp-man-jump',
175 ;;    `bmkp-man-jump-other-window', `bmkp-menu-jump-other-window'
176 ;;    (Emacs 20, 21), `bmkp-navlist-bmenu-list',
177 ;;    `bmkp-next-autonamed-bookmark',
178 ;;    `bmkp-next-autonamed-bookmark-repeat', `bmkp-next-bookmark',
179 ;;    `bmkp-next-bookmark-list-bookmark',
180 ;;    `bmkp-next-bookmark-list-bookmark-repeat',
181 ;;    `bmkp-next-bookmark-repeat', `bmkp-next-bookmark-this-buffer',
182 ;;    `bmkp-next-bookmark-this-buffer-repeat',
183 ;;    `bmkp-next-bookmark-w32', `bmkp-next-bookmark-w32-repeat',
184 ;;    `bmkp-next-desktop-bookmark',
185 ;;    `bmkp-next-desktop-bookmark-repeat', `bmkp-next-dired-bookmark',
186 ;;    `bmkp-next-dired-bookmark-repeat', `bmkp-next-file-bookmark',
187 ;;    `bmkp-next-file-bookmark-repeat', `bmkp-next-gnus-bookmark',
188 ;;    `bmkp-next-gnus-bookmark-repeat', `bmkp-next-info-bookmark',
189 ;;    `bmkp-next-info-bookmark-repeat', `bmkp-next-lighted-bookmark',
190 ;;    `bmkp-next-lighted-bookmark-repeat',
191 ;;    `bmkp-next-local-file-bookmark',
192 ;;    `bmkp-next-local-file-bookmark-repeat',
193 ;;    `bmkp-next-man-bookmark', `bmkp-next-man-bookmark-repeat',
194 ;;    `bmkp-next-non-file-bookmark',
195 ;;    `bmkp-next-non-file-bookmark-repeat',
196 ;;    `bmkp-next-remote-file-bookmark',
197 ;;    `bmkp-next-remote-file-bookmark-repeat',
198 ;;    `bmkp-next-specific-buffers-bookmark',
199 ;;    `bmkp-next-specific-buffers-bookmark-repeat',
200 ;;    `bmkp-next-specific-files-bookmark',
201 ;;    `bmkp-next-specific-files-bookmark-repeat',
202 ;;    `bmkp-next-variable-list-bookmark',
203 ;;    `bmkp-next-variable-list-bookmark-repeat',
204 ;;    `bmkp-next-url-bookmark', `bmkp-next-url-bookmark-repeat',
205 ;;    `bmkp-non-file-jump', `bmkp-non-file-jump-other-window',
206 ;;    `bmkp-occur-create-autonamed-bookmarks',
207 ;;    `bmkp-occur-target-set', `bmkp-occur-target-set-all',
208 ;;    `bmkp-paste-add-tags', `bmkp-paste-replace-tags',
209 ;;    `bmkp-previous-bookmark', `bmkp-previous-bookmark-repeat',
210 ;;    `bmkp-previous-bookmark-this-buffer',
211 ;;    `bmkp-previous-bookmark-this-buffer-repeat',
212 ;;    `bmkp-previous-bookmark-w32',
213 ;;    `bmkp-previous-bookmark-w32-repeat',
214 ;;    `bmkp-purge-notags-autofiles', `bmkp-read-bookmark-for-type',
215 ;;    `bmkp-region-jump', `bmkp-region-jump-other-window',
216 ;;    `bmkp-remote-file-jump', `bmkp-remote-file-jump-other-window',
217 ;;    `bmkp-remove-all-tags', `bmkp-remove-tags',
218 ;;    `bmkp-remove-tags-from-all', `bmkp-rename-tag',
219 ;;    `bmkp-save-menu-list-state', `bmkp-send-bug-report',
220 ;;    `bmkp-set-autonamed-bookmark',
221 ;;    `bmkp-set-autonamed-bookmark-at-line',
222 ;;    `bmkp-set-autonamed-regexp-buffer',
223 ;;    `bmkp-set-autonamed-regexp-region',
224 ;;    `bmkp-set-bookmark-file-bookmark', `bmkp-set-desktop-bookmark',
225 ;;    `bmkp-set-restrictions-bookmark', `bmkp-set-tag-value',
226 ;;    `bmkp-set-tag-value-for-navlist',
227 ;;    `bmkp-set-variable-list-bookmark', `bmkp-some-tags-jump',
228 ;;    `bmkp-some-tags-jump-other-window',
229 ;;    `bmkp-some-tags-regexp-jump',
230 ;;    `bmkp-some-tags-regexp-jump-other-window',
231 ;;    `bmkp-specific-buffers-jump',
232 ;;    `bmkp-specific-buffers-jump-other-window',
233 ;;    `bmkp-specific-files-jump',
234 ;;    `bmkp-specific-files-jump-other-window',
235 ;;    `bmkp-switch-bookmark-file',
236 ;;    `bmkp-switch-to-last-bookmark-file', `bmkp-tag-a-file',
237 ;;    `bmkp-this-buffer-bmenu-list', `bmkp-this-buffer-jump',
238 ;;    `bmkp-this-buffer-jump-other-window',
239 ;;    `bmkp-toggle-autonamed-bookmark-set/delete',
240 ;;    `bmkp-toggle-bookmark-set-refreshes',
241 ;;    `bmkp-toggle-saving-bookmark-file',
242 ;;    `bmkp-toggle-saving-menu-list-state',
243 ;;    `bmkp-toggle-bookmark-set-refreshes', `bmkp-unomit-all',
244 ;;    `bmkp-untag-a-file', `bmkp-url-target-set', `bmkp-url-jump',
245 ;;    `bmkp-url-jump-other-window', `bmkp-use-bookmark-file-create',
246 ;;    `bmkp-variable-list-jump', `bmkp-version', `bmkp-w3m-jump',
247 ;;    `bmkp-w3m-jump-other-window', `old-bookmark-insert',
248 ;;    `old-bookmark-relocate'.
249 ;;
250 ;;  User options defined here:
251 ;;
252 ;;    `bmkp-autoname-bookmark-function', `bmkp-autoname-format',
253 ;;    `bmkp-bookmark-name-length-max', `bmkp-crosshairs-flag',
254 ;;    `bmkp-default-bookmark-name',
255 ;;    `bmkp-default-handler-associations',
256 ;;    `bmkp-desktop-no-save-vars',
257 ;;    `bmkp-guess-default-handler-for-file-flag',
258 ;;    `bmkp-handle-region-function', `bmkp-incremental-filter-delay',
259 ;;    `bmkp-menu-popup-max-length', `bmkp-other-window-pop-to-flag',
260 ;;    `bmkp-prompt-for-tags-flag', `bmkp-region-search-size',
261 ;;    `bmkp-save-new-location-flag',
262 ;;    `bmkp-sequence-jump-display-function',
263 ;;    `bmkp-show-end-of-region', `bmkp-sort-comparer',
264 ;;    `bmkp-su-or-sudo-regexp',
265 ;;    `bmkp-this-buffer-cycle-sort-comparer', `bmkp-use-region',
266 ;;    `bmkp-w3m-allow-multi-tabs'.
267 ;;
268 ;;  Non-interactive functions defined here:
269 ;;
270 ;;    `bmkext-jump-gnus', `bmkext-jump-man', `bmkext-jump-w3m',
271 ;;    `bmkext-jump-woman', `bmkp-all-exif-data',
272 ;;    `bmkp-all-tags-alist-only', `bmkp-all-tags-regexp-alist-only',
273 ;;    `bmkp-alpha-cp', `bmkp-alpha-p', `bmkp-annotated-alist-only',
274 ;;    `bmkp-autofile-alist-only', `bmkp-autofile-bookmark-p',
275 ;;    `bmkp-autoname-bookmark', `bmkp-autonamed-alist-only',
276 ;;    `bmkp-autonamed-bookmark-for-buffer-p',
277 ;;    `bmkp-autonamed-bookmark-p',
278 ;;    `bmkp-autonamed-this-buffer-alist-only',
279 ;;    `bmkp-bookmark-creation-cp', `bmkp-bookmark-description',
280 ;;    `bmkp-bookmark-last-access-cp', `bmkp-bookmark-file-alist-only',
281 ;;    `bmkp-bookmark-list-alist-only',
282 ;;    `bmkp-bookmark-file-bookmark-p',
283 ;;    `bmkp-bookmark-image-bookmark-p',
284 ;;    `bmkp-bookmark-list-bookmark-p', `bmkp-bookmark-name-member',
285 ;;    `bmkp-bookmark-type', `bmkp-buffer-last-access-cp',
286 ;;    `bmkp-buffer-names', `bmkp-compilation-file+line-at',
287 ;;    `bmkp-completing-read-1', `bmkp-completing-read-buffer-name',
288 ;;    `bmkp-completing-read-file-name', `bmkp-completing-read-lax',
289 ;;    `bmkp-cp-not', `bmkp-create-variable-list-bookmark',
290 ;;    `bmkp-current-bookmark-list-state', `bmkp-current-sort-order',
291 ;;    `bmkp-cycle-1', `bmkp-default-bookmark-name',
292 ;;    `bmkp-default-handler-for-file', `bmkp-default-handler-user',
293 ;;    `bmkp-delete-autonamed-no-confirm',
294 ;;    `bmkp-delete-autonamed-this-buffer-no-confirm',
295 ;;    `bmkp-delete-bookmark-name-from-list',
296 ;;    `bmkp-desktop-alist-only', `bmkp-desktop-bookmark-p',
297 ;;    `bmkp-desktop-kill', `bmkp-dired-alist-only',
298 ;;    `bmkp-dired-bookmark-p', `bmkp-dired-subdirs',
299 ;;    `bmkp-dired-this-dir-alist-only',
300 ;;    `bmkp-dired-this-dir-bookmark-p', `bmkp-edit-tags-mode',
301 ;;    `bmkp-end-position-post-context',
302 ;;    `bmkp-end-position-pre-context', `bmkp-every', `bmkp-face-prop',
303 ;;    `bmkp-file-alist-only', `bmkp-file-all-tags-alist-only',
304 ;;    `bmkp-file-all-tags-regexp-alist-only', `bmkp-file-alpha-cp',
305 ;;    `bmkp-file-attribute-0-cp', `bmkp-file-attribute-1-cp',
306 ;;    `bmkp-file-attribute-2-cp', `bmkp-file-attribute-3-cp',
307 ;;    `bmkp-file-attribute-4-cp', `bmkp-file-attribute-5-cp',
308 ;;    `bmkp-file-attribute-6-cp', `bmkp-file-attribute-7-cp',
309 ;;    `bmkp-file-attribute-8-cp', `bmkp-file-attribute-9-cp',
310 ;;    `bmkp-file-attribute-10-cp', `bmkp-file-attribute-11-cp',
311 ;;    `bmkp-file-bookmark-p', `bmkp-file-names', `bmkp-file-remote-p',
312 ;;    `bmkp-file-some-tags-alist-only',
313 ;;    `bmkp-file-some-tags-regexp-alist-only',
314 ;;    `bmkp-file-this-dir-alist-only',
315 ;;    `bmkp-file-this-dir-all-tags-alist-only',
316 ;;    `bmkp-file-this-dir-all-tags-regexp-alist-only',
317 ;;    `bmkp-file-this-dir-bookmark-p',
318 ;;    `bmkp-file-this-dir-some-tags-alist-only',
319 ;;    `bmkp-file-this-dir-some-tags-regexp-alist-only',
320 ;;    `bmkp-float-time', `bmkp-full-tag', `bmkp-function-bookmark-p',
321 ;;    `bmkp-get-autofile-bookmark', `bmkp-get-buffer-name',
322 ;;    `bmkp-get-end-position', `bmkp-get-tag-value', `bmkp-get-tags',
323 ;;    `bmkp-get-visit-time', `bmkp-get-visits-count',
324 ;;    `bmkp-gnus-alist-only', `bmkp-gnus-bookmark-p', `bmkp-gnus-cp',
325 ;;    `bmkp-goto-position', `bmkp-handle-region-default',
326 ;;    `bmkp-handler-cp', `bmkp-has-tag-p', `bmkp-info-alist-only',
327 ;;    `bmkp-info-bookmark-p', `bmkp-info-cp', `bmkp-isearch-bookmarks'
328 ;;    (Emacs 23+), `bmkp-isearch-bookmarks-regexp' (Emacs 23+),
329 ;;    `bmkp-isearch-next-bookmark-buffer' (Emacs 23+), `bmkp-jump-1',
330 ;;    `bmkp-jump-bookmark-file', `bmkp-jump-bookmark-list',
331 ;;    `bmkp-jump-desktop', `bmkp-jump-dired', `bmkp-jump-function',
332 ;;    `bmkp-jump-gnus', `bmkp-jump-man', `bmkp-jump-sequence',
333 ;;    `bmkp-jump-url-browse', `bmkp-jump-variable-list',
334 ;;    `bmkp-jump-w3m', `bmkp-jump-w3m-new-session',
335 ;;    `bmkp-jump-w3m-only-one-tab', `bmkp-jump-woman',
336 ;;    `bmkp-last-specific-buffer-alist-only',
337 ;;    `bmkp-last-specific-buffer-p',
338 ;;    `bmkp-last-specific-file-alist-only',
339 ;;    `bmkp-last-specific-file-p', `bmkp-line-number-at-pos',
340 ;;    `bmkp-list-position', `bmkp-local-directory-bookmark-p',
341 ;;    `bmkp-local-file-accessed-more-recently-cp',
342 ;;    `bmkp-local-file-alist-only', `bmkp-local-file-bookmark-p',
343 ;;    `bmkp-local-file-size-cp', `bmkp-local-file-type-cp',
344 ;;    `bmkp-local-file-updated-more-recently-cp',
345 ;;    `bmkp-make-bookmark-file-record',
346 ;;    `bmkp-make-bookmark-list-record', `bmkp-make-desktop-record',
347 ;;    `bmkp-make-dired-record', `bmkp-make-gnus-record',
348 ;;    `bmkp-make-man-record', `bmkp-make-plain-predicate',
349 ;;    `bmkp-make-record-for-target-file',
350 ;;    `bmkp-make-url-browse-record', `bmkp-make-variable-list-record',
351 ;;    `bmkp-make-w3m-record', `bmkp-make-woman-record' (Emacs 21+),
352 ;;    `bmkp-man-alist-only', `bmkp-man-bookmark-p',
353 ;;    `bmkp-marked-bookmark-p', `bmkp-marked-bookmarks-only',
354 ;;    `bmkp-marked-cp', `bmkp-maybe-save-bookmarks',
355 ;;    `bmkp-msg-about-sort-order', `bmkp-multi-sort',
356 ;;    `bmkp-names-same-bookmark-p', `bmkp-non-autonamed-alist-only',
357 ;;    `bmkp-non-file-alist-only', `bmkp-non-file-bookmark-p',
358 ;;    `bmkp-omitted-alist-only', `bmkp-position-after-whitespace',
359 ;;    `bmkp-position-before-whitespace', `bmkp-position-cp',
360 ;;    `bmkp-position-post-context',
361 ;;    `bmkp-position-post-context-region',
362 ;;    `bmkp-position-pre-context', `bmkp-position-pre-context-region',
363 ;;    `bmkp-printable-p', `bmkp-printable-vars+vals',
364 ;;    `bmkp-read-bookmark-file-name', `bmkp-read-tag-completing',
365 ;;    `bmkp-read-tags', `bmkp-read-tags-completing',
366 ;;    `bmkp-read-variable', `bmkp-read-variables-completing',
367 ;;    `bmkp-record-visit', `bmkp-refresh-latest-bookmark-list',
368 ;;    `bmkp-refresh-menu-list',
369 ;;    `bmkp-regexp-filtered-annotation-alist-only',
370 ;;    `bmkp-regexp-filtered-bookmark-name-alist-only',
371 ;;    `bmkp-regexp-filtered-file-name-alist-only',
372 ;;    `bmkp-regexp-filtered-tags-alist-only',
373 ;;    `bmkp-region-alist-only', `bmkp-region-bookmark-p',
374 ;;    `bmkp-remote-file-alist-only', `bmkp-remote-file-bookmark-p',
375 ;;    `bmkp-remove-dups', `bmkp-remove-if', `bmkp-remove-if-not',
376 ;;    `bmkp-remove-omitted', `bmkp-repeat-command',
377 ;;    `bmkp-replace-existing-bookmark', `bmkp-root-or-sudo-logged-p',
378 ;;    `bmkp-same-creation-time-p', `bmkp-same-file-p',
379 ;;    `bmkp-save-new-region-location',
380 ;;    `bmkp-select-buffer-other-window', `bmkp-sequence-bookmark-p',
381 ;;    `bmkp-set-tag-value-for-bookmarks', `bmkp-set-union',
382 ;;    `bmkp-some', `bmkp-some-marked-p', `bmkp-some-tags-alist-only',
383 ;;    `bmkp-some-tags-regexp-alist-only', `bmkp-some-unmarked-p'
384 ;;    `bmkp-sort-omit', `bmkp-sound-jump',
385 ;;    `bmkp-specific-buffers-alist-only',
386 ;;    `bmkp-specific-files-alist-only', `bmkp-tag-name',
387 ;;    `bmkp-tags-list', `bmkp-this-buffer-alist-only',
388 ;;    `bmkp-this-buffer-p', `bmkp-this-file-alist-only',
389 ;;    `bmkp-this-file-p', `bmkp-unmarked-bookmarks-only',
390 ;;    `bmkp-upcase', `bmkp-update-autonamed-bookmark',
391 ;;    `bmkp-url-alist-only', `bmkp-url-bookmark-p',
392 ;;    `bmkp-url-browse-alist-only', `bmkp-url-browse-bookmark-p',
393 ;;    `bmkp-url-cp', `bmkp-variable-list-alist-only',
394 ;;    `bmkp-variable-list-bookmark-p', `bmkp-visited-more-cp',
395 ;;    `bmkp-w3m-alist-only', `bmkp-w3m-bookmark-p', `bmkp-w3m-cp',
396 ;;    `bmkp-w3m-set-new-buffer-name'.
397 ;;
398 ;;  Internal variables defined here:
399 ;;
400 ;;    `bmkp-after-set-hook', `bmkp-bookmark-file-history',
401 ;;    `bmkp-bookmark-list-history', `bmkp-current-bookmark-file',
402 ;;    `bmkp-current-nav-bookmark', `bmkp-desktop-history',
403 ;;    `bmkp-dired-history', `bmkp-edit-bookmark-record-mode-map',
404 ;;    `bmkp-edit-tags-mode-map', `bmkp-file-bookmark-handlers',
405 ;;    `bmkp-file-history', `bmkp-gnus-history', `bmkp-info-history',
406 ;;    `bmkp-isearch-bookmarks' (Emacs 23+),
407 ;;    `bmkp-jump-display-function', `bmkp-jump-other-window-map',
408 ;;    `bmkp-last-bmenu-state-file', `bmkp-last-bookmark-file',
409 ;;    `bmkp-last-save-flag-value', `bmkp-last-specific-buffer',
410 ;;    `bmkp-last-specific-file', `bmkp-latest-bookmark-alist',
411 ;;    `bmkp-local-file-history', `bmkp-man-history', `bmkp-nav-alist',
412 ;;    `bmkp-non-file-filename', `bmkp-non-file-history',
413 ;;    `bmkp-region-history', `bmkp-remote-file-history',
414 ;;    `bmkp-return-buffer', `bmkp-reverse-multi-sort-p',
415 ;;    `bmkp-reverse-sort-p', `bmkp-sorted-alist',
416 ;;    `bmkp-specific-buffers-history', `bmkp-specific-files-history',
417 ;;    `bmkp-tag-history', `bmkp-tags-alist', `bmkp-types-alist',
418 ;;    `bmkp-url-history', `bmkp-use-w32-browser-p',
419 ;;    `bmkp-variable-list-history', `bmkp-version-number',
420 ;;    `bmkp-w3m-history'.
421 ;;
422 ;;
423 ;;  ***** NOTE: The following commands defined in `bookmark.el'
424 ;;              have been REDEFINED HERE:
425 ;;
426 ;;    `bookmark-delete', `bookmark-edit-annotation',
427 ;;    `bookmark-edit-annotation-mode', `bookmark-insert',
428 ;;    `bookmark-insert-location', `bookmark-jump',
429 ;;    `bookmark-jump-other-window', `bookmark-load',
430 ;;    `bookmark-relocate', `bookmark-rename', `bookmark-save',
431 ;;    `bookmark-send-edited-annotation', `bookmark-set',
432 ;;    `bookmark-yank-word'.
433 ;;
434 ;;
435 ;;  ***** NOTE: The following non-interactive functions defined in
436 ;;              `bookmark.el' have been REDEFINED HERE:
437 ;;
438 ;;    `bookmark--jump-via', `bookmark-all-names',
439 ;;    `bookmark-completing-read', `bookmark-default-handler',
440 ;;    `bookmark-exit-hook-internal', `bookmark-get-bookmark' (Emacs
441 ;;    20-22), `bookmark-get-bookmark-record' (Emacs 20-22),
442 ;;    `bookmark-get-handler' (Emacs 20-22),
443 ;;    `bookmark-handle-bookmark', `bookmark-jump-noselect' (Emacs
444 ;;    20-22), `bookmark-location', `bookmark-make-record' (Emacs
445 ;;    20-22), `bookmark-make-record-default', `bookmark-maybe-message'
446 ;;    (Emacs 20-21), `bookmark-prop-get' (Emacs 20-22),
447 ;;    `bookmark-prop-set', `bookmark-show-annotation',
448 ;;    `bookmark-show-all-annotations', `bookmark-store' (Emacs 20-22),
449 ;;    `bookmark-write-file'.
450 ;;
451 ;;
452 ;;  ***** NOTE: The following variables defined in `bookmark.el'
453 ;;              have been REDEFINED HERE:
454 ;;
455 ;;    `bookmark-alist' (doc string only),
456 ;;    `bookmark-make-record-function' (Emacs 20-22).
457 ;;
458 ;;
459 ;;  ***** NOTE: The following functions defined in `info.el'
460 ;;              have been REDEFINED HERE:
461 ;;
462 ;;    `Info-bookmark-jump' (Emacs 20-22), `Info-bookmark-make-record'
463 ;;    (Emacs 20-22).
464 ;;
465 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
466 ;;
467 ;; This program is free software; you can redistribute it and/or modify
468 ;; it under the terms of the GNU General Public License as published by
469 ;; the Free Software Foundation; either version 3, or (at your option)
470 ;; any later version.
471 ;;
472 ;; This program is distributed in the hope that it will be useful,
473 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
474 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
475 ;; GNU General Public License for more details.
476 ;;
477 ;; You should have received a copy of the GNU General Public License
478 ;; along with this program; see the file COPYING.  If not, write to
479 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
480 ;; Floor, Boston, MA 02110-1301, USA.
481 ;;
482 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
483 ;;
484 ;;; Code:
485
486 ;;;;;;;;;;;;;;;;;;;;;;;
487
488 (unless (fboundp 'file-remote-p) (require 'ffap)) ;; ffap-file-remote-p
489 (eval-when-compile (require 'gnus)) ;; mail-header-id (really in `nnheader.el')
490 (eval-when-compile (require 'gnus-sum)) ;; gnus-summary-article-header
491 (eval-when-compile (require 'cl)) ;; case, multiple-value-bind
492
493 (require 'bookmark)
494 ;; bookmark-alist, bookmark-alist-from-buffer,
495 ;; bookmark-alist-modification-count, bookmark-annotation-name,
496 ;; bookmark-automatically-show-annotations, bookmark-bmenu-bookmark,
497 ;; bookmark-bmenu-surreptitiously-rebuild-list,
498 ;; bookmark-bmenu-toggle-filenames, bookmark-buffer-file-name,
499 ;; bookmark-buffer-name, bookmark-completion-ignore-case,
500 ;; bookmark-current-bookmark, bookmark-default-file,
501 ;; bookmark-edit-annotation, bookmark-get-annotation,
502 ;; bookmark-get-bookmark, bookmark-get-bookmark-record,
503 ;; bookmark-get-filename, bookmark-get-front-context-string,
504 ;; bookmark-get-handler, bookmark-get-position,
505 ;; bookmark-get-rear-context-string, bookmark-import-new-list,
506 ;; bookmark-insert-file-format-version-stamp, bookmark-kill-line,
507 ;; bookmark-make-record, bookmark-maybe-historicize-string,
508 ;; bookmark-maybe-load-default-file, bookmark-maybe-message,
509 ;; bookmark-maybe-upgrade-file-format, bookmark-menu-popup-paned-menu,
510 ;; bookmark-name-from-full-record, bookmark-name-from-record,
511 ;; bookmark-popup-menu-and-apply-function, bookmark-prop-get,
512 ;; bookmarks-already-loaded, bookmark-save-flag, bookmark-search-size,
513 ;; bookmark-set-annotation, bookmark-set-filename, bookmark-set-name,
514 ;; bookmark-set-position, bookmark-store, bookmark-time-to-save-p,
515 ;; bookmark-use-annotations, bookmark-version-control,
516 ;; bookmark-yank-point
517
518 ;;; Fix incompatibility introduced by gratuitous Emacs name change.
519 (cond ((and (fboundp 'bookmark-name-from-record) (not (fboundp 'bookmark-name-from-full-record)))
520        (defalias 'bookmark-name-from-full-record 'bookmark-name-from-record))
521       ((and (fboundp 'bookmark-name-from-full-record) (not (fboundp 'bookmark-name-from-record)))
522        (defalias 'bookmark-name-from-record 'bookmark-name-from-full-record)))
523
524 (require 'bookmark+-mac)
525 ;; bmkp-define-cycle-command, bmkp-define-file-sort-predicate, bmkp-menu-bar-make-toggle,
526 ;; bmkp-replace-regexp-in-string
527
528 (eval-when-compile (require 'bookmark+-bmu))
529 ;; bmkp-bmenu-before-hide-marked-alist,
530 ;; bmkp-bmenu-before-hide-unmarked-alist, bmkp-bmenu-commands-file,
531 ;; bmkp-bmenu-filter-function, bmkp-bmenu-filter-pattern,
532 ;; bmkp-bmenu-first-time-p, bmkp-bmenu-goto-bookmark-named,
533 ;; bmkp-bmenu-marked-bookmarks, bmkp-bmenu-omitted-bookmarks, bmkp-bmenu-refresh-menu-list,
534 ;; bmkp-bmenu-show-all, bmkp-bmenu-state-file, bmkp-bmenu-title,
535 ;; bmkp-maybe-unpropertize-bookmark-names, bmkp-sort-orders-alist
536
537 ;; (eval-when-compile (require 'bookmark+-lit nil t))
538 ;; bmkp-light-bookmark, bmkp-light-bookmarks, bmkp-light-this-buffer
539
540
541 ;; For the redefinition of `bookmark-get-bookmark' in Emacs < 23.
542 (provide 'bookmark+-1)                  ; Ensure this library is loaded before we compile it.
543 (require 'bookmark+-1)                  ; So be sure to put this library in your `load-path' before
544                                         ; trying to byte-compile it.
545
546 ;;;;;;;;;;;;;;;;;;;;;;;
547
548 ;; Quiet the byte-compiler
549
550 (defvar bmkp-auto-light-when-set)       ; Defined in `bookmark+-lit.el'.
551 (defvar bmkp-auto-light-when-jump)      ; Defined in `bookmark+-lit.el'.
552 (defvar bmkp-light-priorities)          ; Defined in `bookmark+-lit.el'.
553 (defvar bookmark-current-point)         ; Defined in `bookmark.el', but not in Emacs 23+.
554 (defvar bookmark-edit-annotation-text-func) ; Defined in `bookmark.el'.
555 (defvar bookmark-read-annotation-text-func) ; Defined in `bookmark.el', but not in Emacs 23+.
556 (defvar bookmark-make-record-function)  ; Defined in `bookmark.el'.
557 (defvar desktop-buffer-args-list)       ; Defined in `desktop.el'.
558 (defvar desktop-delay-hook)             ; Defined in `desktop.el'.
559 (defvar desktop-dirname)                ; Defined in `desktop.el'.
560 (defvar desktop-file-modtime)           ; Defined in `desktop.el'.
561 (defvar desktop-globals-to-save)        ; Defined in `desktop.el'.
562 (defvar desktop-save-mode)              ; Defined in `desktop.el'.
563 (defvar desktop-save)                   ; Defined in `desktop.el'.
564 (defvar dired-actual-switches)          ; Defined in `dired.el'.
565 (defvar dired-buffers)                  ; Defined in `dired.el'.
566 (defvar dired-directory)                ; Defined in `dired.el'.
567 (defvar dired-guess-shell-case-fold-search) ; Defined in `dired-x.el'.
568 (defvar dired-subdir-alist)             ; Defined in `dired.el'.
569 (defvar gnus-article-current)           ; Defined in `gnus-sum.el'.
570 (defvar Info-current-node)              ; Defined in `info.el'.
571 (defvar Info-current-file)              ; Defined in `info.el'.
572 (defvar Man-arguments)                  ; Defined in `man.el'.
573 (defvar read-file-name-completion-ignore-case) ; Emacs 23+.
574 (defvar last-repeatable-command)        ; Defined in `repeat.el'.
575 (defvar w3m-current-title)              ; Defined in `w3m.el'.
576 (defvar w3m-current-url)                ; Defined in `w3m.el'.
577 (defvar w3m-mode-map)                   ; Defined in `w3m.el'.
578 (defvar wide-n-restrictions)            ; Defined in `wide-n.el'.
579 (defvar woman-last-file-name)           ; Defined in `woman.el'.
580  
581 ;;(@* "User Options (Customizable)")
582 ;;; User Options (Customizable) --------------------------------------
583
584 ;;;###autoload
585 (defcustom bmkp-autoname-bookmark-function 'bmkp-autoname-bookmark
586   "*Function to automatically name a bookmark at point (cursor position)."
587   :type 'function :group 'bookmark-plus)
588
589 ;;;###autoload
590 (defcustom bmkp-autoname-format (if (> emacs-major-version 21) "^[0-9]\\{9\\} %s" "^[0-9]+ %s")
591   "*Format string to match an autonamed bookmark name.
592 It must have a single `%s' that to accept the buffer name."
593   :type 'string :group 'bookmark-plus)
594
595 ;;;###autoload
596 (defcustom bmkp-bookmark-name-length-max 70
597   "*Max number of chars for default name for a bookmark with a region."
598   :type 'integer :group 'bookmark-plus)
599
600 ;;;###autoload
601 (defcustom bmkp-crosshairs-flag (> emacs-major-version 21)
602   "*Non-nil means highlight with crosshairs when you visit a bookmark.
603 The highlighting is temporary - until your next action.
604 You need library `crosshairs.el' for this feature, and you need Emacs
605 22 or later.
606
607 If you use this option in Lisp code, you will want to add/remove
608 `bmkp-crosshairs-highlight' to/from `bookmark-after-jump-hook'."
609   :set (lambda (sym new-val)
610          (custom-set-default sym new-val)
611          (if (and bmkp-crosshairs-flag (> emacs-major-version 21)
612                   (condition-case nil (require 'crosshairs nil t) (error nil)))
613              (add-hook 'bookmark-after-jump-hook 'bmkp-crosshairs-highlight)
614            (remove-hook 'bookmark-after-jump-hook 'bmkp-crosshairs-highlight)))         
615   :type 'boolean :group 'bookmark-plus)
616
617 ;;;###autoload
618 (defcustom bmkp-default-bookmark-name 'highlighted
619   "*Default bookmark name preference.
620 In `*Bookmark List*' use the name of the current line's bookmark.
621 Otherwise, if `bookmark+-lit.el' is not loaded then use the name of
622  the last-used bookmark in the current file.
623
624 Otherwise, use this option to determine the default, by preferring one
625 of the following, if available:
626
627 * a highlighted bookmark at point
628 * the last-used bookmark in the current file"
629   :type '(choice
630           (const :tag "Highlighted bookmark at point"    highlighted)
631           (const :tag "Last used bookmark in same file"  last-used))
632   :group 'bookmark-plus)
633
634 ;;;###autoload
635 (defcustom bmkp-default-handler-associations
636   (and (require 'dired-x)               ; It in turn requires `dired-aux.el'
637        (let ((assns  ()))
638          (dolist (shell-assn  dired-guess-shell-alist-user)
639            (push (cons (car shell-assn)
640                        `(lambda (bmk) 
641                          (dired-run-shell-command
642                           (dired-shell-stuff-it ,(cadr shell-assn) (list (bookmark-get-filename bmk))
643                            nil nil))))
644                  assns))
645          assns))
646   "*File associations for bookmark handlers used for indirect bookmarks.
647 Each element of the alist is (REGEXP . COMMAND).
648 REGEXP matches a file name.
649 COMMAND is a sexp that evaluates to either a shell command (a string)
650  or an Emacs function (a symbol or a lambda form).
651
652 Example value:
653
654  ((\"\\.pdf$\" . \"AcroRd32.exe\") ; Adobe Acrobat Reader
655   (\"\\.ps$\" . \"gsview32.exe\")) ; Ghostview (PostScript viewer)
656
657 When you change this option using Customize, if you want to use a
658 literal string as COMMAND then you must double-quote the text:
659 \"...\".  (But do not use double-quotes for the REGEXP.)  If you want
660 to use a symbol as COMMAND, then single-quote it - e.g. 'foo.
661
662 This option is used by `bmkp-default-handler-for-file' to determine
663 the default handler for a given file.  If a given file name does not
664 match this option, and if `bmkp-guess-default-handler-for-file-flag'
665 is non-nil, then then `bmkp-default-handler-for-file' tries to guess a
666 shell command to use in the default handler.  For that it uses
667 `dired-guess-default' and (Emacs 23+ only) mailcap entries, in that
668 order."
669   :type '(alist :key-type
670           regexp :value-type
671           (sexp :tag "Shell command (string) or Emacs function (symbol or lambda form)"))
672   :group 'bookmark-plus)
673
674 ;;;###autoload
675 (defcustom bmkp-desktop-no-save-vars '(search-ring regexp-search-ring kill-ring)
676   "*List of variables not to save when creating a desktop bookmark.
677 They are removed from `desktop-globals-to-save' for the duration of
678 the save (only)."
679   :type '(repeat (variable :tag "Variable")) :group 'bookmark-plus)
680
681 ;;;###autoload
682 (defcustom bmkp-handle-region-function 'bmkp-handle-region-default
683   "*Function to handle a bookmarked region."
684   :type 'function :group 'bookmark-plus)
685
686 ;;;###autoload
687 (defcustom bmkp-incremental-filter-delay 0.6
688   "*Seconds to wait before updating display when filtering bookmarks."
689   :type 'number :group 'bookmark-plus)
690
691 ;;;###autoload
692 (defcustom bmkp-menu-popup-max-length 20
693   "*Max number of bookmarks for `bookmark-completing-read' to use a menu.
694 When choosing a bookmark from a list of bookmarks using
695 `bookmark-completing-read', this controls whether to use a menu or
696 minibuffer input with completion.
697 If t, then always use a menu.
698 If nil, then never use a menu.
699 If an integer, then use a menu only if there are fewer bookmark
700  choices than the value."
701   :type '(choice
702           (integer :tag "Use a menu if there are fewer bookmark choices than this" 20)
703           (const   :tag "Always use a menu to choose a bookmark" t)
704           (const   :tag "Never use a menu to choose a bookmark" nil))
705   :group 'bookmark-plus)
706
707 ;;;###autoload
708 (defcustom bmkp-other-window-pop-to-flag t
709   "*Non-nil means other-window bookmark jumping uses `pop-to-buffer'.
710 Use nil if you want the vanilla Emacs behavior, which uses
711 `switch-to-buffer-other-window'.  That creates a new window even if
712 there is already another window showing the buffer."
713   :type 'boolean :group 'bookmark-plus)
714
715 ;;;###autoload
716 (defcustom bmkp-prompt-for-tags-flag nil
717   "*Non-nil means `bookmark-set' prompts for tags (when called interactively)."
718   :type 'boolean :group 'bookmark-plus)
719
720 ;;;###autoload
721 (defcustom bmkp-region-search-size 40
722   "*Same as `bookmark-search-size', but specialized for bookmark regions."
723   :type 'integer :group 'bookmark-plus)
724
725 ;;;###autoload
726 (defcustom bmkp-save-new-location-flag t
727   "*Non-nil means save automatically relocated bookmarks.
728 If nil, then the new bookmark location is visited, but it is not saved
729 as part of the bookmark definition."
730   :type 'boolean :group 'bookmark-plus)
731
732 ;;;###autoload
733 (defcustom bmkp-sequence-jump-display-function 'pop-to-buffer
734   "*Function used to display the bookmarks in a bookmark sequence."
735   :type 'function :group 'bookmark-plus)
736
737 ;;;###autoload
738 (defcustom bmkp-show-end-of-region t
739   "*Show end of region with `exchange-point-and-mark' when activating a region.
740 If nil show only beginning of region."
741   :type 'boolean :group 'bookmark-plus)
742
743 ;;;###autoload
744 (defcustom bmkp-sort-comparer '((bmkp-info-cp bmkp-gnus-cp bmkp-url-cp bmkp-local-file-type-cp)
745                                 bmkp-alpha-p) ; This corresponds to `s k'.
746   ;; $$$$$$ An alternative default value: `bmkp-alpha-p', which corresponds to `s n'.
747   "*Predicate or predicates for sorting (comparing) bookmarks.
748 This defines the default sort for bookmarks in the bookmark list.
749
750 Various sorting commands, such as \\<bookmark-bmenu-mode-map>\
751 `\\[bmkp-bmenu-sort-by-bookmark-visit-frequency]', change the value of this
752 option dynamically (but they do not save the changed value).
753
754 The value must be one of the following:
755
756 * nil, meaning do not sort
757
758 * a predicate that takes two bookmarks as args
759
760 * a list of the form ((PRED...) FINAL-PRED), where each PRED and
761   FINAL-PRED are predicates that take two bookmarks as args
762
763 If the value is a list of predicates, then each PRED is tried in turn
764 until one returns a non-nil value.  In that case, the result is the
765 car of that value.  If no non-nil value is returned by any PRED, then
766 FINAL-PRED is used and its value is the result.
767
768 Each PRED should return `(t)' for true, `(nil)' for false, or nil for
769 undecided.  A nil value means that the next PRED decides (or
770 FINAL-PRED, if there is no next PRED).
771
772 Thus, a PRED is a special kind of predicate that indicates either a
773 boolean value (as a singleton list) or \"I cannot decide - let the
774 next guy else decide\".  (Essentially, each PRED is a hook function
775 that is run using `run-hook-with-args-until-success'.)
776
777 Examples:
778
779  nil           - No sorting.
780
781  string-lessp  - Single predicate that returns nil or non-nil.
782
783  ((p1 p2))     - Two predicates `p1' and `p2', which each return
784                  (t) for true, (nil) for false, or nil for undecided.
785
786  ((p1 p2) string-lessp)
787                - Same as previous, except if both `p1' and `p2' return
788                  nil, then the return value of `string-lessp' is used.
789
790 Note that these two values are generally equivalent, in terms of their
791 effect (*):
792
793  ((p1 p2))
794  ((p1) p2-plain) where p2-plain is (bmkp-make-plain-predicate p2)
795
796 Likewise, these three values generally act equivalently (*):
797
798  ((p1))
799  (() p1-plain)
800  p1-plain        where p1-plain is (bmkp-make-plain-predicate p1)
801
802 The PRED form lets you easily combine predicates: use `p1' unless it
803 cannot decide, in which case try `p2', and so on.  The value ((p2 p1))
804 tries the predicates in the opposite order: first `p2', then `p1' if
805 `p2' returns nil.
806
807 Using a single predicate or FINAL-PRED makes it easy to reuse an
808 existing predicate that returns nil or non-nil.
809
810 You can also convert a PRED-type predicate (which returns (t), (nil),
811 or nil) into an ordinary predicate, by using function
812 `bmkp-make-plain-predicate'.  That lets you reuse elsewhere, as
813 ordinary predicates, any PRED-type predicates you define.
814
815 For example, this defines a plain predicate to compare by URL:
816  (defalias 'bmkp-url-p (bmkp-make-plain-predicate 'bmkp-url-cp))
817
818 Note: As a convention, predefined Bookmark+ PRED-type predicate names
819 have the suffix `-cp' (for \"component predicate\") instead of `-p'.
820
821 --
822 * If you use `\\[bmkp-reverse-multi-sort-order]', then there is a difference in \
823 behavior between
824
825    (a) using a plain predicate as FINAL-PRED and
826    (b) using the analogous PRED-type predicate (and no FINAL-PRED).
827
828   In the latter case, `\\[bmkp-reverse-multi-sort-order]' affects when the predicate \
829 is tried and
830   its return value.  See `bmkp-reverse-multi-sort-order'."
831   :type '(choice
832           (const    :tag "None (do not sort)" nil)
833           (function :tag "Sorting Predicate")
834           (list     :tag "Sorting Multi-Predicate"
835            (repeat (function :tag "Component Predicate"))
836            (choice
837             (const    :tag "None" nil)
838             (function :tag "Final Predicate"))))
839   :group 'bookmark-plus)
840
841 ;;;###autoload
842 (defcustom bmkp-su-or-sudo-regexp "\\(/su:\\|/sudo:\\)"
843   "*Regexp to recognize `su' or `sudo' Tramp bookmarks."
844   :type 'regexp :group 'bookmark-plus)
845
846 ;;;###autoload
847 (defcustom bmkp-this-buffer-cycle-sort-comparer '((bmkp-position-cp))
848   "*`bmkp-sort-comparer' value for cycling this-buffer bookmarks.
849 Some values you might want to use: ((bmkp-position-cp)),
850  ((bmkp-bookmark-creation-cp)), ((bmkp-visited-more-cp)).
851 See `bmkp-sort-comparer'."
852   :type '(choice
853           (const    :tag "None (do not sort)" nil)
854           (function :tag "Sorting Predicate")
855           (list     :tag "Sorting Multi-Predicate"
856            (repeat (function :tag "Component Predicate"))
857            (choice
858             (const    :tag "None" nil)
859             (function :tag "Final Predicate"))))
860   :group 'bookmark-plus)
861
862 ;;;###autoload
863 (defcustom bmkp-guess-default-handler-for-file-flag nil
864   "*Non-nil means guess the default handler when creating a file bookmark.
865 This is ignored if a handler can be found using option
866 `bmkp-default-handler-associations'.  Otherwise, this is used by
867 function `bmkp-default-handler-for-file' to determine the default
868 handler for a given file."
869   :type 'boolean :group 'bookmark-plus)
870
871 ;;;###autoload
872 (defcustom bmkp-use-region t
873   "*Non-nil means visiting a bookmark activates its recorded region."
874   :type '(choice
875           (const :tag "Activate bookmark region (except during cycling)"  t)
876           (const :tag "Do not activate bookmark region"                   nil)
877           (const :tag "Activate bookmark region even during cycling"      cycling-too))
878   :group 'bookmark-plus)
879
880 ;;;###autoload
881 (defcustom bmkp-w3m-allow-multi-tabs t
882   "*Non-nil means jump to W3M bookmarks in a new session."
883   :type 'boolean :group 'bookmark-plus)
884  
885 ;;(@* "Internal Variables")
886 ;;; Internal Variables -----------------------------------------------
887
888 (defconst bmkp-non-file-filename "   - no file -"
889   "Name to use for `filename' entry, for non-file bookmarks.")
890
891 (defconst bmkp-types-alist '(("bookmark-file"    . bmkp-bookmark-file-history)
892                              ("bookmark-list"    . bmkp-bookmark-list-history)
893                              ("desktop"          . bmkp-desktop-history)
894                              ("dired"            . bmkp-dired-history)
895                              ("dired-this-dir"   . bmkp-dired-history)
896                              ("file"             . bmkp-file-history)
897                              ("file-this-dir"    . bmkp-file-history)
898                              ("gnus"             . bmkp-gnus-history)
899                              ("info"             . bmkp-info-history)
900                              ("local-file"       . bmkp-local-file-history)
901                              ("man"              . bmkp-man-history)
902                              ("non-file"         . bmkp-non-file-history)
903                              ("region"           . bmkp-region-history)
904                              ("remote-file"      . bmkp-remote-file-history)
905                              ("specific-buffers" . bmkp-specific-buffers-history)
906                              ("specific-files"   . bmkp-specific-files-history)
907                              ("url"              . bmkp-url-history)
908                              ("variable-list"    . bmkp-variable-list-history))
909   "Alist of bookmark types used by `bmkp-jump-to-type'.
910 Keys are bookmark type names.  Values are corresponding history variables.")
911
912 (defvar bmkp-bookmark-file-history ()    "History for bookmark-file bookmarks.")
913 (defvar bmkp-bookmark-list-history ()    "History for bookmark-list bookmarks.")
914 (defvar bmkp-desktop-history ()          "History for desktop bookmarks.")
915 (defvar bmkp-dired-history ()            "History for Dired bookmarks.")
916 (defvar bmkp-file-history ()             "History for file bookmarks.")
917 (defvar bmkp-gnus-history ()             "History for Gnus bookmarks.")
918 (defvar bmkp-info-history ()             "History for Info bookmarks.")
919 (defvar bmkp-last-bmenu-state-file nil   "Last value of option `bmkp-bmenu-state-file'.")
920 (defvar bmkp-local-file-history ()       "History for local-file bookmarks.")
921 (defvar bmkp-man-history ()              "History for `man'-page bookmarks.")
922 (defvar bmkp-non-file-history ()         "History for buffer (non-file) bookmarks.")
923 (defvar bmkp-region-history ()           "History for bookmarks that activate the region.")
924 (defvar bmkp-remote-file-history ()      "History for remote-file bookmarks.")
925 (defvar bmkp-specific-buffers-history () "History for specific-buffers bookmarks.")
926 (defvar bmkp-specific-files-history ()   "History for specific-files bookmarks.")
927 (defvar bmkp-url-history ()              "History for URL bookmarks.")
928 (defvar bmkp-variable-list-history ()    "History for variable-list bookmarks.")
929 (defvar bmkp-w3m-history ()              "History for W3M bookmarks.")
930
931 (defvar bmkp-after-set-hook nil "Hook run after `bookmark-set' sets a bookmark.")
932
933 (defvar bmkp-copied-tags ()
934   "List of tags copied from a bookmark, for pasting to other bookmarks.")
935
936 (defvar bmkp-current-bookmark-file bookmark-default-file
937   "Current bookmark file.
938 When you start Emacs, this is initialized to `bookmark-default-file'.
939 When you load bookmarks using `bmkp-switch-bookmark-file', this is set
940 to the file you load.  When you save bookmarks using `bookmark-save'
941 with no prefix arg, they are saved to this file.
942
943 However, this does not change the value of `bookmark-default-file'.
944 The value of `bookmark-default-file' is never changed, except by your
945 customizations.  Each Emacs session uses `bookmark-default-file' for
946 the initial set of bookmarks.")
947
948 (defvar bmkp-file-bookmark-handlers '(bmkp-jump-dired image-bookmark-jump)
949   "List of functions that handle file or directory bookmarks.
950 This is used to determine `bmkp-file-bookmark-p'.")
951
952 (defvar bmkp-last-bookmark-file bookmark-default-file
953   "Last bookmark file used in this session (or default bookmark file).
954 This is a backup for `bmkp-current-bookmark-file'.")
955
956 (defvar bmkp-current-nav-bookmark nil "Current bookmark for navigation.")
957
958 (defvar bmkp-jump-display-function nil "Function used currently to display a bookmark.")
959
960 (defvar bmkp-last-specific-buffer ""
961   "Name of buffer used by `bmkp-last-specific-buffer-p'.")
962
963 (defvar bmkp-last-specific-file ""
964   "(Absolute) file name used by `bmkp-last-specific-file-p'.")
965
966 (defvar bmkp-nav-alist () "Current bookmark alist used for navigation.")
967
968 (defvar bmkp-return-buffer nil "Name of buffer to return to.")
969
970 (defvar bmkp-reverse-sort-p nil "Non-nil means the sort direction is reversed.")
971
972 (defvar bmkp-reverse-multi-sort-p nil
973   "Non-nil means the truth values returned by predicates are complemented.
974 This changes the order of the sorting groups, but it does not in
975 general reverse that order.  The order within each group is unchanged
976 \(not reversed).")
977
978 (defvar bmkp-use-w32-browser-p nil
979   "Non-nil means use `w32-browser' in the default bookmark handler.
980 That is, use the default Windows application for the bookmarked file.
981 This has no effect if `w32-browser' is not defined.")
982
983 (defvar bmkp-latest-bookmark-alist () "Copy of `bookmark-alist' as last filtered.")
984
985 (defvar bmkp-last-save-flag-value nil "Last value of option `bookmark-save-flag'.")
986
987 (defvar bmkp-sorted-alist ()
988   "Copy of current bookmark alist, as sorted for buffer `*Bookmark List*'.
989 Has the same structure as `bookmark-alist'.")
990
991 (defvar bmkp-tag-history () "History of tags read from the user.")
992
993 (defvar bmkp-tags-alist ()         
994   "Alist of all tags, from all bookmarks.
995 Each entry is a cons whose car is a tag name, a string.
996 This is set by function `bmkp-tags-list'.
997 Use that function to update the value.")
998
999
1000 ;; REPLACES ORIGINAL DOC STRING in `bookmark.el'.
1001 ;;
1002 ;; Doc string reflects Bookmark+ enhancements.
1003 ;;
1004 (put 'bookmark-alist 'variable-documentation
1005      "Association list of bookmarks and their records.
1006 Bookmark functions update the value automatically.
1007 You probably do not want to change the value yourself.
1008
1009 The value is an alist with entries of the form
1010  (BOOKMARK-NAME . PARAM-ALIST)
1011 or the deprecated form (BOOKMARK-NAME PARAM-ALIST).
1012
1013  BOOKMARK-NAME is the name you gave to the bookmark when creating it.
1014  PARAM-ALIST is an alist of bookmark information.  The order of the
1015   entries in PARAM-ALIST is not important.  The possible entries are
1016   described below.  An entry with a key but null value means the entry
1017   is not used.
1018
1019 Bookmarks created using vanilla Emacs (`bookmark.el'):
1020
1021  (filename . FILENAME)
1022  (location . LOCATION)
1023  (position . POS)
1024  (front-context-string . STR-AFTER-POS)
1025  (rear-context-string  . STR-BEFORE-POS)
1026  (handler . HANDLER)
1027  (annotation . ANNOTATION)
1028
1029  FILENAME names the bookmarked file.
1030  LOCATION names the bookmarked file, URL, or other place (Emacs 23+).
1031   FILENAME or LOCATION is what is shown in the bookmark list
1032   (`C-x r l') when you use `M-t'.
1033  POS is the bookmarked buffer position (position in the file).
1034  STR-AFTER-POS is buffer text that immediately follows POS.
1035  STR-BEFORE-POS is buffer text that immediately precedes POS.
1036  ANNOTATION is a string that you can provide to identify the bookmark.
1037   See options `bookmark-use-annotations' and
1038   `bookmark-automatically-show-annotations'.
1039  HANDLER is a function that provides the bookmark-jump behavior
1040   for a specific kind of bookmark.  This is the case for Info
1041   bookmarks, for instance (starting with Emacs 23).
1042
1043 Bookmarks created using Bookmark+ are the same as for vanilla Emacs,
1044 except for the following differences.
1045
1046 1. Visit information is recorded, using entries `visits' and `time':
1047
1048  (visits . NUMBER-OF-VISITS)
1049  (time . TIME-LAST-VISITED)
1050
1051  NUMBER-OF-VISITS is a whole-number counter.
1052
1053  TIME-LAST-VISITED is an Emacs time representation, such as is
1054  returned by function `current-time'.
1055
1056 2. The buffer name is recorded, using entry `buffer-name'.  It need
1057 not be associated with a file.
1058
1059 3. If no file is associated with the bookmark, then FILENAME is
1060    `   - no file -'.
1061
1062 4. Bookmarks can be tagged by users.  The tag information is recorded
1063 using entry `tags':
1064
1065  (tags . TAGS-ALIST)
1066
1067  TAGS-ALIST is an alist with string keys.
1068
1069 5. Bookmarks can have individual highlighting, provided by users.
1070 This overrides any default highlighting.
1071
1072  (lighting . HIGHLIGHTING)
1073
1074  HIGHLIGHTING is a property list that contain any of these keyword
1075  pairs:
1076
1077    `:style' - Highlighting style.  Cdrs of `bmkp-light-styles-alist'
1078               entries are the possible values.
1079    `:face'  - Highlighting face, a symbol.
1080    `:when'  - A sexp to be evaluated.  Return value of `:no-light'
1081               means do not highlight.
1082
1083 6. The following additional entries are used to record region
1084 information.  When a region is bookmarked, POS represents the region
1085 start position.
1086
1087  (end-position . END-POS)
1088  (front-context-region-string . STR-BEFORE-END-POS)
1089  (rear-context-region-string . STR-AFTER-END-POS))
1090
1091  END-POS is the region end position.
1092  STR-BEFORE-END-POS is buffer text that precedes END-POS.
1093  STR-AFTER-END-POS is buffer text that follows END-POS.
1094
1095 The two context region strings are non-nil only when a region is
1096 bookmarked.
1097
1098  NOTE: The relative locations of `front-context-region-string' and
1099  `rear-context-region-string' are reversed from those of
1100  `front-context-string' and `rear-context-string'.  For example,
1101  `front-context-string' is the text that *follows* `position', but
1102  `front-context-region-string' *precedes* `end-position'.
1103
1104 7. The following additional entries are used for a Dired bookmark.
1105
1106  (dired-marked . MARKED-FILES)
1107  (dired-switches . SWITCHES)
1108
1109  MARKED-FILES is the list of files that were marked.
1110  SWITCHES is the string of `dired-listing-switches'.
1111
1112 8. The following additional entries are used for a Gnus bookmark.
1113
1114  (group . GNUS-GROUP-NAME)
1115  (article . GNUS-ARTICLE-NUMBER)
1116  (message-id . GNUS-MESSAGE-ID)
1117
1118  GNUS-GROUP-NAME is the name of a Gnus group.
1119  GNUS-ARTICLE-NUMBER is the number of a Gnus article.
1120  GNUS-MESSAGE-ID is the identifier of a Gnus message.
1121
1122 9. For a URL bookmark, FILENAME or LOCATION is a URL.
1123
1124 10. A sequence bookmark has this additional entry:
1125
1126  (sequence . COMPONENT-BOOKMARKS)
1127
1128  COMPONENT-BOOKMARKS is the list of component bookmark names.
1129
1130 11. A function bookmark has this additional entry, which records the
1131 FUNCTION:
1132
1133  (function . FUNCTION)
1134
1135 12. A bookmark-list bookmark has this additional entry, which records
1136 the state of buffer `*Bookmark List*' at the time it is created:
1137
1138  (bookmark-list . STATE)
1139
1140  STATE records the sort order, filter function, omit list, and title.")
1141  
1142 ;;(@* "Compatibility Code for Older Emacs Versions")
1143 ;;; Compatibility Code for Older Emacs Versions ----------------------
1144
1145 (when (< emacs-major-version 23)
1146
1147   ;; These definitions are for Emacs versions prior to Emacs 23.
1148   ;; They are the same as the vanilla Emacs 23+ definitions, except as noted.
1149   ;; They let older versions of Emacs handle bookmarks created with Emacs 23.
1150
1151   (defun bookmark-get-bookmark-record (bookmark)
1152     "Return the guts of the entry for BOOKMARK in `bookmark-alist'.
1153 That is, all information but the name.
1154 BOOKMARK is a bookmark name or a bookmark record."
1155     (let ((alist  (cdr (bookmark-get-bookmark bookmark))))
1156       ;; A bookmark record is either (NAME ALIST) or (NAME . ALIST).
1157       (if (and (null (cdr alist)) (consp (caar alist)))
1158           (car alist)
1159         alist)))
1160
1161   (defun Info-bookmark-make-record ()
1162     "Create an Info bookmark record."
1163     `(,Info-current-node
1164       ,@(bookmark-make-record-default 'no-file)
1165       (filename . ,Info-current-file)
1166       (info-node . ,Info-current-node)
1167       (handler . Info-bookmark-jump)))
1168
1169   ;; Requires `info.el' explicitly (not autoloaded for `Info-find-node'.
1170   (defun Info-bookmark-jump (bookmark)
1171     "Jump to Info bookmark BOOKMARK.
1172 BOOKMARK is a bookmark name or a bookmark record."
1173     (require 'info)
1174     ;; Implements the `handler' for the record type returned by `Info-bookmark-make-record'.
1175     (let* ((file       (bookmark-prop-get bookmark 'filename))
1176            (info-node  (bookmark-prop-get bookmark 'info-node))
1177            (buf        (save-window-excursion ; VANILLA EMACS FIXME: doesn't work with frames!
1178                          (Info-find-node file info-node) (current-buffer))))
1179       ;; Use `bookmark-default-handler' to move to appropriate location within Info node.
1180       (bookmark-default-handler
1181        `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
1182
1183   (add-hook 'Info-mode-hook (lambda () (set (make-local-variable 'bookmark-make-record-function)
1184                                             'Info-bookmark-make-record)))
1185
1186   (defvar bookmark-make-record-function 'bookmark-make-record-default
1187     "Function called with no arguments, to create a bookmark record.
1188 It should return the new record, which should be a cons cell of the
1189 form (NAME . ALIST) or just ALIST, where ALIST is as described in
1190 `bookmark-alist'.  If it cannot construct the record, then it should
1191 raise an error.
1192
1193 NAME is a string that names the new bookmark.  NAME can be nil, in
1194 which case a default name is used.
1195
1196 ALIST can contain an entry (handler . FUNCTION) which sets the handler
1197 to FUNCTION, which is then used instead of `bookmark-default-handler'.
1198 FUNCTION must accept the same arguments as `bookmark-default-handler'.
1199
1200 You can set this variable buffer-locally to enable bookmarking of
1201 locations that should be treated specially, such as Info nodes, news
1202 posts, images, pdf documents, etc.")
1203
1204   (defun bookmark-make-record ()
1205     "Return a new bookmark record (NAME . ALIST) for the current location."
1206     (let ((record  (funcall bookmark-make-record-function)))
1207       ;; Set up default name.
1208       (if (stringp (car record))
1209           record                        ; The function already provided a default name.
1210         (when (car record) (push nil record))
1211         (setcar record  (or bookmark-current-bookmark (bookmark-buffer-name)))
1212         record)))
1213
1214   (defun bookmark-prop-get (bookmark prop)
1215     "Return property PROP of BOOKMARK, or nil if no such property.
1216 BOOKMARK is a bookmark name or a bookmark record."
1217     (cdr (assq prop (bookmark-get-bookmark-record bookmark))))
1218
1219   (defun bookmark-get-handler (bookmark)
1220     "Return the `handler' entry for BOOKMARK.
1221 BOOKMARK is a bookmark name or a bookmark record."
1222     (bookmark-prop-get bookmark 'handler))
1223
1224   (defun bookmark-jump-noselect (bookmark)
1225     "Return the location recorded for BOOKMARK.
1226 BOOKMARK is a bookmark name or a bookmark record.
1227 The return value has the form (BUFFER . POINT), where BUFFER is a
1228 buffer and POINT is the location within BUFFER."
1229     (save-excursion (bookmark-handle-bookmark bookmark) (cons (current-buffer) (point)))))
1230
1231 (when (< emacs-major-version 22)
1232
1233   ;; These definitions are for Emacs versions prior to Emacs 22.
1234   ;; They are the same as the vanilla Emacs 22+ definitions, except as noted.
1235
1236   ;; Emacs 22+ just uses `bookmark-jump-other-window' for the menu also.
1237   (defun bmkp-menu-jump-other-window (event)
1238     "Jump to BOOKMARK (a point in some file) in another window.
1239 See `bookmark-jump-other-window'."
1240     (interactive "e")
1241     (bookmark-popup-menu-and-apply-function 'bookmark-jump-other-window
1242                                             "Jump to Bookmark (Other Window)" event))
1243
1244   (defun bookmark-maybe-message (fmt &rest args)
1245     "Apply `message' to FMT and ARGS, but only if the display is fast enough."
1246     (when (>= baud-rate 9600) (apply 'message fmt args))))
1247  
1248 ;;(@* "Core Replacements (`bookmark-*' except `bookmark-bmenu-*')")
1249 ;;; Core Replacements (`bookmark-*' except `bookmark-bmenu-*') -------
1250
1251
1252 ;; REPLACES ORIGINAL in `bookmark.el'.
1253 ;;
1254 ;; 1. If BOOKMARK is a bookmark-name string that has non-nil property `bmkp-full-record'
1255 ;;    then return the value of that property.
1256 ;; 2. Handle the should-not-happen case of non-string, non-cons.
1257 ;; 3. Document NOERROR in doc string.
1258 (defun bookmark-get-bookmark (bookmark &optional noerror)
1259   "Return the bookmark record corresponding to BOOKMARK.
1260 BOOKMARK is a bookmark name or a bookmark record.
1261 If BOOKMARK is a bookmark record, just return it.
1262 Otherwise look for the corresponding bookmark in `bookmark-alist'.
1263 Return the bookmark found or raise an error if none found.
1264
1265 If BOOKMARK is a bookmark-name string that has non-nil property
1266 `bmkp-full-record' then return the value of that property.
1267
1268 Non-nil optional arg NOERROR means return nil if BOOKMARK
1269 is not a valid bookmark - do not raise an error."
1270   (cond ((consp bookmark) bookmark)
1271         ((stringp bookmark)
1272          (let ((full  (get-text-property 0 'bmkp-full-record bookmark)))
1273            (or full                     ; If unpropertized string then punt.
1274                (if (fboundp 'assoc-string) ; Emacs 22+.
1275                    (assoc-string bookmark bookmark-alist bookmark-completion-ignore-case)
1276                  (assoc bookmark bookmark-alist))
1277                (unless noerror (error "Invalid bookmark: `%s'" bookmark)))))
1278         (t (unless noerror (error "Invalid bookmark: `%s'" bookmark)))))
1279
1280
1281 ;; REPLACES ORIGINAL in `bookmark.el'.
1282 ;;
1283 ;; 1. Put the full bookmark on the bookmark name as property `bmkp-full-record'.
1284 ;; 2. Use `bmkp-maybe-save-bookmarks'.
1285 ;; 3. Return the bookmark.
1286 ;;
1287 (defun bookmark-store (bookmark-name data no-overwrite)
1288   "Store the bookmark named BOOKMARK-NAME, giving it DATA.
1289 If NO-OVERWRITE is non-nil and bookmark BOOKMARK-NAME already exists,
1290 then record the new bookmark but do not discard the old one.
1291
1292 The check for existence uses `bookmark-get-bookmark', so if string
1293 BOOKMARK-NAME has property `bmkp-full-record' then the check is for a
1294 particular bookmark with the given name.  If BOOKMARK-NAME does not
1295 have that property then the check is for any bookmark with that name.
1296
1297 Return the new bookmark."
1298   (bookmark-maybe-load-default-file)
1299   (let ((bname  (copy-sequence bookmark-name))
1300         bmk)
1301     (unless (featurep 'xemacs)
1302       ;; XEmacs's `set-text-properties' doesn't work on free-standing strings, apparently.
1303       (set-text-properties 0 (length bname) () bname))
1304     (if (and (not no-overwrite) (bookmark-get-bookmark bname 'noerror))
1305         ;; Existing bookmark under that name and no prefix arg means just overwrite existing.
1306         ;; Use the new (NAME . ALIST) format.
1307         (setcdr (setq bmk  (bookmark-get-bookmark bname)) data)
1308       (push (setq bmk  (cons bname data)) bookmark-alist)
1309       ;; Put the bookmark on the name as property `bmkp-full-record'.
1310       (when (and (> emacs-major-version 20) ; Emacs 21+.  Cannot just use (boundp 'print-circle).
1311                  bmkp-propertize-bookmark-names-flag)
1312         (put-text-property 0 (length bname) 'bmkp-full-record bmk bname)))
1313     (bmkp-maybe-save-bookmarks)
1314     (setq bookmark-current-bookmark  bname)
1315     (bookmark-bmenu-surreptitiously-rebuild-list)
1316     bmk))                               ; Return the bookmark.
1317
1318
1319 ;; REPLACES ORIGINAL in `bookmark.el'.
1320 ;;
1321 ;; BUG fix: Need bookmark arg in `interactive' spec.
1322 ;;
1323 ;;;###autoload
1324 (defun bookmark-edit-annotation-mode (bookmark)
1325   "Mode for editing the annotation of bookmark BOOKMARK.
1326 When you have finished composing, type \\[bookmark-send-annotation].
1327 BOOKMARK is a bookmark name or a bookmark record.
1328
1329 \\{bookmark-edit-annotation-mode-map}"
1330   (interactive (list (bookmark-completing-read "Edit annotation of bookmark"
1331                                                (bmkp-default-bookmark-name)
1332                                                (bmkp-annotated-alist-only))))
1333   (kill-all-local-variables)
1334   (make-local-variable 'bookmark-annotation-name)
1335   (setq bookmark-annotation-name  bookmark)
1336   (use-local-map bookmark-edit-annotation-mode-map)
1337   (setq major-mode  'bookmark-edit-annotation-mode
1338         mode-name  "Edit Bookmark Annotation")
1339   (insert (funcall (if (boundp 'bookmark-edit-annotation-text-func)
1340                        bookmark-edit-annotation-text-func
1341                      bookmark-read-annotation-text-func)
1342                    bookmark))
1343   (let ((annotation  (bookmark-get-annotation bookmark)))
1344     (if (and annotation (not (string-equal annotation "")))
1345         (insert annotation)))
1346   (if (fboundp 'run-mode-hooks)
1347       (run-mode-hooks 'text-mode-hook)
1348     (run-hooks 'text-mode-hook)))
1349
1350
1351 ;; REPLACES ORIGINAL in `bookmark.el'.
1352 ;;
1353 ;; 1. BUG fix: Put point back where it was (on the bookmark just annotated).
1354 ;; 2. Refresh menu list, to pick up the `a' marker.
1355 ;; 3. Make sure it's the annotation buffer that gets killed.
1356 ;; 4. Delete window also, if `misc-cmds.el' loaded.
1357 ;;
1358 ;;;###autoload
1359 (defun bookmark-send-edited-annotation ()
1360   "Use buffer contents as annotation for a bookmark.
1361 Lines beginning with `#' are ignored."
1362   (interactive)
1363   (unless (eq major-mode 'bookmark-edit-annotation-mode)
1364     (error "Not in bookmark-edit-annotation-mode"))
1365   (goto-char (point-min))
1366   (while (< (point) (point-max))
1367     (if (looking-at "^#")
1368         (bookmark-kill-line t)
1369       (forward-line 1)))
1370   (let ((annotation      (buffer-substring-no-properties (point-min) (point-max)))
1371         (bookmark        bookmark-annotation-name)
1372         (annotation-buf  (current-buffer)))
1373     (bookmark-set-annotation bookmark annotation)
1374     (setq bookmark-alist-modification-count  (1+ bookmark-alist-modification-count))
1375     (bookmark-bmenu-surreptitiously-rebuild-list)
1376     (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
1377       (with-current-buffer (get-buffer "*Bookmark List*")
1378         (bmkp-refresh-menu-list bookmark))) ; So the `a' marker is displayed (updated).
1379     (if (fboundp 'kill-buffer-and-its-windows)
1380         (kill-buffer-and-its-windows annotation-buf) ; Defined in `misc-cmds.el'.
1381       (kill-buffer annotation-buf))))
1382
1383
1384 ;; REPLACES ORIGINAL in `bookmark.el'.
1385 ;;
1386 ;; Added `interactive' spec.
1387 ;;
1388 ;;;###autoload
1389 (defun bookmark-edit-annotation (bookmark)
1390   "Pop up a buffer for editing bookmark BOOKMARK's annotation.
1391 BOOKMARK is a bookmark name or a bookmark record."
1392   (interactive (list (bookmark-completing-read "Edit annotation of bookmark"
1393                                                (bmkp-default-bookmark-name)
1394                                                (bmkp-annotated-alist-only))))
1395   (pop-to-buffer (generate-new-buffer-name "*Bookmark Annotation Compose*"))
1396   (bookmark-edit-annotation-mode bookmark))
1397
1398
1399 ;; REPLACES ORIGINAL in `bookmark.el'.
1400 ;;
1401 ;; Added optional arg ALIST.
1402 ;;
1403 (defun bookmark-all-names (&optional alist)
1404   "Return a list of all bookmark names.
1405 The names are those of the bookmarks in ALIST or, if nil,
1406 `bookmark-alist'."
1407   (bookmark-maybe-load-default-file)
1408   (mapcar (lambda (bmk) (bookmark-name-from-full-record bmk)) (or alist bookmark-alist)))
1409
1410
1411 ;; REPLACES ORIGINAL in `bookmark.el'.
1412 ;;
1413 ;; 1. Added optional args ALIST, PRED, and HIST.
1414 ;; 2. Define using helper function `bmkp-completing-read-1',
1415 ;;    which binds `icicle-delete-candidate-object' to (essentially) `bookmark-delete'.
1416 ;;
1417 (defun bookmark-completing-read (prompt &optional default alist pred hist)
1418   "Read a bookmark name, prompting with PROMPT.
1419 PROMPT is automatically suffixed with \": \", so do not include that.
1420
1421 Optional arg DEFAULT is a string to return if the user enters the
1422  empty string.
1423 The alist argument used for completion is ALIST or, if nil,
1424  `bookmark-alist'.
1425 Optional arg PRED is a predicate used for completion.
1426 Optional arg HIST is a history variable for completion.  Default is
1427  `bookmark-history'.
1428
1429 If you access this function from a menu, then, depending on the value
1430 of option `bmkp-menu-popup-max-length' and the number of
1431 bookmarks in ALIST, you choose the bookmark using a menu or using
1432 completion.
1433
1434 If you use Icicles, then you can use `S-delete' during completion of a
1435 bookmark name to delete the bookmark named by the current completion
1436 candidate."
1437   (bmkp-completing-read-1 prompt default alist pred hist nil))
1438
1439
1440 ;; REPLACES ORIGINAL in `bookmark.el'.
1441 ;;
1442 ;; 1. Handles also regions and non-file buffers.
1443 ;; 2. Do not use NO-CONTEXT or POSN if < Emacs 24.
1444 ;;
1445 (defun bookmark-make-record-default (&optional no-file no-context position visits)
1446   "Return the record describing the location of a new bookmark.
1447 Point must be where the bookmark is to be set.
1448
1449 Non-nil NO-FILE means return only the subset of the record that
1450  pertains to the location within the buffer (not also the file name).
1451
1452 Non-nil NO-CONTEXT means do not include the front and rear context
1453 strings in the record enough.
1454
1455 Non-nil POSITION means record it, not point, as the `position' entry.
1456
1457 Non-nil VISITS means record it as the `visits' entry."
1458   (let* ((dired-p  (and (boundp 'dired-buffers) (car (rassq (current-buffer) dired-buffers))))
1459          (buf      (buffer-name))
1460          (ctime    (current-time))
1461
1462          ;; Begin `let*' dependencies.
1463          (regionp  (and transient-mark-mode mark-active (not (eq (mark) (point)))))
1464          (beg      (if regionp (region-beginning) (or position (point))))
1465          (end      (if regionp (region-end) (point)))
1466          (fcs      (if regionp
1467                        (bmkp-position-post-context-region beg end)
1468                      (bmkp-position-post-context beg)))
1469          (rcs      (if regionp
1470                        (bmkp-position-pre-context-region beg)
1471                      (bmkp-position-pre-context beg)))
1472          (fcrs     (when regionp (bmkp-end-position-pre-context beg end)))
1473          (ecrs     (when regionp (bmkp-end-position-post-context end))))
1474     `(,@(unless no-file
1475                 `((filename . ,(cond ((buffer-file-name)
1476                                       (bookmark-buffer-file-name))
1477                                      (dired-p  nil)
1478                                      (t        bmkp-non-file-filename)))))
1479       (buffer-name . ,buf)
1480       ,@(unless (and no-context (> emacs-major-version 23))
1481                 `((front-context-string . ,fcs)))
1482       ,@(unless (and no-context (> emacs-major-version 23))
1483                 `((rear-context-string . ,rcs)))
1484       ,@(unless (and no-context (> emacs-major-version 23))
1485                 `((front-context-region-string . ,fcrs)))
1486       ,@(unless (and no-context (> emacs-major-version 23))
1487                 `((rear-context-region-string  . ,ecrs)))
1488       (visits       . ,(or visits 0))
1489       (time         . ,ctime)
1490       (created      . ,ctime)
1491       (position     . ,beg)
1492       (end-position . ,end))))
1493
1494
1495 ;; REPLACES ORIGINAL in `bookmark.el'.
1496 ;;
1497 ;; 1. Use `bookmark-make-record'.
1498 ;; 2. Use special default prompts for active region, W3M, and Gnus.
1499 ;; 3. Use `bmkp-completing-read-lax', choosing from current buffer's bookmarks.
1500 ;; 4. Numeric prefix arg (diff from plain): all bookmarks as completion candidates.
1501 ;; 5. Prompt for tags if `bmkp-prompt-for-tags-flag' is non-nil.
1502 ;; 6. Possibly highlight bookmark and other bookmarks in buffer, per `bmkp-auto-light-when-set'.
1503 ;;
1504 ;;;###autoload
1505 (defun bookmark-set (&optional name parg interactivep) ; `C-x r m'
1506   "Set a bookmark named NAME, then run `bmkp-after-set-hook'.
1507 If the region is active (`transient-mark-mode') and nonempty, then
1508 record the region limits in the bookmark.
1509
1510 If NAME is nil, then prompt for the bookmark name.  The default name
1511 for prompting is as follows (in order of priority):
1512
1513  * If the region is active and nonempty, then the buffer name followed
1514    by \": \" and the region prefix (up to
1515    `bmkp-bookmark-name-length-max' chars).
1516
1517  * If in W3M mode, then the current W3M title.
1518
1519  * If in a Gnus mode, then the Gnus summary article header.
1520
1521  * If on a `man' page, then the page name (command and section).
1522
1523  * Otherwise, the current buffer name.
1524
1525 While entering a bookmark name at the prompt:
1526
1527  * You can use (lax) completion against bookmarks in the same buffer.
1528    If there are no bookmarks in the current buffer, then all bookmarks
1529    are completion candidates.  (See also below, about a numeric prefix
1530    argument.)
1531
1532  * You can use `C-w' to yank words from the buffer to the minibuffer.
1533    Repeating `C-w' yanks successive words.
1534
1535  * You can use `C-u' to insert the name of the last bookmark used in
1536    the buffer.  This can be useful as an aid to track your progress
1537    through a large file.  (If no bookmark has yet been used, then
1538    `C-u' inserts the name of the visited file.)
1539
1540 A prefix argument changes the behavior as follows:
1541
1542  * Numeric prefix arg: Use all bookmarks as completion candidates,
1543    instead of just the bookmarks for the current buffer.
1544
1545  * Plain prefix arg (`C-u'): Do not overwrite a bookmark that has the
1546    same name as NAME, if such a bookmark already exists.  Instead,
1547    push the new bookmark onto the bookmark alist.
1548
1549    The most recently set bookmark named NAME is thus the one in effect
1550    at any given time, but any others named NAME are still available,
1551    should you decide to delete the most recent one.
1552
1553 Use `\\[bookmark-delete]' to remove bookmarks (you give it a name, and it removes
1554 only the first instance of a bookmark with that name from the list of
1555 bookmarks)."
1556   (interactive (list nil current-prefix-arg t))
1557   (bookmark-maybe-load-default-file)
1558   (setq bookmark-current-point   (point) ; `bookmark-current-point' is a free var here.
1559         bookmark-current-buffer  (current-buffer))
1560   (save-excursion (skip-chars-forward " ") (setq bookmark-yank-point  (point)))
1561   (let* ((record   (bookmark-make-record))
1562          (regionp  (and transient-mark-mode mark-active (not (eq (mark) (point)))))
1563          (regname  (concat (buffer-name) ": "
1564                            (buffer-substring (if regionp (region-beginning) (point))
1565                                              (if regionp
1566                                                  (region-end)
1567                                                (save-excursion (end-of-line) (point))))))
1568          (defname  (bmkp-replace-regexp-in-string
1569                     "\n" " "
1570                     (cond (regionp (save-excursion (goto-char (region-beginning))
1571                                                    (skip-chars-forward " ")
1572                                                    (setq bookmark-yank-point  (point)))
1573                                    (substring regname 0 (min bmkp-bookmark-name-length-max
1574                                                              (length regname))))
1575                           ((eq major-mode 'w3m-mode) w3m-current-title)
1576                           ((eq major-mode 'gnus-summary-mode) (elt (gnus-summary-article-header) 1))
1577                           ((memq major-mode '(Man-mode woman-mode))
1578                            (buffer-substring (point-min) (save-excursion (goto-char (point-min))
1579                                                                          (skip-syntax-forward "^ ")
1580                                                                          (point))))
1581                           (t (car record)))))
1582          (bname    (or name  (bmkp-completing-read-lax
1583                               "Set bookmark " defname
1584                               (and (or (not parg) (consp parg)) ; No numeric PARG: all bookmarks.
1585                                    (bmkp-specific-buffers-alist-only))
1586                               nil 'bookmark-history))))
1587     (when (string-equal bname "") (setq bname  defname))
1588     (bookmark-store bname (cdr record) (consp parg))
1589     (when (and interactivep bmkp-prompt-for-tags-flag)
1590       (bmkp-add-tags bname (bmkp-read-tags-completing)))
1591     (case (and (boundp 'bmkp-auto-light-when-set) bmkp-auto-light-when-set)
1592       (autonamed-bookmark       (when (bmkp-autonamed-bookmark-p bname)
1593                                   (bmkp-light-bookmark bname)))
1594       (non-autonamed-bookmark   (unless (bmkp-autonamed-bookmark-p bname)
1595                                   (bmkp-light-bookmark bname)))
1596       (any-bookmark             (bmkp-light-bookmark bname))
1597       (autonamed-in-buffer      (bmkp-light-bookmarks (bmkp-remove-if-not
1598                                                            #'bmkp-autonamed-bookmark-p
1599                                                            (bmkp-this-buffer-alist-only)) nil 'MSG))
1600       (non-autonamed-in-buffer  (bmkp-light-bookmarks (bmkp-remove-if
1601                                                            #'bmkp-autonamed-bookmark-p
1602                                                            (bmkp-this-buffer-alist-only)) nil 'MSG))
1603       (all-in-buffer            (bmkp-light-this-buffer nil 'MSG)))
1604     (run-hooks 'bmkp-after-set-hook)
1605     (if bookmark-use-annotations
1606         (bookmark-edit-annotation bname)
1607       (goto-char bookmark-current-point)))) ; `bookmark-current-point' is a free var here.
1608
1609
1610 ;; REPLACES ORIGINAL in `bookmark.el'.
1611 ;;
1612 ;; Prevent adding a newline in a bookmark name when yanking.
1613 ;; 
1614 ;;;###autoload
1615 (defun bookmark-yank-word ()            ; Bound to `C-w' in minibuffer when setting bookmark.
1616   "Yank the word at point in `bookmark-current-buffer'.
1617 Repeat to yank subsequent words from the buffer, appending them.
1618 Newline characters are stripped out."
1619   (interactive)
1620   (let ((string  (with-current-buffer bookmark-current-buffer
1621                    (goto-char bookmark-yank-point)
1622                    (buffer-substring-no-properties (point)
1623                                                    (progn (forward-word 1)
1624                                                           (setq bookmark-yank-point  (point)))))))
1625     (setq string  (bmkp-replace-regexp-in-string "\n" "" string))
1626     (insert string)))
1627
1628
1629 ;; REPLACES ORIGINAL in `bookmark.el'.
1630 ;;
1631 ;; 1. Save DISPLAY-FUNCTION to `bmkp-jump-display-function' before calling
1632 ;;    `bookmark-handle-bookmark'.
1633 ;; 2. Update the name and position of an autonamed bookmark, in case it moved.
1634 ;; 3. Possibly highlight bookmark and other bookmarks in buffer, per `bmkp-auto-light-when-jump'.
1635 ;; 4. Added `catch', so a handler can throw to skip the rest of the processing if it wants.
1636 ;;
1637 (defun bookmark--jump-via (bookmark display-function)
1638   "Display BOOKMARK using DISPLAY-FUNCTION.
1639 Then run `bookmark-after-jump-hook' and show annotations for BOOKMARK.
1640 BOOKMARK is a bookmark name or a bookmark record."
1641   (bmkp-record-visit bookmark 'batch)
1642   (setq bmkp-jump-display-function  display-function)
1643   (catch 'bookmark--jump-via
1644     (bookmark-handle-bookmark bookmark)
1645     (let ((win  (get-buffer-window (current-buffer) 0)))
1646       (when win (set-window-point win (point))))
1647     ;; If this is an autonamed bookmark, update its name and position, in case it moved.
1648     ;; But don't do it if we're using w32, since we might not have moved to the bookmark position.
1649     (when (and (bmkp-autonamed-bookmark-for-buffer-p bookmark (buffer-name))
1650                (not bmkp-use-w32-browser-p))
1651       (setq bookmark  (bmkp-update-autonamed-bookmark bookmark)))
1652     (case (and (boundp 'bmkp-auto-light-when-jump) bmkp-auto-light-when-jump)
1653       (autonamed-bookmark       (when (bmkp-autonamed-bookmark-p bookmark)
1654                                   (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT)))
1655       (non-autonamed-bookmark   (unless (bmkp-autonamed-bookmark-p bookmark)
1656                                   (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT)))
1657       (any-bookmark             (bmkp-light-bookmark bookmark nil nil nil 'USE-POINT))
1658       (autonamed-in-buffer      (bmkp-light-bookmarks (bmkp-remove-if-not
1659                                                        #'bmkp-autonamed-bookmark-p
1660                                                        (bmkp-this-buffer-alist-only)) nil 'MSG))
1661       (non-autonamed-in-buffer  (bmkp-light-bookmarks (bmkp-remove-if
1662                                                        #'bmkp-autonamed-bookmark-p
1663                                                        (bmkp-this-buffer-alist-only)) nil 'MSG))
1664       (all-in-buffer            (bmkp-light-this-buffer nil 'MSG)))
1665     (let ((orig-buff  (current-buffer))) ; Used by `crosshairs-highlight'.
1666       (run-hooks 'bookmark-after-jump-hook))
1667     (let ((jump-fn  (bmkp-get-tag-value bookmark "bmkp-jump")))
1668       (when jump-fn (funcall jump-fn)))
1669     (when bookmark-automatically-show-annotations (bookmark-show-annotation bookmark))))
1670
1671
1672 ;; REPLACES ORIGINAL in `bookmark.el'.
1673 ;;
1674 ;; 1. Add to beginning, not end, of bookmark record.
1675 ;; 2. Do not use `nconc'.
1676 ;; 3. Respect both old and new bookmark formats.
1677 ;;
1678 (defun bookmark-prop-set (bookmark prop val)
1679   "Set the property PROP of BOOKMARK to VAL."
1680   (let* ((bmk   (bookmark-get-bookmark bookmark))
1681          (cell  (assq prop (bookmark-get-bookmark-record bmk))))
1682     (if cell
1683         (setcdr cell val)
1684       (if (consp (car (cadr bmk)))      ; Old format: ("name" ((filename . "f")...))
1685           (setcdr bmk (list (cons (cons prop val) (cadr bmk))))
1686         (setcdr bmk (cons (cons prop val) (cdr bmk))))))) ; New: ("name" (filename . "f")...)
1687
1688
1689 ;; REPLACES ORIGINAL in `bookmark.el'.
1690 ;;
1691 ;; 1. Added optional arg USE-REGION-P.
1692 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
1693 ;; 3. Use `bmkp-jump-1'.
1694 ;; 4. Added note about Icicles `S-delete' to doc string.
1695 ;;
1696 ;;;###autoload
1697 (defun bookmark-jump (bookmark          ; Bound to `C-x j j', `C-x r b', `C-x p g'
1698                       &optional display-function use-region-p)
1699   "Jump to bookmark BOOKMARK.
1700 You may have a problem using this function if the value of variable
1701 `bookmark-alist' is nil.  If that happens, you need to load in some
1702 bookmarks.  See function `bookmark-load' for more about this.
1703
1704 If the file pointed to by BOOKMARK no longer exists, you are asked if
1705 you wish to give the bookmark a new location.  If so, `bookmark-jump'
1706 jumps to the new location and saves it.
1707
1708 If the bookmark defines a region, then the region is activated if
1709 `bmkp-use-region' is not-nil or it is nil and you use a prefix
1710 argument.  A prefix arg temporarily flips the value of
1711 `bmkp-use-region'.
1712
1713 If you use Icicles, then you can use `S-delete' during completion of a
1714 bookmark name to delete the bookmark named by the current completion
1715 candidate.
1716
1717 In Lisp code:
1718 BOOKMARK is a bookmark name or a bookmark record.
1719 Non-nil DISPLAY-FUNCTION is a function to display the bookmark.  By
1720  default, `switch-to-buffer' is used.
1721 Non-nil USE-REGION-P flips the value of `bmkp-use-region'."
1722   (interactive (list (bookmark-completing-read "Jump to bookmark" (bmkp-default-bookmark-name))
1723                      nil
1724                      current-prefix-arg))
1725   (bmkp-jump-1 bookmark (or display-function 'switch-to-buffer) use-region-p))
1726
1727
1728 ;; REPLACES ORIGINAL in `bookmark.el'.
1729 ;;
1730 ;; 1. Added optional arg USE-REGION-P.
1731 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
1732 ;; 3. Use `bmkp-jump-1'.
1733 ;;
1734 ;;;###autoload
1735 (defun bookmark-jump-other-window (bookmark &optional use-region-p) ; `C-x 4 j j', `C-x p o'
1736   "Jump to bookmark BOOKMARK in another window.
1737 See `bookmark-jump', in particular for info about using a prefix arg."
1738   (interactive (list (bookmark-completing-read "Jump to bookmark (in another window)"
1739                                                (bmkp-default-bookmark-name))
1740                      current-prefix-arg))
1741   (bmkp-jump-1 bookmark 'bmkp-select-buffer-other-window use-region-p))
1742
1743
1744 ;; REPLACES ORIGINAL in `bookmark.el'.
1745 ;;
1746 ;; Different relocation message for non-file bookmark.
1747 ;;
1748 (defun bookmark-handle-bookmark (bookmark)
1749   "Call BOOKMARK's handler, or `bookmark-default-handler' if it has none.
1750 The default handler changes the current buffer and point.
1751 BOOKMARK is a bookmark name or a bookmark record.
1752 Returns nil or raises an error.
1753
1754 If the default handler is used and a file error is raised, the error
1755 is handled as follows:
1756  If BOOKMARK has no `filename' entry, do nothing.
1757  Else prompt to relocate the file.
1758    If relocated, then try again to handle.  Else raise a file error."
1759   (if (bookmark-get-handler bookmark)
1760       (funcall (bookmark-get-handler bookmark) (bookmark-get-bookmark bookmark))
1761     (condition-case err
1762         (funcall 'bookmark-default-handler (bookmark-get-bookmark bookmark))
1763       (bookmark-error-no-filename         ; `file-error'
1764        ;; BOOKMARK can be either a bookmark name or a bookmark record.
1765        ;; If a record, do nothing - assume it is a bookmark used internally by some other package.
1766        (when (stringp bookmark)
1767          (let ((file             (bookmark-get-filename bookmark))
1768                (use-dialog-box   nil)
1769                (use-file-dialog  nil))
1770            (when file
1771              ;; Ask user whether to relocate the file.  If no, signal the file error.
1772              (unless (string= file bmkp-non-file-filename) (setq file  (expand-file-name file)))
1773              (ding)
1774              (cond ((y-or-n-p (if (and (string= file bmkp-non-file-filename)
1775                                        (bmkp-get-buffer-name bookmark))
1776                                   "Bookmark's buffer does not exist.  Re-create it? "
1777                                 (concat (file-name-nondirectory file) " nonexistent.  Relocate \""
1778                                         bookmark "\"? ")))
1779                     (if (string= file bmkp-non-file-filename)
1780                         ;; This is probably not the right way to get the correct buffer, but it's
1781                         ;; better than nothing, and it gives the user a chance to DTRT.
1782                         (pop-to-buffer (bmkp-get-buffer-name bookmark)) ; Create buffer.
1783                       (bookmark-relocate bookmark)) ; Relocate to file.
1784                     (funcall (or (bookmark-get-handler bookmark) 'bookmark-default-handler)
1785                              (bookmark-get-bookmark bookmark))) ; Try again
1786                    (t
1787                     (message "Bookmark not relocated: `%s'" bookmark)
1788                     (signal (car err) (cdr err))))))))))
1789   (when (stringp bookmark) (setq bookmark-current-bookmark  bookmark))
1790   ;; $$$$$$ The vanilla code returns nil, but there is no explanation of why and no code seems
1791   ;; to use the return value.  Perhaps we should return the bookmark instead?
1792   nil)                                  ; Return nil if no error.
1793
1794 (put 'bookmark-error-no-filename 'error-conditions
1795      '(error bookmark-errors bookmark-error-no-filename))
1796 (put 'bookmark-error-no-filename 'error-message "Bookmark has no associated file (or directory)")
1797
1798
1799 ;; REPLACES ORIGINAL in `bookmark.el'.
1800 ;;
1801 ;; 1. Support regions and buffer names.
1802 ;; 2. Handles w32 `Open' command if `bmkp-use-w32-browser-p' and if `w32-browser' is defined.
1803 ;;
1804 (defun bookmark-default-handler (bookmark)
1805   "Default handler to jump to the location of BOOKMARK.
1806 BOOKMARK is a bookmark name or a bookmark record.
1807 If BOOKMARK records a nonempty region, and `bmkp-use-region' is
1808  non-nil, then activate the region.
1809
1810 Non-nil `bmkp-use-w32-browser-p' means just call `w32-browser'
1811  (if defined).  That is, use the default MS Windows application for
1812  the bookmarked file.
1813
1814 Return nil or signal `file-error'."
1815   (let* ((bmk            (bookmark-get-bookmark bookmark))
1816          (file           (bookmark-get-filename bmk))
1817          (buf            (bookmark-prop-get bmk 'buffer))
1818          (bufname        (bmkp-get-buffer-name bmk))
1819          (pos            (bookmark-get-position bmk))
1820          (end-pos        (bmkp-get-end-position bmk))
1821          (old-info-node  (and (not (bookmark-get-handler bookmark))
1822                               (bookmark-prop-get bmk 'info-node))))
1823     (if (and bmkp-use-w32-browser-p (fboundp 'w32-browser) file)
1824         (w32-browser file)
1825       (if old-info-node                 ; Emacs 20-21 Info bookmarks - no handler entry.
1826           (progn (require 'info) (Info-find-node file old-info-node) (goto-char pos))
1827         (if (not (and bmkp-use-region end-pos (/= pos end-pos)))
1828             ;; Single-position bookmark (no region).  Go to it.
1829             (bmkp-goto-position bmk file buf bufname pos
1830                                 (bookmark-get-front-context-string bmk)
1831                                 (bookmark-get-rear-context-string bmk))
1832           ;; Bookmark with a region.  Go to it and activate the region.
1833           (if (and file (file-readable-p file) (not (buffer-live-p buf)))
1834               (with-current-buffer (find-file-noselect file) (setq buf  (buffer-name)))
1835             ;; No file found.  If no buffer either, then signal that file doesn't exist.
1836             (unless (or (and buf (get-buffer buf))
1837                         (and bufname (get-buffer bufname) (not (string= buf bufname))))
1838               (signal 'bookmark-error-no-filename (list 'stringp file))))
1839           (set-buffer (or buf bufname))
1840           (when bmkp-jump-display-function
1841             (save-current-buffer (funcall bmkp-jump-display-function (current-buffer)))
1842             (raise-frame))
1843           (goto-char (min pos (point-max)))
1844           (when (> pos (point-max)) (error "Bookmark position is beyond buffer end"))
1845           ;; Activate region.  Relocate it if it moved.  Save relocated bookmark if confirm.
1846           (funcall bmkp-handle-region-function bmk))))
1847     ;; $$$$$$ The vanilla code returns nil, but there is no explanation of why and no code seems
1848     ;; to use the return value.  Perhaps we should return the bookmark instead?
1849     nil))                               ; Return nil if no file error.
1850
1851
1852 ;; REPLACES ORIGINAL in `bookmark.el'.
1853 ;;
1854 ;; 1. Added bookmark default for interactive use.
1855 ;; 2. Added note about `S-delete' to doc string.
1856 ;; 3. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1857 ;; 4. Refresh menu list, to show new location.
1858 ;;
1859 (or (fboundp 'old-bookmark-relocate)
1860 (fset 'old-bookmark-relocate (symbol-function 'bookmark-relocate)))
1861
1862 ;;;###autoload
1863 (defun bookmark-relocate (bookmark-name) ; Not bound
1864   "Relocate the bookmark named BOOKMARK-NAME to another file.
1865 You are prompted for the new file name.
1866 Changes the file associated with the bookmark.
1867 Useful when a file has been renamed after a bookmark was set in it.
1868
1869 If you use Icicles, then you can use `S-delete' during completion of a
1870 bookmark name to delete the bookmark named by the current completion
1871 candidate."
1872   (interactive (list (bookmark-completing-read "Bookmark to relocate"
1873                                                (bmkp-default-bookmark-name))))
1874   (old-bookmark-relocate bookmark-name)
1875   (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0)
1876              bookmark-bmenu-toggle-filenames)
1877     (with-current-buffer (get-buffer "*Bookmark List*")
1878       (bmkp-refresh-menu-list bookmark-name)))) ; So the new location is displayed.
1879
1880
1881 ;; REPLACES ORIGINAL in `bookmark.el'.
1882 ;;
1883 ;; 1. Added bookmark default for interactive use.
1884 ;; 2. Do not add any text properties here.  That's done in `bmkp-bmenu-propertize-item'.
1885 ;; 3. Added note about `S-delete' to doc string.
1886 ;; 4. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1887 ;;
1888 ;;;###autoload
1889 (defun bookmark-insert-location (bookmark-name &optional no-history) ; `C-x p I' (original: `C-x p f')
1890   "Insert file or buffer name for the bookmark named BOOKMARK-NAME.
1891 If a file is bookmarked, insert the recorded file name.
1892 If a non-file buffer is bookmarked, insert the recorded buffer name.
1893
1894 Optional second arg NO-HISTORY means do not record this in the
1895 minibuffer history list `bookmark-history'.
1896
1897 If you use Icicles, then you can use `S-delete' during completion of a
1898 bookmark name to delete the bookmark named by the current completion
1899 candidate."
1900   (interactive
1901    (let ((bmk  (bookmark-completing-read "Insert bookmark location" (bmkp-default-bookmark-name))))
1902      (if (> emacs-major-version 21) (list bmk) bmk)))
1903   (or no-history (bookmark-maybe-historicize-string bookmark-name))
1904   (insert (bookmark-location bookmark-name))) ; Return the line inserted.
1905
1906
1907 ;; REPLACES ORIGINAL in `bookmark.el'.
1908 ;;
1909 ;; 1. Pass full bookmark to the various "get" functions.
1910 ;; 2. Location returned can be a buffer name, instead of a file name.
1911 ;;
1912 (defun bookmark-location (bookmark)
1913   "Return the name of the file or buffer associated with BOOKMARK.
1914 BOOKMARK is a bookmark name or a bookmark record.
1915 Return \"-- Unknown location --\" if no such name can be found."
1916   (bookmark-maybe-load-default-file)
1917   (setq bookmark  (bookmark-get-bookmark bookmark)) ; Possibly from the propertized string.
1918   (or (bookmark-prop-get bookmark 'location)
1919       (bookmark-get-filename bookmark)
1920       (bmkp-get-buffer-name bookmark)
1921       (bookmark-prop-get bookmark 'buffer)
1922       "-- Unknown location --"))
1923       ;; $$$$$$$$$ ""))
1924       ;; $$$$ (error "Bookmark has no file or buffer name: %S" bookmark)))
1925
1926
1927 ;; REPLACES ORIGINAL in `bookmark.el'.
1928 ;;
1929 ;; 1. Added bookmark default for interactive use.
1930 ;; 2. Added note about `S-delete' to doc string.
1931 ;; 3. Added BATCH arg.
1932 ;; 4. Put `bmkp-full-record' property on new name.
1933 ;; 5. Refresh menu list, to show new name.
1934 ;;
1935 ;;;###autoload
1936 (defun bookmark-rename (old &optional new batch) ; Bound to `C-x p r'
1937   "Change bookmark's name from OLD to NEW.
1938 Interactively:
1939  If called from the keyboard, then prompt for OLD.
1940  If called from the menubar, select OLD from a menu.
1941 If NEW is nil, then prompt for its string value.
1942
1943 If BATCH is non-nil, then do not rebuild the bookmark list.
1944
1945 While the user enters the new name, repeated `C-w' inserts consecutive
1946 words from the buffer into the new bookmark name.
1947
1948 If you use Icicles, then you can use `S-delete' during completion of a
1949 bookmark name to delete the bookmark named by the current completion
1950 candidate."
1951   (interactive (list (bookmark-completing-read "Old bookmark name"
1952                                                (bmkp-default-bookmark-name))))
1953   (bookmark-maybe-historicize-string old)
1954   (bookmark-maybe-load-default-file)
1955   (setq bookmark-current-point  (point)) ; `bookmark-current-point' is a free var here.
1956   (save-excursion (skip-chars-forward " ") (setq bookmark-yank-point  (point)))
1957   (setq bookmark-current-buffer  (current-buffer))
1958   (let ((newname  (or new  (read-from-minibuffer "New name: " nil
1959                                                  (let ((now-map  (copy-keymap minibuffer-local-map)))
1960                                                    (define-key now-map  "\C-w" 'bookmark-yank-word)
1961                                                    now-map)
1962                                                  nil 'bookmark-history))))
1963     (bookmark-set-name old newname)
1964     (when (and (> emacs-major-version 20) ; Emacs 21+.  Cannot just use (boundp 'print-circle).
1965                bmkp-propertize-bookmark-names-flag)
1966       (put-text-property 0 (length newname) 'bmkp-full-record (bookmark-get-bookmark newname) newname))
1967     (setq bookmark-current-bookmark  newname)
1968     (unless batch
1969       (bookmark-bmenu-surreptitiously-rebuild-list)
1970       (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
1971         (with-current-buffer (get-buffer "*Bookmark List*")
1972           (bmkp-refresh-menu-list newname)))) ; So the new name is displayed.
1973     (bmkp-maybe-save-bookmarks)
1974     newname))
1975
1976
1977 ;; REPLACES ORIGINAL in `bookmark.el'.
1978 ;;
1979 ;; 1. Added bookmark default for interactive use.
1980 ;; 2. Added note about `S-delete' to doc string.
1981 ;; 3. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
1982 ;;
1983 (or (fboundp 'old-bookmark-insert)
1984 (fset 'old-bookmark-insert (symbol-function 'bookmark-insert)))
1985
1986 ;;;###autoload
1987 (defun bookmark-insert (bookmark-name)  ; Bound to `C-x p i'
1988   "Insert the text of a bookmarked file.
1989 BOOKMARK-NAME is the name of the bookmark.
1990 You may have a problem using this function if the value of variable
1991 `bookmark-alist' is nil.  If that happens, you need to load in some
1992 bookmarks.  See function `bookmark-load' for more about this.
1993
1994 If you use Icicles, then you can use `S-delete' during completion of a
1995 bookmark name to delete the bookmark named by the current completion
1996 candidate."
1997   (interactive (list (bookmark-completing-read "Insert bookmark contents"
1998                                                (bmkp-default-bookmark-name))))
1999   (old-bookmark-insert bookmark-name))
2000
2001
2002 ;; REPLACES ORIGINAL in `bookmark.el'.
2003 ;;
2004 ;; 1. Accept a bookmark or a bookmark name as arg.
2005 ;; 2. Use `bmkp-default-bookmark-name' as default when interactive.
2006 ;; 3. If it is a name and it  has property `bmkp-full-record' then use that to find the bookmark to delete.
2007 ;; 4. Remove highlighting for the bookmark.
2008 ;; 5. Doc string includes note about `S-delete' for Icicles.
2009 ;; 6. Update `bmkp-latest-bookmark-alist' and `bmkp-bmenu-omitted-bookmarks'.
2010 ;; 7. Increment `bookmark-alist-modification-count' even when using `batch' arg.
2011 ;;
2012 ;;;###autoload
2013 (defun bookmark-delete (bookmark &optional batch) ; Bound to `C-x p d'
2014   "Delete the BOOKMARK from the bookmark list.
2015 BOOKMARK is a bookmark name or a bookmark record.
2016 Interactively, default to the \"current\" bookmark (that is, the one
2017 most recently used in this file), if it exists.
2018
2019 If BOOKMARK is a name and it has property `bmkp-full-record' then use
2020 that property along with the name to find the bookmark to delete.
2021 If it is a name without property `bmkp-full-record' then delete (only)
2022 the first bookmark in `bookmark-alist' with that name.
2023
2024 Optional second arg BATCH means do not update the bookmark list buffer
2025 \(probably because we were called from there).
2026
2027 If you use Icicles, then you can use `S-delete' during completion of a
2028 bookmark name to delete the bookmark named by the current completion
2029 candidate.  In this way, you can delete multiple bookmarks."
2030   (interactive
2031    (list (bookmark-completing-read "Delete bookmark" (bmkp-default-bookmark-name))))
2032   ;; $$$$$$ Instead of loading unconditionally, maybe we should just try to delete conditionally?
2033   ;; IOW, why not (when bookmarks-already-loaded BODY) instead of `bookmark-maybe-load-default-file'?
2034   ;; If it gets called on a hook that gets run before ever loading, then should probably do nothing.
2035   ;; Leaving it as is for now (2011-04-06).
2036   (bookmark-maybe-load-default-file)
2037   (let* ((bmk    (bookmark-get-bookmark bookmark 'NOERROR))
2038          (bname  (and (bookmark-name-from-full-record bmk)))) ; BOOKMARK might have been a bookmark.
2039     (when bname                         ; Do nothing if BOOKMARK does not represent a bookmark.
2040       (bookmark-maybe-historicize-string bname)
2041       (when (fboundp 'bmkp-unlight-bookmark) (bmkp-unlight-bookmark bmk 'NOERROR))
2042       (setq bookmark-alist                (delq bmk bookmark-alist)
2043             bmkp-latest-bookmark-alist    (delq bmk bmkp-latest-bookmark-alist)
2044             bmkp-bmenu-omitted-bookmarks  (bmkp-delete-bookmark-name-from-list
2045                                            bname bmkp-bmenu-omitted-bookmarks))
2046       ;; Added by DB.  `bookmark-current-bookmark' should be nil if last occurrence was deleted.
2047       (unless (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
2048         (setq bookmark-current-bookmark  nil))
2049       ;; Do not rebuild the list when using `batch' arg
2050       (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2051       (bmkp-maybe-save-bookmarks))))
2052
2053
2054 ;;; ;; REPLACES ORIGINAL in `bookmark.el'.
2055 ;;; ;;
2056 ;;; ;; 1. Changed arg name: BOOKMARK -> BOOKMARK-NAME.
2057 ;;; ;; 2. If BOOKMARK-NAME has property `bmkp-full-record' then use that to find the bookmark to delete.
2058 ;;; ;; 3. Remove highlighting for the bookmark.
2059 ;;; ;; 4. Added note about `S-delete' to doc string.
2060 ;;; ;; 5. Use `bmkp-default-bookmark-name' as default when interactive.
2061 ;;; ;; 6. Update `bmkp-latest-bookmark-alist' and `bmkp-bmenu-omitted-bookmarks'.
2062 ;;; ;; 7. Increment `bookmark-alist-modification-count' even when using `batch' arg.
2063 ;;; ;;
2064 ;;; $$$$$$$
2065 ;;; (defun bookmark-delete (bookmark-name &optional batch) ; Bound to `C-x p d'
2066 ;;;   "Delete the bookmark named BOOKMARK-NAME from the bookmark list.
2067 ;;; Removes only the first instance of a bookmark with that name.
2068 ;;; If there are other bookmarks with the same name, they are not deleted.
2069 ;;; Defaults to the \"current\" bookmark (that is, the one most recently
2070 ;;; used in this file), if it exists.  Optional second arg BATCH means do
2071 ;;; not update the bookmark list buffer (probably because we were called
2072 ;;; from there).
2073
2074 ;;; If BOOKMARK-NAME has property `bmkp-full-record' then that is used
2075 ;;; along with the name to find the first matching bookmark to delete.
2076
2077 ;;; If you use Icicles, then you can use `S-delete' during completion of a
2078 ;;; bookmark name to delete the bookmark named by the current completion
2079 ;;; candidate.  In this way, you can delete multiple bookmarks."
2080 ;;;   (interactive
2081 ;;;    (list (bookmark-completing-read "Delete bookmark" (bmkp-default-bookmark-name))))
2082 ;;;   (bookmark-maybe-historicize-string bookmark-name)
2083 ;;;   ;; $$$$$$ Instead of loading unconditionally, maybe we should just try to delete conditionally?
2084 ;;;   ;; IOW, why not (when bookmarks-already-loaded BODY) instead of `bookmark-maybe-load-default-file'?
2085 ;;;   ;; If it gets called on a hook that gets run before ever loading, then should probably do nothing.
2086 ;;;   ;; Leaving it as is for now (2011-04-06).
2087 ;;;   (bookmark-maybe-load-default-file)
2088 ;;;   (let ((bmk  (bookmark-get-bookmark bookmark-name 'NOERROR)))
2089 ;;;     (when (fboundp 'bmkp-unlight-bookmark) (bmkp-unlight-bookmark bmk 'NOERROR))
2090 ;;;     (setq bookmark-alist                (delq bmk bookmark-alist)
2091 ;;;           bmkp-latest-bookmark-alist    (delq bmk bmkp-latest-bookmark-alist)
2092 ;;;           bmkp-bmenu-omitted-bookmarks  (bmkp-delete-bookmark-name-from-list
2093 ;;;                                          bookmark-name bmkp-bmenu-omitted-bookmarks)))
2094 ;;;   ;; Added by DB.  `bookmark-current-bookmark' should be nil if last occurrence was deleted.
2095 ;;;   (unless (bookmark-get-bookmark bookmark-current-bookmark 'noerror)
2096 ;;;     (setq bookmark-current-bookmark  nil))
2097 ;;;   ;; Do not rebuild the list when using `batch' arg
2098 ;;;   (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2099 ;;;   (bmkp-maybe-save-bookmarks))
2100
2101
2102 ;; REPLACES ORIGINAL in `bookmark.el'.
2103 ;;
2104 ;; Use `bmkp-current-bookmark-file', not `bookmark-default-file'.
2105 ;;
2106 ;;;###autoload
2107 (defun bookmark-save (&optional parg file) ; Bound to `C-x p s'
2108   "Save currently defined bookmarks.
2109 Save by default in the file named by variable
2110 `bmkp-current-bookmark-file'.  With a prefix arg, you are prompted for
2111 the file to save to.
2112
2113 To load bookmarks from a specific file, use `\\[bookmark-load]'
2114 \(`bookmark-load').
2115
2116 If called from Lisp:
2117  Witn nil PARG, use file `bmkp-current-bookmark-file'.
2118  With non-nil PARG and non-nil FILE, use file FILE.
2119  With non-nil PARG and nil FILE, prompt the user for the file to use."
2120   (interactive "P")
2121   (bookmark-maybe-load-default-file)
2122   (cond ((and (not parg) (not file)) (bookmark-write-file bmkp-current-bookmark-file))
2123         ((and (not parg) file) (bookmark-write-file file))
2124         ((and parg (not file))
2125          (bookmark-write-file (bmkp-read-bookmark-file-name "File to save bookmarks in: ")))
2126         (t (bookmark-write-file file)))
2127   ;; Indicate by the count that we have synced the current bookmark file.
2128   ;; If an error has already occurred somewhere, the count will not be set, which is what we want.
2129   (setq bookmark-alist-modification-count 0))
2130
2131
2132 ;; REPLACES ORIGINAL in `bookmark.el'.
2133 ;;
2134 ;; 1. Added optional arg ALT-MSG.
2135 ;; 2. Insert code piecewise, to improve performance when saving `bookmark-alist'.
2136 ;;    (Do not let `pp' parse all of `bookmark-alist' at once.)
2137 ;; 3. Unless `bmkp-propertize-bookmark-names-flag', remove text properties from bookmark name and file name.
2138 ;; 4. Bind `print-circle' to t around pp, to record bookmark name with `bmkp-full-record' property.
2139 ;; 4. Use `case', not `cond'.
2140 ;;
2141 (defun bookmark-write-file (file &optional alt-msg)
2142   "Write `bookmark-alist' to FILE.
2143 Non-nil ALT-MSG is a message format string to use in place of the
2144 default, \"Saving bookmarks to file `%s'...\".  The string must
2145 contain a `%s' construct, so that it can be passed along with FILE to
2146 `format'.  At the end, \"done\" is appended to the message."
2147   (let ((msg  (or alt-msg "Saving bookmarks to file `%s'..." file)))
2148     (bookmark-maybe-message (or alt-msg "Saving bookmarks to file `%s'...") file)
2149     (with-current-buffer (get-buffer-create " *Bookmarks*")
2150       (goto-char (point-min))
2151       (delete-region (point-min) (point-max))
2152       (let ((print-length  nil)
2153             (print-level   nil)
2154             bname fname last-fname)
2155         (bookmark-insert-file-format-version-stamp)
2156         (insert "(")
2157         (dolist (bmk  bookmark-alist)
2158           (setq bname  (car bmk)
2159                 fname  (bookmark-get-filename bmk))
2160           (when (or (not (> emacs-major-version 20)) ; Emacs 20.  Cannot use (not (boundp 'print-circle)).
2161                     (not bmkp-propertize-bookmark-names-flag))
2162             (set-text-properties 0 (length bname) () bname)
2163             (when fname (set-text-properties 0 (length fname) () fname)))
2164           (setcar bmk bname)
2165           (when (setq last-fname  (assq 'filename bmk)) (setcdr last-fname fname))
2166           (let ((print-circle  t)) (pp bmk (current-buffer))))
2167         (insert ")")
2168         (let ((version-control  (case bookmark-version-control
2169                                   ((nil)      nil)
2170                                   (never      'never)
2171                                   (nospecial  version-control)
2172                                   (t          t))))
2173           (condition-case nil
2174               (write-region (point-min) (point-max) file)
2175             (file-error (message "Cannot write file `%s'" file)))
2176           (kill-buffer (current-buffer))
2177           (bookmark-maybe-message (concat msg "done") file))))))
2178
2179
2180 ;; REPLACES ORIGINAL in `bookmark.el'.
2181 ;;
2182 ;; 1. Prefix arg means OVERWRITE.
2183 ;; 2. Update `bmkp-current-bookmark-file' if OVERWRITE is non-nil.
2184 ;; 3. New doc string.
2185 ;; 4. Final msg says whether overwritten.
2186 ;; 5. Call `bmkp-bmenu-refresh-menu-list' at end.
2187 ;;
2188 ;;;###autoload
2189 (defun bookmark-load (file &optional overwrite no-msg) ; Bound to `C-x p l'
2190   "Load bookmarks from FILE (which must be in the standard format).
2191 Without a prefix argument (argument OVERWRITE is nil), add the newly
2192 loaded bookmarks to those already current.  They will be saved to the
2193 current bookmark file when bookmarks are saved.  If you have never
2194 switched bookmark files, then this is the default file,
2195 `bookmark-default-file'.
2196
2197 If you do not use a prefix argument, then no existing bookmarks are
2198 overwritten.  If you load some bookmarks that have the same names as
2199 bookmarks already defined in your Emacs session, numeric suffixes
2200 \"<2>\", \"<3>\",... are appended as needed to the names of those new
2201 bookmarks to distinguish them.
2202
2203 With a prefix argument, switch the bookmark file currently used,
2204 *replacing* all currently existing bookmarks with the newly loaded
2205 bookmarks.  The value of `bmkp-current-bookmark-file' is changed to
2206 FILE, so bookmarks will subsequently be saved to FILE.  The value
2207 `bookmark-default-file' is unaffected, so your next Emacs session will
2208 still use the same default set of bookmarks.
2209
2210 When called from Lisp, non-nil NO-MSG means do not display any
2211 messages while loading.
2212
2213 You do not need to manually load your default bookmark file
2214 \(`bookmark-default-file') - it is loaded automatically by Emacs the
2215 first time you use bookmarks in a session.  Use `bookmark-load' only
2216 to load extra bookmarks (with no prefix arg) or an alternative set of
2217 bookmarks (with a prefix arg).
2218
2219 If you use `bookmark-load' to load a file that does not contain a
2220 proper bookmark alist, then when bookmarks are saved the current
2221 bookmark file will likely become corrupted.  You should load only
2222 bookmark files that were created using the bookmark functions."
2223   (interactive
2224    (list (let ((bfile  (if (bmkp-same-file-p bmkp-current-bookmark-file bmkp-last-bookmark-file)
2225                            bookmark-default-file
2226                          bmkp-last-bookmark-file)))
2227            (bmkp-read-bookmark-file-name
2228             (if current-prefix-arg "Switch to bookmark file: " "Add bookmarks from file: ")
2229             (or (file-name-directory bfile) "~/")
2230             bfile
2231             'CONFIRM))
2232          current-prefix-arg))
2233   (setq file  (abbreviate-file-name (expand-file-name file)))
2234   (unless (file-readable-p file) (error "Cannot read bookmark file `%s'" file))
2235   (unless no-msg (bookmark-maybe-message "Loading bookmarks from `%s'..." file))
2236   (with-current-buffer (let ((enable-local-variables nil)) (find-file-noselect file))
2237     (goto-char (point-min))
2238     (bookmark-maybe-upgrade-file-format)
2239     (let ((blist  (bookmark-alist-from-buffer)))
2240       (unless (listp blist) (error "Invalid bookmark list in `%s'" file))
2241       (if overwrite
2242           (setq bmkp-last-bookmark-file            bmkp-current-bookmark-file
2243                 bmkp-current-bookmark-file         file
2244                 bookmark-alist                     blist
2245                 bookmark-alist-modification-count  0)
2246         (bookmark-import-new-list blist)
2247         (setq bookmark-alist-modification-count  (1+ bookmark-alist-modification-count)))
2248       (when (string-equal (abbreviate-file-name (expand-file-name bookmark-default-file)) file)
2249         (setq bookmarks-already-loaded  t))
2250       (bookmark-bmenu-surreptitiously-rebuild-list))
2251     (kill-buffer (current-buffer)))
2252   (unless no-msg (message "%s bookmark file `%s'" (if overwrite "Switched to" "Loaded") file))
2253   (let ((bmklistbuf  (get-buffer "*Bookmark List*")))
2254     (when bmklistbuf (with-current-buffer bmklistbuf (bmkp-bmenu-refresh-menu-list)))))
2255
2256
2257 ;; REPLACES ORIGINAL in `bookmark.el'.
2258 ;;
2259 ;; 1. Added optional arg MSGP.  Show message if no annotation.
2260 ;; 2. Name buffer after the bookmark.
2261 ;; 3. MSGP means message if no annotation.
2262 ;; 4. Use `view-mode'.  `q' uses `quit-window'.
2263 ;; 5. Fit frame to buffer if `one-windowp'.
2264 ;; 6. Restore frame selection.
2265 ;;
2266 (defun bookmark-show-annotation (bookmark &optional msgp)
2267   "Display the annotation for BOOKMARK.
2268 If no annotation and MSGP is non-nil, show a no-annotation message."
2269   (let* ((bmk       (bookmark-get-bookmark bookmark))
2270          (bmk-name  (bookmark-name-from-full-record bmk))
2271          (ann       (bookmark-get-annotation bmk)))
2272     (if (not (and ann (not (string-equal ann ""))))
2273         (when msgp (message "Bookmark has no annotation"))
2274       (let ((oframe  (selected-frame)))
2275         (save-selected-window
2276           (pop-to-buffer (get-buffer-create (format "*`%s' Annotation*" bmk-name)))
2277           (let ((buffer-read-only  nil)) ; Because buffer might already exist, in view mode.
2278             (delete-region (point-min) (point-max))
2279             (insert (concat "Annotation for bookmark '" bmk-name "':\n\n"))
2280             (put-text-property (line-beginning-position -1) (line-end-position 1)
2281                                'face 'bmkp-heading)
2282             (insert ann))
2283           (goto-char (point-min))
2284           (view-mode-enter (cons (selected-window) (cons nil 'quit-window)))
2285           (when (fboundp 'fit-frame-if-one-window) (fit-frame-if-one-window)))
2286         (select-frame-set-input-focus oframe)))))
2287
2288
2289 ;; REPLACES ORIGINAL in `bookmark.el'.
2290 ;;
2291 ;; 1. Use name `*Bookmark Annotations*', not `*Bookmark Annotation*'.
2292 ;; 2. Don't list bookmarks that have no annotation.
2293 ;; 3. Highlight bookmark names.  Don't indent annotations.  Add a blank line after each annotation.
2294 ;; 4. Use `view-mode'.  `q' uses `quit-window'.
2295 ;; 5. Fit frame to buffer if `one-windowp'.
2296 ;; 6. Restore frame selection.
2297 ;;
2298 (defun bookmark-show-all-annotations ()
2299   "Display the annotations for all bookmarks."
2300   (let ((oframe  (selected-frame)))
2301     (save-selected-window
2302       (pop-to-buffer (get-buffer-create "*Bookmark Annotations*"))
2303       (let ((buffer-read-only  nil))    ; Because buffer might already exist, in view mode.
2304         (delete-region (point-min) (point-max))
2305         (dolist (full-record  bookmark-alist) ; (Could use `bmkp-annotated-alist-only' here instead.)
2306           (let ((ann  (bookmark-get-annotation full-record)))
2307             (when (and ann (not (string-equal ann "")))
2308               (insert (concat (bookmark-name-from-full-record full-record) ":\n"))
2309               (put-text-property (line-beginning-position 0) (line-end-position 0)
2310                                  'face 'bmkp-heading)
2311               (insert ann) (unless (bolp) (insert "\n\n")))))
2312         (goto-char (point-min))
2313         (view-mode-enter (cons (selected-window) (cons nil 'quit-window)))
2314         (when (fboundp 'fit-frame-if-one-window) (fit-frame-if-one-window))))
2315     (select-frame-set-input-focus oframe)))
2316
2317
2318 ;; REPLACES ORIGINAL in `bookmark.el'.
2319 ;;
2320 ;; Save menu-list state to `bmkp-bmenu-state-file'.
2321 ;;
2322 (defun bookmark-exit-hook-internal ()   ; This goes on `kill-emacs-hook'.
2323   "Save currently defined bookmarks and perhaps bookmark menu-list state.
2324 Run `bookmark-exit-hook', then save bookmarks if they were updated.
2325 Then save menu-list state to file `bmkp-bmenu-state-file', but only if
2326 that option is non-nil."
2327   (run-hooks 'bookmark-exit-hook)
2328   (when (and bookmark-alist (bookmark-time-to-save-p t)) (bookmark-save))
2329   (bmkp-save-menu-list-state))
2330  
2331 ;;(@* "Bookmark+ Functions (`bmkp-*')")
2332 ;;; Bookmark+ Functions (`bmkp-*') -----------------------------------
2333
2334 (defun bmkp-completing-read-lax (prompt &optional default alist pred hist)
2335   "Read a bookmark name, prompting with PROMPT.
2336 Same as `bookmark-completing-read', but completion is lax."
2337   (unwind-protect
2338        (progn (define-key minibuffer-local-completion-map "\C-w" 'bookmark-yank-word)
2339               (define-key minibuffer-local-completion-map "\C-u" 'bookmark-insert-current-bookmark)
2340               (bmkp-completing-read-1 prompt default alist pred hist t))
2341     (define-key minibuffer-local-completion-map "\C-w" nil)
2342     (define-key minibuffer-local-completion-map "\C-u" nil)))
2343
2344 (defun bmkp-completing-read-1 (prompt default alist pred hist laxp)
2345   "Helper for `bookmark-completing-read(-lax)'.
2346 LAXP non-nil means use lax completion."
2347   (bookmark-maybe-load-default-file)
2348   (setq alist  (or alist bookmark-alist))
2349   (if (and (not laxp)
2350            (listp last-nonmenu-event)
2351            (or (eq t bmkp-menu-popup-max-length)
2352                (and (integerp bmkp-menu-popup-max-length)
2353                     (< (length alist) bmkp-menu-popup-max-length))))
2354       (bookmark-menu-popup-paned-menu
2355        t prompt
2356        (if bmkp-sort-comparer           ; Test whether to sort, but always use `string-lessp'.
2357            (sort (bookmark-all-names alist) 'string-lessp)
2358          (bookmark-all-names alist)))
2359     (let* ((icicle-delete-candidate-object  (lambda (cand) ; For `S-delete' in Icicles.
2360                                               (bookmark-delete
2361                                                (icicle-transform-multi-completion cand))))
2362            (completion-ignore-case          bookmark-completion-ignore-case)
2363            (default                         default)
2364            (prompt                          (if default
2365                                                 (concat prompt (format " (%s): " default))
2366                                               (concat prompt ": ")))
2367            (str                             (completing-read
2368                                              prompt alist pred (not laxp) nil
2369                                              (or hist 'bookmark-history) default)))
2370       (if (and (string-equal "" str) default) default str))))
2371
2372 (defun bmkp-jump-1 (bookmark display-function use-region-p)
2373   "Helper function for `bookmark-jump' commands.
2374 BOOKMARK is a bookmark name (a string) or a bookmark record.
2375 DISPLAY-FUNCTION is passed to `bookmark--jump-via'.
2376 Non-nil USE-REGION-P means activate the region, if recorded."
2377   (setq bookmark  (bookmark-get-bookmark bookmark 'NOERROR))
2378   (unless bookmark (error "No bookmark specified"))
2379   (bookmark-maybe-historicize-string (bookmark-name-from-full-record bookmark))
2380   (let ((bmkp-use-region  (if use-region-p (not bmkp-use-region) bmkp-use-region)))
2381     (bookmark--jump-via bookmark display-function)))
2382
2383 (defun bmkp-select-buffer-other-window (buffer)
2384   "Select BUFFER in another window.
2385 If `bmkp-other-window-pop-to-flag' is non-nil, then use
2386 `pop-to-buffer'.  Otherwise, use `switch-to-buffer-other-window'."
2387   (if bmkp-other-window-pop-to-flag
2388       (pop-to-buffer buffer t)
2389     (switch-to-buffer-other-window buffer)))  
2390
2391 (defun bmkp-maybe-save-bookmarks ()
2392   "Increment save counter and maybe save `bookmark-alist'."
2393   (setq bookmark-alist-modification-count  (1+ bookmark-alist-modification-count))
2394   (when (bookmark-time-to-save-p) (bookmark-save)))
2395
2396 ;;;###autoload
2397 (defun bmkp-edit-bookmark (bookmark &optional internalp) ; Bound to `C-x p E'
2398   "Edit BOOKMARK's name and file name, and maybe save them.
2399 BOOKMARK is a bookmark name (a string) or a bookmark record.
2400 With a prefix argument, edit the complete bookmark record (the
2401 internal, Lisp form).
2402 Return a list of the new bookmark name and new file name."
2403   (interactive
2404    (list (bookmark-completing-read
2405           (concat "Edit " (and current-prefix-arg "internal record for ") "bookmark")
2406           (bmkp-default-bookmark-name))
2407          current-prefix-arg))
2408   (setq bookmark  (bookmark-get-bookmark bookmark))
2409   (if internalp
2410       (bmkp-edit-bookmark-record bookmark)
2411     (let* ((bookmark-name      (bookmark-name-from-full-record bookmark))
2412            (bookmark-filename  (bookmark-get-filename bookmark-name))
2413            (new-bmk-name       (read-from-minibuffer "New bookmark name: " nil nil nil nil
2414                                                      bookmark-name))
2415            (new-filename       (read-from-minibuffer "New file name (location): " nil nil nil nil
2416                                                      bookmark-filename)))
2417       (when (and (or (not (equal new-bmk-name "")) (not (equal new-filename "")))
2418                  (y-or-n-p "Save changes? "))
2419         (bookmark-rename bookmark-name new-bmk-name 'batch)
2420         (bookmark-set-filename new-bmk-name new-filename)
2421         ;; Change location for Dired too, but not if different from original file name (e.g. a cons).
2422         (let ((dired-dir  (bookmark-prop-get new-bmk-name 'dired-directory)))
2423           (when (and dired-dir (equal dired-dir bookmark-filename))
2424             (bookmark-prop-set new-bmk-name 'dired-directory new-filename)))
2425         (bmkp-maybe-save-bookmarks)
2426         (list new-bmk-name new-filename)))))
2427
2428 (define-derived-mode bmkp-edit-bookmark-record-mode emacs-lisp-mode
2429     "Edit Bookmark Record"
2430   "Mode for editing an internal bookmark record.
2431 When you have finished composing, type \\[bmkp-edit-bookmark-record-send]."
2432   :group 'bookmark-plus)
2433
2434 ;; This binding must be defined *after* the mode, so `bmkp-edit-bookmark-record-mode-map' is defined.
2435 ;; (Alternatively, we could use a `defvar' to define `bmkp-edit-bookmark-record-mode-map' before
2436 ;; calling `define-derived-mode'.)
2437 (define-key bmkp-edit-bookmark-record-mode-map "\C-c\C-c" 'bmkp-edit-bookmark-record-send)
2438
2439 ;;;###autoload
2440 (defun bmkp-edit-bookmark-record (bookmark)
2441   "Edit the internal record for bookmark BOOKMARK.
2442 When you have finished, Use `\\[bmkp-edit-bookmark-record-send]'.
2443 BOOKMARK is a bookmark name (a string) or a bookmark record."
2444   (interactive (list (bookmark-completing-read "Edit internal record for bookmark"
2445                                                (bmkp-default-bookmark-name))))
2446   (setq bookmark  (bookmark-get-bookmark bookmark))
2447   (let* ((bmk-name  (bookmark-name-from-full-record bookmark))
2448          (bufname   (format "*Edit Bookmark `%s'*" bmk-name)))
2449     (with-output-to-temp-buffer bufname
2450       (princ
2451        (substitute-command-keys
2452         (concat ";; Edit the internal record for bookmark\n;;\n"
2453                 ";; " bmk-name "\n;;\n"
2454                 ";; Type \\<bmkp-edit-bookmark-record-mode-map>\
2455 `\\[bmkp-edit-bookmark-record-send]' when done.\n;;\n"
2456                 ";; NOTE: If you edit the bookmark *name* within the record, then\n"
2457                 ";;       a new bookmark is created for the new name, and the\n"
2458                 ";;       original (unedited) bookmark continues to exist as well.\n\n")))
2459       (let ((print-circle  t)) (pp bookmark))
2460       (goto-char (point-min)))
2461     (pop-to-buffer bufname)
2462     (buffer-enable-undo)
2463     (with-current-buffer (get-buffer bufname) (bmkp-edit-bookmark-record-mode))))
2464
2465 ;;;###autoload
2466 (defun bmkp-edit-bookmark-record-send (arg &optional force)
2467   "Use buffer contents as a bookmark record.
2468 Lines beginning with `;;' are ignored.
2469 With a prefix argument, do not update `time' or `visits' entries."
2470   (interactive "P")
2471   (unless (eq major-mode 'bmkp-edit-bookmark-record-mode)
2472     (error "Not in `bmkp-edit-bookmark-record-mode'"))
2473   (goto-char (point-min))
2474   (let ((bmk  (read (current-buffer))))
2475     (when (or force (bmkp-bookmark-type bmk) ; Must pass BMK, not BMK-NAME, since might be renamed.
2476               (and (interactive-p)
2477                    (or (y-or-n-p "Bookmard record not recognized as valid.  Save anyway? ")
2478                        (error "Canceled"))))
2479       (unless arg (bmkp-record-visit bmk t))
2480       (bookmark-store (bookmark-name-from-full-record bmk) (bookmark-get-bookmark-record bmk) nil)
2481       (when (interactive-p) (message "Updated bookmark file"))))
2482   (kill-buffer (current-buffer)))
2483
2484 (define-derived-mode bmkp-edit-tags-mode emacs-lisp-mode
2485     "Edit Bookmark Tags"
2486   "Mode for editing bookmark tags.
2487 When you have finished composing, type \\[bmkp-edit-tags-send]."
2488   :group 'bookmark-plus)
2489
2490 ;; This binding must be defined *after* the mode, so `bmkp-edit-tags-mode-map' is defined.
2491 ;; (Alternatively, we could use a `defvar' to define `bmkp-edit-tags-mode-map' before
2492 ;; calling `define-derived-mode'.)
2493 (define-key bmkp-edit-tags-mode-map "\C-c\C-c" 'bmkp-edit-tags-send)
2494
2495 ;;;###autoload
2496 (defun bmkp-edit-tags (bookmark)        ; Bound to `C-x p t e'
2497   "Edit BOOKMARK's tags, and maybe save the result.
2498 The edited value must be a list each of whose elements is either a
2499  string or a cons whose key is a string.
2500 BOOKMARK is a bookmark name (a string) or a bookmark record."
2501   (interactive (list (bookmark-completing-read "Edit tags for bookmark" (bmkp-default-bookmark-name))))
2502   (setq bookmark  (bookmark-get-bookmark bookmark))
2503   (let* ((btags    (bmkp-get-tags bookmark))
2504          (bmkname  (bookmark-name-from-full-record bookmark))
2505          (edbuf    (format "*Edit Tags for Bookmark `%s'*" bmkname)))
2506     (setq bmkp-return-buffer  (current-buffer))
2507     (with-output-to-temp-buffer edbuf
2508       (princ
2509        (substitute-command-keys
2510         (concat ";; Edit tags for bookmark\n;;\n;; \"" bmkname "\"\n;;\n"
2511                 ";; The edited value must be a list each of whose elements is\n"
2512                 ";; either a string or a cons whose key is a string.\n;;\n"
2513                 ";; DO NOT MODIFY THESE COMMENTS.\n;;\n"
2514                 ";; Type \\<bmkp-edit-tags-mode-map>`\\[bmkp-edit-tags-send]' when done.\n\n")))
2515       (let ((print-circle  t)) (pp btags))
2516       (goto-char (point-min)))
2517     (pop-to-buffer edbuf)
2518     (buffer-enable-undo)
2519     (with-current-buffer (get-buffer edbuf) (bmkp-edit-tags-mode))))  
2520
2521 ;;;###autoload
2522 (defun bmkp-edit-tags-send ()
2523   "Use buffer contents as the internal form of a bookmark's tags.
2524 DO NOT MODIFY the header comment lines, which begin with `;;'."
2525   (interactive)
2526   (unless (eq major-mode 'bmkp-edit-tags-mode) (error "Not in `bmkp-edit-tags-mode'"))
2527   (let (bname)
2528     (unwind-protect
2529          (let (tags bmk)
2530            (goto-char (point-min))
2531            (unless (search-forward ";; Edit tags for bookmark\n;;\n;; ")
2532              (error "Missing header in edit buffer"))
2533            (unless (stringp (setq bname  (read (current-buffer))))
2534              (error "Bad bookmark name in edit-buffer header"))
2535            (unless (setq bmk  (bookmark-get-bookmark bname)) (error "No such bookmark: `%s'" bname))
2536            (unless (bmkp-bookmark-type bmk) (error "Invalid bookmark"))
2537            (goto-char (point-min))
2538            (setq tags  (read (current-buffer)))
2539            (unless (listp tags) (error "Tags sexp is not a list of strings or an alist with string keys"))
2540            (bookmark-prop-set bmk 'tags tags)
2541            (setq bname  (bookmark-name-from-full-record bmk))
2542            (bmkp-record-visit bmk)      ; Not BATCH.
2543            (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
2544              (with-current-buffer (get-buffer "*Bookmark List*")
2545                (bmkp-refresh-menu-list bname))) ; To update display.
2546            (bmkp-maybe-save-bookmarks)
2547            (when (interactive-p) (message "Updated bookmark file with edited tags")))
2548       (kill-buffer (current-buffer)))
2549     (when bmkp-return-buffer
2550       (pop-to-buffer bmkp-return-buffer)
2551       (when (equal (buffer-name (current-buffer)) "*Bookmark List*")
2552         (bmkp-bmenu-goto-bookmark-named bname)))))
2553
2554 (defun bmkp-bookmark-type (bookmark)
2555   "Return the type of BOOKMARK or nil if no type is recognized.
2556 Return nil if the bookmark record is not recognized (invalid).
2557 See the code for the possible non-nil return values.
2558 BOOKMARK is a bookmark name or a bookmark record."
2559   (condition-case nil
2560       (progn
2561         ;; If BOOKMARK is already a bookmark record, not a bookmark name, then we must use it.
2562         ;; If we used the name instead, then tests such as `bookmark-get-filename' would fail,
2563         ;; because they call `bookmark-get-bookmark', which, for a string, checks whether the
2564         ;; bookmark exists in `bookmark-alist'.  But we want to be able to use `bmkp-bookmark-type'
2565         ;; to get the type of any bookmark record, not necessarily one that is in `bookmark-alist'.
2566         (when (stringp bookmark) (setq bookmark  (bookmark-get-bookmark bookmark)))
2567         (let ((filep  (bookmark-get-filename bookmark)))
2568           (cond ((bmkp-sequence-bookmark-p bookmark)             'bmkp-sequence-bookmark-p)
2569                 ((bmkp-function-bookmark-p bookmark)             'bmkp-function-bookmark-p)
2570                 ((bmkp-variable-list-bookmark-p bookmark)        'bmkp-variable-list-bookmark-p)
2571                 ((bmkp-url-bookmark-p bookmark)                  'bmkp-url-bookmark-p)
2572                 ((bmkp-gnus-bookmark-p bookmark)                 'bmkp-gnus-bookmark-p)
2573                 ((bmkp-desktop-bookmark-p bookmark)              'bmkp-desktop-bookmark-p)
2574                 ((bmkp-bookmark-file-bookmark-p bookmark)        'bmkp-bookmark-file-bookmark-p)
2575                 ((bmkp-bookmark-list-bookmark-p bookmark)        'bmkp-bookmark-list-bookmark-p)
2576                 ((bmkp-man-bookmark-p bookmark)                  'bmkp-man-bookmark-p)
2577                 ((bmkp-info-bookmark-p bookmark)                 'bmkp-info-bookmark-p)
2578                 ((bookmark-get-handler bookmark)                 'bookmark-get-handler)
2579                 ((bmkp-region-bookmark-p bookmark)               'bmkp-region-bookmark-p)
2580                 ;; Make sure we test for remoteness before any other tests of the file itself
2581                 ;; (e.g. `file-exists-p'). We do not want to prompt for a password etc.
2582                 ((and filep (bmkp-file-remote-p filep))          'remote-file)
2583                 ((and filep (file-directory-p filep))            'local-directory)
2584                 (filep                                           'local-file)
2585                 ((and (bmkp-get-buffer-name bookmark)
2586                       (or (not filep)
2587                           (equal filep bmkp-non-file-filename))) 'buffer)
2588                 (t                                               nil))))
2589     (error nil)))
2590
2591 (defun bmkp-record-visit (bookmark &optional batch)
2592   "Update the data recording a visit to BOOKMARK.
2593 BOOKMARK is a bookmark name or a bookmark record.
2594 This increments the `visits' entry and sets the `time' entry to the
2595 current time.  If either an entry is not present, it is added (with 0
2596 value for `visits').
2597 With non-nil optional arg BATCH, do not rebuild the menu list."
2598   (let ((vis  (bookmark-prop-get bookmark 'visits)))
2599     (if vis  (bookmark-prop-set bookmark 'visits (1+ vis))  (bookmark-prop-set bookmark 'visits 0))
2600     (bookmark-prop-set bookmark 'time (current-time))
2601     (unless batch (bookmark-bmenu-surreptitiously-rebuild-list))
2602     (let ((bookmark-save-flag  nil))  (bmkp-maybe-save-bookmarks))))
2603
2604 (defun bmkp-default-bookmark-name (&optional alist)
2605   "Default bookmark name.  See option `bmkp-default-bookmark-name'.
2606 Non-nil ALIST means return nil unless the default names a bookmark in
2607 ALIST."
2608   (let ((bname  (if (equal (buffer-name (current-buffer)) "*Bookmark List*")
2609                     (bookmark-bmenu-bookmark)
2610                   (if (fboundp 'bmkp-default-lighted)
2611                       (if (eq 'highlighted bmkp-default-bookmark-name)
2612                           (or (bmkp-default-lighted) bookmark-current-bookmark)
2613                         (or bookmark-current-bookmark (bmkp-default-lighted)))
2614                     bookmark-current-bookmark))))
2615     (when (and bname alist)
2616       (let ((bookmark-alist  alist))
2617         (setq bname  (bookmark-name-from-full-record (bookmark-get-bookmark bname)))))
2618     bname))
2619
2620 (defun bmkp-buffer-names ()
2621   "Buffer names used by existing bookmarks that really have buffers.
2622 This excludes buffers for bookmarks such as desktops that are not
2623 really associated with a buffer."
2624   (let ((bufs  ())
2625         buf)
2626     (dolist (bmk  bookmark-alist)
2627       (when (and (not (bmkp-desktop-bookmark-p        bmk))
2628                  (not (bmkp-bookmark-file-bookmark-p  bmk))
2629                  (not (bmkp-sequence-bookmark-p       bmk))
2630                  (not (bmkp-function-bookmark-p       bmk))
2631                  (not (bmkp-variable-list-bookmark-p  bmk))
2632                  (setq buf  (bmkp-get-buffer-name     bmk)))
2633         (add-to-list 'bufs buf)))
2634     bufs))
2635
2636 (defun bmkp-file-names ()
2637   "The absolute file names used by the existing bookmarks.
2638 This excludes the pseudo file name `bmkp-non-file-filename'."
2639   (let ((files  ())
2640         file)
2641     (dolist (bmk  bookmark-alist)
2642       (when (and (setq file  (bookmark-get-filename bmk))  (not (equal file bmkp-non-file-filename)))
2643         (add-to-list 'files file)))
2644     files))
2645
2646 ;;;###autoload
2647 (defun bmkp-send-bug-report ()          ; Not bound
2648   "Send a bug report about a Bookmark+ problem."
2649   (interactive)
2650   (browse-url (format (concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
2651 Bookmark+ bug: \
2652 &body=Describe bug below, using a precise recipe that starts with `emacs -Q' or `emacs -q'.  \
2653 Be sure to mention the `Update #' from header of the particular Bookmark+ file header.\
2654 %%0A%%0AEmacs version: %s")
2655                       (emacs-version))))
2656
2657 ;;;###autoload
2658 (defun bmkp-toggle-bookmark-set-refreshes () ; Not bound
2659   "Toggle `bookmark-set' refreshing `bmkp-latest-bookmark-alist'.
2660 Add/remove `bmkp-refresh-latest-bookmark-list' to/from
2661 `bmkp-after-set-hook'."
2662   (interactive)
2663   (if (member 'bmkp-refresh-latest-bookmark-list bmkp-after-set-hook)
2664       (remove-hook 'bmkp-after-set-hook 'bmkp-refresh-latest-bookmark-list)
2665     (add-hook 'bmkp-after-set-hook 'bmkp-refresh-latest-bookmark-list)))
2666
2667 (defun bmkp-refresh-latest-bookmark-list ()
2668   "Refresh `bmkp-latest-bookmark-alist' to reflect `bookmark-alist'."
2669   (setq bmkp-latest-bookmark-alist  (if bmkp-bmenu-filter-function
2670                                         (funcall bmkp-bmenu-filter-function)
2671                                       bookmark-alist)))
2672
2673 ;;;###autoload
2674 (defun bmkp-toggle-saving-menu-list-state () ; Bound to `M-l' in bookmark list
2675   "Toggle the value of option `bmkp-bmenu-state-file'.
2676 Tip: You can use this before quitting Emacs, to not save the state.
2677 If the initial value of `bmkp-bmenu-state-file' is nil, then this
2678 command has no effect."
2679   (interactive)
2680   (unless (or bmkp-last-bmenu-state-file bmkp-bmenu-state-file)
2681     (error "Cannot toggle: initial value of `bmkp-bmenu-state-file' is nil"))
2682   (setq bmkp-last-bmenu-state-file  (prog1 bmkp-bmenu-state-file
2683                                       (setq bmkp-bmenu-state-file  bmkp-last-bmenu-state-file)))
2684   (message (if bmkp-bmenu-state-file
2685                "Autosaving of bookmark list state is now ON"
2686              "Autosaving of bookmark list state is now OFF")))
2687
2688 ;;;###autoload
2689 (defun bmkp-save-menu-list-state ()     ; Used in `bookmark-exit-hook-internal'.
2690   "Save menu-list state, unless not saving or list has not yet been shown.
2691 The state is saved to the value of `bmkp-bmenu-state-file'."
2692   (interactive)
2693   (when (and (not bmkp-bmenu-first-time-p) bmkp-bmenu-state-file)
2694     (let ((config-list
2695            `((last-sort-comparer                    . ,bmkp-sort-comparer)
2696              (last-reverse-sort-p                   . ,bmkp-reverse-sort-p)
2697              (last-reverse-multi-sort-p             . ,bmkp-reverse-multi-sort-p)
2698              (last-latest-bookmark-alist            . ,(bmkp-maybe-unpropertize-bookmark-names
2699                                                         bmkp-latest-bookmark-alist))
2700              (last-bmenu-omitted-bookmarks          . ,(bmkp-maybe-unpropertize-bookmark-names
2701                                                         bmkp-bmenu-omitted-bookmarks))
2702              (last-bmenu-marked-bookmarks           . ,(bmkp-maybe-unpropertize-bookmark-names
2703                                                         bmkp-bmenu-marked-bookmarks))
2704              (last-bmenu-filter-function            . ,bmkp-bmenu-filter-function)
2705              (last-bmenu-filter-pattern             . ,bmkp-bmenu-filter-pattern)
2706              (last-bmenu-title                      . ,bmkp-bmenu-title)
2707              (last-bmenu-bookmark                   . ,(and (get-buffer "*Bookmark List*")
2708                                                             (with-current-buffer
2709                                                                 (get-buffer "*Bookmark List*")
2710                                                               (bookmark-bmenu-bookmark))))
2711              (last-specific-buffer                  . ,bmkp-last-specific-buffer)
2712              (last-specific-file                    . ,bmkp-last-specific-file)
2713              (last-bmenu-toggle-filenames           . ,bookmark-bmenu-toggle-filenames)
2714              (last-bmenu-before-hide-marked-alist   . ,(bmkp-maybe-unpropertize-bookmark-names
2715                                                         bmkp-bmenu-before-hide-marked-alist))
2716              (last-bmenu-before-hide-unmarked-alist . ,(bmkp-maybe-unpropertize-bookmark-names
2717                                                         bmkp-bmenu-before-hide-unmarked-alist))
2718              (last-bookmark-file                    . ,(convert-standard-filename
2719                                                         (expand-file-name
2720                                                          bmkp-current-bookmark-file))))))
2721       (with-current-buffer (get-buffer-create " *Menu-List State*")
2722         (goto-char (point-min))
2723         (delete-region (point-min) (point-max))
2724         (let ((print-length  nil)
2725               (print-level   nil)
2726               (print-circle  t))
2727           (pp config-list (current-buffer)))
2728         (condition-case nil
2729             (write-region (point-min) (point-max) bmkp-bmenu-state-file)
2730           (file-error (message "Cannot write `%s'" bmkp-bmenu-state-file)))
2731         (kill-buffer (current-buffer))))))
2732
2733 ;;;###autoload
2734 (defun bmkp-toggle-saving-bookmark-file () ; Bound to `M-~' in bookmark list
2735   "Toggle the value of option `bookmark-save-flag'.
2736 If the initial value of `bookmark-save-flag' is nil, then this
2737 command has no effect."
2738   (interactive)
2739   (unless (or bmkp-last-save-flag-value bookmark-save-flag)
2740     (error "Cannot toggle: initial value of `bookmark-save-flag' is nil"))
2741   (setq bmkp-last-save-flag-value  (prog1 bookmark-save-flag
2742                                      (setq bookmark-save-flag  bmkp-last-save-flag-value)))
2743   (message (if bookmark-save-flag
2744                "Autosaving of current bookmark file is now ON"
2745              "Autosaving of current bookmark file is now OFF")))
2746
2747 ;;;###autoload
2748 (defun bmkp-make-function-bookmark (bookmark-name function) ; Not bound
2749   "Create a bookmark that invokes FUNCTION when \"jumped\" to.
2750 You are prompted for the bookmark name and the name of the function.
2751 Returns the new bookmark (internal record)."
2752   (interactive
2753    (let ((icicle-unpropertize-completion-result-flag  t))
2754      (list (read-string "Bookmark: ")
2755            (completing-read "Function: " obarray 'functionp))))
2756   (bookmark-store bookmark-name `((filename . ,bmkp-non-file-filename)
2757                                   (position . 0)
2758                                   (function . ,(read function))
2759                                   (handler  . bmkp-jump-function))
2760                   nil)
2761   (let ((new  (bookmark-get-bookmark bookmark-name 'noerror)))
2762     (unless (memq new bmkp-latest-bookmark-alist)
2763       (setq bmkp-latest-bookmark-alist  (cons new bmkp-latest-bookmark-alist)))
2764     (bookmark-bmenu-surreptitiously-rebuild-list)
2765     new))
2766
2767 ;;;###autoload
2768 (defun bmkp-switch-bookmark-file (file &optional no-msg) ; Bound to `L' in bookmark list
2769   "Switch to a different bookmark file, FILE.
2770 Replace all currently existing bookmarks with the newly loaded
2771 bookmarks.  Change the value of `bmkp-current-bookmark-file' to FILE,
2772 so bookmarks will subsequently be saved to FILE.
2773
2774 `bookmark-default-file' is unaffected, so your next Emacs session will
2775 still use `bookmark-default-file' for the initial set of bookmarks."
2776   (interactive
2777    (list
2778     (let ((bfile  (if (bmkp-same-file-p bmkp-current-bookmark-file
2779                                         bmkp-last-bookmark-file)
2780                       bookmark-default-file
2781                     bmkp-last-bookmark-file)))
2782       (bmkp-read-bookmark-file-name "Switch to bookmark file: "
2783                                     (or (file-name-directory bfile) "~/")
2784                                     bfile
2785                                     'CONFIRM))))
2786   (bookmark-load file t no-msg))
2787
2788 ;;;###autoload
2789 (defun bmkp-switch-to-last-bookmark-file (&optional no-msg) ; Not bound
2790   "Switch back to the last-used bookmarkfile.
2791 Replace all currently existing bookmarks with those newly loaded from
2792 the last-used file.  Swap the values of `bmkp-last-bookmark-file' and
2793 `bmkp-current-bookmark-file'."
2794   (interactive)
2795   (bookmark-load (or bmkp-last-bookmark-file bookmark-default-file) t no-msg))
2796
2797 ;;;###autoload
2798 (defun bmkp-use-bookmark-file-create (file) ; Not bound
2799   "Switch current bookmark file to FILE, creating it if it does not exist.
2800 Interactively, you are prompted for the file name.  The default is
2801 `.emacs.bmk' in the current directory, but you can enter any file
2802 name, anywhere.
2803
2804 If there is no file with the name you provide then a new, an empty
2805 bookmark file with that name is created.
2806
2807 You are prompted to confirm the bookmark-file switch.
2808
2809 Returns FILE."
2810   (interactive (list (bmkp-read-bookmark-file-name)))
2811   (unless (file-readable-p file) (bmkp-empty-file file))
2812   (when (y-or-n-p (format "Use `%s' as the current bookmark file? " file))
2813     (bmkp-switch-bookmark-file file))
2814   file)
2815
2816 (defun bmkp-read-bookmark-file-name (&optional prompt dir default-filename require-match)
2817   "Read and return an (absolute) bookmark file name.
2818 PROMPT is the prompt to use (default: \"Use bookmark file: \").
2819 The other args are the same as for `read-file-name'."
2820   (let ((insert-default-directory  t))
2821     (expand-file-name
2822      (read-file-name (or prompt "Use bookmark file: ")
2823                      dir
2824                      (or default-filename
2825                          (if (> emacs-major-version 22)
2826                              (list ".emacs.bmk" bookmark-default-file)
2827                            ".emacs.bmk"))
2828                      require-match))))
2829
2830 ;;;###autoload
2831 (defun bmkp-empty-file (file)           ; Bound to `C-x p 0'
2832   "Empty the bookmark file FILE, or create FILE (empty) if it does not exist.
2833 In either case, FILE will become an empty bookmark file.  Return FILE.
2834
2835 NOTE: If FILE already exists and you confirm emptying it, no check is
2836       made that it is in fact a bookmark file before emptying it.
2837       It is simply replaced by an empty bookmark file and saved.
2838
2839 This does NOT also make FILE the current bookmark file.  To do that,
2840 use `\\[bmkp-switch-bookmark-file]' (`bmkp-switch-bookmark-file')."
2841   (interactive (list (read-file-name "Create empty bookmark file: " "~/")))
2842   (setq file  (expand-file-name file))
2843   (bookmark-maybe-load-default-file)
2844   (when (and (file-exists-p file)
2845              (not (y-or-n-p (format "CONFIRM: Empty the existing file `%s'? " file))))
2846     (error "OK, cancelled"))
2847   (let ((bookmark-alist  ()))
2848     (bookmark-write-file file (if (file-exists-p file)
2849                                   "Emptying bookmark file `%s'..."
2850                                 "Creating new, empty bookmark file `%s'...")))
2851   file)
2852
2853 ;;;###autoload
2854 (defun bmkp-crosshairs-highlight ()     ; Not bound
2855   "Call `crosshairs-highlight', unless the region is active.
2856 You can add this to hook `bookmark-after-jump-hook'.
2857 You need library `crosshairs.el' to use this command."
2858   (interactive)
2859   (when (> emacs-major-version 21)      ; No-op for Emacs 20-21.
2860     (unless (condition-case nil (require 'crosshairs nil t) (error nil))
2861       (error "You need library `crosshairs.el' to use this command"))
2862     (unless mark-active
2863       (let ((crosshairs-overlay-priority  (and (boundp 'bmkp-light-priorities)
2864                                                (1+ (apply #'max
2865                                                           (mapcar #'cdr bmkp-light-priorities))))))
2866         (crosshairs-highlight)))))
2867
2868 ;;;###autoload
2869 (defun bmkp-choose-navlist-from-bookmark-list (bookmark-name &optional alist) ; Bound to `C-x p B'
2870   "Choose a bookmark-list bookmark and set the bookmark navigation list.
2871 The navigation-list variable, `bmkp-nav-alist', is set to the list of
2872 bookmarks that would be displayed by `bookmark-bmenu-list' (`C-x r l')
2873 for the chosen bookmark-list bookmark, sorted and filtered as
2874 appropriate.
2875
2876 Instead of choosing a bookmark-list bookmark, you can choose the
2877 pseudo-bookmark `CURRENT *Bookmark List*'.  The bookmarks used for the
2878 navigation list are those that would be currently shown in the
2879 `*Bookmark List*' (even if the list is not currently displayed)."
2880   (interactive
2881    (let ((bookmark-alist  (cons (bmkp-current-bookmark-list-state) (bmkp-bookmark-list-alist-only))))
2882      (list (bmkp-read-bookmark-for-type "bookmark-list " bookmark-alist nil nil
2883                                         'bmkp-bookmark-list-history "Choose ")
2884            bookmark-alist)))
2885   (let ((state  (let ((bookmark-alist  (or alist (cons (bmkp-current-bookmark-list-state)
2886                                                        (bmkp-bookmark-list-alist-only)))))
2887                   (bookmark-prop-get bookmark-name 'bookmark-list))))
2888     (let ((bmkp-sort-comparer               (cdr (assq 'last-sort-comparer              state)))
2889           (bmkp-reverse-sort-p              (cdr (assq 'last-reverse-sort-p             state)))
2890           (bmkp-reverse-multi-sort-p        (cdr (assq 'last-reverse-multi-sort-p       state)))
2891           (bmkp-bmenu-omitted-bookmarks     (cdr (assq 'last-bmenu-omitted-bookmarks    state)))
2892           (bmkp-bmenu-filter-function       (cdr (assq 'last-bmenu-filter-function      state)))
2893           (bmkp-bmenu-filter-pattern        (or (cdr (assq 'last-bmenu-filter-pattern   state)) ""))
2894           (bmkp-bmenu-title                 (cdr (assq 'last-bmenu-title                state)))
2895           (bookmark-bmenu-toggle-filenames  (cdr (assq 'last-bmenu-toggle-filenames     state))))
2896       (setq bmkp-nav-alist             (bmkp-sort-omit
2897                                         (if bmkp-bmenu-filter-function
2898                                             (funcall bmkp-bmenu-filter-function)
2899                                           (if (string= "CURRENT *Bookmark List*" bookmark-name)
2900                                               (or bmkp-latest-bookmark-alist bookmark-alist)
2901                                             bookmark-alist))
2902                                         (and (not (eq bmkp-bmenu-filter-function
2903                                                       'bmkp-omitted-alist-only))
2904                                              bmkp-bmenu-omitted-bookmarks))
2905             bmkp-current-nav-bookmark  (car bmkp-nav-alist))))
2906   (message "Bookmark navigation list is now %s"
2907            (if (and (string= "CURRENT *Bookmark List*" bookmark-name)
2908                     (not (get-buffer "*Bookmark List*")))
2909                "the global bookmark list"
2910              (format "`%s'" bookmark-name))))
2911
2912 (defun bmkp-current-bookmark-list-state ()
2913   "Pseudo-bookmark for the current `*Bookmark List*' state."
2914   (bookmark-bmenu-surreptitiously-rebuild-list)
2915   (cons "CURRENT *Bookmark List*" (bmkp-make-bookmark-list-record)))
2916
2917 ;;;###autoload
2918 (defun bmkp-choose-navlist-of-type (type) ; Bound to `C-x p :'
2919   "Set the bookmark navigation list to the bookmarks of a type you choose.
2920 The pseudo-type `any' sets the navigation list to all bookmarks.
2921 This sets variable `bmkp-nav-alist'."
2922   (interactive
2923    (let* ((completion-ignore-case                      t)
2924           (icicle-unpropertize-completion-result-flag  t)
2925           (type                                        (completing-read "Type: "
2926                                                                         (cons '("any" . bookmark-history)
2927                                                                               bmkp-types-alist)
2928                                                                         nil t nil nil "any")))
2929      (list type)))
2930   (setq bmkp-nav-alist  (if (equal "any" type)
2931                             bookmark-alist
2932                           (funcall (intern (format "bmkp-%s-alist-only" type)))))
2933   (unless bmkp-nav-alist (error "No bookmarks"))
2934   (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist))
2935   (message "Bookmark navigation list is now %s"
2936            (if (equal "any" type) "all bookmarks" (format "for type `%s'" type))))
2937
2938 (defun bmkp-autonamed-bookmark-p (bookmark)
2939   "Return non-nil if BOOKMARK is a (valid) autonamed bookmark.
2940 BOOKMARK is a bookmark name or a bookmark record."
2941   (setq bookmark  (bookmark-get-bookmark bookmark 'NOERROR))
2942   (if (not bookmark)
2943       nil
2944     (string-match (format bmkp-autoname-format ".*")
2945                   (bookmark-name-from-full-record bookmark))))
2946
2947 (defun bmkp-autonamed-bookmark-for-buffer-p (bookmark buffer-name)
2948   "Return non-nil if BOOKMARK is a (valid) autonamed bookmark for BUFFER.
2949 BOOKMARK is a bookmark name or a bookmark record.
2950 BUFFER-NAME is a string matching the buffer-name part of an autoname."
2951   (setq bookmark  (bookmark-get-bookmark bookmark 'NOERROR))
2952   (if (not bookmark)
2953       nil
2954     (string-match (format bmkp-autoname-format (regexp-quote buffer-name))
2955                   (bookmark-name-from-full-record bookmark))))
2956
2957 (defun bmkp-update-autonamed-bookmark (bookmark)
2958   "Update the name and position of the autonamed BOOKMARK at point.
2959 BOOKMARK is a bookmark name or a bookmark record.
2960 Return the updated BOOKMARK: If input was a bookmark name, then return
2961  then new name, else the new (full) bookmark.
2962 It is a good idea to set BOOKMARK to the result of this call."
2963   (let ((namep  (stringp bookmark)))
2964     (setq bookmark  (bookmark-get-bookmark bookmark))
2965     (bookmark-set-position bookmark (point))
2966     ;; Autonamed bookmarks do not have regions.  Update `end-position' to be the same as `position'.
2967     (when (bmkp-get-end-position bookmark)
2968       (bookmark-prop-set bookmark 'end-position (point)))
2969     (let ((newname  (funcall bmkp-autoname-bookmark-function (point))))
2970       (bookmark-rename (bookmark-name-from-full-record bookmark) newname 'batch)
2971       (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
2972         (with-current-buffer (get-buffer "*Bookmark List*")
2973           (bmkp-refresh-menu-list (bookmark-name-from-full-record bookmark)))) ; So display new name.
2974       (bmkp-maybe-save-bookmarks))
2975     (if namep (bookmark-name-from-full-record bookmark) bookmark))) ; Return updated bookmark or name.
2976
2977 ;;;###autoload
2978 (defun bmkp-this-buffer-bmenu-list ()   ; Bound to `C-x p .'
2979   "Show the bookmark list just for bookmarks for the current buffer.
2980 Set `bmkp-last-specific-buffer' to the current buffer name."
2981   (interactive)
2982   (setq bmkp-last-specific-buffer   (buffer-name)
2983         bmkp-bmenu-filter-function  'bmkp-last-specific-buffer-alist-only
2984         bmkp-bmenu-title            (format "Buffer `%s' Bookmarks" bmkp-last-specific-buffer))
2985   (let ((bookmark-alist         (funcall bmkp-bmenu-filter-function))
2986         (bmkp-bmenu-state-file  nil))   ; Prevent restoring saved state.
2987     (unless bookmark-alist (error "No bookmarks for buffer `%s'" bmkp-last-specific-buffer))
2988     (setq bmkp-latest-bookmark-alist  bookmark-alist)
2989     (pop-to-buffer (get-buffer-create "*Bookmark List*"))
2990     (bookmark-bmenu-list 'filteredp))
2991   (when (interactive-p)
2992     (bmkp-msg-about-sort-order (bmkp-current-sort-order)
2993                                (format "Only bookmarks for buffer `%s' are shown"
2994                                        bmkp-last-specific-buffer)))
2995   (raise-frame))
2996
2997 ;;;###autoload
2998 (defun bmkp-navlist-bmenu-list ()       ; Bound to `C-x p N'
2999   "Show the bookmark list just for bookmarks from the navigation list."
3000   (interactive)
3001   (unless bmkp-nav-alist
3002     (bookmark-maybe-load-default-file)
3003     (setq bmkp-nav-alist  bookmark-alist)
3004     (unless bmkp-nav-alist (error "No bookmarks"))
3005     (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist))
3006     (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
3007   (setq bmkp-bmenu-title  "Current Navlist Bookmarks")
3008   (let ((bookmark-alist         bmkp-nav-alist)
3009         (bmkp-bmenu-state-file  nil))   ; Prevent restoring saved state.
3010     (unless bookmark-alist (error "No bookmarks"))
3011     (setq bmkp-latest-bookmark-alist  bookmark-alist)
3012     (pop-to-buffer (get-buffer-create "*Bookmark List*"))
3013     (bookmark-bmenu-list 'filteredp))
3014   (when (interactive-p)
3015     (bmkp-msg-about-sort-order (bmkp-current-sort-order)
3016                                "Only bookmarks for the navigation list are shown"))
3017   (raise-frame))
3018
3019 (defun bmkp-completing-read-buffer-name (&optional no-default-p)
3020   "Read the name of a buffer associated with a bookmark.
3021 The candidates are the buffers in `bmkp-buffer-names'.
3022 Non-nil NO-DEFAULT-P means provide no default value.  Used when
3023  called in a loop, to let the user exit using empty input.
3024 If NO-DEFAULT-P is nil, then the default is the current buffer's name,
3025  or the value of `bmkp-last-specific-buffer' if the current buffer has
3026  no bookmarks."
3027   (bookmark-maybe-load-default-file)
3028   (let ((icicle-unpropertize-completion-result-flag  t))
3029     (completing-read "Buffer: " (mapcar #'list (bmkp-buffer-names)) nil t nil 'buffer-name-history
3030                      (and (not no-default-p)
3031                           (if (member (buffer-name) (bmkp-buffer-names))
3032                               (buffer-name)
3033                             bmkp-last-specific-buffer)))))
3034
3035 (defun bmkp-completing-read-file-name (&optional no-default-p)
3036   "Read the name of a file associated with a bookmark.
3037 The candidates are the absolute file names in `bmkp-file-names'.
3038 Non-nil NO-DEFAULT-P means provide no default value.  Used when
3039  called in a loop, to let the user exit using empty input.
3040 If NO-DEFAULT-P is nil, then the default is the current buffer's file
3041  name, if any, or the value of `bmkp-last-specific-file' if the
3042  current buffer has no associated file or the file has no bookmarks."
3043   (bookmark-maybe-load-default-file)
3044   (let ((completion-ignore-case                      (if (boundp 'read-file-name-completion-ignore-case)
3045                                                          read-file-name-completion-ignore-case
3046                                                        (memq system-type
3047                                                              '(ms-dos windows-nt darwin cygwin))))
3048         (icicle-unpropertize-completion-result-flag  t))
3049     (completing-read "File: " (mapcar #'list (bmkp-file-names)) nil t nil 'file-name-history
3050                      (and (not no-default-p)
3051                           (let ((file  (buffer-file-name)))
3052                             (if (and file (member file (bmkp-file-names)))
3053                                 file
3054                               bmkp-last-specific-file))))))
3055
3056 (defun bmkp-refresh-menu-list (&optional bookmark)
3057   "Refresh (revert) the bookmark list (\"menu list\").
3058 This brings the displayed list up to date.  It does not change the
3059 current filtering or sorting of the displayed list.
3060 Non-nil optional arg BOOKMARK means move cursor to BOOKMARK's line."
3061   (unless (stringp bookmark) (setq bookmark  (bookmark-name-from-full-record bookmark)))
3062   (let ((bookmark-alist  (if bmkp-bmenu-filter-function
3063                              (funcall bmkp-bmenu-filter-function)
3064                            bookmark-alist)))
3065     (setq bmkp-latest-bookmark-alist  bookmark-alist)
3066     (bookmark-bmenu-list 'filteredp)
3067     (when bookmark
3068       (with-current-buffer (get-buffer "*Bookmark List*")
3069         (bmkp-bmenu-goto-bookmark-named bookmark)
3070         (let ((bmenu-win  (get-buffer-window (current-buffer) 0)))
3071           (when bmenu-win (set-window-point bmenu-win (point))))))))
3072
3073 ;;;###autoload
3074 (defun bmkp-unomit-all ()               ; Bound to `O U' in bookmark list
3075   "Remove all bookmarks from the list of omitted bookmarks.
3076 All bookmarks will henceforth be available for display."
3077   (interactive)
3078   (unless bmkp-bmenu-omitted-bookmarks (error "No omitted bookmarks to UN-omit"))
3079   (message "UN-omitting ALL omitted bookmarks...")
3080   (let ((count  0))
3081     (dolist (bmk-name  bmkp-bmenu-omitted-bookmarks)
3082       (setq bmkp-bmenu-omitted-bookmarks  (bmkp-delete-bookmark-name-from-list
3083                                            bmk-name bmkp-bmenu-omitted-bookmarks)
3084             count                         (1+ count)))
3085     (bookmark-bmenu-surreptitiously-rebuild-list)
3086     (message "UN-omitted %d bookmarks" count))
3087   (when (equal (buffer-name (current-buffer)) "*Bookmark List*") (bmkp-bmenu-show-all))
3088   (when (and (fboundp 'fit-frame-if-one-window)
3089              (equal (buffer-name (current-buffer)) "*Bookmark List*"))
3090     (fit-frame-if-one-window)))
3091
3092 (defun bmkp-omitted-alist-only ()
3093   "`bookmark-alist', filtered to retain only the omitted bookmarks.
3094 A new list is returned (no side effects)."
3095   (bookmark-maybe-load-default-file)
3096   (bmkp-remove-if-not #'bmkp-omitted-bookmark-p bookmark-alist))
3097
3098 (defun bmkp-omitted-bookmark-p (bookmark)
3099   "Return non-nil if BOOKMARK is an omitted bookmark.
3100 BOOKMARK is a bookmark name or a bookmark record."
3101   (unless (stringp bookmark) (setq bookmark  (bookmark-name-from-full-record bookmark)))
3102   (bmkp-bookmark-name-member bookmark bmkp-bmenu-omitted-bookmarks))
3103
3104
3105 ;;(@* "Search-and-Replace Locations of Marked Bookmarks")
3106 ;;  *** Search-and-Replace Locations of Marked Bookmarks ***
3107
3108 (when (> emacs-major-version 22)
3109   (defvar bmkp-isearch-bookmarks nil
3110     "Bookmarks whose locations are to be incrementally searched.")
3111
3112   (defun bmkp-isearch-next-bookmark-buffer (&optional bookmark wrap)
3113     "Return the next buffer in a series of bookmark buffers.
3114 Used as a value for `multi-isearch-next-buffer-function', for Isearch
3115 of multiple bookmarks.
3116
3117 Variable `bmkp-isearch-bookmarks' is a list of bookmarks.  Each
3118 bookmark in the list is visited by `bookmark--jump-via', and the
3119 corresponding bookmark buffer is returned."
3120     (let ((bookmarks  (if isearch-forward bmkp-isearch-bookmarks (reverse bmkp-isearch-bookmarks))))
3121       (bookmark--jump-via
3122        (if wrap
3123            (car bookmarks)
3124          (let ((this-bmk  (catch 'bmkp-isearch-next-bookmark-buffer
3125                             (dolist (bmk  bookmarks)
3126                               (when (if (bmkp-get-buffer-name bmk)
3127                                         (equal (bmkp-get-buffer-name bmk) (buffer-name))
3128                                       (equal (bookmark-get-filename bmk) (buffer-file-name)))
3129                                 (throw 'bmkp-isearch-next-bookmark-buffer bmk)))
3130                             (car bookmarks))))
3131            (cadr (member this-bmk bookmarks))))
3132        'ignore)
3133       (current-buffer)))
3134
3135   (defun bmkp-isearch-bookmarks (bookmarks)
3136     "Start multi-bookmark Isearch on BOOKMARKS."
3137     (let ((multi-isearch-next-buffer-function  'bmkp-isearch-next-bookmark-buffer)
3138           (bmkp-isearch-bookmarks              bookmarks))
3139       (bookmark-jump (car bookmarks))
3140       (goto-char (if isearch-forward (point-min) (point-max)))
3141       (isearch-forward)))
3142
3143   (defun bmkp-isearch-bookmarks-regexp (bookmarks)
3144     "Start multi-bookmark regexp Isearch on BOOKMARKS."
3145     (let ((multi-isearch-next-buffer-function  'bmkp-isearch-next-bookmark-buffer)
3146           (bmkp-isearch-bookmarks              bookmarks))
3147       (bookmark-jump (car bookmarks))
3148       (goto-char (if isearch-forward (point-min) (point-max)))
3149       (isearch-forward-regexp))))
3150
3151
3152 ;;(@* "Tags")
3153 ;;  *** Tags ***
3154
3155 (defun bmkp-get-tags (bookmark)
3156   "Return the `tags' entry for BOOKMARK.
3157 BOOKMARK is a bookmark name or a bookmark record."
3158   (bookmark-prop-get bookmark 'tags))
3159
3160 (defun bmkp-get-tag-value (bookmark tag &optional msgp)
3161   "Return value of BOOKMARK's tag TAG.
3162 BOOKMARK is a bookmark name or a bookmark record.
3163 TAG is a string.
3164 Return nil if BOOKMARK has no such TAG or if TAG has no value."
3165   (assoc-default tag (bmkp-get-tags bookmark)))
3166
3167 (defun bmkp-has-tag-p (bookmark tag &optional msgp)
3168   "Return non-nil if BOOKMARK is tagged with TAG.
3169 BOOKMARK is a bookmark name or a bookmark record.
3170 TAG is a string."
3171   (assoc-default tag (bmkp-get-tags bookmark) nil t))
3172
3173 ;; NOT USED currently - we use `bmkp-read-tags-completing' instead.
3174 (defun bmkp-read-tags ()
3175   "Read tags one by one, and return them as a list."
3176   (let ((tag    (read-string "Tag (RET for each, empty input to finish): "))
3177         (btags  ()))
3178     (while (not (string= "" tag))
3179       (push tag btags)
3180       (setq tag  (read-string "Tag: ")))
3181     btags))
3182
3183 (defun bmkp-read-tag-completing (&optional prompt candidate-tags require-match update-tags-alist-p)
3184   "Read a tag with completion, and return it as a string.
3185 PROMPT is the prompt string.  If nil, then use \"Tag: \".
3186 CANDIDATE-TAGS is an alist of tags to use for completion.
3187  If nil, then all tags from all bookmarks are used for completion.
3188  The set of all tags is taken from variable `bmkp-tags-alist'.
3189 REQUIRE-MATCH is passed to `completing-read'.
3190 Non-nil UPDATE-TAGS-ALIST-P means update var `bmkp-tags-alist'."
3191   (bookmark-maybe-load-default-file)
3192   (let ((cand-tags                                   (copy-sequence
3193                                                       (or candidate-tags
3194                                                           (and (not update-tags-alist-p)
3195                                                                bmkp-tags-alist) ; Use cached list.
3196                                                           (bmkp-tags-list)))) ; Update the cache.
3197         (icicle-unpropertize-completion-result-flag  t))
3198     (completing-read (or prompt "Tag: ") cand-tags nil require-match nil 'bmkp-tag-history)))
3199
3200 (defun bmkp-read-tags-completing (&optional candidate-tags require-match update-tags-alist-p)
3201   "Read tags with completion, and return them as a list of strings.
3202 Reads tags one by one, until you hit `RET' twice consecutively.
3203 CANDIDATE-TAGS is an alist of tags to use for completion.
3204  If nil, then all tags from all bookmarks are used for completion.
3205  The set of all tags is taken from variable `bmkp-tags-alist'.
3206 REQUIRE-MATCH is passed to `completing-read'.
3207 Non-nil UPDATE-TAGS-ALIST-P means update var `bmkp-tags-alist'."
3208   (bookmark-maybe-load-default-file)
3209   (let ((cands                                       ())
3210         (btags                                       ())
3211         (prompt1                                     "Tag (RET for each, empty input to finish): ")
3212         (prompt2                                     "Tag: ")
3213         (icicle-unpropertize-completion-result-flag  t)
3214         tag old-tag)
3215     ;; Make a new candidates alist, with just one entry per tag name.  The original cdr is discarded.
3216     (dolist (full-tag  (or candidate-tags
3217                            (and (not update-tags-alist-p) bmkp-tags-alist) ; Use cached list.
3218                            (bmkp-tags-list)))
3219       (add-to-list 'cands (list (if (consp full-tag) (car full-tag) full-tag))))
3220     (setq tag    (completing-read prompt1 cands nil require-match nil 'bmkp-tag-history)
3221           cands  (delete (assoc tag cands) cands)) ; Tag read is no longer a candidate.
3222     (while (not (string= "" tag))
3223       (if (member tag btags)            ; User can enter it more than once, if not REQUIRE-MATCH.
3224           (message "Tag `%s' already included" tag)
3225         (push tag btags))               ; But we only add it once.
3226       (setq tag    (completing-read prompt2 cands nil require-match nil 'bmkp-tag-history)
3227             cands  (delete (assoc tag cands) cands)))
3228     (nreverse btags)))
3229
3230 ;;;###autoload
3231 (defun bmkp-list-all-tags (fullp)       ; Bound to `T l' in bookmark list, `C-x p t l' elsewhere
3232   "List all tags used for any bookmarks.
3233 With a prefix argument, list the full alist of tags.
3234 Otherwise, list only the tag names.
3235
3236 Note that when the full alist is shown, the same tag name will appear
3237 once for each of its different values.
3238
3239 Show list in minibuffer or, if not enough space, buffer `*All Tags*'."
3240   (interactive "P")
3241   (require 'pp)
3242   (pp-display-expression (bmkp-tags-list (not fullp)) "*All Tags"))
3243   
3244 (defun bmkp-tags-list (&optional names-only-p)
3245   "Current list of all tags, from all bookmarks.
3246 Non-nil NAMES-ONLY-P means return a list of only the tag names.
3247 Otherwise, return an alist of the full tags and set variable
3248 `bmkp-tags-alist' to that alist, as a cache."
3249   (bookmark-maybe-load-default-file)
3250   (let ((tags  ())
3251         bmk-tags)
3252     (dolist (bmk  bookmark-alist)
3253       (setq bmk-tags  (bmkp-get-tags bmk))
3254       (dolist (tag  bmk-tags)
3255         (add-to-list 'tags (if names-only-p (bmkp-tag-name tag) (bmkp-full-tag tag)))))
3256     (unless names-only-p (setq bmkp-tags-alist  tags))
3257     tags))
3258
3259 (defun bmkp-tag-name (tag)
3260   "Name of TAG.  If TAG is an atom, then TAG, else (car TAG)."
3261   (if (atom tag) tag (car tag)))
3262
3263 (defun bmkp-full-tag (tag)
3264   "Full cons entry for TAG.  If TAG is a cons, then TAG, else (list TAG)."
3265   (if (consp tag) tag (list tag)))
3266
3267 ;; `T 0' in bookmark list, `C-x p t 0' elsewhere.
3268 ;;;###autoload
3269 (defun bmkp-remove-all-tags (bookmark &optional msgp no-cache-update-p)
3270   "Remove all tags from BOOKMARK.
3271 Non-nil optional arg MSGP means display a message about the removal.
3272 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'."
3273   (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3274   (when (and msgp (null (bmkp-get-tags bookmark))) (error "Bookmark has no tags to remove"))
3275   (let ((nb-removed  (and (interactive-p) (length (bmkp-get-tags bookmark)))))
3276     (bookmark-prop-set bookmark 'tags ())
3277     (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3278     (bmkp-maybe-save-bookmarks)
3279     (when (and msgp nb-removed) (message "%d tags removed" nb-removed)))
3280   (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3281     (with-current-buffer (get-buffer "*Bookmark List*")
3282       (bmkp-refresh-menu-list bookmark)))) ; So the `t' markers are removed.
3283
3284 ;; `T +' in bookmark list, `C-x p t + b' elsewhere (`b' for bookmark)
3285 ;;;###autoload
3286 (defun bmkp-add-tags (bookmark tags &optional msgp no-cache-update-p)
3287   "Add TAGS to BOOKMARK.
3288 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3289 You can use completion to enter the bookmark name and each tag.
3290 Completion for the bookmark name is strict.
3291 Completion for tags is lax: you are not limited to existing tags.
3292
3293 TAGS is a list of strings.
3294 Non-nil MSGP means display a message about the addition.
3295 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3296 Return the number of tags added."
3297   (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name))
3298                      (bmkp-read-tags-completing)
3299                      'msg))
3300   (let* ((newtags  (copy-alist (bmkp-get-tags bookmark)))
3301          (olen     (length newtags)))
3302     (dolist (tag  tags)  (unless (or (assoc tag newtags) (member tag newtags))  (push tag newtags)))
3303     (bookmark-prop-set bookmark 'tags newtags)
3304     (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3305     (bmkp-maybe-save-bookmarks)
3306     (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3307       (with-current-buffer (get-buffer "*Bookmark List*")
3308         (bmkp-refresh-menu-list bookmark))) ; So the `t' markers are displayed (updated).
3309     (let ((nb-added  (- (length newtags) olen)))
3310       (when msgp (message "%d tags added. Now: %S" nb-added ; Echo just the tag names.
3311                           (let ((tgs  (mapcar #'bmkp-tag-name newtags)))
3312                             (setq tgs (sort tgs #'string<)))))
3313       nb-added)))
3314
3315 ;; $$$$$$ NOT YET USED
3316 ;;;###autoload
3317 (defun bmkp-set-tag-value-for-navlist (tag value) ; Bound to `C-x p t V'
3318   "Set the value of TAG to VALUE, for each bookmark in the navlist.
3319 If any of the bookmarks has no tag named TAG, then add one with VALUE."
3320   (interactive (list (bmkp-read-tag-completing) (read (read-string "Value: ")) 'msg))
3321   (bmkp-set-tag-value-for-bookmarks bmkp-nav-alist tag value))
3322
3323 ;; $$$$$$ NOT YET USED
3324 (defun bmkp-set-tag-value-for-bookmarks (bookmarks tag value) ; Not bound
3325   "Set the value of TAG to VALUE, for each of the BOOKMARKS.
3326 If any of the BOOKMARKS has no tag named TAG, then add one with VALUE."
3327   (dolist (bmk  bookmarks) (bmkp-set-tag-value bmk tag value)))
3328
3329 ;;;###autoload
3330 (defun bmkp-set-tag-value (bookmark tag value &optional msgp) ; Bound to `C-x p t v'
3331   "For BOOKMARK's TAG, set the value to VALUE.
3332 If BOOKMARK has no tag named TAG, then add one with value VALUE."
3333   (interactive
3334    (let* ((bmk  (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)))
3335           (tag  (bmkp-read-tag-completing "Tag: " (mapcar 'bmkp-full-tag (bmkp-get-tags bmk)))))
3336      (list bmk tag (read (read-string "Value: ")) 'msg)))
3337   (unless (bmkp-has-tag-p bookmark tag) (bmkp-add-tags bookmark (list tag)))
3338   (let* ((newtags     (copy-alist (bmkp-get-tags bookmark)))
3339          (assoc-tag   (assoc tag newtags))
3340          (member-tag  (and (not assoc-tag) (member tag newtags))))
3341     (if assoc-tag (setcdr assoc-tag value) (setcar member-tag (cons (car member-tag) value)))
3342     (bookmark-prop-set bookmark 'tags newtags))
3343   (when msgp "Tag value added"))
3344
3345 ;; `T -' in bookmark list, `C-x p t - b' elsewhere (`b' for bookmark)
3346 ;;;###autoload
3347 (defun bmkp-remove-tags (bookmark tags &optional msgp no-cache-update-p) ; `T -' in bookmark list
3348   "Remove TAGS from BOOKMARK.
3349 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3350 You can use completion to enter the bookmark name and each tag.
3351
3352 TAGS is a list of strings.  The corresponding tags are removed.
3353 Non-nil MSGP means display messages.
3354 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3355 Return the number of tags removed."
3356   (interactive (let ((bmk  (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name))))
3357                  (list bmk
3358                        (bmkp-read-tags-completing (mapcar 'bmkp-full-tag (bmkp-get-tags bmk)) t)
3359                        'msg)))
3360   (let* ((remtags  (copy-alist (bmkp-get-tags bookmark)))
3361          (olen     (length remtags)))
3362     (if (null remtags)
3363         (when msgp (message "Bookmark has no tags to remove")) ; Do nothing if bookmark has no tags.
3364       (setq remtags  (bmkp-remove-if #'(lambda (tag)
3365                                          (if (atom tag) (member tag tags) (member (car tag) tags)))
3366                                      remtags))
3367       (bookmark-prop-set bookmark 'tags remtags)
3368       (unless no-cache-update-p (bmkp-tags-list)) ; Update the tags cache.
3369       (bmkp-maybe-save-bookmarks)
3370       (when (and (get-buffer "*Bookmark List*") (get-buffer-window (get-buffer "*Bookmark List*") 0))
3371         (with-current-buffer (get-buffer "*Bookmark List*")
3372           (bmkp-refresh-menu-list bookmark))) ; So the `t' markers are removed.
3373       (let ((nb-removed  (- olen (length remtags))))
3374         (when msgp (message "%d tags removed. Now: %S" nb-removed ; Echo just the tag names.
3375                             (let ((tgs  (mapcar #'bmkp-tag-name remtags)))
3376                               (setq tgs (sort tgs #'string<)))))
3377         nb-removed))))
3378
3379 ;;;###autoload
3380 (defun bmkp-remove-tags-from-all (tags &optional msgp) ; Bound to `T d' in bookmark list
3381   "Remove TAGS from all bookmarks.
3382 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
3383 You can use completion to enter each tag.
3384 This affects all bookmarks, even those not showing in bookmark list.
3385
3386 TAGS is a list of strings.  The corresponding tags are removed.
3387 Non-nil optional arg MSGP means display a message about the deletion."
3388   (interactive
3389    (if (not (y-or-n-p "Delete the tags you specify from ALL bookmarks? "))
3390        (error "Deletion cancelled")
3391      (list (bmkp-read-tags-completing nil t)  'MSG)))
3392   (dolist (bmk  (bookmark-all-names)) (bmkp-remove-tags bmk tags nil 'NO-CACHE-UPDATE))
3393   (bmkp-tags-list)                      ; Update the tags cache (only once, at end).
3394   (when msgp (message "Tags removed from all bookmarks: %S" tags)))
3395
3396 ;;;###autoload
3397 (defun bmkp-rename-tag (tag newname &optional msgp) ; Bound to `T r' in bookmark list, `C-x p t r' elsewhere
3398   "Rename TAG to NEWNAME in all bookmarks, even those not showing.
3399 Non-nil optional arg MSGP means display a message about the deletion."
3400   (interactive (list (bmkp-read-tag-completing "Tag (old name): ")
3401                      (bmkp-read-tag-completing "New name: ")
3402                      'MSG))
3403   (let ((tag-exists-p  nil))
3404     (dolist (bmk  (bookmark-all-names))
3405       (let ((newtags  (copy-alist (bmkp-get-tags bmk))))
3406         (when newtags
3407           (let* ((assoc-tag   (assoc tag newtags))
3408                  (member-tag  (and (not assoc-tag) (member tag newtags))))
3409             (cond (assoc-tag  (setcar assoc-tag newname))
3410                   (member-tag (setcar member-tag newname)))
3411             (when (or assoc-tag member-tag)
3412               (setq tag-exists-p  t)
3413               (bookmark-prop-set bmk 'tags newtags))))))
3414     (if tag-exists-p
3415         (bmkp-tags-list)                ; Update the tags cache.
3416       (error "No such tag: `%s'" tag))
3417     (when msgp (message "Renamed"))))
3418
3419 ;;;###autoload
3420 (defun bmkp-copy-tags (bookmark &optional msgp) ; `C-x p t c',  `C-x p t M-w'
3421   "Copy tags from BOOKMARK, so you can paste them to another bookmark.
3422 Note that you can copy from a BOOKMARK that has no tags or has an
3423 empty tags list.  In that case, the copied-tags list is empty, so if
3424 you paste it as a replacement then the recipient bookmark will end up
3425 with no tags.
3426
3427 Non-nil optional arg MSGP means display a message about the number of
3428 tags copied."
3429   (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3430   (let ((btags  (bmkp-get-tags bookmark)))
3431     (setq bmkp-copied-tags  (copy-alist btags))
3432     (when msgp (message "%d tags now available for pasting" (length btags)))))
3433
3434 ;;;###autoload
3435 (defun bmkp-paste-add-tags (bookmark &optional msgp no-cache-update-p) ; `C-x p t p',  ; `C-x p t C-y'
3436   "Add tags to BOOKMARK that were previously copied from another bookmark.
3437 The tags are copied from `bmkp-copied-tags'.
3438 Non-nil MSGP means display a message about the addition.
3439 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3440 Return the number of tags added."
3441   (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3442   (unless (listp bmkp-copied-tags)
3443     (error "`bmkp-paste-add-tags': `bmkp-copied-tags' is not a list"))
3444   (bmkp-add-tags bookmark bmkp-copied-tags msgp no-cache-update-p))
3445
3446 ;;;###autoload
3447 (defun bmkp-paste-replace-tags (bookmark &optional msgp no-cache-update-p) ; `C-x p t q'
3448   "Replace tags for BOOKMARK with those copied from another bookmark.
3449 The tags are copied from `bmkp-copied-tags'.
3450 Any previously existing tags for BOOKMARK are lost.
3451 Non-nil MSGP means display a message about the addition.
3452 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
3453 Return the number of tags."
3454   (interactive (list (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name)) 'msg))
3455   (unless (listp bmkp-copied-tags)
3456     (error "`bmkp-paste-replace-tags': `bmkp-copied-tags' is not a list"))
3457   (when (and (bmkp-get-tags bookmark) msgp)
3458     (y-or-n-p "Existing tags will be lost - really replace them? "))
3459   (bmkp-remove-all-tags bookmark msgp no-cache-update-p)
3460   (bmkp-add-tags bookmark bmkp-copied-tags msgp no-cache-update-p))
3461
3462
3463 ;;(@* "Bookmark Predicates")
3464 ;;  *** Bookmark Predicates ***
3465
3466 (defun bmkp-autofile-bookmark-p (bookmark &optional prefix)
3467   "Return non-nil if BOOKMARK is an autofile bookmark.
3468 That means that it is `bmkp-file-bookmark-p' and also its
3469 non-directory file name is the same as the bookmark name.
3470 BOOKMARK is a bookmark name or a bookmark record.
3471
3472 Non-nil optional arg PREFIX means that the bookmark name is actually
3473 expected to be the file name prefixed by PREFIX (a string)."
3474   (setq bookmark  (bookmark-get-bookmark bookmark))
3475   (and (bmkp-file-bookmark-p bookmark)
3476        (let ((fname  (file-name-nondirectory (bookmark-get-filename bookmark))))
3477          (string= (if prefix (concat prefix fname) fname)
3478                   (bookmark-name-from-full-record bookmark)))))
3479
3480 (defun bmkp-bookmark-file-bookmark-p (bookmark)
3481   "Return non-nil if BOOKMARK is a bookmark-file bookmark.
3482 BOOKMARK is a bookmark name or a bookmark record."
3483   (eq (bookmark-get-handler bookmark) 'bmkp-jump-bookmark-file))
3484
3485 (defun bmkp-bookmark-image-bookmark-p (bookmark)
3486   "Return non-nil if BOOKMARK is an image-file bookmark.
3487 BOOKMARK is a bookmark name or a bookmark record."
3488   (or (eq (bookmark-get-handler bookmark) 'image-bookmark-jump)
3489       (and (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
3490            (bmkp-file-bookmark-p bookmark)
3491            (not (bmkp-dired-bookmark-p bookmark))
3492            (if (fboundp 'string-match-p)
3493                (string-match-p (image-file-name-regexp) (bookmark-get-filename bookmark))
3494              (save-match-data
3495                (string-match (image-file-name-regexp) (bookmark-get-filename bookmark)))))))
3496
3497 (defun bmkp-bookmark-list-bookmark-p (bookmark)
3498   "Return non-nil if BOOKMARK is a bookmark-list bookmark.
3499 BOOKMARK is a bookmark name or a bookmark record."
3500   (eq (bookmark-get-handler bookmark) 'bmkp-jump-bookmark-list))
3501
3502 (defun bmkp-desktop-bookmark-p (bookmark)
3503   "Return non-nil if BOOKMARK is a desktop bookmark.
3504 BOOKMARK is a bookmark name or a bookmark record."
3505   (eq (bookmark-get-handler bookmark) 'bmkp-jump-desktop))
3506
3507 ;; Note: To avoid remote access, if bookmark does not have the Dired handler, then we insist
3508 ;; that it be for a local directory.  IOW, we do not include remote directories that were not
3509 ;; bookmarked by Bookmark+ (and so do not have the Dired handler).
3510 (defun bmkp-dired-bookmark-p (bookmark)
3511   "Return non-nil if BOOKMARK is a Dired bookmark.
3512 BOOKMARK is a bookmark name or a bookmark record."
3513   (or (eq (bookmark-get-handler bookmark) 'bmkp-jump-dired)
3514       (bmkp-local-directory-bookmark-p bookmark)))
3515
3516 (defun bmkp-dired-this-dir-bookmark-p (bookmark)
3517   "Return non-nil if BOOKMARK is a Dired bookmark for the `default-directory'.
3518 BOOKMARK is a bookmark name or a bookmark record."
3519   (and (bmkp-dired-bookmark-p bookmark)  (let ((dir  (bookmark-get-filename bookmark)))
3520                                            (bmkp-same-file-p dir default-directory))))
3521
3522 (defun bmkp-file-bookmark-p (bookmark)
3523   "Return non-nil if BOOKMARK bookmarks a file or directory.
3524 BOOKMARK is a bookmark name or a bookmark record.
3525 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3526   (let* ((filename   (bookmark-get-filename bookmark))
3527          (nonfile-p  (equal filename bmkp-non-file-filename))
3528          (handler    (bookmark-get-handler bookmark)))
3529     (and filename (not nonfile-p)
3530          (or (not handler)
3531              (memq handler bmkp-file-bookmark-handlers)
3532              (equal handler (bmkp-default-handler-for-file filename)))
3533          (not (and (bookmark-prop-get bookmark 'info-node)))))) ; Emacs 20-21 Info: no handler.
3534
3535 (defun bmkp-file-this-dir-bookmark-p (bookmark)
3536   "Return non-nil if BOOKMARK bookmarks file/subdir in `default-directory'.
3537 BOOKMARK is a bookmark name or a bookmark record.
3538 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3539   (and (bmkp-file-bookmark-p bookmark)
3540        (equal (file-name-directory (bookmark-get-filename bookmark)) default-directory)))
3541
3542 (defun bmkp-function-bookmark-p (bookmark)
3543   "Return non-nil if BOOKMARK is a function bookmark.
3544 BOOKMARK is a bookmark name or a bookmark record."
3545   (eq (bookmark-get-handler bookmark) 'bmkp-jump-function))
3546
3547 (defun bmkp-gnus-bookmark-p (bookmark)
3548   "Return non-nil if BOOKMARK is a Gnus bookmark.
3549 BOOKMARK is a bookmark name or a bookmark record."
3550   (memq (bookmark-get-handler bookmark)
3551         '(gnus-summary-bookmark-jump bmkp-jump-gnus bmkext-jump-gnus)))
3552
3553 (defun bmkp-info-bookmark-p (bookmark)
3554   "Return non-nil if BOOKMARK is an Info bookmark.
3555 BOOKMARK is a bookmark name or a bookmark record."
3556   (or (eq (bookmark-get-handler bookmark) 'Info-bookmark-jump)
3557       (and (not (bookmark-get-handler bookmark))
3558            (or (string= "*info*" (bmkp-get-buffer-name bookmark))
3559                (bookmark-prop-get bookmark 'info-node))))) ; Emacs 20-21 - no `buffer-name' entry.
3560
3561 (defun bmkp-local-directory-bookmark-p (bookmark)
3562   "Return non-nil if BOOKMARK bookmarks a local directory.
3563 BOOKMARK is a bookmark name or a bookmark record."
3564   (let ((file  (bookmark-get-filename bookmark)))
3565     (and (bmkp-local-file-bookmark-p bookmark) (file-directory-p file))))
3566
3567 (defun bmkp-local-file-bookmark-p (bookmark)
3568   "Return non-nil if BOOKMARK bookmarks a local file or directory.
3569 BOOKMARK is a bookmark name or a bookmark record.
3570 This excludes bookmarks of a more specific kind (e.g. Info, Gnus)."
3571   (and (bmkp-file-bookmark-p bookmark) (not (bmkp-remote-file-bookmark-p bookmark))))
3572
3573 (defun bmkp-man-bookmark-p (bookmark)
3574   "Return non-nil if BOOKMARK is a `man' page bookmark.
3575 BOOKMARK is a bookmark name or a bookmark record."
3576   (memq (bookmark-get-handler bookmark) '(bmkp-jump-man bmkp-jump-woman
3577                                           bmkext-jump-man bmkext-jump-woman)))
3578
3579 (defun bmkp-marked-bookmark-p (bookmark)
3580   "Return non-nil if BOOKMARK is a marked bookmark.
3581 BOOKMARK is a bookmark name or a bookmark record."
3582   (unless (stringp bookmark) (setq bookmark  (car bookmark)))
3583   (bmkp-bookmark-name-member bookmark bmkp-bmenu-marked-bookmarks))
3584
3585 (defun bmkp-non-file-bookmark-p (bookmark)
3586   "Return non-nil if BOOKMARK is a non-file bookmark (e.g *scratch*).
3587 This excludes bookmarks of a more specific kind (e.g. Info, Gnus).
3588 It includes bookmarks to existing buffers, as well as bookmarks
3589 defined for buffers that do not currently exist."
3590   (let* ((filename   (bookmark-get-filename bookmark))
3591          (nonfile-p  (equal filename bmkp-non-file-filename))) 
3592     (and (bmkp-get-buffer-name bookmark)
3593          (or (not filename) nonfile-p
3594              ;; Ensure not remote before calling `file-exists-p'.  (Do not prompt for password.)
3595              (and (not (bmkp-file-remote-p filename)) (not (file-exists-p filename))))
3596          (not (bookmark-get-handler bookmark)))))
3597
3598 (defun bmkp-region-bookmark-p (bookmark)
3599   "Return non-nil if BOOKMARK has region information.
3600 BOOKMARK is a bookmark name or a bookmark record."
3601   (and (bmkp-get-end-position bookmark)
3602        (/= (bookmark-get-position bookmark) (bmkp-get-end-position bookmark))))
3603
3604 (defun bmkp-remote-file-bookmark-p (bookmark)
3605   "Return non-nil if BOOKMARK bookmarks a remote file or directory.
3606 BOOKMARK is a bookmark name or a bookmark record.
3607 This includes remote Dired bookmarks, but otherwise excludes bookmarks
3608 with handlers (e.g. Info, Gnus)."
3609   (let* ((handler   (bookmark-get-handler bookmark))
3610          (file      (bookmark-get-filename bookmark))
3611          (rem-file  (and file  (bmkp-file-remote-p file))))
3612     (and rem-file  (or (not handler) (eq handler 'bmkp-jump-dired)))))
3613
3614 (defun bmkp-this-buffer-p (bookmark)
3615   "Return non-nil if BOOKMARK's buffer is the current buffer.
3616 But return nil for bookmarks, such as desktops, that are not really
3617 associated with a buffer, even if they have a `buffer-name' entry.
3618 BOOKMARK is a bookmark name or a bookmark record."
3619   (and (equal (bmkp-get-buffer-name bookmark) (buffer-name))
3620        (not (bmkp-desktop-bookmark-p        bookmark))
3621        (not (bmkp-bookmark-file-bookmark-p  bookmark))
3622        (not (bmkp-sequence-bookmark-p       bookmark))
3623        (not (bmkp-function-bookmark-p       bookmark))
3624        (not (bmkp-variable-list-bookmark-p  bookmark))))
3625
3626 (defun bmkp-this-file-p (bookmark)
3627   "Return non-nil if BOOKMARK's filename is the current (absolute) file name.
3628 BOOKMARK is a bookmark name or a bookmark record."
3629   (let ((bmk-file   (bookmark-get-filename bookmark))
3630         (this-file  (or (buffer-file-name)  (and (eq major-mode 'dired-mode)  (if (consp dired-directory)
3631                                                                                   (car dired-directory)
3632                                                                                 dired-directory)))))
3633     (and bmk-file (equal bmk-file this-file))))
3634
3635 (defun bmkp-last-specific-buffer-p (bookmark)
3636   "Return t if BOOKMARK's `buffer-name' is `bmkp-last-specific-buffer'.
3637 But return nil for bookmarks, such as desktops, that are not really
3638 associated with a buffer, even if they have a `buffer-name' entry.
3639 It does not matter whether the buffer exists.
3640 BOOKMARK is a bookmark name or a bookmark record."
3641   (let ((buf  (bmkp-get-buffer-name bookmark)))
3642     (and buf (string= buf bmkp-last-specific-buffer)
3643          (not (bmkp-desktop-bookmark-p        bookmark))
3644          (not (bmkp-bookmark-file-bookmark-p  bookmark))
3645          (not (bmkp-sequence-bookmark-p       bookmark))
3646          (not (bmkp-function-bookmark-p       bookmark))
3647          (not (bmkp-variable-list-bookmark-p  bookmark)))))
3648
3649 (defun bmkp-last-specific-file-p (bookmark)
3650   "Return t if BOOKMARK's `filename' is `bmkp-last-specific-file'.
3651 BOOKMARK is a bookmark name or a bookmark record."
3652   (let ((file  (bookmark-get-filename bookmark)))
3653     (and file (string= file bmkp-last-specific-file))))
3654
3655 (defun bmkp-sequence-bookmark-p (bookmark)
3656   "Return non-nil if BOOKMARK is a sequence bookmark.
3657 BOOKMARK is a bookmark name or a bookmark record."
3658   (eq (bookmark-get-handler bookmark) 'bmkp-jump-sequence))
3659
3660 (defun bmkp-url-bookmark-p (bookmark)
3661   "Return non-nil if BOOKMARK is a URL bookmark.
3662 That means that it satifies either `bmkp-url-browse-bookmark-p' or
3663 `bmkp-w3m-bookmark-p'.
3664 BOOKMARK is a bookmark name or a bookmark record."
3665   (or (bmkp-url-browse-bookmark-p bookmark) (bmkp-w3m-bookmark-p bookmark)))
3666
3667 (defun bmkp-url-browse-bookmark-p (bookmark)
3668   "Return non-nil if BOOKMARK is a `browse-url' bookmark.
3669 BOOKMARK is a bookmark name or a bookmark record."
3670   (eq (bookmark-get-handler bookmark) 'bmkp-jump-url-browse))
3671
3672 (defun bmkp-variable-list-bookmark-p (bookmark)
3673   "Return non-nil if BOOKMARK is a variable-list bookmark.
3674 BOOKMARK is a bookmark name or a bookmark record."
3675   (eq (bookmark-get-handler bookmark) 'bmkp-jump-variable-list))
3676
3677 (defun bmkp-w3m-bookmark-p (bookmark)
3678   "Return non-nil if BOOKMARK is a W3M bookmark.
3679 BOOKMARK is a bookmark name or a bookmark record."
3680   (memq (bookmark-get-handler bookmark) '(bmkp-jump-w3m bmkext-jump-w3m)))
3681
3682
3683 ;;(@* "Filter Functions")
3684 ;;  *** Filter Functions ***
3685
3686 (defun bmkp-all-tags-alist-only (tags)
3687   "`bookmark-alist', but with only bookmarks having all their tags in TAGS.
3688 Does not include bookmarks that have no tags.
3689 A new list is returned (no side effects)."
3690   (bmkp-remove-if-not
3691    #'(lambda (bmk)
3692        (let ((bmk-tags  (bmkp-get-tags bmk)))
3693          (and bmk-tags (bmkp-every #'(lambda (tag) (member (bmkp-tag-name tag) tags)) bmk-tags))))
3694    bookmark-alist))
3695
3696 (defun bmkp-all-tags-regexp-alist-only (regexp)
3697   "`bookmark-alist', but with only bookmarks having all tags match REGEXP.
3698 Does not include bookmarks that have no tags.
3699 A new list is returned (no side effects)."
3700   (bmkp-remove-if-not
3701    #'(lambda (bmk)
3702        (let ((bmk-tags  (bmkp-get-tags bmk)))
3703          (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3704                                    bmk-tags))))
3705    bookmark-alist))
3706
3707 (defun bmkp-annotated-alist-only ()
3708   "`bookmark-alist', but only for bookmarks with non-empty annotations.
3709 A new list is returned (no side effects)."
3710   (bookmark-maybe-load-default-file)
3711   (bmkp-remove-if-not (lambda (bmk)
3712                         (let ((annotation  (bookmark-get-annotation bmk)))
3713                           (and annotation (not (string-equal annotation "")))))
3714                       bookmark-alist))
3715
3716 (defun bmkp-autofile-alist-only (&optional prefix)
3717   "`bookmark-alist', filtered to retain only autofile bookmarks.
3718 With non-nil arg PREFIX, the bookmark names must all have that PREFIX."
3719   (bookmark-maybe-load-default-file)
3720   (if (not prefix)
3721       (bmkp-remove-if-not #'bmkp-autofile-bookmark-p bookmark-alist)
3722     (bmkp-remove-if-not #'(lambda (bb) (bmkp-autofile-bookmark-p bb prefix)) bookmark-alist)))
3723
3724 (defun bmkp-autonamed-alist-only ()
3725   "`bookmark-alist', with only autonamed bookmarks (from any buffers).
3726 A new list is returned (no side effects)."
3727   (bookmark-maybe-load-default-file)
3728   (bmkp-remove-if-not #'bmkp-autonamed-bookmark-p bookmark-alist))
3729
3730 (defun bmkp-autonamed-this-buffer-alist-only ()
3731   "`bookmark-alist', with only autonamed bookmarks for the current buffer.
3732 A new list is returned (no side effects)."
3733   (bookmark-maybe-load-default-file)
3734   (bmkp-remove-if-not (lambda (bmk) (bmkp-autonamed-bookmark-for-buffer-p bmk (buffer-name)))
3735                       bookmark-alist))
3736
3737 (defun bmkp-bookmark-file-alist-only ()
3738   "`bookmark-alist', filtered to retain only bookmark-file bookmarks.
3739 A new list is returned (no side effects)."
3740   (bookmark-maybe-load-default-file)
3741   (bmkp-remove-if-not #'bmkp-bookmark-file-bookmark-p bookmark-alist))
3742
3743 (defun bmkp-bookmark-list-alist-only ()
3744   "`bookmark-alist', filtered to retain only bookmark-list bookmarks.
3745 A new list is returned (no side effects)."
3746   (bookmark-maybe-load-default-file)
3747   (bmkp-remove-if-not #'bmkp-bookmark-list-bookmark-p bookmark-alist))
3748
3749 (defun bmkp-desktop-alist-only ()
3750   "`bookmark-alist', filtered to retain only desktop bookmarks.
3751 A new list is returned (no side effects)."
3752   (bookmark-maybe-load-default-file)
3753   (bmkp-remove-if-not #'bmkp-desktop-bookmark-p bookmark-alist))
3754
3755 (defun bmkp-dired-alist-only ()
3756   "`bookmark-alist', filtered to retain only Dired bookmarks.
3757 A new list is returned (no side effects)."
3758   (bookmark-maybe-load-default-file)
3759   (bmkp-remove-if-not #'bmkp-dired-bookmark-p bookmark-alist))
3760
3761 (defun bmkp-dired-this-dir-alist-only ()
3762   "`bookmark-alist', with only Dired bookmarks for the current directory.
3763 A new list is returned (no side effects)."
3764   (bookmark-maybe-load-default-file)
3765   (bmkp-remove-if-not #'bmkp-dired-this-dir-bookmark-p bookmark-alist))
3766
3767 (defun bmkp-file-alist-only ()
3768   "`bookmark-alist', filtered to retain only file and directory bookmarks.
3769 This excludes bookmarks that might contain file information but are
3770 particular in some way - for example, Info bookmarks or Gnus bookmarks.
3771 A new list is returned (no side effects)."
3772   (bookmark-maybe-load-default-file)
3773   (bmkp-remove-if-not #'bmkp-file-bookmark-p bookmark-alist))
3774
3775 (defun bmkp-file-all-tags-alist-only (tags)
3776   "`bookmark-alist', with only file bookmarks having all tags in TAGS.
3777 A new list is returned (no side effects)."
3778   (bmkp-remove-if-not
3779    #'(lambda (bmk)
3780        (and (bmkp-file-bookmark-p bmk)
3781             (let ((bmk-tags  (bmkp-get-tags bmk)))
3782               (and bmk-tags (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))))
3783    bookmark-alist))
3784
3785 (defun bmkp-file-all-tags-regexp-alist-only (regexp)
3786   "`bookmark-alist', with only file bookmarks having all tags match REGEXP.
3787 A new list is returned (no side effects)."
3788   (bmkp-remove-if-not
3789    #'(lambda (bmk)
3790        (and (bmkp-file-bookmark-p bmk)
3791             (let ((bmk-tags  (bmkp-get-tags bmk)))
3792               (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3793                                         bmk-tags)))))
3794    bookmark-alist))
3795
3796 (defun bmkp-file-some-tags-alist-only (tags)
3797   "`bookmark-alist', with only file bookmarks having some tags in TAGS.
3798 A new list is returned (no side effects)."
3799   (bmkp-remove-if-not
3800    #'(lambda (bmk) (and (bmkp-file-bookmark-p bmk)
3801                         (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))
3802    bookmark-alist))
3803
3804 (defun bmkp-file-some-tags-regexp-alist-only (regexp)
3805   "`bookmark-alist', with only file bookmarks having some tags match REGEXP.
3806 A new list is returned (no side effects)."
3807   (bmkp-remove-if-not
3808    #'(lambda (bmk) (and (bmkp-file-bookmark-p bmk)
3809                         (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3810                                    (bmkp-get-tags bmk))))
3811    bookmark-alist))
3812
3813 (defun bmkp-file-this-dir-alist-only ()
3814   "`bookmark-alist', filtered with `bmkp-file-this-dir-bookmark-p'.
3815 Include only files and subdir that are in `default-directory'.
3816 This excludes bookmarks that might contain file information but are
3817 particular in some way - for example, Info bookmarks or Gnus bookmarks.
3818 A new list is returned (no side effects)."
3819   (bookmark-maybe-load-default-file)
3820   (bmkp-remove-if-not #'bmkp-file-this-dir-bookmark-p bookmark-alist))
3821
3822 (defun bmkp-file-this-dir-all-tags-alist-only (tags)
3823   "`bookmark-alist', for files in this dir having all tags in TAGS.
3824 Include only files and subdir that are in `default-directory'.
3825 A new list is returned (no side effects)."
3826   (bmkp-remove-if-not
3827    #'(lambda (bmk)
3828        (and (bmkp-file-this-dir-bookmark-p bmk)
3829             (let ((bmk-tags  (bmkp-get-tags bmk)))
3830               (and bmk-tags (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))))
3831    bookmark-alist))
3832
3833 (defun bmkp-file-this-dir-all-tags-regexp-alist-only (regexp)
3834   "`bookmark-alist', for files in this dir having all tags match REGEXP.
3835 Include only files and subdir that are in `default-directory'.
3836 A new list is returned (no side effects)."
3837   (bmkp-remove-if-not
3838    #'(lambda (bmk)
3839        (and (bmkp-file-this-dir-bookmark-p bmk)
3840             (let ((bmk-tags  (bmkp-get-tags bmk)))
3841               (and bmk-tags (bmkp-every #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3842                                         bmk-tags)))))
3843    bookmark-alist))
3844
3845 (defun bmkp-file-this-dir-some-tags-alist-only (tags)
3846   "`bookmark-alist', for files in this dir having some tags in TAGS.
3847 Include only files and subdir that are in `default-directory'.
3848 A new list is returned (no side effects)."
3849   (bmkp-remove-if-not
3850    #'(lambda (bmk) (and (bmkp-file-this-dir-bookmark-p bmk)
3851                         (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags)))
3852    bookmark-alist))
3853
3854 (defun bmkp-file-this-dir-some-tags-regexp-alist-only (regexp)
3855   "`bookmark-alist', for files in this dir having some tags match REGEXP.
3856 Include only files and subdir that are in `default-directory'.
3857 A new list is returned (no side effects)."
3858   (bmkp-remove-if-not
3859    #'(lambda (bmk) (and (bmkp-file-this-dir-bookmark-p bmk)
3860                         (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
3861                                    (bmkp-get-tags bmk))))
3862    bookmark-alist))
3863
3864 (defun bmkp-gnus-alist-only ()
3865   "`bookmark-alist', filtered to retain only Gnus bookmarks.
3866 A new list is returned (no side effects)."
3867   (bookmark-maybe-load-default-file)
3868   (bmkp-remove-if-not #'bmkp-gnus-bookmark-p bookmark-alist))
3869
3870 (defun bmkp-info-alist-only ()
3871   "`bookmark-alist', filtered to retain only Info bookmarks.
3872 A new list is returned (no side effects)."
3873   (bookmark-maybe-load-default-file)
3874   (bmkp-remove-if-not #'bmkp-info-bookmark-p bookmark-alist))
3875
3876 (defun bmkp-last-specific-buffer-alist-only ()
3877   "`bookmark-alist', but only for `bmkp-last-specific-buffer'.
3878 A new list is returned (no side effects)."
3879   (bookmark-maybe-load-default-file)
3880   (bmkp-remove-if-not #'bmkp-last-specific-buffer-p bookmark-alist))
3881
3882 (defun bmkp-last-specific-file-alist-only ()
3883   "`bookmark-alist', but only for `bmkp-last-specific-file'.
3884 A new list is returned (no side effects)."
3885   (bookmark-maybe-load-default-file)
3886   (bmkp-remove-if-not #'bmkp-last-specific-file-p bookmark-alist))
3887
3888 (defun bmkp-man-alist-only ()
3889   "`bookmark-alist', filtered to retain only `man' page bookmarks.
3890 A new list is returned (no side effects)."
3891   (bookmark-maybe-load-default-file)
3892   (bmkp-remove-if-not #'bmkp-man-bookmark-p bookmark-alist))
3893
3894 (defun bmkp-local-file-alist-only ()
3895   "`bookmark-alist', filtered to retain only local-file bookmarks.
3896 A new list is returned (no side effects)."
3897   (bookmark-maybe-load-default-file)
3898   (bmkp-remove-if-not #'bmkp-local-file-bookmark-p bookmark-alist))
3899
3900 (defun bmkp-non-autonamed-alist-only ()
3901   "`bookmark-alist', with only non-autonamed bookmarks (from any buffers).
3902 A new list is returned (no side effects)."
3903   (bookmark-maybe-load-default-file)
3904   (bmkp-remove-if-not (lambda (bmk) (not (bmkp-autonamed-bookmark-p bmk))) bookmark-alist))
3905
3906 (defun bmkp-non-file-alist-only ()
3907   "`bookmark-alist', filtered to retain only non-file bookmarks.
3908 A new list is returned (no side effects)."
3909   (bookmark-maybe-load-default-file)
3910   (bmkp-remove-if-not #'bmkp-non-file-bookmark-p bookmark-alist))
3911
3912 (defun bmkp-regexp-filtered-annotation-alist-only ()
3913   "`bookmark-alist' for annotations matching `bmkp-bmenu-filter-pattern'."
3914   (bookmark-maybe-load-default-file)
3915   (bmkp-remove-if-not
3916    #'(lambda (bmk)
3917        (let ((annot  (bookmark-get-annotation bmk)))
3918          (and (stringp annot) (not (string= "" annot))
3919               (string-match bmkp-bmenu-filter-pattern annot))))
3920    bookmark-alist))                     ; (Could use `bmkp-annotated-alist-only' here instead.)
3921
3922 (defun bmkp-regexp-filtered-bookmark-name-alist-only ()
3923   "`bookmark-alist' for bookmarks matching `bmkp-bmenu-filter-pattern'."
3924   (bookmark-maybe-load-default-file)
3925   (bmkp-remove-if-not
3926    #'(lambda (bmk) (string-match bmkp-bmenu-filter-pattern (car bmk))) bookmark-alist))
3927
3928 (defun bmkp-regexp-filtered-file-name-alist-only ()
3929   "`bookmark-alist' for files matching `bmkp-bmenu-filter-pattern'."
3930   (bookmark-maybe-load-default-file)
3931   (let (fname)
3932     (bmkp-remove-if-not #'(lambda (bmk) (and (setq fname  (bookmark-get-filename bmk))
3933                                              (string-match bmkp-bmenu-filter-pattern fname)))
3934                         bookmark-alist)))
3935
3936 (defun bmkp-regexp-filtered-tags-alist-only ()
3937   "`bookmark-alist' for tags matching `bmkp-bmenu-filter-pattern'."
3938   (bookmark-maybe-load-default-file)
3939   (let (tags)
3940     (bmkp-remove-if-not
3941      #'(lambda (bmk) (and (setq tags  (bmkp-get-tags bmk))
3942                           (bmkp-some (lambda (tag)
3943                                        (string-match bmkp-bmenu-filter-pattern (bmkp-tag-name tag)))
3944                                      tags)))
3945      bookmark-alist)))
3946
3947 (defun bmkp-region-alist-only ()
3948   "`bookmark-alist', filtered to retain only bookmarks that have regions.
3949 A new list is returned (no side effects)."
3950   (bookmark-maybe-load-default-file)
3951   (bmkp-remove-if-not #'bmkp-region-bookmark-p bookmark-alist))
3952
3953 (defun bmkp-remote-file-alist-only ()
3954   "`bookmark-alist', filtered to retain only remote-file bookmarks.
3955 A new list is returned (no side effects)."
3956   (bookmark-maybe-load-default-file)
3957   (bmkp-remove-if-not #'bmkp-remote-file-bookmark-p bookmark-alist))
3958
3959 (defun bmkp-some-tags-alist-only (tags)
3960   "`bookmark-alist', but with only bookmarks having some tags in TAGS.
3961 A new list is returned (no side effects)."
3962   (bmkp-remove-if-not
3963    #'(lambda (bmk) (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))
3964    bookmark-alist))
3965
3966 (defun bmkp-some-tags-regexp-alist-only (regexp)
3967   "`bookmark-alist', but with only bookmarks having some tags match REGEXP.
3968 A new list is returned (no side effects)."
3969   (bmkp-remove-if-not
3970    #'(lambda (bmk)
3971        (bmkp-some #'(lambda (tag) (string-match regexp (bmkp-tag-name tag))) (bmkp-get-tags bmk)))
3972    bookmark-alist))
3973
3974 (defun bmkp-specific-buffers-alist-only (&optional buffers)
3975   "`bookmark-alist', filtered to retain only bookmarks to buffers BUFFERS.
3976 BUFFERS is a list of buffer names.
3977 It defaults to a singleton list with the current buffer's name.
3978 A new list is returned (no side effects).
3979
3980 Note: Bookmarks created by vanilla Emacs do not record the buffer
3981 name.  They are therefore excluded from the returned alist."
3982   (unless buffers  (setq buffers  (list (buffer-name))))
3983   (bookmark-maybe-load-default-file)
3984   (bmkp-remove-if-not (lambda (bmk) (and (not (bmkp-desktop-bookmark-p       bmk)) ; Exclude these
3985                                          (not (bmkp-bookmark-file-bookmark-p bmk))
3986                                          (not (bmkp-sequence-bookmark-p      bmk))
3987                                          (not (bmkp-function-bookmark-p      bmk))
3988                                          (not (bmkp-variable-list-bookmark-p bmk))
3989                                          (member (bmkp-get-buffer-name bmk) buffers)))
3990                       bookmark-alist))
3991
3992 (defun bmkp-specific-files-alist-only (&optional files)
3993   "`bookmark-alist', filtered to retain only bookmarks to files FILES.
3994 FILES is a list of absolute file names.
3995 It defaults to a singleton list with the current buffer's file name.
3996 A new list is returned (no side effects)."
3997   (unless files  (setq files  (list (buffer-file-name))))
3998   (bookmark-maybe-load-default-file)
3999   (bmkp-remove-if-not (lambda (bmk) (member (bookmark-get-filename bmk) files)) bookmark-alist))
4000
4001 (defun bmkp-this-buffer-alist-only ()
4002   "`bookmark-alist', with only bookmarks for the current buffer.
4003 A new list is returned (no side effects)."
4004   (bookmark-maybe-load-default-file)
4005   (bmkp-remove-if-not #'bmkp-this-buffer-p bookmark-alist))
4006
4007 (defun bmkp-this-file-alist-only ()
4008   "`bookmark-alist', with only bookmarks for the current file.
4009 A new list is returned (no side effects)."
4010   (bookmark-maybe-load-default-file)
4011   (bmkp-remove-if-not #'bmkp-this-file-p bookmark-alist))
4012
4013 (defun bmkp-url-alist-only ()
4014   "`bookmark-alist', filtered to retain only URL bookmarks.
4015 A new list is returned (no side effects)."
4016   (bookmark-maybe-load-default-file)
4017   (bmkp-remove-if-not #'bmkp-url-bookmark-p bookmark-alist))
4018
4019 (defun bmkp-url-browse-alist-only ()
4020   "`bookmark-alist', filtered to retain only non-W3M URL bookmarks.
4021 A new list is returned (no side effects)."
4022   (bookmark-maybe-load-default-file)
4023   (bmkp-remove-if-not #'bmkp-url-browse-bookmark-p bookmark-alist))
4024
4025 (defun bmkp-variable-list-alist-only ()
4026   "`bookmark-alist', filtered to retain only variable-list bookmarks.
4027 A new list is returned (no side effects)."
4028   (bookmark-maybe-load-default-file)
4029   (bmkp-remove-if-not #'bmkp-variable-list-bookmark-p bookmark-alist))
4030
4031 (defun bmkp-w3m-alist-only ()
4032   "`bookmark-alist', filtered to retain only W3M bookmarks.
4033 A new list is returned (no side effects)."
4034   (bookmark-maybe-load-default-file)
4035   (bmkp-remove-if-not #'bmkp-w3m-bookmark-p bookmark-alist))
4036
4037
4038 ;;; Marked bookmarks
4039
4040 (defun bmkp-marked-bookmarks-only ()
4041   "Return the list of marked bookmarks."
4042   (bmkp-remove-if-not #'bmkp-marked-bookmark-p bookmark-alist))
4043
4044 (defun bmkp-unmarked-bookmarks-only ()
4045   "Return the list of unmarked bookmarks."
4046   (bmkp-remove-if #'bmkp-marked-bookmark-p bookmark-alist))
4047
4048 (defun bmkp-some-marked-p (alist)
4049   "Return non-nil if ALIST is nonempty and includes a marked bookmark."
4050   (catch 'break (dolist (i  alist)  (and (bmkp-marked-bookmark-p i)  (throw 'break t)))))
4051
4052 (defun bmkp-some-unmarked-p (alist)
4053   "Return non-nil if ALIST is nonempty and includes an unmarked bookmark."
4054   (catch 'break (dolist (i  alist)  (and (not (bmkp-marked-bookmark-p i))  (throw 'break t)))))
4055
4056
4057 ;;(@* "General Utility Functions")
4058 ;;  *** General Utility Functions ***
4059
4060 (defun bmkp-remove-dups (list)
4061   "Copy of LIST with duplicate elements removed.  Tested with `equal'."
4062   (let ((tail  list)
4063         new)
4064     (while tail
4065       (unless (member (car tail) new) (push (car tail) new))
4066       (pop tail))
4067     (nreverse new)))
4068
4069 ;; Similar to `bmkp-assoc-delete-all', but compares also property `bmkp-full-record'.
4070 (defun bmkp-delete-bookmark-name-from-list (delname bnames)
4071   "Delete names that represent the same bookmark as DELNAME from BNAMES.
4072 This menas that they are `string=' and have the same value of property
4073 `bmkp-full-record'.
4074 Return the modified list BNAMES."
4075   (let ((delprop  (get-text-property 0 'bmkp-full-record delname)))
4076     (if (not delprop)
4077         (delete delname bnames)         ; Unpropertized - just use `delete'.
4078       (while (and bnames  (string= delname (car bnames))
4079                   (eq delprop (get-text-property 0 'bmkp-full-record (car bnames))))
4080         (setq bnames  (cdr bnames)))
4081       (let ((tail  bnames)
4082             tail-cdr)
4083         (while (setq tail-cdr  (cdr tail))
4084           (if (and (car tail-cdr)  (string= delname (car tail-cdr))
4085                    (eq delprop (get-text-property 0 'bmkp-full-record (car tail-cdr))))
4086               (setcdr tail  (cdr tail-cdr))
4087             (setq tail  tail-cdr))))
4088       bnames)))
4089
4090 (defun bmkp-bookmark-name-member (name names) ; $$$$$$ PUT BACK `eq' instead of `equal'?.
4091   "Like `member', but tests also bookmark NAME's `bmkp-full-record' property.
4092 If NAME has no `bmkp-full-record' property then this is just `member'.
4093
4094 If NAME has property `bmkp-full-record', then test whether both:
4095  a. NAME is a member of NAMES and
4096  b. NAME has the same `bmkp-full-record' value as an element of NAMES.
4097 Return the tail of NAMES whose car is NAME with the property match."
4098   (let ((prop  (get-text-property 0 'bmkp-full-record name)))
4099     (if (not prop)
4100         (member name names)             ; Unpropertized - just use `member'.
4101       (while (and names  (not (and (string= name (car names)) ; = `bmkp-names-same-bookmark-p'.
4102                                    (equal prop (get-text-property 0 'bmkp-full-record (car names))))))
4103         (setq names  (cdr names)))
4104       names)))
4105
4106 (defun bmkp-names-same-bookmark-p (name1 name2) ; $$$$$$ PUT BACK `eq' instead of `equal'?.
4107   "Return non-nil if the two strings name the same bookmark.
4108 The strings are `string=' and their `bmkp-full-record' property values
4109 for the first character are `eq'."
4110   (and (string= name1 name2)
4111        (equal (get-text-property 0 'bmkp-full-record name1)
4112               (get-text-property 0 'bmkp-full-record name2))))
4113
4114 (defun bmkp-remove-if (pred xs)
4115   "A copy of list XS with no elements that satisfy predicate PRED."
4116   (let ((result  ()))
4117     (dolist (x  xs)  (unless (funcall pred x) (push x result)))
4118     (nreverse result)))
4119
4120 (defun bmkp-remove-if-not (pred xs)
4121   "A copy of list XS with only elements that satisfy predicate PRED."
4122   (let ((result  ()))
4123     (dolist (x  xs)  (when (funcall pred x) (push x result)))
4124     (nreverse result)))
4125
4126 ;; Similar to `every' in `cl-extra.el', without non-list sequences and multiple sequences.
4127 (defun bmkp-every (predicate list)
4128   "Return t if PREDICATE is true for all elements of LIST; else nil."
4129   (while (and list (funcall predicate (car list))) (setq list  (cdr list)))
4130   (null list))
4131
4132 ;; Similar to `some' in `cl-extra.el', without non-list sequences and multiple sequences.
4133 (defun bmkp-some (predicate list)
4134   "Return non-nil if PREDICATE is true for some element of LIST; else nil.
4135 Return the first non-nil value returned by PREDICATE."
4136   (let ((res  nil))
4137     (while (and list (not (setq res  (funcall predicate (pop list))))))
4138     res))
4139
4140 ;; From `cl-seq.el', function `union', without keyword treatment.
4141 ;; (Same as `simple-set-union' in `misc-fns.el' and `icicle-set-union' in `icicles-fn.el'.)
4142 (defun bmkp-set-union (list1 list2)
4143   "Combine LIST1 and LIST2 using a set-union operation.
4144 The result list contains all items that appear in either LIST1 or
4145 LIST2.  Comparison is done using `equal'.  This is a non-destructive
4146 function; it copies the data if necessary."
4147   (cond ((null list1)         list2)
4148         ((null list2)         list1)
4149         ((equal list1 list2)  list1)
4150         (t
4151          (unless (>= (length list1) (length list2))
4152            (setq list1  (prog1 list2 (setq list2  list1)))) ; Swap them.
4153          (while list2
4154            (unless (member (car list2) list1)  (setq list1  (cons (car list2) list1)))
4155            (setq list2  (cdr list2)))
4156          list1)))
4157
4158 (defun bmkp-upcase (string)
4159   "`upcase', but in case of error, return original STRING.
4160 This works around an Emacs 20 problem that occurs if STRING contains
4161 binary data (weird chars)."
4162   (condition-case nil (upcase string) (error string)))
4163
4164 (defun bmkp-same-file-p (file1 file2)
4165   "Return non-nil if FILE1 and FILE2 name the same file.
4166 If either name is not absolute, then it is considered relative to
4167 `default-directory'."
4168   (string= (file-truename (expand-file-name file1)) (file-truename (expand-file-name file2))))
4169
4170 (defun bmkp-file-remote-p (file-name)
4171   "Returns non-nil if string FILE-NAME is likely to name a remote file."
4172   (if (fboundp 'file-remote-p)
4173       (file-remote-p file-name)
4174     (and (fboundp 'ffap-file-remote-p) (ffap-file-remote-p file-name))))
4175
4176 (defun bmkp-float-time (&optional specified-time)
4177   "Same as `float-time'.  (Needed for Emacs 20.)"
4178   (if (fboundp 'float-time)
4179       (float-time specified-time)
4180     (unless specified-time (setq specified-time  (current-time)))
4181     (+ (* (float (nth 0 specified-time)) (expt 2 16))  (nth 1 specified-time))))
4182
4183 (defun bmkp-face-prop (value)
4184   "Return a list with elements `face' or `font-lock-face' and VALUE.
4185 Starting with Emacs 22, the first element is `font-lock-face'."
4186   (list (if (> emacs-major-version 21) 'font-lock-face 'face) value))  
4187
4188 (defun bmkp-make-plain-predicate (pred &optional final-pred)
4189   "Return a plain predicate that corresponds to component-predicate PRED.
4190 PRED and FINAL-PRED correspond to their namesakes in
4191 `bmkp-sort-comparer' (which see).
4192
4193 PRED should return `(t)', `(nil)', or nil.
4194
4195 Optional arg FINAL-PRED is the final predicate to use if PRED cannot
4196 decide (returns nil).  If FINAL-PRED is nil, then `bmkp-alpha-p', the
4197 plain-predicate equivalent of `bmkp-alpha-cp' is used as the final
4198 predicate."
4199   `(lambda (b1 b2) (let ((res  (funcall ',pred b1 b2)))
4200                      (if res (car res) (funcall ',(or final-pred 'bmkp-alpha-p) b1 b2)))))
4201
4202 (defun bmkp-repeat-command (command)
4203   "Repeat COMMAND."
4204   (let ((repeat-message-function  'ignore))
4205     (setq last-repeatable-command  command)
4206     (repeat nil)))
4207
4208
4209 ;;; If you need this for some reason, uncomment it.
4210 ;;; (defun bmkp-fix-bookmark-alist-and-save ()
4211 ;;;   "Update format of `bookmark-default-file' created in summer of 2009.
4212 ;;; You DO NOT NEED THIS, unless you happen to have used `bookmark+.el' in
4213 ;;; the summer of 2009 to create non-file bookmarks.  If you did that,
4214 ;;; then some of those bookmarks might cause vanilla Emacs (emacs -Q) to
4215 ;;; raise an error.  You can use this command to fix that problem: it
4216 ;;; modifies your existing `bookmark-default-file' (`.emacs.bmk'), after
4217 ;;; backing up that file (suffixing the name with \"_saveNUMBER\")."
4218 ;;;   (interactive)
4219 ;;;   (require 'cl)                         ; For `gensym'
4220 ;;;   (if (not (yes-or-no-p
4221 ;;;              "This will modify your bookmark file, after backing it up.  OK? "))
4222 ;;;       (message "OK, nothing done")
4223 ;;;     (bookmark-maybe-load-default-file)
4224 ;;;     (let ((bkup-file  (concat bookmark-default-file "_" (symbol-name (gensym "save")))))
4225 ;;;       (when (condition-case err
4226 ;;;                 (progn
4227 ;;;                   (with-current-buffer (find-file-noselect bookmark-default-file)
4228 ;;;                     (write-file bkup-file))
4229 ;;;                   (dolist (bmk  bookmark-alist)
4230 ;;;                     (let ((fn-tail  (member '(filename) bmk))
4231 ;;;                           (hdlr     (bookmark-get-handler (car bmk))))
4232 ;;;                       (cond (fn-tail
4233 ;;;                              (setcar fn-tail (cons 'filename bmkp-non-file-filename)))
4234 ;;;                             ((and (eq hdlr 'bmkp-jump-gnus)
4235 ;;;                                   (not (assoc 'filename bmk)))
4236 ;;;                              (setcdr bmk (cons (cons 'filename bmkp-non-file-filename)
4237 ;;;                                                (cdr bmk)))))))
4238 ;;;                   t)                    ; Be sure `dolist' exit with t to allow saving.
4239 ;;;               (error (error "No changes made. %s" (error-message-string err))))
4240 ;;;         (bookmark-save)
4241 ;;;         (message "Bookmarks file fixed.  Old version is `%s'" bkup-file)))))
4242
4243
4244 ;;(@* "Bookmark Entry Access Functions")
4245 ;;  *** Bookmark Entry Access Functions ***
4246
4247 (defun bmkp-get-buffer-name (bookmark)
4248   "Return the `buffer-name' value for BOOKMARK.
4249 BOOKMARK is a bookmark name or a bookmark record."
4250   (bookmark-prop-get bookmark 'buffer-name))
4251
4252 (defun bmkp-get-end-position (bookmark)
4253   "Return the `end-position' value for BOOKMARK.
4254 BOOKMARK is a bookmark name or a bookmark record."
4255   (bookmark-prop-get bookmark 'end-position))
4256
4257 (defun bmkp-get-visits-count (bookmark)
4258   "Return the `visits' count for BOOKMARK.
4259 BOOKMARK is a bookmark name or a bookmark record."
4260   (bookmark-prop-get bookmark 'visits))
4261
4262 (defun bmkp-get-visit-time (bookmark)
4263   "Return the `time' value for BOOKMARK.
4264 BOOKMARK is a bookmark name or a bookmark record."
4265   ;; Should just be a prop-get, but when first implemented, we used a float
4266   ;; instead of a time cons, so we need to convert any such obsolete recorded times.
4267   (let ((vt  (bookmark-prop-get bookmark 'time)))
4268     (when (numberp vt)                  ; Convert mid-2009 time values (floats) to cons form.
4269       (setq vt  (if (boundp 'seconds-to-time)
4270                     (seconds-to-time vt)
4271                   (list (floor vt 65536) ; Inlined `seconds-to-time', for Emacs 20-21.
4272                         (floor (mod vt 65536))
4273                         (floor (* (- vt (ffloor vt)) 1000000))))))
4274     vt))
4275
4276
4277 ;;(@* "Sorting - General Functions")
4278 ;;  *** Sorting - General Functions ***
4279
4280 (defun bmkp-sort-omit (alist &optional omit)
4281   "Sort a copy of ALIST, omitting any elements whose keys are in OMIT.
4282 Return the copy.
4283 Do not sort if `bmkp-sort-comparer' is nil.
4284 This is a non-destructive operation: ALIST is not modified.
4285
4286 Sorting is done using using `bmkp-sort-comparer'.
4287 If `bmkp-reverse-sort-p' is non-nil, then reverse the sort order.
4288 Keys are compared for sorting using `equal'.
4289
4290 If optional arg OMIT is non-nil, then it is a list of keys.  Omit from
4291 the return value any elements with keys in the list."
4292   (let ((new-alist  (bmkp-remove-omitted alist omit))
4293         (sort-fn  (and bmkp-sort-comparer  (if (and (not (functionp bmkp-sort-comparer))
4294                                                     (consp bmkp-sort-comparer))
4295                                                'bmkp-multi-sort
4296                                              bmkp-sort-comparer))))
4297     (when sort-fn
4298       (setq new-alist  (sort new-alist (if bmkp-reverse-sort-p
4299                                            (lambda (a b) (not (funcall sort-fn a b)))
4300                                          sort-fn))))
4301     new-alist))
4302
4303 (defun bmkp-remove-omitted (alist &optional omit)
4304   "Copy of bookmark ALIST without bookmarks whose names are in list OMIT.
4305 Name comparison is done using `bmkp-bookmark-name-member'.
4306 If optional arg OMIT is non-nil, then omit from the return value any
4307 elements with keys in list OMIT."
4308   (let ((new  ()))
4309     (dolist (ii  alist)  (unless (bmkp-bookmark-name-member (car ii) omit)  (push ii new)))
4310     (nreverse new)))
4311
4312 ;;; $$$$$$ No longer used.
4313 ;;; (defun bmkp-sort-and-remove-dups (alist &optional omit)
4314 ;;;   "Remove duplicates from a copy of ALIST, then sort it and return it.
4315 ;;; Do not sort if `bmkp-sort-comparer' is nil.
4316 ;;; Always remove duplicates.  Keep only the first element with a given
4317 ;;; key.  This is a non-destructive operation: ALIST is not modified.
4318
4319 ;;; Sorting is done using using `bmkp-sort-comparer'.
4320 ;;; If `bmkp-reverse-sort-p' is non-nil, then reverse the sort order.
4321 ;;; Keys are compared for sorting using `equal'.
4322 ;;; If optional arg OMIT is non-nil, then omit from the return value any
4323 ;;; elements with keys in list OMIT."
4324 ;;;   (let ((new-alist  (bmkp-remove-assoc-dups alist omit))
4325 ;;;         (sort-fn  (and bmkp-sort-comparer  (if (and (not (functionp bmkp-sort-comparer))
4326 ;;;                                                     (consp bmkp-sort-comparer))
4327 ;;;                                                'bmkp-multi-sort
4328 ;;;                                              bmkp-sort-comparer))))
4329 ;;;     (when sort-fn
4330 ;;;       (setq new-alist  (sort new-alist (if bmkp-reverse-sort-p
4331 ;;;                                            (lambda (a b) (not (funcall sort-fn a b)))
4332 ;;;                                          sort-fn))))
4333 ;;;     new-alist))
4334
4335 ;;; KEEP this simpler version also.  This uses `run-hook-with-args-until-success', but it
4336 ;;; does not respect `bmkp-reverse-multi-sort-p'.
4337 ;;; (defun bmkp-multi-sort (b1 b2)
4338 ;;;   "Try predicates in `bmkp-sort-comparer', in order, until one decides.
4339 ;;; See the description of `bmkp-sort-comparer'."
4340 ;;;   (let* ((preds   (append (car bmkp-sort-comparer) (cdr bmkp-sort-comparer)))
4341 ;;;          (result  (run-hook-with-args-until-success 'preds b1 b2)))
4342 ;;;     (if (consp result)
4343 ;;;         (car result)
4344 ;;;       result)))
4345
4346 ;;; $$$$$$ No longer used.
4347 ;;; (defun bmkp-remove-assoc-dups (alist &optional omit)
4348 ;;;   "Shallow copy of ALIST without elements that have duplicate keys.
4349 ;;; Only the first element of those with the same key is kept.
4350 ;;; Keys are compared using `equal'.
4351 ;;; If optional arg OMIT is non-nil, then omit from the return value any
4352 ;;; elements with keys in list OMIT."
4353 ;;;   (let ((new  ()))
4354 ;;;     (dolist (ii  alist)  (unless (or (assoc (car ii) new) (member (car ii) omit))  (push ii new)))
4355 ;;;     (nreverse new)))
4356
4357
4358 ;; This Lisp definition respects `bmkp-reverse-multi-sort-p', and can be extended.
4359 (defun bmkp-multi-sort (b1 b2)
4360   "Try predicates in `bmkp-sort-comparer', in order, until one decides.
4361 See the description of `bmkp-sort-comparer'.
4362 If `bmkp-reverse-multi-sort-p' is non-nil, then reverse the order for
4363 using multi-sorting predicates."
4364   (let ((preds       (car bmkp-sort-comparer))
4365         (final-pred  (cadr bmkp-sort-comparer))
4366         (result      nil))
4367     (when bmkp-reverse-multi-sort-p (setq preds  (reverse preds)))
4368     (catch 'bmkp-multi-sort
4369       (dolist (pred  preds)
4370         (setq result  (funcall pred b1 b2))
4371         (when (consp result)
4372           (when bmkp-reverse-multi-sort-p (setq result  (list (not (car result)))))
4373           (throw 'bmkp-multi-sort (car result))))
4374       (and final-pred  (if bmkp-reverse-multi-sort-p
4375                            (not (funcall final-pred b1 b2))
4376                          (funcall final-pred b1 b2))))))
4377
4378 ;; The message is only approximate.  The effect of `bmkp-reverse-multi-sort-p' is not
4379 ;; always intuitive, but it can often be useful.  What's not always intuitive is the placement
4380 ;; (the order) of bookmarks that are not sorted by the PREDs.
4381 ;; 
4382 (defun bmkp-msg-about-sort-order (order &optional prefix-msg suffix-msg)
4383   "Display a message mentioning the current sort ORDER and direction.
4384 Optional arg PREFIX-MSG is prepended to the constructed message, and
4385 terminated with a period.
4386 Similarly, SUFFIX-MSG is appended, after appending \".  \"."
4387   (let ((msg  (if (not bmkp-sort-comparer)
4388                   "Bookmarks NOT sorted"
4389                 (format "%s%s" (concat "Sorted " order)
4390                         (if (not (and (consp bmkp-sort-comparer) ; Ordinary single predicate.
4391                                       (consp (car bmkp-sort-comparer))))
4392                             (if bmkp-reverse-sort-p "; REVERSED" "")
4393                           (if (not (cadr (car bmkp-sort-comparer)))
4394                               ;; Single PRED.
4395                               (if (or (and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4396                                       (and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p)))
4397                                   "; REVERSED"
4398                                 "")
4399
4400                             ;; In case we want to distinguish:
4401                             ;; (if (and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4402                             ;;     "; reversed"
4403                             ;;   (if (and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p))
4404                             ;;       "; reversed +"
4405                             ;;     ""))
4406
4407                             ;; At least two PREDs.
4408                             (cond ((and bmkp-reverse-sort-p (not bmkp-reverse-multi-sort-p))
4409                                    "; REVERSED")
4410                                   ((and bmkp-reverse-multi-sort-p (not bmkp-reverse-sort-p))
4411                                    "; each predicate group reversed")
4412                                   ((and bmkp-reverse-multi-sort-p bmkp-reverse-sort-p)
4413                                    "; order of predicate groups reversed")
4414                                   (t ""))))))))
4415     (when prefix-msg (setq msg  (concat prefix-msg ".  " msg)))
4416     (when suffix-msg (setq msg  (concat msg ".  " suffix-msg)))
4417     (message msg)))
4418
4419
4420 ;;(@* "Sorting - Commands")
4421 ;;  *** Sorting - Commands ***
4422
4423 (defun bmkp-current-sort-order ()
4424   "Current sort order or sort function, as a string suitable in a message."
4425   (or (car (rassoc bmkp-sort-comparer bmkp-sort-orders-alist))  (format "%s" bmkp-sort-comparer)))
4426
4427
4428 ;;(@* "Sorting - General Predicates")
4429 ;;  *** Sorting - General Predicates ***
4430
4431 (defun bmkp-marked-cp (b1 b2)
4432   "True if bookmark B1 is marked and bookmark B2 is not.
4433 B1 and B2 are bookmarks or bookmark names.
4434 Reverse the roles of B1 and B2 for a false value.
4435 A true value is returned as `(t)', a false value as `(nil)'.
4436 Return nil if incomparable as described."
4437   (setq b1  (bookmark-get-bookmark b1)
4438         b2  (bookmark-get-bookmark b2))
4439   (let ((m1  (bmkp-marked-bookmark-p b1))
4440         (m2  (bmkp-marked-bookmark-p b2)))
4441     (cond ((and m1 m2)  nil)
4442           (m1           '(t))
4443           (m2           '(nil))
4444           (t            nil))))
4445
4446 (defun bmkp-visited-more-cp (b1 b2)
4447   "True if bookmark B1 was visited more often than B2.
4448 B1 and B2 are bookmarks or bookmark names.
4449 True also if B1 was visited but B2 was not.
4450 Reverse the roles of B1 and B2 for a false value.
4451 A true value is returned as `(t)', a false value as `(nil)'.
4452 Return nil if incomparable as described."
4453   (setq b1  (bookmark-get-bookmark b1)
4454         b2  (bookmark-get-bookmark b2))
4455   (let ((v1  (bmkp-get-visits-count b1))
4456         (v2  (bmkp-get-visits-count b2)))
4457     (cond ((and v1 v2)
4458            (cond ((> v1 v2)  '(t))
4459                  ((> v2 v1)  '(nil))
4460                  (t          nil)))
4461           (v1                '(t))
4462           (v2                '(nil))
4463           (t                 nil))))
4464
4465 (defun bmkp-bookmark-creation-cp (b1 b2)
4466   "True if bookmark B1 was created more recently than B2.
4467 B1 and B2 are bookmarks or bookmark names.
4468 True also if B1 has a `created' entry but B2 has none.
4469 Reverse the roles of B1 and B2 for a false value.
4470 A true value is returned as `(t)', a false value as `(nil)'.
4471 Return nil if incomparable as described."
4472   (setq b1  (bookmark-get-bookmark b1)
4473         b2  (bookmark-get-bookmark b2))
4474   (let ((t1  (bookmark-prop-get b1 'created))
4475         (t2  (bookmark-prop-get b2 'created)))
4476     (cond ((and t1 t2)
4477            (setq t1  (bmkp-float-time t1)
4478                  t2  (bmkp-float-time t2))
4479            (cond ((> t1 t2)  '(t))
4480                  ((> t2 t1)  '(nil))
4481                  (t          nil)))
4482           (t1                '(t))
4483           (t2                '(nil))
4484           (t                 nil))))
4485
4486 ;; Not used currently.
4487 (defun bmkp-same-creation-time-p (b1 b2)
4488   "Return non-nil if `B1 and B2 have same `created' entry.
4489 B1 and B2 are bookmarks or bookmark names.
4490 If neither has a `created' entry (vanilla bookmarks), then return
4491 non-nil if the full bookmarks are `equal'."
4492   (let ((time1  (bookmark-prop-get b1 'created))
4493         (time2  (bookmark-prop-get b2 'created)))
4494     (if (or time1 time2)
4495         (equal time1 time2)
4496       (equal b1 b2))))
4497
4498 (defun bmkp-bookmark-last-access-cp (b1 b2)
4499   "True if bookmark B1 was visited more recently than B2.
4500 B1 and B2 are bookmarks or bookmark names.
4501 True also if B1 was visited but B2 was not.
4502 Reverse the roles of B1 and B2 for a false value.
4503 A true value is returned as `(t)', a false value as `(nil)'.
4504 Return nil if incomparable as described."
4505   (setq b1  (bookmark-get-bookmark b1)
4506         b2  (bookmark-get-bookmark b2))
4507   (let ((t1  (bmkp-get-visit-time b1))
4508         (t2  (bmkp-get-visit-time b2)))
4509     (cond ((and t1 t2)
4510            (setq t1  (bmkp-float-time t1)
4511                  t2  (bmkp-float-time t2))
4512            (cond ((> t1 t2)  '(t))
4513                  ((> t2 t1)  '(nil))
4514                  (t          nil)))
4515           (t1                '(t))
4516           (t2                '(nil))
4517           (t                 nil))))
4518
4519 (defun bmkp-buffer-last-access-cp (b1 b2)
4520   "True if bookmark B1's buffer or file was visited more recently than B2's.
4521 B1 and B2 are bookmarks or bookmark names.
4522 A bookmark to an existing buffer sorts before a file bookmark, even if
4523 the buffer has not been visited during this session.
4524
4525 True also if B1 has a buffer but B2 does not.
4526 Reverse the roles of B1 and B2 for a false value.
4527 A true value is returned as `(t)', a false value as `(nil)'.
4528 Return nil if incomparable as described."
4529   (setq b1  (bookmark-get-bookmark b1)
4530         b2  (bookmark-get-bookmark b2))
4531   (let ((buf1  (bmkp-get-buffer-name b1))
4532         (buf2  (bmkp-get-buffer-name b2))
4533         f1 f2 t1 t2)
4534     (setq buf1  (and buf1 (get-buffer buf1))
4535           buf2  (and buf2 (get-buffer buf2)))
4536     (cond ((and buf1 buf2)              ; Both buffers exist.   See whether they were accessed.
4537            (when buf1 (setq buf1  (member buf1 (buffer-list))
4538                             buf1  (length buf1)))
4539            (when buf2 (setq buf2  (member buf2 (buffer-list))
4540                             buf2  (length buf2)))
4541            (cond ((and buf1 buf2)       ; Both were accessed.  Priority to most recent access.
4542                   (cond ((< buf1 buf2)  '(t))
4543                         ((< buf2 buf1)  '(nil))
4544                         (t              nil)))
4545                  (buf1                  '(t)) ; Only buf1 was accessed.
4546                  (buf2                  '(nil)) ; Only buf2 was accessed.
4547                  (t                     nil))) ; Neither was accessed.
4548
4549           (buf1                         '(t)) ; Only buf1 exists.
4550           (buf2                         '(nil)) ; Only buf2 exists.
4551           (t                            nil)))) ; Neither buffer exists
4552
4553 (defun bmkp-handler-cp (b1 b2)
4554   "True if bookmark B1's handler name sorts alphabetically before B2's.
4555 B1 and B2 are bookmarks or bookmark names.
4556 Two bookmarks with handlers are compared alphabetically, by their
4557 handler-function names, respecting `case-fold-search'.
4558 True also if B1 has a handler but B2 has not.
4559 Reverse the roles of B1 and B2 for a false value.
4560 A true value is returned as `(t)', a false value as `(nil)'.
4561 Return nil if neither sorts before the other."
4562   (setq b1  (bookmark-get-bookmark b1)
4563         b2  (bookmark-get-bookmark b2))
4564   (let ((h1  (bookmark-get-handler b1))
4565         (h2  (bookmark-get-handler b2)))
4566     (cond ((and h1 h2 (symbolp h1) (symbolp h2))
4567            ;; Pretend woman bookmarks are man bookmarks, to keep them together.
4568            (when (eq h1 'bmkp-jump-woman) (setq h1  'bmkp-jump-man))
4569            (when (eq h2 'bmkp-jump-woman) (setq h2  'bmkp-jump-man))
4570            (setq h1  (symbol-name h1)
4571                  h2  (symbol-name h2))
4572            (when case-fold-search (setq h1  (bmkp-upcase h1)
4573                                         h2  (bmkp-upcase h2)))
4574            (cond ((string-lessp h1 h2)  '(t))
4575                  ((string-lessp h2 h1)  '(nil))
4576                  (t                     nil)))
4577           (h1                           '(t))
4578           (h2                           '(nil))
4579           (t                            nil))))
4580
4581 (defun bmkp-info-cp (b1 b2)
4582   "True if bookmark B1 sorts as an Info bookmark before B2.
4583 B1 and B2 are bookmarks or bookmark names.
4584 Two Info bookmarks are compared first by file name (corresponding to
4585 the manual), then by node name, then by position.
4586 True also if B1 is an Info bookmark but B2 is not.
4587 Reverse the roles of B1 and B2 for a false value.
4588 A true value is returned as `(t)', a false value as `(nil)'.
4589 Return nil if neither sorts before the other."
4590   (setq b1  (bookmark-get-bookmark b1)
4591         b2  (bookmark-get-bookmark b2))
4592   (let ((i1  (bmkp-info-bookmark-p b1))
4593         (i2  (bmkp-info-bookmark-p b2)))
4594     (cond ((and i1 i2)
4595            (setq i1  (abbreviate-file-name (bookmark-get-filename b1))
4596                  i2  (abbreviate-file-name (bookmark-get-filename b2)))
4597            (when case-fold-search (setq i1  (bmkp-upcase i1)
4598                                         i2  (bmkp-upcase i2)))
4599            (cond ((string-lessp i1 i2)                  '(t)) ; Compare manuals (file names).
4600                  ((string-lessp i2 i1)                  '(nil))
4601                  (t                     ; Compare node names.
4602                   (setq i1  (bookmark-prop-get b1 'info-node)
4603                         i2  (bookmark-prop-get b2 'info-node))
4604                   (cond ((string-lessp i1 i2)           '(t))
4605                         ((string-lessp i2 i1)           '(nil))
4606                         (t
4607                          (setq i1  (bookmark-get-position b1)
4608                                i2  (bookmark-get-position b2))
4609                          (cond ((or (not i1) (not i2))  '(t)) ; Fallback if no `position' entry.
4610                                ((<= i1 i2)              '(t))
4611                                ((< i2 i1)               '(nil))))))))
4612           (i1                                           '(t))
4613           (i2                                           '(nil))
4614           (t                                            nil))))
4615
4616 (defun bmkp-gnus-cp (b1 b2)
4617   "True if bookmark B1 sorts as a Gnus bookmark before B2.
4618 B1 and B2 are bookmarks or bookmark names.
4619 Two Gnus bookmarks are compared first by Gnus group name, then by
4620 article number, then by message ID.
4621 True also if B1 is a Gnus bookmark but B2 is not.
4622 Reverse the roles of B1 and B2 for a false value.
4623 A true value is returned as `(t)', a false value as `(nil)'.
4624 Return nil if neither sorts before the other."
4625   (setq b1  (bookmark-get-bookmark b1)
4626         b2  (bookmark-get-bookmark b2))
4627   (let ((g1  (bmkp-gnus-bookmark-p b1))
4628         (g2  (bmkp-gnus-bookmark-p b2)))
4629     (cond ((and g1 g2)
4630            (setq g1  (bookmark-prop-get b1 'group)
4631                  g2  (bookmark-prop-get b2 'group))
4632            (cond ((string-lessp g1 g2)                '(t)) ; Compare groups.
4633                  ((string-lessp g2 g1)                '(nil))
4634                  (t                     ; Compare article numbers.
4635                   (setq g1  (bookmark-prop-get b1 'article)
4636                         g2  (bookmark-prop-get b2 'article))
4637                   (cond ((< g1 g2)                    '(t))
4638                         ((< g2 g1)                    '(nil))
4639                         (t
4640                          (setq g1  (bookmark-prop-get b1 'message-id)
4641                                g2  (bookmark-prop-get b2 'message-id))
4642                          (cond ((string-lessp g1 g2)  '(t)) ; Compare message IDs.
4643                                ((string-lessp g2 g1)  '(nil))
4644                                (t                     nil)))))))   
4645           (g1                                         '(t))
4646           (g2                                         '(nil))
4647           (t                                          nil))))
4648
4649 (defun bmkp-url-cp (b1 b2)
4650   "True if bookmark B1 sorts as a URL bookmark before B2.
4651 B1 and B2 are bookmarks or bookmark names.
4652 Two URL bookmarks are compared alphabetically, by their URLs.
4653 True also if B1 is a URL bookmark but B2 is not.
4654 Reverse the roles of B1 and B2 for a false value.
4655 A true value is returned as `(t)', a false value as `(nil)'.
4656 Return nil if neither sorts before the other."
4657   (setq b1  (bookmark-get-bookmark b1)
4658         b2  (bookmark-get-bookmark b2))
4659   (let ((u1  (bmkp-url-bookmark-p b1))
4660         (u2  (bmkp-url-bookmark-p b2)))
4661     (cond ((and u1 u2)
4662            (setq u1  (or (bookmark-prop-get b1 'location) (bookmark-get-filename b1))
4663                  u2  (or (bookmark-prop-get b2 'location) (bookmark-get-filename b2)))
4664            (cond ((string-lessp u1 u2)  '(t))
4665                  ((string-lessp u2 u1)  '(nil))
4666                  (t                     nil)))
4667           (u1                           '(t))
4668           (u2                           '(nil))
4669           (t                            nil))))
4670
4671 ;; Not used now.
4672 (defun bmkp-w3m-cp (b1 b2)
4673   "True if bookmark B1 sorts as a W3M URL bookmark before B2.
4674 B1 and B2 are bookmarks or bookmark names.
4675 Two W3M URL bookmarks are compared alphabetically, by their URLs.
4676 True also if B1 is a W3M bookmark but B2 is not.
4677 Reverse the roles of B1 and B2 for a false value.
4678 A true value is returned as `(t)', a false value as `(nil)'.
4679 Return nil if neither sorts before the other."
4680   (setq b1  (bookmark-get-bookmark b1)
4681         b2  (bookmark-get-bookmark b2))
4682   (let ((w1  (bmkp-w3m-bookmark-p b1))
4683         (w2  (bmkp-w3m-bookmark-p b2)))
4684     (cond ((and w1 w2)
4685            (setq w1  (bookmark-get-filename b1)
4686                  w2  (bookmark-get-filename b2))
4687            (cond ((string-lessp w1 w2)  '(t))
4688                  ((string-lessp w2 w1)  '(nil))
4689                  (t                     nil)))
4690           (w1                           '(t))
4691           (w2                           '(nil))
4692           (t                            nil))))
4693
4694 (defun bmkp-position-cp (b1 b2)
4695   "True if the `position' of B1 is not greater than that of B2.
4696 B1 and B2 are bookmarks or bookmark names.
4697 Reverse the roles of B1 and B2 for a false value.
4698 A true value is returned as `(t)', a false value as `(nil)'.
4699 Return nil if B1 and B2 do not bookmark the same buffer or they have
4700 the same `position' value."
4701   (setq b1  (bookmark-get-bookmark b1)
4702         b2  (bookmark-get-bookmark b2))
4703   (let ((buf1  (bmkp-get-buffer-name b1))
4704         (buf2  (bmkp-get-buffer-name b2)))
4705     (and buf1 buf2 (equal buf1 buf2)
4706          (let ((i1  (bookmark-get-position b1))
4707                (i2  (bookmark-get-position b2)))
4708            (cond ((or (not i1) (not i2))  '(t)) ; Fallback if no `position' entry.
4709                  ((<= i1 i2)              '(t))
4710                  ((< i2 i1)               '(nil)))))))
4711
4712 (defun bmkp-alpha-cp (b1 b2)
4713   "True if bookmark B1's name sorts alphabetically before B2's.
4714 B1 and B2 are bookmarks or bookmark names.
4715 The bookmark names are compared, respecting `case-fold-search'.
4716 Reverse the roles of B1 and B2 for a false value.
4717 A true value is returned as `(t)', a false value as `(nil)'.
4718 Return nil if neither sorts before the other."
4719   (setq b1  (bookmark-get-bookmark b1)
4720         b2  (bookmark-get-bookmark b2))
4721   (let ((s1  (car b1))
4722         (s2  (car b2)))
4723     (when case-fold-search (setq s1  (bmkp-upcase s1)
4724                                  s2  (bmkp-upcase s2)))
4725     (cond ((string-lessp s1 s2)  '(t))
4726           ((string-lessp s2 s1)  '(nil))
4727           (t                     nil))))
4728
4729 ;; Do not use `bmkp-make-plain-predicate', because it falls back on `bookmark-alpha-p'.
4730 ;; Return nil if `bookmark-alpha-cp' cannot decide.
4731 (defun bmkp-alpha-p (b1 b2)
4732   "True if bookmark B1's name sorts alphabetically before B2's.
4733 B1 and B2 are bookmarks or bookmark names.
4734 The bookmark names are compared, respecting `case-fold-search'."
4735   (setq b1  (bookmark-get-bookmark b1)
4736         b2  (bookmark-get-bookmark b2))
4737   (car (bmkp-alpha-cp b1 b2)))
4738
4739
4740 ;;(@* "Sorting - File-Name Predicates")
4741 ;;  *** Sorting - File-Name Predicates ***
4742
4743 (defun bmkp-file-alpha-cp (b1 b2)
4744   "True if bookmark B1's file name sorts alphabetically before B2's.
4745 B1 and B2 are bookmarks or bookmark names.
4746 The file names are shortened using `abbreviate-file-name', then they
4747 are compared respecting `case-fold-search'.
4748
4749 Reverse the roles of B1 and B2 for a false value.
4750 A true value is returned as `(t)', a false value as `(nil)'.
4751 Return nil if neither sorts before the other."
4752   (setq b1  (bookmark-get-bookmark b1)
4753         b2  (bookmark-get-bookmark b2))
4754   (let ((f1  (bmkp-file-bookmark-p b1))
4755         (f2  (bmkp-file-bookmark-p b2)))
4756     (cond ((and f1 f2)
4757            ;; Call `abbreviate-file-name' mainly to get letter case right per platform.
4758            (setq f1  (abbreviate-file-name (bookmark-get-filename b1))
4759                  f2  (abbreviate-file-name (bookmark-get-filename b2)))
4760            (when case-fold-search (setq f1  (bmkp-upcase f1)
4761                                         f2  (bmkp-upcase f2)))
4762            (cond ((string-lessp f1 f2)  '(t))
4763                  ((string-lessp f2 f1)  '(nil))
4764                  (t                     nil)))
4765           (f1                           '(t))
4766           (f2                           '(nil))
4767           (t                            nil))))
4768
4769 ;; We define all file-attribute predicates, in case you want to use them.
4770 ;;
4771 ;; `bmkp-file-attribute-0-cp'  - type
4772 ;; `bmkp-file-attribute-1-cp'  - links
4773 ;; `bmkp-file-attribute-2-cp'  - uid
4774 ;; `bmkp-file-attribute-3-cp'  - gid
4775 ;; `bmkp-file-attribute-4-cp'  - last access time
4776 ;; `bmkp-file-attribute-5-cp'  - last update time
4777 ;; `bmkp-file-attribute-6-cp'  - last status change
4778 ;; `bmkp-file-attribute-7-cp'  - size
4779 ;; `bmkp-file-attribute-8-cp'  - modes
4780 ;; `bmkp-file-attribute-9-cp'  - gid change
4781 ;; `bmkp-file-attribute-10-cp' - inode
4782 ;; `bmkp-file-attribute-11-cp' - device
4783 ;;
4784 (bmkp-define-file-sort-predicate 0) ; Type: file, symlink, dir
4785 (bmkp-define-file-sort-predicate 1) ; Links
4786 (bmkp-define-file-sort-predicate 2) ; Uid
4787 (bmkp-define-file-sort-predicate 3) ; Gid
4788 (bmkp-define-file-sort-predicate 4) ; Last access time
4789 (bmkp-define-file-sort-predicate 5) ; Last modification time
4790 (bmkp-define-file-sort-predicate 6) ; Last status-change time
4791 (bmkp-define-file-sort-predicate 7) ; Size
4792 (bmkp-define-file-sort-predicate 8) ; Modes
4793 (bmkp-define-file-sort-predicate 9) ; Gid would change if re-created
4794 (bmkp-define-file-sort-predicate 10) ; Inode
4795 (bmkp-define-file-sort-predicate 11) ; Device
4796
4797 (defun bmkp-local-file-accessed-more-recently-cp (b1 b2)
4798   "True if bookmark B1's local file was accessed more recently than B2's.
4799 B1 and B2 are bookmarks or bookmark names.
4800 A local file sorts before a remote file, which sorts before other
4801 bookmarks.  Two remote files are considered incomparable - their
4802 access times are not examined.
4803
4804 Reverse the roles of B1 and B2 for a false value.
4805 A true value is returned as `(t)', a false value as `(nil)'.
4806 Return nil if neither sorts before the other."
4807   (setq b1  (bookmark-get-bookmark b1)
4808         b2  (bookmark-get-bookmark b2))
4809   (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4810          (bmkp-cp-not (bmkp-file-attribute-4-cp b1 b2)))
4811         ((bmkp-local-file-bookmark-p b1)         '(t))
4812         ((bmkp-local-file-bookmark-p b2)         '(nil))
4813         ((and (bmkp-remote-file-bookmark-p b1)
4814               (bmkp-remote-file-bookmark-p b2))  nil)
4815         ((bmkp-remote-file-bookmark-p b1)        '(t))
4816         ((bmkp-remote-file-bookmark-p b2)        '(nil))
4817         (t                                       nil)))
4818
4819 (defun bmkp-local-file-updated-more-recently-cp (b1 b2)
4820   "True if bookmark B1's local file was updated more recently than B2's.
4821 B1 and B2 are bookmarks or bookmark names.
4822 A local file sorts before a remote file, which sorts before other
4823 bookmarks.  Two remote files are considered incomparable - their
4824 update times are not examined.
4825
4826 Reverse the roles of B1 and B2 for a false value.
4827 A true value is returned as `(t)', a false value as `(nil)'.
4828 Return nil if neither sorts before the other."
4829   (setq b1  (bookmark-get-bookmark b1)
4830         b2  (bookmark-get-bookmark b2))
4831   (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4832          (bmkp-cp-not (bmkp-file-attribute-5-cp b1 b2)))
4833         ((bmkp-local-file-bookmark-p b1)         '(t))
4834         ((bmkp-local-file-bookmark-p b2)         '(nil))
4835         ((and (bmkp-remote-file-bookmark-p b1)
4836               (bmkp-remote-file-bookmark-p b2))  nil)
4837         ((bmkp-remote-file-bookmark-p b1)        '(t))
4838         ((bmkp-remote-file-bookmark-p b2)        '(nil))
4839         (t                                       nil)))
4840
4841 (defun bmkp-local-file-size-cp (b1 b2)
4842   "True if bookmark B1's local file is larger than B2's.
4843 B1 and B2 are bookmarks or bookmark names.
4844 A local file sorts before a remote file, which sorts before other
4845 bookmarks.  Two remote files are considered incomparable - their
4846 sizes are not examined.
4847
4848 Reverse the roles of B1 and B2 for a false value.
4849 A true value is returned as `(t)', a false value as `(nil)'.
4850 Return nil if neither sorts before the other."
4851   (setq b1  (bookmark-get-bookmark b1)
4852         b2  (bookmark-get-bookmark b2))
4853   (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4854          (bmkp-cp-not (bmkp-file-attribute-7-cp b1 b2)))
4855         ((bmkp-local-file-bookmark-p b1)         '(t))
4856         ((bmkp-local-file-bookmark-p b2)         '(nil))
4857         ((and (bmkp-remote-file-bookmark-p b1)
4858               (bmkp-remote-file-bookmark-p b2))  nil)
4859         ((bmkp-remote-file-bookmark-p b1)        '(t))
4860         ((bmkp-remote-file-bookmark-p b2)        '(nil))
4861         (t                                       nil)))
4862
4863 (defun bmkp-local-file-type-cp (b1 b2)
4864   "True if bookmark B1 sorts by local file type before B2.
4865 B1 and B2 are bookmarks or bookmark names.
4866 For two local files, a file sorts before a symlink, which sorts before
4867 a directory.
4868
4869 A local file sorts before a remote file, which sorts before other
4870 bookmarks.  Two remote files are considered incomparable - their file
4871 types are not examined.
4872
4873 Reverse the roles of B1 and B2 for a false value.
4874 A true value is returned as `(t)', a false value as `(nil)'.
4875 Return nil if neither sorts before the other."
4876   (setq b1  (bookmark-get-bookmark b1)
4877         b2  (bookmark-get-bookmark b2))
4878   (cond ((and (bmkp-local-file-bookmark-p b1) (bmkp-local-file-bookmark-p b2))
4879          (bmkp-file-attribute-0-cp b1 b2))
4880         ((bmkp-local-file-bookmark-p b1)         '(t))
4881         ((bmkp-local-file-bookmark-p b2)         '(nil))
4882         ((and (bmkp-remote-file-bookmark-p b1)
4883               (bmkp-remote-file-bookmark-p b2))  nil)
4884         ((bmkp-remote-file-bookmark-p b1)        '(t))
4885         ((bmkp-remote-file-bookmark-p b2)        '(nil))
4886         (t                                       nil)))
4887
4888 (defun bmkp-cp-not (truth)
4889   "Return the negation of boolean value TRUTH.
4890 If TRUTH is (t), return (nil), and vice versa.
4891 If TRUTH is nil, return nil."
4892   (and truth (if (car truth) '(nil) '(t))))
4893
4894
4895 ;;(@* "Indirect Bookmarking Functions")
4896 ;;  *** Indirect Bookmarking Functions ***
4897
4898 ;;;###autoload
4899 (defun bmkp-url-target-set (url &optional prefix-only-p name/prefix) ; `C-x p c u'
4900   "Set a bookmark for a URL.  Return the bookmark.
4901 Interactively you are prompted for the URL.  Completion is available.
4902 Use `M-n' to pick up the url at point as the default.
4903
4904 You are also prompted for the bookmark name.  But with a prefix arg,
4905 you are prompted only for a bookmark-name prefix.  In that case, the
4906 bookmark name is the prefix followed by the URL."
4907   (interactive
4908    (list (if (require 'ffap nil t)
4909              (ffap-read-file-or-url "URL: " (or (thing-at-point 'url)  (and (fboundp 'url-get-url-at-point)
4910                                                                            (url-get-url-at-point))))
4911            (read-file-name "URL: " nil (or (thing-at-point 'url)  (and (fboundp 'url-get-url-at-point)
4912                                                                        (url-get-url-at-point)))))
4913          current-prefix-arg
4914          (if current-prefix-arg
4915              (read-string "Prefix for bookmark name: ")
4916            (bmkp-completing-read-lax "Bookmark name"))))
4917   (unless name/prefix (setq name/prefix  ""))
4918   (let ((bookmark-make-record-function  (if (eq major-mode 'w3m-mode)
4919                                             'bmkp-make-w3m-record
4920                                           (lambda () (bmkp-make-url-browse-record url))))
4921         bmk failure)
4922     (condition-case err
4923         (setq bmk  (bookmark-store (if prefix-only-p (concat name/prefix url) name/prefix)
4924                                    (cdr (bookmark-make-record)) nil))
4925       (error (setq failure  err)))
4926     (if (not failure)
4927         bmk                             ; Return the bookmark.
4928       (error "Failed to create bookmark for `%s':\n%s\n" url failure))))
4929
4930 ;;; Bound to `C-x p c f'
4931 ;;;###autoload
4932 (defun bmkp-file-target-set (file &optional prefix-only-p name/prefix no-overwrite msgp)
4933   "Set a bookmark for FILE.  Return the bookmark.
4934 The bookmarked position is the beginning of the file.
4935 Interactively you are prompted for FILE.  Completion is available.
4936 You can use `M-n' to pick up the file name at point, or if none then
4937 the visited file.
4938
4939 You are also prompted for the bookmark name.  But with a prefix arg,
4940 you are prompted only for a bookmark-name prefix.  In that case, the
4941 bookmark name is the prefix followed by the non-directory part of
4942 FILE.
4943
4944 From Lisp code, optional arg NO-OVERWRITE is passed to
4945 `bookmark-store': non-nil means do not overwrite an existing bookmark
4946 that has the same name."
4947   (interactive
4948    (list (read-file-name "File: " nil
4949                          (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
4950                                  (run-hook-with-args-until-success 'file-name-at-point-functions)
4951                                (ffap-guesser))
4952                              (thing-at-point 'filename)
4953                              (buffer-file-name)))
4954          current-prefix-arg
4955          (if current-prefix-arg
4956              (read-string "Prefix for bookmark name: ")
4957            (bmkp-completing-read-lax "Bookmark name"))
4958          'MSG))
4959   (unless name/prefix (setq name/prefix  ""))
4960   (let ((bookmark-make-record-function  (bmkp-make-record-for-target-file file))
4961         bmk failure)
4962     (condition-case err
4963         (setq bmk  (bookmark-store
4964                     (if prefix-only-p (concat name/prefix (file-name-nondirectory file)) name/prefix)
4965                     (cdr (bookmark-make-record)) no-overwrite))
4966       (error (setq failure  (error-message-string err))))
4967     (if (not failure)
4968         (prog1 bmk                      ; Return the bookmark.
4969           (when (and msgp  (not (file-exists-p file)))
4970             (message "File name is now bookmarked, but no such file yet: `%s'"
4971                      (expand-file-name file))))
4972       (error "Failed to create bookmark for `%s':\n%s\n" file failure))))
4973
4974 (defun bmkp-make-record-for-target-file (file)
4975   "Return a function that creates a bookmark record for FILE.
4976 The bookmarked position will be the beginning of the file."
4977   ;; $$$$$$ Maybe need a way to bypass default handler, at least for autofiles.
4978   ;;        Doesn't seem to make much sense to use a handler such as a shell cmd in this context. (?)
4979   (let ((default-handler  (condition-case nil (bmkp-default-handler-for-file file) (error nil))))
4980     (cond (default-handler              ; User default handler
4981               `(lambda () '((filename . ,file) (position . 0) (handler . ,default-handler))))
4982           ;; Non-user defaults.
4983           ((and (require 'image nil t) (require 'image-mode nil t) ; Image
4984                 (condition-case nil (image-type file) (error nil)))
4985            ;; Last two lines of function are from `image-bookmark-make-record'.
4986            ;; But don't use that directly, because it uses
4987            ;; `bookmark-make-record-default', which gets nil for `filename'.
4988
4989            ;; NEED to keep this code sync'd with `diredp-bookmark'.
4990            (lambda ()
4991              `((filename   . ,file)
4992                (position   . 0)
4993                (image-type . ,(image-type file))
4994                (handler    . image-bookmark-jump))))
4995           ((let ((case-fold-search  t)) (string-match "\\([.]au$\\|[.]wav$\\)" file)) ; Sound
4996            `(lambda () '((filename . ,file) (handler . bmkp-sound-jump))))
4997           (t
4998            `(lambda () '((filename . ,file) (position . 0)))))))
4999
5000 ;;;###autoload
5001 (defalias 'bmkp-bookmark-a-file 'bmkp-autofile-set)
5002 ;;;###autoload
5003 (defun bmkp-autofile-set (file &optional dir prefix msgp) ; Bound to `C-x p c a'
5004   "Set a bookmark for FILE, autonaming the bookmark for the file.
5005 Return the bookmark.
5006 Interactively, you are prompted for FILE.  You can use `M-n' to pick
5007 up the file name at point, or if none then the visited file.
5008
5009 The bookmark name is the non-directory part of FILE, but with a prefix
5010 arg you are also prompted for a PREFIX string to prepend to the
5011 bookmark name.  The bookmarked position is the beginning of the file.
5012
5013 Note that if you provide PREFIX then the bookmark will not satisfy
5014 `bmkp-autofile-bookmark-p' unless you provide the same PREFIX to that
5015 predicate.
5016
5017 The bookmark's file name is FILE if absolute.  If relative then it is
5018 FILE expanded in DIR, if non-nil, or in the current directory
5019 \(`default-directory').
5020
5021 If a bookmark with the same name already exists for the same file name
5022 then do nothing.
5023
5024 Otherwise, create a new bookmark for the file, even if a bookmark with
5025 the same name already exists.  This means that you can have more than
5026 one autofile bookmark with the same bookmark name and the same
5027 relative file name (non-directory part), but with different absolute
5028 file names."
5029   (interactive
5030    (list (read-file-name "File: " nil
5031                          (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5032                                  (run-hook-with-args-until-success 'file-name-at-point-functions)
5033                                (ffap-guesser))
5034                              (thing-at-point 'filename)
5035                              (buffer-file-name)))
5036          nil
5037          (and current-prefix-arg (read-string "Prefix for bookmark name: "))
5038          'MSG))
5039   (let* ((dir-to-use  (if (file-name-absolute-p file)
5040                          (file-name-directory file)
5041                        (or dir default-directory)))
5042         ;; Look for existing bookmark with same name, same file, in `dir-to-use'.
5043          (bmk         (bmkp-get-autofile-bookmark file dir-to-use prefix)))
5044     ;; If BMK was found, then instead of doing nothing we could replace the existing BMK with a new
5045     ;; one, as follows:
5046     ;; (let ((bookmark-make-record-function (bmkp-make-record-for-target-file file)))
5047     ;;   (bmkp-replace-existing-bookmark bmk)) ; Update the existing bookmark.    
5048     (or bmk                             ; Do nothing, and return the bookmark.
5049         ;; Create a new bookmark, and return it.
5050         (bmkp-file-target-set (expand-file-name file dir-to-use) t prefix 'NO-OVERWRITE msgp))))
5051
5052 (defun bmkp-get-autofile-bookmark (file &optional dir prefix)
5053   "Return an existing autofile bookmark for FILE, or nil if there is none.
5054 The bookmark name is the non-directory part of FILE, but if PREFIX is
5055 non-nil then it is PREFIX prepended to the non-directory part of FILE.
5056
5057 The directory part of property `filename' is the directory part of
5058 FILE, if FILE is absolute.  Otherwise, it is DIR, if non-nil, or
5059 `default-directory' otherwise.
5060
5061 FILE and the `filename' property of the bookmark returned are the
5062 same, except possibly for their directory parts (see previous)."
5063   (let* ((fname       (file-name-nondirectory file))
5064          (bname       (if prefix (concat prefix fname) fname))
5065          (dir-to-use  (if (file-name-absolute-p file)
5066                           (file-name-directory file)
5067                         (or dir default-directory))))
5068     ;; Look for existing bookmark with same name, same file, in `dir-to-use'.
5069     (catch 'bmkp-get-autofile-bookmark
5070       (dolist (bmk  bookmark-alist)
5071         (when (string= bname (bookmark-name-from-full-record bmk))
5072           (let* ((bfil  (bookmark-get-filename bmk))
5073                  (bdir  (and bfil (file-name-directory bfil))))
5074             (when (and bfil
5075                        (bmkp-same-file-p fname (file-name-nondirectory bfil))
5076                        (bmkp-same-file-p bdir dir-to-use))
5077               (throw 'bmkp-get-autofile-bookmark bmk))))) ; Return the bookmark.
5078       nil)))
5079
5080 ;;;###autoload
5081 (defalias 'bmkp-tag-a-file 'bmkp-autofile-add-tags) ; Bound to `C-x p t + a'
5082 ;;;###autoload
5083 (defun bmkp-autofile-add-tags (file tags &optional dir prefix msgp no-cache-update-p)
5084   "Add TAGS to autofile bookmark for FILE.
5085 Interactively, you are prompted for FILE and then TAGS.
5086 When prompted for FILE you can use `M-n' to pick up the file name at
5087 point, or if none then the visited file.
5088
5089 When prompted for tags, hit `RET' to enter each tag, then hit `RET'
5090 again after the last tag.  You can use completion to enter each tag.
5091 Completion is lax: you are not limited to existing tags.
5092
5093 TAGS is a list of strings. DIR, PREFIX are as for `bmkp-autofile-set'.
5094 Non-nil MSGP means display a message about the addition.
5095 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
5096 Return the number of tags added."
5097   (interactive
5098    (list (read-file-name "File: " nil
5099                          (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5100                                  (run-hook-with-args-until-success 'file-name-at-point-functions)
5101                                (ffap-guesser))
5102                              (thing-at-point 'filename)
5103                              (buffer-file-name)))
5104          (bmkp-read-tags-completing)
5105          nil
5106          (and current-prefix-arg (read-string "Prefix for bookmark name: "))
5107          'msg))
5108   (bmkp-add-tags (bmkp-autofile-set file dir prefix) tags msgp no-cache-update-p))
5109   
5110 ;;;###autoload
5111 (defalias 'bmkp-untag-a-file 'bmkp-autofile-remove-tags) ; Bound to `C-x p t - a'
5112 ;;;###autoload
5113 (defun bmkp-autofile-remove-tags (file tags &optional dir prefix msgp no-cache-update-p)
5114   "Remove TAGS from autofile bookmark for FILE.
5115 Interactively, you are prompted for TAGS and then FILE.
5116 With Emacs 22 and later, only files with at least one of the given
5117 tags are candidates.
5118
5119 When prompted for tags, hit `RET' to enter each tag to be removed,
5120 then hit `RET' again after the last tag.  You can use completion to
5121 enter each tag.
5122
5123 When prompted for FILE you can use `M-n' to pick up the file name at
5124 point, or if none then the visited file.
5125
5126 TAGS is a list of strings. DIR, PREFIX are as for `bmkp-autofile-set'.
5127 Non-nil MSGP means display a message about the addition.
5128 Non-nil NO-CACHE-UPDATE-P means do not update `bmkp-tags-alist'.
5129 Return the number of tags removed."
5130   (interactive
5131    (let* ((pref  (and current-prefix-arg (read-string "Prefix for bookmark name: ")))
5132           (tgs   (bmkp-read-tags-completing))
5133           (fil   (condition-case nil
5134                      (read-file-name
5135                       "File: " nil
5136                       (or (if (boundp 'file-name-at-point-functions) ; In `files.el', Emacs 23.2+.
5137                               (run-hook-with-args-until-success 'file-name-at-point-functions)
5138                             (ffap-guesser))
5139                           (thing-at-point 'filename)
5140                           (buffer-file-name))
5141                       t nil
5142                       (lambda (ff)      ; PREDICATE - only for Emacs 22+.
5143                         (let* ((bmk   (bmkp-get-autofile-bookmark ff nil pref))
5144                                (btgs  (and bmk (bmkp-get-tags bmk))))
5145                           (and btgs  (catch 'bmkp-autofile-remove-tags
5146                                        (dolist (tag  tgs)
5147                                          (when (not (member tag btgs))
5148                                            (throw 'bmkp-autofile-remove-tags nil)))
5149                                        t)))))
5150                    (error (read-file-name
5151                            "File: " nil
5152                            (or (ffap-guesser)
5153                                (thing-at-point 'filename)
5154                                (buffer-file-name)))))))
5155      (list fil tgs nil pref 'MSG)))
5156   (bmkp-remove-tags (bmkp-autofile-set file dir prefix) tags msgp no-cache-update-p))
5157
5158 ;;;###autoload
5159 (defun bmkp-purge-notags-autofiles (&optional prefix) ; Not bound
5160   "Delete all autofile bookmarks that have no tags.
5161 With a prefix arg, you are prompted for a PREFIX for the bookmark name."
5162   (interactive (if (not (y-or-n-p "Delete all autofile bookmarks that do not have tags? "))
5163                    (error "Deletion cancelled")
5164                  (list (and current-prefix-arg (read-string "Prefix for bookmark name: ")))))
5165   (let ((bmks  (bmkp-autofile-alist-only prefix))
5166         record tags)
5167     ;; Needs Bookmark+ version of `bookmark-delete', which accepts a bookmark, not just its name.
5168     (dolist (bmk  bmks)
5169       (when (and (setq tags  (assq 'tags (bookmark-get-bookmark-record bmk)))
5170                  (or (not tags) (null (cdr tags))))
5171         (bookmark-delete bmk)))
5172     (bmkp-tags-list)))                  ; Update the tags cache.
5173
5174 ;; $$$$$$ Not used currently.
5175 (defun bmkp-replace-existing-bookmark (bookmark)
5176   "Replace existing BOOKMARK with a new one of the same name.
5177 Return the new bookmark.
5178 BOOKMARK is a full bookmark record, not a bookmark name.
5179 This just replaces the existing bookmark data with the data for a new
5180 bookmark, based on `bookmark-make-record-function'.  The bookmark name
5181 is unchanged."
5182   (let (failure)
5183     (condition-case err
5184         (progn                          ; Code similar to `bookmark-store'.
5185           (setcdr bookmark (cdr (bookmark-make-record)))
5186           (bmkp-maybe-save-bookmarks)
5187           (setq bookmark-current-bookmark  (bookmark-name-from-full-record bookmark))
5188           (bookmark-bmenu-surreptitiously-rebuild-list))
5189       (error (setq failure  (error-message-string err))))
5190     (if (not failure)
5191         bookmark                        ; Return the bookmark.
5192       (error "Failed to update bookmark `%s':\n%s\n"
5193              (bookmark-name-from-full-record bookmark) failure))))
5194
5195 (defun bmkp-default-handler-for-file (filename)
5196   "Return a default bookmark handler for FILENAME, or nil.
5197 If non-nil, it is a Lisp function, determined as follows:
5198
5199 1. Match FILENAME against `bmkp-default-handler-associations'.  If it
5200 matches a Lisp function, return that function.  If it matches a shell
5201 command, return a Lisp function that invokes that command.
5202
5203 2. If no match is found and `bmkp-guess-default-handler-for-file-flag'
5204 is non-nil, then try to find an appropriate shell command using, in
5205 order, `dired-guess-default' and (Emacs 23+ only)
5206 `mailcap-file-default-commands'.  If a match is found then return a
5207 Lisp function that invokes that shell command."
5208   (let* ((bmkp-user  (bmkp-default-handler-user filename))
5209          (shell-cmd  (if (stringp bmkp-user)
5210                          bmkp-user
5211                        (and (not bmkp-user)
5212                             bmkp-guess-default-handler-for-file-flag
5213                             (or (and (require 'dired-x nil t)
5214                                      (let* ((case-fold-search
5215                                              (or (and (boundp 'dired-guess-shell-case-fold-search)
5216                                                       dired-guess-shell-case-fold-search)
5217                                                  case-fold-search))
5218                                             (default  (dired-guess-default (list filename))))
5219                                        (if (consp default) (car default) default)))
5220                                 (and (require 'mailcap nil t) ; Emacs 23+
5221                                      (car (mailcap-file-default-commands (list filename)))))))))
5222     (cond ((stringp shell-cmd) `(lambda (bmk) (dired-do-shell-command ,shell-cmd nil ',(list filename))))
5223           ((or (functionp bmkp-user) (and bmkp-user (symbolp bmkp-user)))
5224            `(lambda (bmk) (funcall #',bmkp-user ,filename)))
5225           (t nil))))    
5226
5227 (defun bmkp-default-handler-user (filename)
5228   "Return default handler for FILENAME.
5229 The value is based on `bmkp-default-handler-associations'."
5230   (catch 'bmkp-default-handler-user
5231     (dolist (assn  bmkp-default-handler-associations)
5232       (when (string-match (car assn) filename)
5233         (throw 'bmkp-default-handler-user (cdr assn))))
5234     nil))
5235
5236 (defun bmkp-sound-jump (bookmark)
5237   "Handler for sound files: play the sound file that is BOOKMARK's file."
5238   (play-sound-file (bookmark-get-filename bookmark)))
5239
5240 (when (> emacs-major-version 21)
5241   (defun bmkp-compilation-target-set (&optional prefix) ; `C-c C-b'
5242     "Set a bookmark at the start of the line for this compilation hit.
5243 You are prompted for the bookmark name.  But with a prefix arg, you
5244 are prompted only for a PREFIX string.  In that case, and in Lisp
5245 code, the bookmark name is PREFIX followed by the (relative) file name
5246 of the hit, followed by the line number of the hit."
5247     (interactive "P")
5248     (let* ((file+line  (bmkp-compilation-file+line-at (line-beginning-position)))
5249            (file       (car file+line))
5250            (line       (cdr file+line)))
5251       (unless (and file line) (error "Cursor is not on a compilation hit"))
5252       (save-excursion
5253         (with-current-buffer (find-file-noselect file)
5254           (goto-char (point-min)) (forward-line (1- line))
5255           (if (not prefix)
5256               (call-interactively #'bookmark-set)
5257             (when (interactive-p)
5258               (setq prefix  (read-string "Prefix for bookmark name: ")))
5259             (unless (stringp prefix) (setq prefix  ""))
5260             (bookmark-set (format "%s%s, line %s" prefix (file-name-nondirectory file) line)
5261                           99 'INTERACTIVEP))))))
5262
5263   (defun bmkp-compilation-file+line-at (&optional pos)
5264     "Return the file and position indicated by this compilation message.
5265 These are returned as a cons: (FILE . POSITION).
5266 POSITION is the beginning of the line indicated by the message."
5267     (unless pos (setq pos (point)))
5268     (let* ((loc        (car (get-text-property pos 'message)))
5269            (line       (cadr loc))
5270            (filename   (caar (nth 2 loc)))
5271            (directory  (cadr (car (nth 2 loc))))
5272            (spec-dir   (if directory (expand-file-name directory) default-directory)))
5273       (cons (expand-file-name filename spec-dir) line))))
5274     
5275 (when (> emacs-major-version 21)
5276   (defun bmkp-compilation-target-set-all (prefix &optional msgp) ; `C-c C-M-b'
5277     "Set a bookmark for each hit of a compilation buffer.
5278 NOTE: You can use `C-x C-q' to make the buffer writable and then
5279       remove any hits that you do not want to bookmark.  Only the hits
5280       remaining in the buffer are bookmarked.
5281
5282 You are prompted for a PREFIX string to prepend to each bookmark name,
5283 the rest of which is the file name of the hit followed by its line
5284 number."
5285     (interactive (list (read-string "Prefix for bookmark name: ") 'MSGP))
5286     (if (y-or-n-p "This will bookmark *EACH* hit in the buffer.  Continue? ")
5287         (let ((count  0))
5288           (save-excursion
5289             (goto-char (point-min))
5290             (when (get-text-property (point) 'message) ; Visible part of buffer starts with a hit
5291               (condition-case nil       ; because buffer is narrowed or header text otherwise removed.
5292                   (bmkp-compilation-target-set prefix) ; Ignore error here (e.g. killed buffer).
5293                 (error nil))
5294               (setq count  (1+ count)))
5295             (while (and (condition-case nil (progn (compilation-next-error 1) t) (error nil))
5296                         (not (eobp)))
5297               (condition-case nil
5298                   (bmkp-compilation-target-set prefix) ; Ignore error here (e.g. killed buffer).
5299                 (error nil))
5300               (setq count  (1+ count)))
5301             (when msgp (message "Set %d bookmarks" count))))
5302       (message "OK - canceled"))))
5303
5304
5305 ;; We could make the `occur' code work for Emacs 20 & 21 also, but you would not be able to
5306 ;; delete some occurrences and bookmark only the remaining ones.
5307
5308 (when (> emacs-major-version 21)
5309   (defun bmkp-occur-target-set (&optional prefix) ; `C-c C-b'
5310     "Set a bookmark at the start of the line for this `(multi-)occur' hit.
5311 You are prompted for the bookmark name.  But with a prefix arg, you
5312 are prompted only for a PREFIX string.  In that case, and in Lisp
5313 code, the bookmark name is PREFIX followed by the buffer name of the
5314 hit, followed by the line number of the hit.
5315
5316 You can use this only in `Occur' mode (commands such as `occur' and
5317 `multi-occur')."
5318     (interactive "P")
5319     (unless (eq major-mode 'occur-mode) (error "You must be in `occur-mode'"))
5320     (let* ((line  (and prefix
5321                        (save-excursion
5322                          (forward-line 0)
5323                          ;; We could use [: ] here, to handle `moccur', but that loses anyway for
5324                          ;; `occur-mode-find-occurrence', so we would need other hoops too.
5325                          (re-search-forward "^\\s-+\\([0-9]+\\):" (line-end-position) 'NOERROR)
5326                          (or (format "%5d" (string-to-number (match-string 1))) ""))))
5327            (mkr   (occur-mode-find-occurrence))
5328            (buf   (marker-buffer mkr))
5329            (file  (or (buffer-file-name buf) bmkp-non-file-filename)))
5330       (save-excursion (with-current-buffer buf
5331                         (goto-char mkr)
5332                         (if (not prefix)
5333                             (call-interactively #'bookmark-set)
5334                           (when (interactive-p)
5335                             (setq prefix  (read-string "Prefix for bookmark name: ")))
5336                           (unless (stringp prefix) (setq prefix  ""))
5337                           (bookmark-set (format "%s%s, line %s" prefix buf line) 99 'INTERACTIVEP)))))))
5338
5339 (when (> emacs-major-version 21)
5340   (defun bmkp-occur-target-set-all (&optional prefix msgp) ; `C-c C-M-b'
5341     "Set a bookmark for each hit of a `(multi-)occur' buffer.
5342 NOTE: You can use `C-x C-q' to make the buffer writable and then
5343       remove any hits that you do not want to bookmark.  Only the hits
5344       remaining in the buffer are bookmarked.
5345
5346 You are prompted for a PREFIX string to prepend to each bookmark name,
5347 the rest of which is the buffer name of the hit followed by its line
5348 number.
5349
5350 You can use this only in `Occur' mode (commands such as `occur' and
5351 `multi-occur').
5352
5353 See also command `bmkp-occur-create-autonamed-bookmarks', which
5354 creates autonamed bookmarks to all `occur' and `multi-occur' hits."
5355     (interactive (list (read-string "Prefix for bookmark name: ") 'MSGP))
5356     (if (y-or-n-p "This will bookmark *EACH* hit in the buffer.  Continue? ")
5357         (let ((count  0))
5358           (save-excursion
5359             (goto-char (point-min))
5360             (while (condition-case nil
5361                        (progn (occur-next) t) ; "No more matches" ends loop.
5362                      (error nil))
5363               (condition-case nil
5364                   (bmkp-occur-target-set prefix) ; Ignore error here (e.g. killed buffer).
5365                 (error nil))
5366               (setq count  (1+ count)))
5367             (when msgp (message "Set %d bookmarks" count))))
5368       (message "OK - canceled"))))
5369
5370
5371 ;;(@* "Other Bookmark+ Functions (`bmkp-*')")
5372 ;;  *** Other Bookmark+ Functions (`bmkp-*') ***
5373
5374 ;;;###autoload
5375 (defun bmkp-describe-bookmark (bookmark &optional defn)
5376   "Describe BOOKMARK.
5377 With a prefix argument, show the internal definition of the bookmark.
5378 BOOKMARK is a bookmark name or a bookmark record.
5379
5380 Starting with Emacs 22, if the file is an image file then:
5381 * Show a thumbnail of the image as well.
5382 * If you have command-line tool `exiftool' installed and in your
5383   `$PATH' or `exec-path', then show EXIF data (metadata) about the
5384   image.  See standard Emacs library `image-dired.el' for more
5385   information about `exiftool'"
5386   (interactive (list (bookmark-completing-read
5387                       "Describe bookmark"
5388                       (or (and (fboundp 'bmkp-default-lighted) (bmkp-default-lighted))
5389                           (bmkp-default-bookmark-name)))
5390                      current-prefix-arg))
5391   (if defn
5392       (bmkp-describe-bookmark-internals bookmark)
5393     (setq bookmark     (bookmark-get-bookmark bookmark))
5394     (help-setup-xref (list #'bmkp-describe-bookmark bookmark) (interactive-p))
5395     (let ((help-text  (bmkp-bookmark-description bookmark)))
5396       (with-output-to-temp-buffer "*Help*" (princ help-text))
5397       (with-current-buffer "*Help*"
5398         (save-excursion
5399           (goto-char (point-min))
5400           (when (re-search-forward "@#%&()_IMAGE-HERE_()&%#@\\(.+\\)" nil t)
5401             (let* ((image-file        (match-string 1))
5402                    (image-string      (save-match-data
5403                                         (apply #'propertize "X"
5404                                                `(display
5405                                                  ,(append (image-dired-get-thumbnail-image image-file)
5406                                                           '(:margin 10))
5407                                                  rear-nonsticky (display)
5408                                                  mouse-face highlight
5409                                                  follow-link t
5410                                                  help-echo "`mouse-2' or `RET': Show full image"
5411                                                  keymap
5412                                                  (keymap
5413                                                   (mouse-2
5414                                                    . (lambda (e) (interactive "e")
5415                                                              (find-file ,image-file)))
5416                                                   (13
5417                                                    . (lambda () (interactive)
5418                                                              (find-file ,image-file))))))))
5419                    (buffer-read-only  nil))
5420               (replace-match image-string)))))
5421       help-text)))
5422
5423 (defun bmkp-bookmark-description (bookmark &optional no-image)
5424   "Help-text description of BOOKMARK.
5425 BOOKMARK is a bookmark name or a bookmark record.
5426 Starting with Emacs 22 and unless optional arg NO-IMAGE is non-nil, if
5427 the file is an image file then the description includes the following:
5428 * A placeholder for a thumbnail image: \"@#%&()_IMAGE-HERE_()&%#@\"
5429 * EXIF data (metadata) about the image, provided you have command-line
5430   tool `exiftool' installed and in your `$PATH' or `exec-path'.  See
5431   standard Emacs library `image-dired.el' for more information about
5432   `exiftool'."
5433   (setq bookmark  (bookmark-get-bookmark bookmark))
5434   (let ((bname            (bookmark-name-from-full-record bookmark))
5435         (buf              (bmkp-get-buffer-name bookmark))
5436         (file             (bookmark-get-filename bookmark))
5437         (image-p          (bmkp-bookmark-image-bookmark-p bookmark))
5438         (location         (bookmark-prop-get bookmark 'location))
5439         (start            (bookmark-get-position bookmark))
5440         (end              (bmkp-get-end-position bookmark))
5441         (created          (bookmark-prop-get bookmark 'created))
5442         (time             (bmkp-get-visit-time bookmark))
5443         (visits           (bmkp-get-visits-count bookmark))
5444         (tags             (mapcar #'bmkp-tag-name (bmkp-get-tags bookmark)))
5445         (sequence-p       (bmkp-sequence-bookmark-p bookmark))
5446         (function-p       (bmkp-function-bookmark-p bookmark))
5447         (variable-list-p  (bmkp-variable-list-bookmark-p bookmark))
5448         (desktop-p        (bmkp-desktop-bookmark-p bookmark))
5449         (bookmark-file-p  (bmkp-bookmark-file-bookmark-p bookmark))
5450         (dired-p          (bmkp-dired-bookmark-p bookmark))
5451         (gnus-p           (bmkp-gnus-bookmark-p bookmark))
5452         (info-p           (bmkp-info-bookmark-p bookmark))
5453         (man-p            (bmkp-man-bookmark-p bookmark))
5454         (url-p            (bmkp-url-bookmark-p bookmark))
5455         (w3m-p            (bmkp-w3m-bookmark-p bookmark))
5456         (annot            (bookmark-get-annotation bookmark))
5457         no-position-p)
5458     (setq no-position-p  (not start))
5459     (when (or sequence-p function-p variable-list-p) (setq no-position-p  t))
5460     (let* ((help-text
5461             (concat
5462              (format "Bookmark `%s'\n%s\n\n" bname (make-string (+ 11 (length bname)) ?-))
5463              (cond (sequence-p       (format "Sequence:\n%s\n"
5464                                              (pp-to-string
5465                                               (bookmark-prop-get bookmark 'sequence))))
5466                    (function-p       (let ((fn  (bookmark-prop-get bookmark 'function)))
5467                                        (if (symbolp fn)
5468                                            (format "Function:\t\t%s\n" fn)
5469                                          (format "Function:\n%s\n"
5470                                                  (pp-to-string
5471                                                   (bookmark-prop-get bookmark 'function))))))
5472                    (variable-list-p  (format "Variable list:\n%s\n"
5473                                              (pp-to-string (bookmark-prop-get bookmark 'variables))))
5474                    (gnus-p           (format "Gnus, group:\t\t%s, article: %s, message-id: %s\n"
5475                                              (bookmark-prop-get bookmark 'group)
5476                                              (bookmark-prop-get bookmark 'article)
5477                                              (bookmark-prop-get bookmark 'message-id)))
5478                    (man-p            (format "UNIX `man' page for:\t`%s'\n"
5479                                              (or (bookmark-prop-get bookmark 'man-args)
5480                                                  ;; WoMan has no variable for the cmd name.
5481                                                  (bookmark-prop-get bookmark 'buffer-name))))
5482                    (info-p           (format "Info node:\t\t(%s) %s\n"
5483                                              (file-name-nondirectory file)
5484                                              (bookmark-prop-get bookmark 'info-node)))
5485                    (w3m-p            (format "W3m URL:\t\t%s\n" file))
5486                    (url-p            (format "URL:\t\t%s\n" location))
5487                    (desktop-p        (format "Desktop file:\t\t%s\n"
5488                                              (bookmark-prop-get bookmark 'desktop-file)))
5489                    (bookmark-file-p  (format "Bookmark file:\t\t%s\n"
5490                                              (bookmark-prop-get bookmark 'bookmark-file)))
5491                    (dired-p          (let ((switches  (bookmark-prop-get bookmark 'dired-switches))
5492                                            (marked    (length (bookmark-prop-get bookmark
5493                                                                                  'dired-marked)))
5494                                            (inserted  (length (bookmark-prop-get bookmark
5495                                                                                  'dired-subdirs)))
5496                                            (hidden    (length
5497                                                        (bookmark-prop-get bookmark
5498                                                                           'dired-hidden-dirs))))
5499                                        (format "Dired%s:%s\t\t%s\nMarked:\t\t\t%s\n\
5500 Inserted subdirs:\t%s\nHidden subdirs:\t\t%s\n"
5501                                                (if switches (format " `%s'" switches) "")
5502                                                (if switches "" (format "\t"))
5503                                                (expand-file-name file)
5504                                                marked inserted hidden)))
5505                    ((equal file bmkp-non-file-filename)
5506                     (format "Buffer:\t\t\t%s\n" (bmkp-get-buffer-name bookmark)))
5507                    (file        (concat (format "File:\t\t\t%s\n" (file-name-nondirectory file))
5508                                         (let ((dir   (file-name-directory (expand-file-name file))))
5509                                           (and dir (format "Directory:\t\t%s\n" dir)))))
5510                    (t           "Unknown\n"))
5511              (unless no-position-p
5512                (if (bmkp-region-bookmark-p bookmark)
5513                    (format "Region:\t\t\t%d to %d (%d chars)\n" start end (- end start))
5514                  (format "Position:\t\t%d\n" start)))
5515              (and visits  (format "Visits:\t\t\t%d\n" visits))
5516              (and time    (format "Last visit:\t\t%s\n" (format-time-string "%c" time)))
5517              (and created (format "Creation:\t\t%s\n" (format-time-string "%c" created)))
5518              (and tags    (format "Tags:\t\t\t%S\n" tags))
5519              (and annot   (format "\nAnnotation:\n%s" annot))
5520              (and (not no-image)
5521                   (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
5522                   (if (fboundp 'string-match-p)
5523                       (string-match-p (image-file-name-regexp) file)
5524                     (save-match-data
5525                       (string-match (image-file-name-regexp) file)))
5526                   (if (fboundp 'display-graphic-p) (display-graphic-p) window-system)
5527                   (require 'image-dired nil t)
5528                   (image-dired-get-thumbnail-image file)
5529                   (concat "\n@#%&()_IMAGE-HERE_()&%#@" file "\n"))
5530              (and (not no-image)
5531                   (fboundp 'image-file-name-regexp) ; In `image-file.el' (Emacs 22+).
5532                   (if (fboundp 'string-match-p)
5533                       (string-match-p (image-file-name-regexp) file)
5534                     (save-match-data
5535                       (string-match (image-file-name-regexp) file)))                       
5536                   (progn (message "Gathering image data...") t)
5537                   (condition-case nil
5538                       (let ((all  (bmkp-all-exif-data (expand-file-name file))))
5539                         (concat
5540                          (and all (not (zerop (length all)))
5541                               (format "\nImage Data (EXIF)\n-----------------\n%s"
5542                                       all))))
5543                     (error nil))))))
5544       help-text)))
5545
5546 ;; This is the same as `help-all-exif-data' in `help-fns+.el', but we avoid requiring that library.
5547 (defun bmkp-all-exif-data (file)
5548   "Return all EXIF data from FILE, using command-line tool `exiftool'."
5549   (with-temp-buffer
5550     (delete-region (point-min) (point-max))
5551     (unless (eq 0 (call-process shell-file-name nil t nil shell-command-switch
5552                                 (format "exiftool -All \"%s\"" file)))
5553       (error "Could not get EXIF data"))
5554     (buffer-substring (point-min) (point-max))))
5555
5556
5557 ;;;###autoload
5558 (defun bmkp-describe-bookmark-internals (bookmark)
5559   "Show the internal definition of the bookmark BOOKMARK.
5560 BOOKMARK is a bookmark name or a bookmark record."
5561   (interactive (list (bookmark-completing-read "Describe bookmark" (bmkp-default-bookmark-name))))
5562   (setq bookmark  (bookmark-get-bookmark bookmark))
5563   (help-setup-xref (list #'bmkp-describe-bookmark-internals bookmark) (interactive-p))
5564   (let* ((bname      (bookmark-name-from-full-record bookmark))
5565          (help-text  (format "Bookmark `%s'\n%s\n\n%s" bname (make-string (+ 11 (length bname)) ?-)
5566                              (pp-to-string bookmark))))
5567     (with-output-to-temp-buffer "*Help*" (princ help-text))
5568     help-text))
5569
5570 ;;;###autoload
5571 (defun bmkp-list-defuns-in-commands-file ()
5572   "List the functions defined in `bmkp-bmenu-commands-file'.
5573 Typically, these are all commands."
5574   (interactive)
5575   (when (and bmkp-bmenu-commands-file (file-readable-p bmkp-bmenu-commands-file))
5576     (let ((fns  ())
5577           (buf  (let ((enable-local-variables  nil))
5578                   (find-file-noselect bmkp-bmenu-commands-file))))
5579       (help-setup-xref (list #'bmkp-list-defuns-in-commands-file) (interactive-p))
5580       (with-current-buffer buf
5581         (goto-char (point-min))
5582         (while (not (eobp))
5583           (when (re-search-forward "\\s-*(defun \\([^ \t\n(\"]+\\)[ \t\n(]" nil 'move)
5584             (push (match-string 1) fns)))
5585         (setq fns  (nreverse fns)
5586               fns  (sort fns 'string-lessp)))
5587       (when (buffer-live-p buf) (kill-buffer buf))
5588       (with-output-to-temp-buffer "*Help*"
5589         (princ "Bookmark Commands You Defined (in `bmkp-bmenu-commands-file')") (terpri)
5590         (princ "------------------------------------------------------------------") (terpri)
5591         (terpri)
5592         (let ((non-dups  (bmkp-remove-dups fns)))
5593           (dolist (fn  non-dups)
5594             (if (and (fboundp (intern fn)) (fboundp 'help-insert-xref-button))
5595                 (with-current-buffer "*Help*"
5596                   (goto-char (point-max))
5597                   (help-insert-xref-button fn 'help-function (intern fn) (commandp (intern fn))))
5598               (princ fn))
5599             (let ((dups   (member fn fns)) ; Sorted, so all dups are together.
5600                   (count  0))
5601               (while (equal fn (car dups))
5602                 (setq count  (1+ count)
5603                       dups   (cdr dups)))
5604               (when (> count 1) (princ (format "  (%d times)" count))))
5605             (terpri)))
5606         (help-make-xrefs (current-buffer)))
5607       fns)))
5608
5609 (defun bmkp-root-or-sudo-logged-p ()
5610   "Return t if the user logged in using Tramp as `root' or `sudo'.
5611 Otherwise, return nil."
5612   (catch 'break
5613     (dolist (ii  (mapcar #'buffer-name (buffer-list)))
5614       (when (string-match "*tramp/\\(su\\|sudo\\) ." ii) (throw 'break t)))))
5615
5616 (defun bmkp-position-post-context (breg)
5617   "Return `bookmark-search-size' chars, starting at position BREG.
5618 Return nil if there are not that many chars.
5619 This is text that follows the bookmark's `position'.
5620 This is used for a non-region bookmark."
5621   (and (>= (- (point-max) breg) bookmark-search-size)
5622        (buffer-substring-no-properties breg (+ breg bookmark-search-size))))
5623
5624 (defun bmkp-position-post-context-region (breg ereg)
5625   "Return the region prefix, at BREG.
5626 Return at most `bmkp-region-search-size' or (- EREG BREG) chars.
5627 This is text that follows the bookmark's `position'.
5628 This is used for a region bookmark."
5629   (buffer-substring-no-properties breg (+ breg (min bmkp-region-search-size (- ereg breg)))))
5630
5631 (defun bmkp-position-pre-context (breg)
5632   "Return `bookmark-search-size' chars that precede BREG.
5633 Return nil if there are not that many chars.
5634 This is text that precedes the bookmark's `position'.
5635 This is used for a non-region bookmark."
5636   (and (>= (- breg (point-min)) bookmark-search-size)
5637        (buffer-substring-no-properties breg (- breg bookmark-search-size))))
5638
5639 (defun bmkp-position-pre-context-region (breg)
5640   "Return the text preceding the region beginning, BREG.
5641 Return at most `bmkp-region-search-size' chars.
5642 This is text that precedes the bookmark's `position'.
5643 This is used for a region bookmark."
5644   (buffer-substring-no-properties (max (- breg bmkp-region-search-size) (point-min)) breg))
5645
5646 (defun bmkp-end-position-pre-context (breg ereg)
5647   "Return the region suffix, ending at EREG.
5648 Return at most `bmkp-region-search-size' or (- EREG BREG) chars.
5649 This is text that precedes the bookmark's `end-position'."
5650   (buffer-substring-no-properties (- ereg (min bmkp-region-search-size (- ereg breg))) ereg))
5651
5652 (defun bmkp-end-position-post-context (ereg)
5653   "Return the text following the region end, EREG.
5654 Return at most `bmkp-region-search-size' chars.
5655 This is text that follows the bookmark's `end-position'."
5656   (buffer-substring-no-properties ereg
5657                                   (+ ereg (min bmkp-region-search-size (- (point-max) (point))))))
5658
5659 (defun bmkp-position-after-whitespace (position)
5660   "Move forward from POSITION, skipping over whitespace.  Return point."
5661   (goto-char position)  (skip-chars-forward " \n\t" (point-max))  (point))
5662
5663 (defun bmkp-position-before-whitespace (position)
5664   "Move backward from POSITION, skipping over whitespace.  Return point."
5665   (goto-char position)  (skip-chars-backward " \n\t" (point-min))  (point))
5666
5667 (defun bmkp-save-new-region-location (bookmark beg end)
5668   "Update and save `bookmark-alist' for BOOKMARK, relocating its region.
5669 BOOKMARK is a bookmark record.
5670 BEG and END are the new region limits for BOOKMARK.
5671 Do nothing and return nil if `bmkp-save-new-location-flag' is nil.
5672 Otherwise, return non-nil if region was relocated."
5673   (and bmkp-save-new-location-flag
5674        (y-or-n-p "Region relocated.  Do you want to save new region limits? ")
5675        (progn
5676          (bookmark-prop-set bookmark 'front-context-string (bmkp-position-post-context-region
5677                                                             beg end))
5678          (bookmark-prop-set bookmark 'rear-context-string (bmkp-position-pre-context-region beg))
5679          (bookmark-prop-set bookmark 'front-context-region-string (bmkp-end-position-pre-context
5680                                                                    beg end))
5681          (bookmark-prop-set bookmark 'rear-context-region-string (bmkp-end-position-post-context end))
5682          (bookmark-prop-set bookmark 'position beg)
5683          (bookmark-prop-set bookmark 'end-position end)
5684          (bmkp-maybe-save-bookmarks)
5685          t)))
5686
5687 (defun bmkp-handle-region-default (bookmark)
5688   "Default function to handle BOOKMARK's region.
5689 BOOKMARK is a bookmark name or a bookmark record.
5690 Relocate the region if necessary, then activate it.
5691 If region was relocated, save it if user confirms saving."
5692   ;; Relocate by searching from the beginning (and possibly the end) of the buffer.
5693   (let* (;; Get bookmark object once and for all.
5694          ;; Actually, we know BOOKMARK is a bookmark object (not a name), but play safe.
5695          (bmk              (bookmark-get-bookmark bookmark))
5696          (bor-str          (bookmark-get-front-context-string bmk))
5697          (eor-str          (bookmark-prop-get bmk 'front-context-region-string))
5698          (br-str           (bookmark-get-rear-context-string bmk))
5699          (ar-str           (bookmark-prop-get bookmark 'rear-context-region-string))
5700          (pos              (bookmark-get-position bmk))
5701          (end-pos          (bmkp-get-end-position bmk))
5702          (reg-retrieved-p  t)
5703          (reg-relocated-p  nil))
5704     (unless (and (string= bor-str (buffer-substring-no-properties (point) (+ (point)
5705                                                                              (length bor-str))))
5706                  (save-excursion
5707                    (goto-char end-pos)
5708                    (string= eor-str (buffer-substring-no-properties (point) (- (point)
5709                                                                                (length bor-str))))))
5710       ;; Relocate region by searching from beginning (and possibly from end) of buffer.
5711       (let ((beg  nil)
5712             (end  nil))
5713         ;;  Go to bob and search forward for END.
5714         (goto-char (point-min))
5715         (if (search-forward eor-str (point-max) t) ; Find END, using `eor-str'.
5716             (setq end  (point))
5717           ;; Verify that region is not before context.
5718           (unless (search-forward br-str (point-max) t)
5719             (when (search-forward ar-str (point-max) t) ; Find END, using `ar-str'.
5720               (setq end  (match-beginning 0)
5721                     end  (and end (bmkp-position-before-whitespace end))))))
5722         ;; If failed to find END, go to eob and search backward for BEG.
5723         (unless end (goto-char (point-max)))
5724         (if (search-backward bor-str (point-min) t) ; Find BEG, using `bor-str'.
5725             (setq beg  (point))
5726           ;; Verify that region is not after context.
5727           (unless (search-backward ar-str (point-min) t)
5728             (when (search-backward br-str (point-min) t) ; Find BEG, using `br-str'.
5729               (setq beg  (match-end 0)
5730                     beg  (and beg (bmkp-position-after-whitespace beg))))))
5731         (setq reg-retrieved-p  (or beg end)
5732               reg-relocated-p  reg-retrieved-p
5733               ;; If only one of BEG or END was found, the relocated region is only
5734               ;; approximate (keep the same length).  If both were found, it is exact.
5735               pos              (or beg  (and end (- end (- end-pos pos)))  pos)
5736               end-pos          (or end  (and beg (+ pos (- end-pos pos)))  end-pos))))
5737     ;; Region is available. Activate it and maybe save it.
5738     (cond (reg-retrieved-p
5739            (goto-char pos)
5740            (push-mark end-pos 'nomsg 'activate)
5741            (setq deactivate-mark  nil)
5742            (when bmkp-show-end-of-region
5743              (let ((end-win  (save-excursion (forward-line (window-height)) (end-of-line) (point))))
5744                ;; Bounce point and mark.
5745                (save-excursion (sit-for 0.6) (exchange-point-and-mark) (sit-for 1))
5746                ;; Recenter if region end is not visible.
5747                (when (> end-pos end-win) (recenter 1))))
5748            ;; Maybe save region.
5749            (if (and reg-relocated-p (bmkp-save-new-region-location bmk pos end-pos))
5750                (message "Saved relocated region (from %d to %d)" pos end-pos)
5751              (message "Region is from %d to %d" pos end-pos)))
5752           (t                            ; No region.  Go to old start.  Don't push-mark.
5753            (goto-char pos) (forward-line 0)
5754            (message "No region from %d to %d" pos end-pos)))))
5755
5756 ;; Same as `line-number-at-pos', which is not available until Emacs 22.
5757 (defun bmkp-line-number-at-pos (&optional pos)
5758   "Buffer line number at position POS. Current line number if POS is nil.
5759 Counting starts at (point-min), so any narrowing restriction applies."
5760   (1+ (count-lines (point-min) (save-excursion (when pos (goto-char pos)) (forward-line 0) (point)))))
5761
5762 (defun bmkp-goto-position (bookmark file buf bufname pos forward-str behind-str)
5763   "Go to a bookmark that has no region.
5764 Update the recorded position if `bmkp-save-new-location-flag'.
5765 Arguments are respectively the bookmark, its file, buffer, buffer
5766 name, recorded position, and the context strings for the position."
5767   (if (and file (file-readable-p file) (not (buffer-live-p buf)))
5768       (with-current-buffer (find-file-noselect file) (setq buf  (buffer-name)))
5769     ;; No file found.  See if a non-file buffer exists for this.  If not, raise error.
5770     (unless (or (and buf (get-buffer buf))
5771                 (and bufname (get-buffer bufname) (not (string= buf bufname))))
5772       (signal 'file-error `("Jumping to bookmark" "No such file or directory" ,file))))
5773   (set-buffer (or buf bufname))
5774   (when bmkp-jump-display-function
5775     (save-current-buffer (funcall bmkp-jump-display-function (current-buffer))))
5776   (setq deactivate-mark  t)
5777   (raise-frame)
5778   (goto-char pos)
5779   ;; Try to relocate position.
5780   ;; Search forward first.  Then, if FORWARD-STR exists and was found in the file, search
5781   ;; backward for BEHIND-STR.  The rationale is that if text was inserted between the two
5782   ;; in the file, then it's better to end up before point, so you can see the text, rather
5783   ;; than after it and not see it.
5784   (when (and forward-str (search-forward forward-str (point-max) t))
5785     (goto-char (match-beginning 0)))
5786   (when (and behind-str (search-backward behind-str (point-min) t)) (goto-char (match-end 0)))
5787   (when (and (/= pos (point))  bmkp-save-new-location-flag)
5788     (bookmark-prop-set bookmark 'position     (point))
5789     (bookmark-prop-set bookmark 'end-position (point))
5790     (bmkp-maybe-save-bookmarks))
5791   (/= pos (point)))                     ; Return value indicates whether POS was accurate.
5792
5793 (defun bmkp-jump-sequence (bookmark)
5794   "Handle a sequence bookmark BOOKMARK.
5795 BOOKMARK is a bookmark name or a bookmark record.
5796 Handler function for sequence bookmarks."
5797   (dolist (bmk  (bookmark-prop-get bookmark 'sequence))
5798     (bookmark--jump-via bmk bmkp-sequence-jump-display-function))
5799   (message "Done invoking bookmarks in sequence `%s'"
5800            (if (stringp bookmark) bookmark (bookmark-name-from-full-record bookmark))))
5801
5802 (defun bmkp-jump-function (bookmark)
5803   "Handle a function bookmark BOOKMARK.
5804 BOOKMARK is a bookmark name or a bookmark record.
5805 Handler function for function bookmarks."
5806   (funcall (bookmark-prop-get bookmark 'function)))
5807
5808 (defun bmkp-make-bookmark-list-record ()
5809   "Create and return a bookmark-list bookmark record.
5810 This records the current state of buffer `*Bookmark List*': the sort
5811 order, filter function, regexp pattern, title, and omit list."
5812   (let ((state  `((last-sort-comparer            . ,bmkp-sort-comparer)
5813                   (last-reverse-sort-p           . ,bmkp-reverse-sort-p)
5814                   (last-reverse-multi-sort-p     . ,bmkp-reverse-multi-sort-p)
5815                   (last-bmenu-filter-function    . ,bmkp-bmenu-filter-function)
5816                   (last-bmenu-filter-pattern     . ,bmkp-bmenu-filter-pattern)
5817                   (last-bmenu-omitted-bookmarks  . ,(bmkp-maybe-unpropertize-bookmark-names
5818                                                      bmkp-bmenu-omitted-bookmarks))
5819                   (last-bmenu-title              . ,bmkp-bmenu-title)
5820                   (last-bmenu-toggle-filenames   . ,bookmark-bmenu-toggle-filenames))))
5821     `(,@(bookmark-make-record-default 'no-file 'no-context)
5822       (filename      . ,bmkp-non-file-filename)
5823       (bookmark-list . ,state)
5824       (handler       . bmkp-jump-bookmark-list))))
5825
5826 (add-hook 'bookmark-bmenu-mode-hook
5827           #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
5828                             'bmkp-make-bookmark-list-record)))
5829
5830 (defun bmkp-jump-bookmark-list (bookmark)
5831   "Jump to bookmark-list bookmark BOOKMARK.
5832 BOOKMARK is a bookmark name or a bookmark record.
5833 Handler function for record returned by `bmkp-make-bookmark-list-record'."
5834   (let ((state  (bookmark-prop-get bookmark 'bookmark-list)))
5835     (setq bmkp-sort-comparer               (cdr (assq 'last-sort-comparer            state))
5836           bmkp-reverse-sort-p              (cdr (assq 'last-reverse-sort-p           state))
5837           bmkp-reverse-multi-sort-p        (cdr (assq 'last-reverse-multi-sort-p     state))
5838           bmkp-bmenu-filter-function       (cdr (assq 'last-bmenu-filter-function    state))
5839           bmkp-bmenu-filter-pattern        (or (cdr (assq 'last-bmenu-filter-pattern state)) "")
5840           bmkp-bmenu-omitted-bookmarks     (cdr (assq 'last-bmenu-omitted-bookmarks  state))
5841           bmkp-bmenu-title                 (cdr (assq 'last-bmenu-title              state))
5842           bookmark-bmenu-toggle-filenames  (cdr (assq 'last-bmenu-toggle-filenames   state))))
5843   (let ((bookmark-alist  (if bmkp-bmenu-filter-function
5844                              (funcall bmkp-bmenu-filter-function)
5845                            bookmark-alist)))
5846     (setq bmkp-latest-bookmark-alist  bookmark-alist)
5847     (bookmark-bmenu-list 'filteredp)
5848     (when (get-buffer "*Bookmark List*") (pop-to-buffer "*Bookmark List*"))))
5849
5850 ;; Bookmark-file bookmarks.
5851 ;;;###autoload
5852 (defun bmkp-set-bookmark-file-bookmark (file &optional msgp) ; Bound to `C-x p x'
5853   "Create a bookmark that loads bookmark-file FILE when \"jumped\" to.
5854 You are prompted for the names of the bookmark file and the bookmark."
5855   (interactive (list (let ((insert-default-directory  t))
5856                        (read-file-name
5857                         "Create bookmark to load bookmark file: " "~/"
5858                         ;; Default file as default choice, unless it's already current.
5859                         (and (not (bmkp-same-file-p bookmark-default-file bmkp-current-bookmark-file))
5860                              bookmark-default-file)
5861                         'confirm))
5862                      'MSG))
5863   (setq file  (expand-file-name file))
5864   (unless (file-readable-p file) (error "Unreadable bookmark file `%s'" file))
5865   (with-current-buffer (let ((enable-local-variables  ())) (find-file-noselect file))
5866     (goto-char (point-min))
5867     (condition-case nil                 ; Check whether it's a valid bookmark file.
5868         (progn (bookmark-maybe-upgrade-file-format)
5869                (unless (listp (bookmark-alist-from-buffer)) (error "")))
5870       (error (error "Not a valid bookmark file: `%s'" file))))
5871   (let ((bookmark-make-record-function  #'(lambda () (bmkp-make-bookmark-file-record file))))
5872     (call-interactively #'bookmark-set))
5873   (when msgp (message "Set bookmark-file bookmark")))
5874
5875 (defun bmkp-make-bookmark-file-record (bookmark-file)
5876   "Create and return a bookmark-file bookmark record.
5877 Records the BOOKMARK-FILE name.
5878 Adds a handler that tests the prefix arg and loads the bookmark file
5879 either as a replacement for the current bookmark file or as a
5880 supplement to it."
5881   `(,@(bookmark-make-record-default 'no-file 'no-context)
5882     (filename      . ,bookmark-file)
5883     (bookmark-file . ,bookmark-file)
5884     (handler       . bmkp-jump-bookmark-file)))
5885
5886 (defun bmkp-jump-bookmark-file (bookmark &optional switchp no-msg)
5887   "Jump to bookmark-file bookmark BOOKMARK, which loads the bookmark file.
5888 BOOKMARK is a bookmark name or a bookmark record.
5889 Non-nil SWITCHP means overwrite the current bookmark list.
5890 Handler function for record returned by `bmkp-make-bookmark-file-record'."
5891   (let ((file        (bookmark-prop-get bookmark 'bookmark-file))
5892         (overwritep  (and (or switchp current-prefix-arg)
5893                           (y-or-n-p "Switch to new bookmark file, instead of just adding it? "))))
5894     (bookmark-load file overwritep))
5895   ;; This `throw' is only for the case where this handler is is called from `bookmark--jump-via'.
5896   ;; It just tells `bookmark--jump-via' to skip the rest of what it does after calling the handler.
5897   (condition-case nil
5898       (throw 'bookmark--jump-via 'BOOKMARK-FILE-JUMP)
5899     (no-catch nil)))
5900
5901 ;;;###autoload
5902 (defun bmkp-bookmark-file-jump (bookmark-name &optional switchp no-msg) ; `C-x j x'
5903   "Jump to a bookmark-file bookmark, which means load its bookmark file.
5904 With a prefix argument, switch to the new bookmark file.
5905 Otherwise, load it to supplement the current bookmark list."
5906   (interactive
5907    (let ((alist  (bmkp-bookmark-file-alist-only)))
5908      (list (bmkp-read-bookmark-for-type "bookmark-file " alist nil nil 'bmkp-bookmark-file-history)
5909            current-prefix-arg)))
5910   (bmkp-jump-bookmark-file bookmark-name switchp no-msg))
5911
5912 ;; Desktop bookmarks
5913 ;;;###autoload
5914 (defun bmkp-set-desktop-bookmark (desktop-file) ; Bound globally to `C-x p K', `C-x r K', `C-x p c K'
5915   "Save the desktop as a bookmark.
5916 You are prompted for the desktop-file location and the bookmark name."
5917   (interactive (list (read-file-name "Save desktop in file: ")))
5918   (set-text-properties 0 (length desktop-file) nil desktop-file)
5919   (unless (file-name-absolute-p desktop-file) (setq desktop-file  (expand-file-name desktop-file)))
5920   (unless (condition-case nil (require 'desktop nil t) (error nil))
5921     (error "You must have library `desktop.el' to use this command"))
5922   (let ((desktop-basefilename     (file-name-nondirectory desktop-file)) ; Emacs < 22
5923         (desktop-base-file-name   (file-name-nondirectory desktop-file)) ; Emacs 23+
5924         (desktop-dir              (file-name-directory desktop-file))
5925         (desktop-restore-eager    t)    ; Don't bother with lazy restore.
5926         (desktop-globals-to-save  (bmkp-remove-if #'(lambda (elt)
5927                                                       (memq elt bmkp-desktop-no-save-vars))
5928                                                   desktop-globals-to-save)))
5929     (if (< emacs-major-version 22)
5930         (desktop-save desktop-dir)      ; Emacs < 22 has no locking.
5931       (desktop-save desktop-dir 'RELEASE))
5932     (message "Desktop saved in `%s'" desktop-file)
5933     (let ((bookmark-make-record-function  #'(lambda () (bmkp-make-desktop-record desktop-file))))
5934       (call-interactively #'bookmark-set))))
5935
5936 (defun bmkp-make-desktop-record (desktop-file)
5937   "Create and return a desktop bookmark record.
5938 DESKTOP-FILE is the absolute file name of the desktop file to use."
5939   `(,@(bookmark-make-record-default 'no-file 'no-context)
5940     (filename     . ,bmkp-non-file-filename)
5941     (desktop-file . ,desktop-file)
5942     (handler      . bmkp-jump-desktop)))
5943
5944 (defun bmkp-jump-desktop (bookmark)
5945   "Jump to desktop bookmark BOOKMARK.
5946 BOOKMARK is a bookmark name or a bookmark record.
5947 Handler function for record returned by `bmkp-make-desktop-record'."
5948   (let ((desktop-file  (bookmark-prop-get bookmark 'desktop-file)))
5949     (unless (condition-case nil (require 'desktop nil t) (error nil))
5950       (error "You must have library `desktop.el' to use this command"))
5951     ;; (unless desktop-file (error "Not a desktop-bookmark: %S" bookmark)) ; Shouldn't happen.
5952     (bmkp-desktop-change-dir desktop-file)
5953     (unless (bmkp-desktop-read desktop-file) (error "Could not load desktop file"))))
5954
5955 ;; Derived from code in `desktop-change-dir'.
5956 ;;;###autoload
5957 (defun bmkp-desktop-change-dir (desktop-file)
5958   "Change to desktop saved in DESKTOP-FILE.
5959 Kill the desktop as specified by variables `desktop-save-mode' and
5960  `desktop-save' (starting with Emacs 22).
5961 Clear the desktop and load DESKTOP-FILE DIRNAME."
5962   (interactive (list (read-file-name "Change to desktop file: ")))
5963   (set-text-properties 0 (length desktop-file) nil desktop-file)
5964   (unless (file-name-absolute-p desktop-file) (setq desktop-file  (expand-file-name desktop-file)))
5965   (unless (condition-case nil (require 'desktop nil t) (error nil))
5966     (error "You must have library `desktop.el' to use this command"))
5967   (let ((desktop-basefilename     (file-name-nondirectory desktop-file)) ; Emacs < 22
5968         (desktop-base-file-name   (file-name-nondirectory desktop-file)) ; Emacs 23+
5969         (desktop-dir              (file-name-directory desktop-file))
5970         (desktop-restore-eager    t)    ; Don't bother with lazy restore.
5971         (desktop-globals-to-save  (bmkp-remove-if #'(lambda (elt)
5972                                                       (memq elt bmkp-desktop-no-save-vars))
5973                                                   desktop-globals-to-save)))
5974     (bmkp-desktop-kill)
5975     (desktop-clear)
5976     (if (< emacs-major-version 22) (desktop-read) (desktop-read desktop-dir))))
5977     
5978 ;; Derived from code in `desktop-kill'.
5979 (defun bmkp-desktop-kill ()
5980   "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
5981 This does nothing in Emacs versions prior to Emacs 22."
5982   ;; We assume here: `desktop.el' has been loaded and `desktop-dirname' is defined.
5983   (when (and (and (boundp 'desktop-save-mode) desktop-save-mode) ; Not defined in Emacs 20-21.
5984              (let ((exists  (file-exists-p (desktop-full-file-name))))
5985                (or (eq desktop-save t)
5986                    (and exists (memq desktop-save '(ask-if-new if-exists)))
5987                    (and (or (memq desktop-save '(ask ask-if-new))
5988                             (and exists (eq desktop-save 'ask-if-exists)))
5989                         (y-or-n-p "Save current desktop? ")))))
5990     (condition-case err
5991         (if (< emacs-major-version 22)
5992             (desktop-save desktop-dirname) ; Emacs < 22 has no locking.
5993           (desktop-save desktop-dirname 'RELEASE))
5994       (file-error (unless (yes-or-no-p "Error while saving the desktop.  Ignore? ")
5995                     (signal (car err) (cdr err))))))
5996   ;; Just release lock, regardless of whether this Emacs process is the owner.
5997   (when (fboundp 'desktop-release-lock) (desktop-release-lock))) ; Not defined for Emacs 20.
5998
5999 ;; Derived from code in `desktop-read'.
6000 ;;;###autoload
6001 (defun bmkp-desktop-read (file)
6002   "Load desktop-file FILE, then run `desktop-after-read-hook'.
6003 Return t if FILE was loaded, nil otherwise."
6004   (interactive)
6005   (unless (file-name-absolute-p file) (setq file  (expand-file-name file))) ; Shouldn't happen.
6006   (setq desktop-dirname  (file-name-directory file))
6007   (if (not (file-readable-p file))
6008       nil                               ; Return nil, meaning not loaded.
6009     (let ((desktop-restore-eager      t) ; Don't bother with lazy restore.
6010           (desktop-first-buffer       nil)
6011           (desktop-buffer-ok-count    0)
6012           (desktop-buffer-fail-count  0)
6013           (desktop-save               nil)) ; Prevent desktop saving during eval of desktop buffer.
6014       (when (fboundp 'desktop-lazy-abort) (desktop-lazy-abort)) ; Emacs 22+.
6015       (load file t t t)
6016       (when (boundp 'desktop-file-modtime) ; Emacs 22+
6017         (setq desktop-file-modtime  (nth 5 (file-attributes file))))
6018       ;; `desktop-create-buffer' puts buffers at end of the buffer list.
6019       ;; We want buffers existing prior to evaluating the desktop (and not reused) to be placed
6020       ;; at the end of the buffer list, so we move them here.
6021       (mapc 'bury-buffer (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
6022       (switch-to-buffer (car (buffer-list)))
6023       (run-hooks 'desktop-delay-hook)
6024       (setq desktop-delay-hook  ())
6025       (run-hooks 'desktop-after-read-hook)
6026       (when (boundp 'desktop-buffer-ok-count) ; Emacs 22+
6027         (message "Desktop: %d buffer%s restored%s%s." desktop-buffer-ok-count
6028                  (if (= 1 desktop-buffer-ok-count) "" "s")
6029                  (if (< 0 desktop-buffer-fail-count)
6030                      (format ", %d failed to restore" desktop-buffer-fail-count)
6031                    "")
6032                  (if (and (boundp 'desktop-buffer-args-list) desktop-buffer-args-list)
6033                      (format ", %d to be restored lazily" (length desktop-buffer-args-list))
6034                    "")))
6035       t)))                              ; Return t, meaning successfully loaded.
6036
6037 ;;;###autoload
6038 (defun bmkp-desktop-delete (bookmark-name)
6039   "Delete desktop bookmark BOOKMARK-NAME, and delete its desktop file.
6040 Release the lock file in that desktop's directory.
6041 \(This means that if you currently have locked a different desktop
6042 in the same directory, then you will need to relock it.)"
6043   (interactive (let ((alist  (bmkp-desktop-alist-only)))
6044                  (list (bmkp-read-bookmark-for-type "desktop " alist nil nil 'bmkp-desktop-history
6045                                                     "Delete "))))
6046   (let ((desktop-file  (bookmark-prop-get bookmark-name 'desktop-file)))
6047     (unless desktop-file (error "Not a desktop-bookmark: `%s'" bookmark-name))
6048     ;; Release the desktop lock file in the same directory as DESKTOP-FILE.
6049     ;; This will NOT be the right thing to do if a desktop file different from DESKTOP-FILE
6050     ;; is currently locked in the same directory.
6051     (let ((desktop-dir  (file-name-directory desktop-file)))
6052       (when (fboundp 'desktop-release-lock) (desktop-release-lock))) ; Not defined for Emacs 20.
6053     (when (file-exists-p desktop-file) (delete-file desktop-file)))
6054   (bookmark-delete bookmark-name))
6055
6056 ;; Variable-list bookmarks
6057 (when (boundp 'wide-n-restrictions)
6058   (defun bmkp-set-restrictions-bookmark ()
6059     "Save the ring of restrictions for the current buffer as a bookmark.
6060 You need library `wide-n.el' to use the bookmark created."
6061     (interactive)
6062     (let ((bookmark-make-record-function
6063            (lambda () (bmkp-make-variable-list-record
6064                        `((wide-n-restrictions
6065                           . ,(mapcar (lambda (x)
6066                                        (if (eq x 'all)
6067                                            'all
6068                                          (let ((beg  (car x)) ; Convert markers to number positions.
6069                                                (end  (cdr x)))
6070                                            (cons (if (markerp beg) (marker-position beg) beg)
6071                                                  (if (markerp end) (marker-position end) end)))))
6072                                      wide-n-restrictions)))))))
6073       (call-interactively #'bookmark-set)
6074       (unless (featurep 'wide-n) (message "Bookmark created, but you need `wide-n.el' to use it")))))
6075
6076 ;;;###autoload
6077 (defun bmkp-set-variable-list-bookmark (variables)
6078   "Save a list of variables as a bookmark.
6079 Interactively, read the variables to save, using
6080 `bmkp-read-variables-completing'."
6081   (interactive (list (bmkp-read-variables-completing)))
6082   (let ((bookmark-make-record-function  #'(lambda () (bmkp-make-variable-list-record variables))))
6083     (call-interactively #'bookmark-set)))
6084
6085 (defun bmkp-create-variable-list-bookmark (bookmark-name vars vals &optional buffer-name)
6086   "Create a variable-list bookmark named BOOKMARK-NAME from VARS and VALS.
6087 VARS and VALS are corresponding lists of variables and their values.
6088
6089 Optional arg BUFFER-NAME is the buffer name to use for the bookmark (a
6090 string).  This is useful if some of the variables are buffer-local.
6091 If BUFFER-NAME is nil, the current buffer name is recorded."
6092   (eval `(multiple-value-bind ,vars ',vals
6093           (let ((bookmark-make-record-function
6094                  (lambda () (bmkp-make-variable-list-record ',vars ,buffer-name))))
6095             (bookmark-set ,bookmark-name nil t)))))
6096
6097 (defun bmkp-read-variables-completing (&optional option)
6098   "Read variable names with completion, and return them as a list of symbols.
6099 Reads names one by one, until you hit `RET' twice consecutively.
6100 Non-nil argument OPTION means read only user option names."
6101   (bookmark-maybe-load-default-file)
6102   (let ((var   (bmkp-read-variable "Variable (RET for each, empty input to finish): " option))
6103         (vars  ())
6104         old-var)
6105     (while (not (string= "" var))
6106       (add-to-list 'vars var)
6107       (setq var  (bmkp-read-variable "Variable: " option)))
6108     (nreverse vars)))
6109
6110 (defun bmkp-read-variable (prompt &optional option default-value)
6111   "Read name of a variable and return it as a symbol.
6112 Prompt with string PROMPT.  
6113 With non-nil OPTION, read the name of a user option.
6114 The default value is DEFAULT-VALUE if non-nil, or the nearest symbol
6115 to the cursor if it is a variable."
6116   (setq option  (if option 'user-variable-p 'boundp))
6117   (let ((symb                                        (cond ((fboundp 'symbol-nearest-point)
6118                                                             (symbol-nearest-point))
6119                                                            ((fboundp 'symbol-at-point) (symbol-at-point))
6120                                                            (t nil)))
6121         (enable-recursive-minibuffers                t)
6122         (icicle-unpropertize-completion-result-flag  t))
6123     (when (and default-value (symbolp default-value))
6124       (setq default-value  (symbol-name default-value)))
6125     (intern (completing-read prompt obarray option t nil 'minibuffer-history
6126                              (or default-value (and (funcall option symb) (symbol-name symb)))
6127                              t))))
6128
6129 (defun bmkp-make-variable-list-record (variables &optional buffer-name)
6130   "Create and return a variable-list bookmark record.
6131 VARIABLES is the list of variables to save.
6132 Each entry in VARIABLES is either a variable (a symbol) or a cons
6133  whose car is a variable and whose cdr is the variable's value.
6134
6135 Optional arg BUFFER-NAME is the buffer to use for the bookmark.  This
6136 is useful if some of the variables are buffer-local.  If BUFFER-NAME
6137 is nil, the current buffer is used."
6138   (let ((record  `(,@(bookmark-make-record-default 'no-file 'no-context)
6139                    (filename     . ,bmkp-non-file-filename)
6140                    (variables    . ,(or (bmkp-printable-vars+vals variables)
6141                                         (error "No variables to bookmark")))
6142                    (handler      . bmkp-jump-variable-list))))
6143     (when buffer-name  (let ((bname  (assq 'buffer-name record)))  (setcdr bname buffer-name)))
6144     record))
6145
6146 (defun bmkp-printable-vars+vals (variables)
6147   "Return an alist of printable VARIABLES paired with their values.
6148 Display a message for any variables that are not printable.
6149 VARIABLES is the list of variables.  Each entry in VARIABLES is either
6150  a variable (a symbol) or a cons whose car is a variable and whose cdr
6151  is the variable's value."
6152   (let ((vars+vals     ())
6153         (unprintables  ()))
6154     (dolist (var  variables)
6155       (let ((val  (if (consp var) (cdr var) (symbol-value var))))
6156         (if (bmkp-printable-p val)
6157             (add-to-list 'vars+vals (if (consp var) var (cons var val)))
6158           (add-to-list 'unprintables var))))
6159     (when unprintables (message "Unsavable (unreadable) vars: %S" unprintables)  (sit-for 3))
6160     vars+vals))
6161
6162 ;; Same as `savehist-printable', except added `print-circle' binding.
6163 (defun bmkp-printable-p (value)
6164   "Return non-nil if VALUE would be Lisp-readable if printed using `prin1'."
6165   (or (stringp value) (numberp value) (symbolp value)
6166       (with-temp-buffer                 ; Print and try to read.
6167         (condition-case nil
6168             (let ((print-readably  t)
6169                   (print-level     nil)
6170                   (print-circle    t))
6171               (prin1 value (current-buffer))
6172               (read (point-min-marker))
6173               t)
6174           (error nil)))))
6175
6176 (defun bmkp-jump-variable-list (bookmark)
6177   "Jump to variable-list bookmark BOOKMARK, restoring the recorded values.
6178 BOOKMARK is a bookmark name or a bookmark record.
6179 Handler function for record returned by `bmkp-make-variable-list-record'."
6180   (let ((buf        (bmkp-get-buffer-name bookmark))
6181         (vars+vals  (bookmark-prop-get bookmark 'variables)))
6182     (unless (get-buffer buf)
6183       (message "Bookmarked for non-existent buffer `%s', so using current buffer" buf) (sit-for 3)
6184       (setq buf (current-buffer)))
6185     (with-current-buffer buf
6186       (dolist (var+val  vars+vals)
6187         (set (car var+val)  (cdr var+val))))
6188     (message "Variables restored in buffer `%s': %S" buf (mapcar #'car vars+vals))
6189     (sit-for 3)))
6190
6191 ;; URL browse support
6192 (defun bmkp-make-url-browse-record (url) 
6193   "Create and return a url bookmark for `browse-url' to visit URL.
6194 The handler is `bmkp-jump-url-browse'."
6195   (require 'browse-url)
6196   (let ((url-0  (copy-sequence url)))
6197     (set-text-properties 0 (length url) () url-0) 
6198     `((filename . ,bmkp-non-file-filename)
6199       (location . ,url-0)
6200       (handler  . bmkp-jump-url-browse))))
6201
6202 (defun bmkp-jump-url-browse (bookmark)
6203   "Handler function for record returned by `bmkp-make-url-browse-record'.
6204 BOOKMARK is a bookmark name or a bookmark record."
6205   (require 'browse-url)
6206   (let ((url  (bookmark-prop-get bookmark 'location)))
6207     (browse-url url)))
6208
6209 ;; W3M support
6210 (defun bmkp-make-w3m-record ()
6211   "Make a special entry for w3m buffers."
6212   (require 'w3m)                        ; For `w3m-current-url'.
6213   `(,w3m-current-title
6214     ,@(bookmark-make-record-default 'no-file)
6215     (filename . ,w3m-current-url)
6216     (handler . bmkp-jump-w3m)))
6217
6218 (add-hook 'w3m-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6219                                            'bmkp-make-w3m-record)))
6220
6221 (defun bmkp-w3m-set-new-buffer-name ()
6222   "Set the w3m buffer name according to the number of w3m buffers already open."
6223   (require 'w3m)                        ; For `w3m-list-buffers'.
6224   (let ((len  (length (w3m-list-buffers 'nosort))))
6225     (if (eq len 0)  "*w3m*"  (format "*w3m*<%d>" (1+ len)))))
6226
6227 (defun bmkp-jump-w3m-new-session (bookmark)
6228   "Jump to W3M bookmark BOOKMARK, setting a new tab."
6229   (require 'w3m)
6230   (let ((buf   (bmkp-w3m-set-new-buffer-name)))
6231     (w3m-browse-url (bookmark-prop-get bookmark 'filename) 'newsession)
6232     (while (not (get-buffer buf)) (sit-for 1)) ; Be sure we have the W3M buffer.
6233     (with-current-buffer buf
6234       (goto-char (point-min))
6235       ;; Wait until data arrives in buffer, before setting region.
6236       (while (eq (line-beginning-position) (line-end-position)) (sit-for 1)))
6237     (bookmark-default-handler
6238      `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
6239
6240 (defun bmkp-jump-w3m-only-one-tab (bookmark)
6241   "Close all W3M sessions and jump to BOOKMARK in a new W3M buffer."
6242   (require 'w3m)
6243   (w3m-quit 'force)                     ; Be sure we start with an empty W3M buffer.
6244   (w3m-browse-url (bookmark-prop-get bookmark 'filename))
6245   (with-current-buffer "*w3m*" (while (eq (point-min) (point-max)) (sit-for 1)))
6246   (bookmark-default-handler
6247    `("" (buffer . ,(buffer-name (current-buffer))) . ,(bookmark-get-bookmark-record bookmark))))
6248
6249 (defalias 'bmkext-jump-w3m 'bmkp-jump-w3m)
6250 (defun bmkp-jump-w3m (bookmark)
6251   "Handler function for record returned by `bmkp-make-w3m-record'.
6252 BOOKMARK is a bookmark name or a bookmark record.
6253 Use multi-tabs in W3M if `bmkp-w3m-allow-multi-tabs' is non-nil."
6254   (require 'w3m)
6255   (if bmkp-w3m-allow-multi-tabs
6256       (bmkp-jump-w3m-new-session bookmark)
6257     (bmkp-jump-w3m-only-one-tab bookmark)))
6258
6259
6260 ;; GNUS support for Emacs < 24.
6261
6262 (defun bmkp-make-gnus-record ()
6263   "Make a bookmark entry for a Gnus summary buffer."
6264   (require 'gnus)
6265   (unless (and (eq major-mode 'gnus-summary-mode) gnus-article-current)
6266     (error "Try again, from the Gnus summary buffer"))
6267   (let* ((grp   (car gnus-article-current))
6268          (art   (cdr gnus-article-current))
6269          (head  (gnus-summary-article-header art))
6270          (id    (mail-header-id head)))
6271     `((elt (gnus-summary-article-header) 1)
6272       ,@(bookmark-make-record-default 'no-file)
6273       (filename . ,bmkp-non-file-filename) (group . ,grp) (article . ,art) (message-id . ,id)
6274       (handler . bmkp-jump-gnus))))
6275
6276 (unless (> emacs-major-version 23)      ; Emacs 24 has `gnus-summary-bookmark-make-record'.
6277   (add-hook 'gnus-summary-mode-hook #'(lambda ()
6278                                         (set (make-local-variable 'bookmark-make-record-function)
6279                                              'bmkp-make-gnus-record))))
6280
6281 (unless (> emacs-major-version 23)      ; Emacs 24 has `gnus-summary-bookmark-make-record'.
6282   (add-hook 'gnus-article-mode-hook #'(lambda ()
6283                                         (set (make-local-variable 'bookmark-make-record-function)
6284                                              'bmkp-make-gnus-record))))
6285
6286 (defun bmkext-jump-gnus (bookmark)      ; Compatibility code.
6287   "`gnus-summary-bookmark-jump' if defined, else `bmkp-jump-gnus'."
6288   (if (fboundp 'gnus-summary-bookmark-jump)
6289       (gnus-summary-bookmark-jump bookmark) ; Emacs 24
6290     (bmkp-jump-gnus bookmark)))
6291
6292 (defun bmkp-jump-gnus (bookmark)
6293   "Handler function for record returned by `bmkp-make-gnus-record'.
6294 BOOKMARK is a bookmark name or a bookmark record."
6295   (let ((group    (bookmark-prop-get bookmark 'group))
6296         (article  (bookmark-prop-get bookmark 'article))
6297         (id       (bookmark-prop-get bookmark 'message-id))
6298         (buf      (bookmark-prop-get bookmark 'buffer)))
6299     (if (< emacs-major-version 22) (gnus-fetch-group group) (gnus-fetch-group group (list article)))
6300     (gnus-summary-insert-cached-articles)
6301     (gnus-summary-goto-article id nil 'force)
6302     (bookmark-default-handler `("" (buffer . ,buf) . ,(bookmark-get-bookmark-record bookmark)))))
6303
6304
6305 ;; `man' and `woman' support for Emacs < 24.
6306
6307 (when (> emacs-major-version 20)
6308   (defun bmkp-make-woman-record ()
6309     "Create bookmark record for `man' page bookmark created by `woman'."
6310     `(,@(bookmark-make-record-default 'no-file)
6311       (filename . ,woman-last-file-name) (handler . bmkp-jump-woman)))
6312
6313   (unless (> emacs-major-version 23)
6314     (add-hook 'woman-mode-hook #'(lambda ()
6315                                    (set (make-local-variable 'bookmark-make-record-function)
6316                                         'bmkp-make-woman-record)))))
6317
6318 (defun bmkp-make-man-record ()
6319   "Create bookmark record for `man' page bookmark created by `man'."
6320   `(,@(bookmark-make-record-default 'no-file)
6321     (filename . ,bmkp-non-file-filename)
6322     (man-args . ,Man-arguments) (handler . bmkp-jump-man)))
6323
6324 (unless (> emacs-major-version 23)
6325   (add-hook 'Man-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6326                                              'bmkp-make-man-record))))
6327
6328 (defun bmkext-jump-woman (bookmark)     ; Compatibility code.
6329   "`woman-bookmark-jump' if defined, else `bmkp-jump-woman'."
6330   (if (fboundp 'woman-bookmark-jump)
6331       (woman-bookmark-jump bookmark)    ; Emacs 24
6332     (bmkp-jump-woman bookmark)))
6333   
6334 (defun bmkp-jump-woman (bookmark)
6335   "Handler function for `man' page bookmark created by `woman'."
6336   (unless (> emacs-major-version 20)
6337     (error "`woman' bookmarks are not supported in Emacs prior to Emacs 21"))
6338   (bookmark-default-handler
6339    `("" (buffer . ,(save-window-excursion (woman-find-file (bookmark-get-filename bookmark))
6340                                           (current-buffer)))
6341      . ,(bookmark-get-bookmark-record bookmark))))
6342
6343 (defun bmkext-jump-man (bookmark)       ; Compatibility code.
6344   "`Man-bookmark-jump' if defined, else `bmkp-jump-man'."
6345   (if (fboundp 'Man-bookmark-jump)
6346       (Man-bookmark-jump bookmark)      ; Emacs 24
6347     (bmkp-jump-man bookmark)))
6348   
6349 (defun bmkp-jump-man (bookmark)
6350   "Handler function for `man' page bookmark created by `man'."
6351   (require 'man)
6352   (let ((Man-notify-method  (case bmkp-jump-display-function
6353                               ((nil display-buffer)                           'quiet)
6354                               (switch-to-buffer                               'pushy)
6355                               ((switch-to-buffer-other-window pop-to-buffer)  'friendly)
6356                               (t                                              'quiet))))
6357     (Man-getpage-in-background (bookmark-prop-get bookmark 'man-args))
6358     (while (get-process "man") (sit-for 0.2))
6359     (bookmark-default-handler (bookmark-get-bookmark bookmark))))
6360
6361 (defun bmkp-make-dired-record ()
6362   "Create and return a Dired bookmark record."
6363   (let ((hidden-dirs  (save-excursion (dired-remember-hidden))))
6364     (unwind-protect
6365          (let ((dir      (expand-file-name (if (consp dired-directory)
6366                                                (file-name-directory (car dired-directory))
6367                                              dired-directory)))
6368                (subdirs  (bmkp-dired-subdirs))
6369                (mfiles   (mapcar #'(lambda (mm) (car mm))
6370                                  (dired-remember-marks (point-min) (point-max)))))
6371            `(,dir
6372              ,@(bookmark-make-record-default 'no-file)
6373              (filename . ,dir) (dired-directory . ,dired-directory)
6374              (dired-marked . ,mfiles) (dired-switches . ,dired-actual-switches)
6375              (dired-subdirs . ,subdirs) (dired-hidden-dirs . ,hidden-dirs)
6376              (handler . bmkp-jump-dired)))
6377       (save-excursion                   ; Hide subdirs that were hidden.
6378         (dolist (dir  hidden-dirs)  (when (dired-goto-subdir dir) (dired-hide-subdir 1)))))))
6379
6380 ;;;###autoload
6381 (defun bmkp-dired-subdirs ()
6382   "Alist of inserted subdirectories, without their positions (markers).
6383 This is like `dired-subdir-alist' but without the top-level dir and
6384 without subdir positions (markers)."
6385   (interactive)
6386   (let ((subdir-alist      (cdr (reverse dired-subdir-alist))) ; Remove top.
6387         (subdirs-wo-posns  ()))
6388     (dolist (sub  subdir-alist)  (push (list (car sub)) subdirs-wo-posns))
6389     subdirs-wo-posns))
6390
6391 (add-hook 'dired-mode-hook #'(lambda () (set (make-local-variable 'bookmark-make-record-function)
6392                                              'bmkp-make-dired-record)))
6393
6394 (defun bmkp-jump-dired (bookmark)
6395   "Jump to Dired bookmark BOOKMARK.
6396 BOOKMARK is a bookmark name or a bookmark record.
6397 Handler function for record returned by `bmkp-make-dired-record'."
6398   (let ((dir          (bookmark-prop-get bookmark 'dired-directory))
6399         (mfiles       (bookmark-prop-get bookmark 'dired-marked))
6400         (switches     (bookmark-prop-get bookmark 'dired-switches))
6401         (subdirs      (bookmark-prop-get bookmark 'dired-subdirs))
6402         (hidden-dirs  (bookmark-prop-get bookmark 'dired-hidden-dirs)))
6403     (case bmkp-jump-display-function
6404       ((nil switch-to-buffer display-buffer)         (dired dir switches))
6405       ((pop-to-buffer switch-to-buffer-other-window) (dired-other-window dir switches))
6406       (t                                             (dired dir switches)))
6407     (let ((inhibit-read-only  t))
6408       (dired-insert-old-subdirs subdirs)
6409       (dired-mark-remembered (mapcar #'(lambda (mf) (cons (expand-file-name mf dir) 42)) mfiles))
6410       (save-excursion
6411         (dolist (dir  hidden-dirs) (when (dired-goto-subdir dir) (dired-hide-subdir 1)))))
6412     (goto-char (bookmark-get-position bookmark))))
6413
6414
6415 (defun bmkp-read-bookmark-for-type (type alist &optional other-win pred hist action)
6416   "Read name of a bookmark of type TYPE.
6417 TYPE is a string used only in the prompt: \"Jump to TYPE bookmark: \".
6418 ALIST is the alist used for completion - nil means `bookmark-alist'.
6419 Non-nil OTHER-WIN means append \" in another window\" to the prompt.
6420 Non-nil PRED is a predicate used for completion.
6421 Non-nil HIST is a history symbol.  Default is `bookmark-history'.
6422 ACTION is the action to mention in the prompt.  `Jump to ', if nil."
6423   (unless alist (error "No bookmarks of type %s" type))
6424   (bookmark-completing-read
6425    (concat (or action "Jump to ") type "bookmark" (and other-win " in another window"))
6426    (bmkp-default-bookmark-name alist)
6427    alist pred hist))
6428
6429 ;;;###autoload
6430 (defun bmkp-jump-to-type (bookmark-name &optional use-region-p) ; `C-x j :'
6431   "Jump to a bookmark of a given type.  You are prompted for the type.
6432 Otherwise, this is the same as `bookmark-jump' - see that, in
6433 particular, for info about using a prefix argument."
6434   (interactive
6435    (let* ((completion-ignore-case                      t)
6436           (type-cands                                  bmkp-types-alist)
6437           (icicle-unpropertize-completion-result-flag  t)
6438           (type                                        (completing-read "Type of bookmark: "
6439                                                                         type-cands nil t))
6440           (alist                                       (funcall (intern
6441                                                                  (format "bmkp-%s-alist-only" type))))
6442           (history                                    (assoc-default type type-cands))
6443           (bmk-name                                   (bmkp-read-bookmark-for-type (concat type " ")
6444                                                                                    alist nil nil history)))
6445      (list bmk-name  (or (equal type "Region") current-prefix-arg))))
6446   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6447
6448 ;;;###autoload
6449 (defun bmkp-jump-to-type-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j :'
6450   "`bmkp-jump-to-type', but in another window."
6451   (interactive
6452    (let* ((completion-ignore-case                      t)
6453           (type-cands                                  bmkp-types-alist)
6454           (icicle-unpropertize-completion-result-flag  t)
6455           (type                                        (completing-read "Type of bookmark: "
6456                                                                         type-cands nil t))
6457           (alist                                       (funcall (intern
6458                                                                  (format "bmkp-%s-alist-only" type))))
6459           (history                                     (assoc-default type type-cands))
6460           (bmk-name                                    (bmkp-read-bookmark-for-type (concat type " ")
6461                                                                                     alist t nil history)))
6462      (list bmk-name (or (equal type "Region") current-prefix-arg))))
6463   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6464
6465 ;;;###autoload
6466 (defun bmkp-autonamed-jump (bookmark-name) ; `C-x j # #'
6467   "Jump to an autonamed bookmark.
6468 This is a specialization of `bookmark-jump'."
6469   (interactive
6470    (let ((alist  (bmkp-autonamed-alist-only)))
6471      (list (bmkp-read-bookmark-for-type "autonamed " alist nil nil 'bmkp-autonamed-history))))
6472   (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6473
6474 ;;;###autoload
6475 (defun bmkp-autonamed-jump-other-window (bookmark-name) ; `C-x 4 j # #'
6476   "`bmkp-autonamed-jump', but in another window."
6477   (interactive
6478    (let ((alist  (bmkp-autonamed-alist-only)))
6479      (list (bmkp-read-bookmark-for-type "autonamed " alist t nil 'bmkp-autonamed-history))))
6480   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window nil))
6481
6482 ;;;###autoload
6483 (defun bmkp-autonamed-this-buffer-jump (bookmark-name) ; `C-x j # .'
6484   "Jump to an autonamed bookmark in the current buffer.
6485 This is a specialization of `bookmark-jump'."
6486   (interactive
6487    (let ((alist  (bmkp-autonamed-this-buffer-alist-only)))
6488      (list (bmkp-read-bookmark-for-type "autonamed " alist nil nil 'bmkp-autonamed-history))))
6489   (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6490
6491 ;;;###autoload
6492 (defun bmkp-autonamed-this-buffer-jump-other-window (bookmark-name) ; `C-x 4 j # .'
6493   "`bmkp-autonamed-this-buffer-jump', but in another window."
6494   (interactive
6495    (let ((alist  (bmkp-autonamed-this-buffer-alist-only)))
6496      (list (bmkp-read-bookmark-for-type "autonamed " alist t nil 'bmkp-autonamed-history))))
6497   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window nil))
6498
6499 ;;;###autoload
6500 (defun bmkp-bookmark-list-jump (bookmark-name &optional use-region-p) ; `C-x j B'
6501   "Jump to a bookmark-list bookmark.
6502 This is a specialization of `bookmark-jump' - see that, in particular
6503 for info about using a prefix argument."
6504   (interactive
6505    (let ((alist  (bmkp-bookmark-list-alist-only)))
6506      (list (bmkp-read-bookmark-for-type "bookmark-list " alist nil nil 'bmkp-bookmark-list-history)
6507            current-prefix-arg)))
6508   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6509
6510 ;;;###autoload
6511 (defun bmkp-desktop-jump (bookmark-name &optional use-region-p) ; `C-x j K'
6512   "Jump to a desktop bookmark.
6513 This is a specialization of `bookmark-jump' - see that, in particular
6514 for info about using a prefix argument."
6515   (interactive
6516    (let ((alist  (bmkp-desktop-alist-only)))
6517      (list (bmkp-read-bookmark-for-type "desktop " alist nil nil 'bmkp-desktop-history)
6518            current-prefix-arg)))
6519   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6520
6521 ;;;###autoload
6522 (defun bmkp-dired-jump (bookmark-name &optional use-region-p) ; `C-x j d'
6523   "Jump to a Dired bookmark.
6524 This is a specialization of `bookmark-jump' - see that, in particular
6525 for info about using a prefix argument."
6526   (interactive
6527    (let ((alist  (bmkp-dired-alist-only)))
6528      (list (bmkp-read-bookmark-for-type "Dired " alist nil nil 'bmkp-dired-history)
6529            current-prefix-arg)))
6530   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6531
6532 ;;;###autoload
6533 (defun bmkp-dired-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j d'
6534   "`bmkp-dired-jump', but in another window."
6535   (interactive
6536    (let ((alist  (bmkp-dired-alist-only)))
6537      (list (bmkp-read-bookmark-for-type "Dired " alist t nil 'bmkp-dired-history)
6538            current-prefix-arg)))
6539   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6540
6541 ;;;###autoload
6542 (defun bmkp-dired-this-dir-jump (bookmark-name &optional use-region-p) ; `C-x j C-d'
6543   "Jump to a Dired bookmark for the `default-directory'.
6544 This is a specialization of `bookmark-jump' - see that, in particular
6545 for info about using a prefix argument."
6546   (interactive
6547    (let ((alist  (bmkp-dired-this-dir-alist-only)))
6548      (list (bmkp-read-bookmark-for-type "Dired-for-this-dir " alist nil nil 'bmkp-dired-history)
6549            current-prefix-arg)))
6550   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6551
6552 ;;;###autoload
6553 (defun bmkp-dired-this-dir-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j C-d'
6554   "`bmkp-dired-this-dir-jump', but in another window."
6555   (interactive
6556    (let ((alist  (bmkp-dired-this-dir-alist-only)))
6557      (list (bmkp-read-bookmark-for-type "Dired-for-this-dir " alist t nil 'bmkp-dired-history)
6558            current-prefix-arg)))
6559   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6560
6561 ;;;###autoload
6562 (defun bmkp-file-jump (bookmark-name &optional use-region-p) ; `C-x j f'
6563   "Jump to a file or directory bookmark.
6564 This is a specialization of `bookmark-jump' - see that, in particular
6565 for info about using a prefix argument."
6566   (interactive
6567    (let ((alist  (bmkp-file-alist-only)))
6568      (list (bmkp-read-bookmark-for-type "file " alist nil nil 'bmkp-file-history)
6569            current-prefix-arg)))
6570   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6571
6572 ;;;###autoload
6573 (defun bmkp-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j f'
6574   "`bmkp-file-jump', but in another window."
6575   (interactive
6576    (let ((alist  (bmkp-file-alist-only)))
6577      (list (bmkp-read-bookmark-for-type "file " alist t nil 'bmkp-file-history)
6578            current-prefix-arg)))
6579   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6580
6581 ;;;###autoload
6582 (defun bmkp-file-this-dir-jump (bookmark-name &optional use-region-p) ; `C-x j C-f'
6583   "Jump to a bookmark for a file or subdir in the `default-directory'.
6584 This is a specialization of `bookmark-jump' - see that, in particular
6585 for info about using a prefix argument."
6586   (interactive
6587    (let ((alist  (bmkp-file-this-dir-alist-only)))
6588      (list (bmkp-read-bookmark-for-type "file-in-this-dir " alist nil nil 'bmkp-file-history)
6589            current-prefix-arg)))
6590   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6591
6592 ;;;###autoload
6593 (defun bmkp-file-this-dir-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j C-f'
6594   "`bmkp-file-this-dir-jump', but in another window."
6595   (interactive
6596    (let ((alist  (bmkp-file-this-dir-alist-only)))
6597      (list (bmkp-read-bookmark-for-type "file-in-this-dir " alist t nil 'bmkp-file-history)
6598            current-prefix-arg)))
6599   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6600
6601 ;;;###autoload
6602 (defun bmkp-gnus-jump (bookmark-name &optional use-region-p) ; `C-x j g'
6603   "Jump to a Gnus bookmark.
6604 This is a specialization of `bookmark-jump' - see that, in particular
6605 for info about using a prefix argument."
6606   (interactive
6607    (let ((alist  (bmkp-gnus-alist-only)))
6608      (list (bmkp-read-bookmark-for-type "Gnus " alist nil nil 'bmkp-gnus-history)
6609            current-prefix-arg)))
6610   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6611
6612 ;;;###autoload
6613 (defun bmkp-gnus-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j g'
6614   "`bmkp-gnus-jump', but in another window."
6615   (interactive
6616    (let ((alist  (bmkp-gnus-alist-only)))
6617      (list (bmkp-read-bookmark-for-type "Gnus " alist t nil 'bmkp-gnus-history)
6618            current-prefix-arg)))
6619   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6620
6621 ;;;###autoload
6622 (defun bmkp-info-jump (bookmark-name &optional use-region-p) ; `C-x j i'
6623   "Jump to an Info bookmark.
6624 This is a specialization of `bookmark-jump' - see that, in particular
6625 for info about using a prefix argument."
6626   (interactive
6627    (let ((alist  (bmkp-info-alist-only)))
6628      (list (bmkp-read-bookmark-for-type "Info " alist nil nil 'bmkp-info-history)
6629            current-prefix-arg)))
6630   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6631
6632 ;;;###autoload
6633 (defun bmkp-info-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j i'
6634   "`bmkp-info-jump', but in another window."
6635   (interactive
6636    (let ((alist  (bmkp-info-alist-only)))
6637      (list (bmkp-read-bookmark-for-type "Info " alist t nil 'bmkp-info-history)
6638            current-prefix-arg)))
6639   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6640
6641 ;;;###autoload
6642 (defun bmkp-local-file-jump (bookmark-name &optional use-region-p) ; `C-x j l'
6643   "Jump to a local file or directory bookmark.
6644 This is a specialization of `bookmark-jump' - see that, in particular
6645 for info about using a prefix argument."
6646   (interactive
6647    (let ((alist  (bmkp-local-file-alist-only)))
6648      (list (bmkp-read-bookmark-for-type "local file " alist nil nil 'bmkp-local-file-history)
6649            current-prefix-arg)))
6650   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6651
6652 ;;;###autoload
6653 (defun bmkp-local-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j l'
6654   "`bmkp-local-file-jump', but in another window."
6655   (interactive
6656    (let ((alist  (bmkp-local-file-alist-only)))
6657      (list (bmkp-read-bookmark-for-type "local file " alist t nil 'bmkp-local-file-history)
6658            current-prefix-arg)))
6659   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6660
6661 ;;;###autoload
6662 (defun bmkp-man-jump (bookmark-name &optional use-region-p) ; `C-x j m'
6663   "Jump to a `man'-page bookmark.
6664 This is a specialization of `bookmark-jump' - see that, in particular
6665 for info about using a prefix argument."
6666   (interactive
6667    (let ((alist  (bmkp-man-alist-only)))
6668      (list (bmkp-read-bookmark-for-type "`man' " alist nil nil 'bmkp-man-history)
6669            current-prefix-arg)))
6670   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6671
6672 ;;;###autoload
6673 (defun bmkp-man-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j m'
6674   "`bmkp-man-jump', but in another window."
6675   (interactive
6676    (let ((alist  (bmkp-man-alist-only)))
6677      (list (bmkp-read-bookmark-for-type "`man' " alist t nil 'bmkp-man-history)
6678            current-prefix-arg)))
6679   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6680
6681 ;;;###autoload
6682 (defun bmkp-non-file-jump (bookmark-name &optional use-region-p) ; `C-x j b'
6683   "Jump to a non-file (buffer) bookmark.
6684 This is a specialization of `bookmark-jump' - see that, in particular
6685 for info about using a prefix argument."
6686   (interactive
6687    (let ((alist  (bmkp-non-file-alist-only)))
6688      (list (bmkp-read-bookmark-for-type "non-file (buffer) " alist nil nil 'bmkp-non-file-history)
6689            current-prefix-arg)))
6690   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6691
6692 ;;;###autoload
6693 (defun bmkp-non-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j b'
6694   "`bmkp-non-file-jump', but in another window."
6695   (interactive
6696    (let ((alist  (bmkp-non-file-alist-only)))
6697      (list (bmkp-read-bookmark-for-type "non-file (buffer) " alist t nil 'bmkp-non-file-history)
6698            current-prefix-arg)))
6699   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6700
6701 ;;;###autoload
6702 (defun bmkp-region-jump (bookmark-name) ; `C-x j r'
6703   "Jump to a region bookmark.
6704 This is a specialization of `bookmark-jump', but without a prefix arg."
6705   (interactive (list (bmkp-read-bookmark-for-type "region " (bmkp-region-alist-only) nil nil
6706                                                   'bmkp-region-history)))
6707   (bmkp-jump-1 bookmark-name 'switch-to-buffer t))
6708
6709 ;;;###autoload
6710 (defun bmkp-region-jump-other-window (bookmark-name) ; `C-x 4 j r'
6711   "`bmkp-region-jump', but in another window."
6712   (interactive (list (bmkp-read-bookmark-for-type "region " (bmkp-region-alist-only) t nil
6713                                                   'bmkp-region-history)))
6714   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window t))
6715
6716 ;;;###autoload
6717 (defun bmkp-remote-file-jump (bookmark-name &optional use-region-p) ; `C-x j n'
6718   "Jump to a remote file or directory bookmark.
6719 This is a specialization of `bookmark-jump' - see that, in particular
6720 for info about using a prefix argument."
6721   (interactive
6722    (let ((alist  (bmkp-remote-file-alist-only)))
6723      (list (bmkp-read-bookmark-for-type "remote file " alist nil nil 'bmkp-remote-file-history)
6724            current-prefix-arg)))
6725   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6726
6727 ;;;###autoload
6728 (defun bmkp-remote-file-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j n'
6729   "`bmkp-remote-file-jump', but in another window."
6730   (interactive
6731    (let ((alist  (bmkp-remote-file-alist-only)))
6732      (list (bmkp-read-bookmark-for-type "remote file " alist t nil 'bmkp-remote-file-history)
6733            current-prefix-arg)))
6734   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6735
6736 ;;;###autoload
6737 (defun bmkp-specific-buffers-jump (buffers bookmark-name &optional use-region-p) ; `C-x j = b'
6738   "Jump to a bookmark for a buffer in list BUFFERS.
6739 Interactively, read buffer names and bookmark name, with completion.
6740
6741 This is a specialization of `bookmark-jump' - see that, in particular
6742 for info about using a prefix argument."
6743   (interactive
6744    (let ((buffs  ())
6745          buff)
6746      (while (and (setq buff  (bmkp-completing-read-buffer-name 'ALLOW-EMPTY)) (not (string= "" buff)))
6747        (add-to-list 'buffs buff))
6748      (let ((alist  (bmkp-specific-buffers-alist-only buffs)))
6749        (list buffs (bmkp-read-bookmark-for-type "specific-buffers " alist) current-prefix-arg))))
6750   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6751
6752 ;;;###autoload
6753 (defun bmkp-specific-buffers-jump-other-window (buffers bookmark-name
6754                                                 &optional use-region-p) ; `C-x 4 j = b'
6755   "`bmkp-specific-buffers-jump', but in another window."
6756   (interactive
6757    (let ((buffs  ())
6758          buff)
6759      (while (and (setq buff  (bmkp-completing-read-buffer-name 'ALLOW-EMPTY)) (not (string= "" buff)))
6760        (add-to-list 'buffs buff))
6761      (let ((alist  (bmkp-specific-buffers-alist-only buffs)))
6762        (list buffs (bmkp-read-bookmark-for-type "specific-buffers " alist) current-prefix-arg))))
6763   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6764
6765 ;;;###autoload
6766 (defun bmkp-specific-files-jump (files bookmark-name &optional use-region-p) ; `C-x j = f'
6767   "Jump to a bookmark for a file in list FILES.
6768 Interactively, read file names and bookmark name, with completion.
6769
6770 This is a specialization of `bookmark-jump' - see that, in particular
6771 for info about using a prefix argument."
6772   (interactive
6773    (let ((use-file-dialog  nil)
6774          (files            ())
6775          file)
6776      (while (and (setq file  (bmkp-completing-read-file-name 'ALLOW-EMPTY)) (not (string= "" file)))
6777        (add-to-list 'files file))
6778      (let ((alist  (bmkp-specific-files-alist-only files)))
6779        (list files (bmkp-read-bookmark-for-type "specific-files " alist) current-prefix-arg))))
6780   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6781
6782 ;;;###autoload
6783 (defun bmkp-specific-files-jump-other-window (files bookmark-name
6784                                               &optional use-region-p) ; `C-x 4 j = f'
6785   "`bmkp-specific-files-jump', but in another window."
6786   (interactive
6787    (let ((use-file-dialog  nil)
6788          (files            ())
6789          file)
6790      (while (and (setq file  (bmkp-completing-read-file-name 'ALLOW-EMPTY)) (not (string= "" file)))
6791        (add-to-list 'files file))
6792      (let ((alist  (bmkp-specific-files-alist-only files)))
6793        (list files (bmkp-read-bookmark-for-type "specific-files " alist) current-prefix-arg))))
6794   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6795
6796 ;;;###autoload
6797 (defun bmkp-this-buffer-jump (bookmark-name &optional use-region-p) ; `C-x j .'
6798   "Jump to a bookmark for the current buffer.
6799 This is a specialization of `bookmark-jump' - see that, in particular
6800 for info about using a prefix argument."
6801   (interactive
6802    (let ((alist  (bmkp-this-buffer-alist-only)))
6803      (unless alist  (error "No bookmarks for this buffer"))
6804      (list (bookmark-completing-read "Jump to bookmark for this buffer"
6805                                      (bmkp-default-bookmark-name alist) alist)
6806            current-prefix-arg)))
6807   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6808
6809 ;;;###autoload
6810 (defun bmkp-this-buffer-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j .'
6811   "`bmkp-this-buffer-jump', but in another window."
6812   (interactive
6813    (let ((alist  (bmkp-this-buffer-alist-only)))
6814      (unless alist  (error "No bookmarks for this buffer"))
6815      (list (bookmark-completing-read "Jump to bookmark for this buffer in another window"
6816                                      (bmkp-default-bookmark-name alist) alist)
6817            current-prefix-arg)))
6818   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6819
6820 ;;; ;;;###autoload
6821 ;;; (defun bmkp-this-file-jump (bookmark-name &optional use-region-p)
6822 ;;;   "Jump to a bookmark for the current file (absolute file name).
6823 ;;; This is a specialization of `bookmark-jump' - see that, in particular
6824 ;;; for info about using a prefix argument."
6825 ;;;   (interactive
6826 ;;;    (progn (unless (or (buffer-file-name) (and (eq major-mode 'dired-mode)
6827 ;;;                                               (if (consp dired-directory)
6828 ;;;                                                   (car dired-directory)
6829 ;;;                                                 dired-directory)))
6830 ;;;             (error "This buffer is not associated with a file"))
6831 ;;;           (let ((alist  (bmkp-this-file-alist-only)))
6832 ;;;             (unless alist  (error "No bookmarks for this file"))
6833 ;;;             (list (bookmark-completing-read "Jump to bookmark for this file"
6834 ;;;                                             (bmkp-default-bookmark-name alist) alist)
6835 ;;;                   current-prefix-arg))))
6836 ;;;   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6837
6838 ;;; ;;;###autoload
6839 ;;; (defun bmkp-this-file-jump-other-window (bookmark-name &optional use-region-p)
6840 ;;;   "`bmkp-this-file-jump', but in another window."
6841 ;;;   (interactive
6842 ;;;    (progn (unless (or (buffer-file-name) (and (eq major-mode 'dired-mode)
6843 ;;;                                               (if (consp dired-directory)
6844 ;;;                                                   (car dired-directory)
6845 ;;;                                                 dired-directory)))
6846 ;;;             (error "This buffer is not associated with a file"))
6847 ;;;           (let ((alist  (bmkp-this-file-alist-only)))
6848 ;;;             (unless alist  (error "No bookmarks for this file"))
6849 ;;;             (list (bookmark-completing-read "Jump to bookmark for this file in another window"
6850 ;;;                                             (bmkp-default-bookmark-name alist) alist)
6851 ;;;                   current-prefix-arg))))
6852 ;;;   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6853
6854 ;;;###autoload
6855 (defun bmkp-variable-list-jump (bookmark-name) ; `C-x j v'
6856   "Jump to a variable-list bookmark.
6857 This is a specialization of `bookmark-jump'."
6858   (interactive
6859    (let ((alist  (bmkp-variable-list-alist-only)))
6860      (list (bmkp-read-bookmark-for-type "variable-list " alist nil nil 'bmkp-variable-list-history))))
6861   (bmkp-jump-1 bookmark-name 'switch-to-buffer nil))
6862
6863 ;;;###autoload
6864 (defun bmkp-url-jump (bookmark-name &optional use-region-p) ; `C-x j u'
6865   "Jump to a URL bookmark.
6866 This is a specialization of `bookmark-jump' - see that, in particular
6867 for info about using a prefix argument."
6868   (interactive
6869    (let ((alist  (if (fboundp 'w3m-list-buffers)
6870                      (bmkp-url-alist-only)
6871                    (bmkp-url-browse-alist-only))))
6872      (list (bmkp-read-bookmark-for-type "URL " alist nil nil 'bmkp-url-history)
6873            current-prefix-arg)))
6874   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6875
6876 ;;;###autoload
6877 (defun bmkp-url-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j u'
6878   "`bmkp-url-jump', but in another window."
6879   (interactive
6880    (let ((alist  (if (fboundp 'w3m-list-buffers)
6881                      (bmkp-url-alist-only)
6882                    (bmkp-url-browse-alist-only))))
6883      (list (bmkp-read-bookmark-for-type "URL " alist t nil 'bmkp-url-history)
6884            current-prefix-arg)))
6885   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6886
6887 ;;;###autoload
6888 (defun bmkp-w3m-jump (bookmark-name &optional use-region-p) ; `C-x j w'
6889   "Jump to a W3M bookmark.
6890 This is a specialization of `bookmark-jump' - see that, in particular
6891 for info about using a prefix argument."
6892   (interactive
6893    (let ((alist  (bmkp-w3m-alist-only)))
6894      (list (bmkp-read-bookmark-for-type "W3M " alist nil nil 'bmkp-w3m-history)
6895            current-prefix-arg)))
6896   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
6897
6898 ;;;###autoload
6899 (defun bmkp-w3m-jump-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j w'
6900   "`bmkp-w3m-jump', but in another window."
6901   (interactive
6902    (let ((alist  (bmkp-w3m-alist-only)))
6903      (list (bmkp-read-bookmark-for-type "W3M " alist t nil 'bmkp-w3m-history)
6904            current-prefix-arg)))
6905   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
6906
6907 ;;;###autoload
6908 (defun bmkp-all-tags-jump (tags bookmark) ; `C-x j t *'
6909   "Jump to a BOOKMARK that has all of the TAGS.
6910 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
6911 You can use completion to enter the bookmark name and each tag.
6912 If you specify no tags, then every bookmark that has some tags is a
6913 candidate."
6914   (interactive
6915    (let* ((tgs    (bmkp-read-tags-completing))
6916           (alist  (bmkp-all-tags-alist-only tgs)))
6917      (unless alist (error "No bookmarks have all of the specified tags"))
6918      (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6919   (bookmark-jump bookmark))
6920
6921 ;;;###autoload
6922 (defun bmkp-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t *'
6923   "`bmkp-all-tags-jump', but in another window."
6924   (interactive
6925    (let* ((tgs    (bmkp-read-tags-completing))
6926           (alist  (bmkp-all-tags-alist-only tgs)))
6927      (unless alist (error "No bookmarks have all of the specified tags"))
6928      (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6929   (bookmark-jump-other-window bookmark))
6930
6931 ;;;###autoload
6932 (defun bmkp-all-tags-regexp-jump (regexp bookmark) ; `C-x j t % *'
6933   "Jump to a BOOKMARK that has each tag matching REGEXP.
6934 You are prompted for the REGEXP.
6935 Then you are prompted for the BOOKMARK (with completion)."
6936   (interactive
6937    (let* ((rgx    (read-string "Regexp for all tags: "))
6938           (alist  (bmkp-all-tags-regexp-alist-only rgx)))
6939      (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6940      (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6941   (bookmark-jump bookmark))
6942
6943 ;;;###autoload
6944 (defun bmkp-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t % *'
6945   "`bmkp-all-tags-regexp-jump', but in another window."
6946   (interactive
6947    (let* ((rgx    (read-string "Regexp for all tags: "))
6948           (alist  (bmkp-all-tags-regexp-alist-only rgx)))
6949      (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6950      (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6951   (bookmark-jump-other-window bookmark))
6952
6953 ;;;###autoload
6954 (defun bmkp-some-tags-jump (tags bookmark) ; `C-x j t +'
6955   "Jump to a BOOKMARK that has at least one of the TAGS.
6956 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
6957 You can use completion to enter the bookmark name and each tag."
6958   (interactive
6959    (let* ((tgs    (bmkp-read-tags-completing))
6960           (alist  (bmkp-some-tags-alist-only tgs)))
6961      (unless tgs (error "You did not specify any tags"))
6962      (unless alist (error "No bookmarks have any of the specified tags"))
6963      (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6964   (bookmark-jump bookmark))
6965
6966 ;;;###autoload
6967 (defun bmkp-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t +'
6968   "`bmkp-some-tags-jump', but in another window."
6969   (interactive
6970    (let* ((tgs    (bmkp-read-tags-completing))
6971           (alist  (bmkp-some-tags-alist-only tgs)))
6972      (unless tgs (error "You did not specify any tags"))
6973      (unless alist (error "No bookmarks have any of the specified tags"))
6974      (list tgs (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6975   (bookmark-jump-other-window bookmark))
6976
6977 ;;;###autoload
6978 (defun bmkp-some-tags-regexp-jump (regexp bookmark) ; `C-x j t % +'
6979   "Jump to a BOOKMARK that has a tag matching REGEXP.
6980 You are prompted for the REGEXP.
6981 Then you are prompted for the BOOKMARK (with completion)."
6982   (interactive
6983    (let* ((rgx    (read-string "Regexp for tags: "))
6984           (alist  (bmkp-some-tags-regexp-alist-only rgx)))
6985      (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6986      (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6987   (bookmark-jump bookmark))
6988
6989 ;;;###autoload
6990 (defun bmkp-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t % +'
6991   "`bmkp-some-tags-regexp-jump', but in another window."
6992   (interactive
6993    (let* ((rgx    (read-string "Regexp for tags: "))
6994           (alist  (bmkp-some-tags-regexp-alist-only rgx)))
6995      (unless alist (error "No bookmarks have tags that match `%s'" rgx))
6996      (list rgx (bookmark-completing-read "Bookmark" (bmkp-default-bookmark-name alist) alist))))
6997   (bookmark-jump-other-window bookmark))
6998
6999 ;;;###autoload
7000 (defun bmkp-file-all-tags-jump (tags bookmark) ; `C-x j t f *'
7001   "Jump to a file or directory BOOKMARK that has all of the TAGS.
7002 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7003 You can use completion to enter the bookmark name and each tag.
7004 If you specify no tags, then every bookmark that has some tags is a
7005 candidate."
7006   (interactive
7007    (let* ((tgs    (bmkp-read-tags-completing))
7008           (alist  (bmkp-file-all-tags-alist-only tgs)))
7009      (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7010      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7011   (bookmark-jump bookmark))
7012
7013 ;;;###autoload
7014 (defun bmkp-file-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t f *'
7015   "`bmkp-file-all-tags-jump', but in another window."
7016   (interactive
7017    (let* ((tgs    (bmkp-read-tags-completing))
7018           (alist  (bmkp-file-all-tags-alist-only tgs)))
7019      (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7020      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7021   (bookmark-jump-other-window bookmark))
7022
7023 ;;;###autoload
7024 (defun bmkp-file-all-tags-regexp-jump (regexp bookmark) ; `C-x j t f % *'
7025   "Jump to a file or directory BOOKMARK that has each tag matching REGEXP.
7026 You are prompted for the REGEXP.
7027 Then you are prompted for the BOOKMARK (with completion)."
7028   (interactive
7029    (let* ((rgx    (read-string "Regexp for tags: "))
7030           (alist  (bmkp-file-all-tags-regexp-alist-only rgx)))
7031      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7032      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7033   (bookmark-jump bookmark))
7034
7035 ;;;###autoload
7036 (defun bmkp-file-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t f % *'
7037   "`bmkp-file-all-tags-regexp-jump', but in another window."
7038   (interactive
7039    (let* ((rgx    (read-string "Regexp for tags: "))
7040           (alist  (bmkp-file-all-tags-regexp-alist-only rgx)))
7041      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7042      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7043   (bookmark-jump-other-window bookmark))
7044
7045 ;;;###autoload
7046 (defun bmkp-file-some-tags-jump (tags bookmark) ; `C-x j t f +'
7047   "Jump to a file or directory BOOKMARK that has at least one of the TAGS.
7048 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7049 You can use completion to enter the bookmark name and each tag."
7050   (interactive
7051    (let* ((tgs    (bmkp-read-tags-completing))
7052           (alist  (bmkp-file-some-tags-alist-only tgs)))
7053      (unless tgs (error "You did not specify any tags"))
7054      (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7055      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7056   (bookmark-jump bookmark))
7057
7058 ;;;###autoload
7059 (defun bmkp-file-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t f +'
7060   "`bmkp-file-some-tags-jump', but in another window."
7061   (interactive
7062    (let* ((tgs    (bmkp-read-tags-completing))
7063           (alist  (bmkp-file-some-tags-alist-only tgs)))
7064      (unless tgs (error "You did not specify any tags"))
7065      (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7066      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7067   (bookmark-jump-other-window bookmark))
7068
7069 ;;;###autoload
7070 (defun bmkp-file-some-tags-regexp-jump (regexp bookmark) ; `C-x j t f % +'
7071   "Jump to a file or directory BOOKMARK that has a tag matching REGEXP.
7072 You are prompted for the REGEXP.
7073 Then you are prompted for the BOOKMARK (with completion)."
7074   (interactive
7075    (let* ((rgx    (read-string "Regexp for tags: "))
7076           (alist  (bmkp-file-some-tags-regexp-alist-only rgx)))
7077      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7078      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7079   (bookmark-jump bookmark))
7080
7081 ;;;###autoload
7082 (defun bmkp-file-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t f % +'
7083   "`bmkp-file-some-tags-regexp-jump', but in another window."
7084   (interactive
7085    (let* ((rgx    (read-string "Regexp for tags: "))
7086           (alist  (bmkp-file-some-tags-regexp-alist-only rgx)))
7087      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7088      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7089   (bookmark-jump-other-window bookmark))
7090
7091 ;;;###autoload
7092 (defun bmkp-file-this-dir-all-tags-jump (tags bookmark) ; `C-x j t C-f *'
7093   "Jump to a file BOOKMARK in this dir that has all of the TAGS.
7094 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7095 You can use completion to enter the bookmark name and each tag.
7096 If you specify no tags, then every bookmark that has some tags is a
7097 candidate."
7098   (interactive
7099    (let* ((tgs    (bmkp-read-tags-completing))
7100           (alist  (bmkp-file-this-dir-all-tags-alist-only tgs)))
7101      (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7102      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7103   (bookmark-jump bookmark))
7104
7105 ;;;###autoload
7106 (defun bmkp-file-this-dir-all-tags-jump-other-window (tags bookmark) ; `C-x 4 j t C-f *'
7107   "`bmkp-file-this-dir-all-tags-jump', but in another window."
7108   (interactive
7109    (let* ((tgs    (bmkp-read-tags-completing))
7110           (alist  (bmkp-file-this-dir-all-tags-alist-only tgs)))
7111      (unless alist (error "No file or dir bookmarks have all of the specified tags"))
7112      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7113   (bookmark-jump-other-window bookmark))
7114
7115 ;;;###autoload
7116 (defun bmkp-file-this-dir-all-tags-regexp-jump (regexp bookmark) ; `C-x j t C-f % *'
7117   "Jump to a file BOOKMARK in this dir that has each tag matching REGEXP.
7118 You are prompted for the REGEXP.
7119 Then you are prompted for the BOOKMARK (with completion)."
7120   (interactive
7121    (let* ((rgx    (read-string "Regexp for tags: "))
7122           (alist  (bmkp-file-this-dir-all-tags-regexp-alist-only rgx)))
7123      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7124      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7125   (bookmark-jump bookmark))
7126
7127 ;;;###autoload
7128 (defun bmkp-file-this-dir-all-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t C-f % *'
7129   "`bmkp-file-this-dir-all-tags-regexp-jump', but in another window."
7130   (interactive
7131    (let* ((rgx    (read-string "Regexp for tags: "))
7132           (alist  (bmkp-file-this-dir-all-tags-regexp-alist-only rgx)))
7133      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7134      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7135   (bookmark-jump-other-window bookmark))
7136
7137 ;;;###autoload
7138 (defun bmkp-file-this-dir-some-tags-jump (tags bookmark) ; `C-x j t C-f +'
7139   "Jump to a file BOOKMARK in this dir that has at least one of the TAGS.
7140 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7141 You can use completion to enter the bookmark name and each tag."
7142   (interactive
7143    (let* ((tgs    (bmkp-read-tags-completing))
7144           (alist  (bmkp-file-this-dir-some-tags-alist-only tgs)))
7145      (unless tgs (error "You did not specify any tags"))
7146      (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7147      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7148   (bookmark-jump bookmark))
7149
7150 ;;;###autoload
7151 (defun bmkp-file-this-dir-some-tags-jump-other-window (tags bookmark) ; `C-x 4 j t C-f +'
7152   "`bmkp-file-this-dir-some-tags-jump', but in another window."
7153   (interactive
7154    (let* ((tgs    (bmkp-read-tags-completing))
7155           (alist  (bmkp-file-this-dir-some-tags-alist-only tgs)))
7156      (unless tgs (error "You did not specify any tags"))
7157      (unless alist (error "No file or dir bookmarks have any of the specified tags"))
7158      (list tgs (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7159   (bookmark-jump-other-window bookmark))
7160
7161 ;;;###autoload
7162 (defun bmkp-file-this-dir-some-tags-regexp-jump (regexp bookmark) ; `C-x j t C-f % +'
7163   "Jump to a file BOOKMARK in this dir that has a tag matching REGEXP.
7164 You are prompted for the REGEXP.
7165 Then you are prompted for the BOOKMARK (with completion)."
7166   (interactive
7167    (let* ((rgx    (read-string "Regexp for tags: "))
7168           (alist  (bmkp-file-this-dir-some-tags-regexp-alist-only rgx)))
7169      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7170      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7171   (bookmark-jump bookmark))
7172
7173 ;;;###autoload
7174 (defun bmkp-file-this-dir-some-tags-regexp-jump-other-window (regexp bookmark) ; `C-x 4 j t C-f % +'
7175   "`bmkp-file-this-dir-some-tags-regexp-jump', but in another window."
7176   (interactive
7177    (let* ((rgx    (read-string "Regexp for tags: "))
7178           (alist  (bmkp-file-this-dir-some-tags-regexp-alist-only rgx)))
7179      (unless alist (error "No file or dir bookmarks have tags that match `%s'" rgx))
7180      (list rgx (bookmark-completing-read "File bookmark" (bmkp-default-bookmark-name alist) alist))))
7181   (bookmark-jump-other-window bookmark))
7182
7183 (when (> emacs-major-version 21)        ; Needs `read-file-name' with a PREDICATE arg.
7184   (defalias 'bmkp-autofile-jump 'bmkp-find-file)
7185   (defun bmkp-find-file ()              ; `C-x j a'
7186     "Visit a file or directory that has an autofile bookmark."
7187     (interactive)
7188     (let ((use-file-dialog  nil)
7189           (pred             #'(lambda (ff) (bmkp-get-autofile-bookmark ff))))
7190       (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7191
7192 (when (> emacs-major-version 21)        ; Needs `read-file-name' with a PREDICATE arg.
7193   (defalias 'bmkp-autofile-jump-other-window 'bmkp-find-file)
7194   (defun bmkp-find-file-other-window () ; `C-x 4 j a'
7195     "`bmkp-find-file', but in another window."
7196     (interactive)
7197     (let ((use-file-dialog  nil)
7198           (pred             #'(lambda (ff) (bmkp-get-autofile-bookmark ff))))
7199       (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7200
7201 (when (> emacs-major-version 21)        ; Needs `read-file-name' with a PREDICATE arg.
7202   (defun bmkp-find-file-all-tags (tags) ; `C-x j t a *'
7203     "Visit a file or directory that has all of the TAGS.
7204 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7205 You can use completion.
7206 If you specify no tags, then every file that has some tags is a
7207 candidate."
7208     (interactive (list (bmkp-read-tags-completing)))
7209     (let ((use-file-dialog  nil)
7210           (pred             #'(lambda (ff)
7211                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7212                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7213                                   (and btgs  (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag))
7214                                                          tags))))))
7215       (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7216
7217 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7218   (defun bmkp-find-file-all-tags-other-window (tags) ; `C-x 4 j t a *'
7219     "`bmkp-find-file-all-tags', but in another window."
7220     (interactive (list (bmkp-read-tags-completing)))
7221     (let ((use-file-dialog  nil)
7222           (pred             #'(lambda (ff)
7223                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7224                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7225                                   (and btgs  (bmkp-every #'(lambda (tag) (bmkp-has-tag-p bmk tag))
7226                                                          tags))))))
7227       (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7228
7229 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7230   (defun bmkp-find-file-all-tags-regexp (regexp) ; `C-x j t a % *'
7231     "Visit a file or directory that has each tag matching REGEXP.
7232 You are prompted for the REGEXP."
7233     (interactive (list (read-string "Regexp for tags: ")))
7234     (let ((use-file-dialog  nil)
7235           (pred             #'(lambda (ff)
7236                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7237                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7238                                   (and btgs  (bmkp-every
7239                                               #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7240                                               btgs))))))
7241       (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7242
7243 ;;;###autoload
7244 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7245   (defun bmkp-find-file-all-tags-regexp-other-window (regexp) ; `C-x 4 j t a % *'
7246     "`bmkp-find-file-all-tags-regexp', but in another window."
7247     (interactive (list (read-string "Regexp for tags: ")))
7248     (let ((use-file-dialog  nil)
7249           (pred             #'(lambda (ff)
7250                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7251                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7252                                   (and btgs  (bmkp-every
7253                                               #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7254                                               btgs))))))
7255       (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7256
7257 ;;;###autoload
7258 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7259   (defun bmkp-find-file-some-tags (tags) ; `C-x j t a +'
7260     "Visit a file or directory that has at least one of the TAGS.
7261 Hit `RET' to enter each tag, then hit `RET' again after the last tag.
7262 You can use completion."
7263     (interactive (list (bmkp-read-tags-completing)))
7264     (let ((use-file-dialog  nil)
7265           (pred             #'(lambda (ff)
7266                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7267                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7268                                   (and btgs  (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))))))
7269       (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7270
7271 ;;;###autoload
7272 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7273   (defun bmkp-find-file-some-tags-other-window (tags) ; `C-x 4 j t a +'
7274     "`bmkp-find-file-some-tags', but in another window."
7275     (interactive (list (bmkp-read-tags-completing)))
7276     (let ((use-file-dialog  nil)
7277           (pred             #'(lambda (ff)
7278                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7279                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7280                                   (and btgs  (bmkp-some #'(lambda (tag) (bmkp-has-tag-p bmk tag)) tags))))))
7281       (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7282
7283 ;;;###autoload
7284 (when (> emacs-major-version 21) ; Needs `read-file-name' with a PREDICATE arg.
7285   (defun bmkp-find-file-some-tags-regexp (regexp)  ; `C-x j t a % +'
7286     "Visit a file or directory that has a tag matching REGEXP.
7287 You are prompted for the REGEXP."
7288     (interactive (list (read-string "Regexp for tags: ")))
7289     (let ((use-file-dialog  nil)
7290           (pred             #'(lambda (ff)
7291                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7292                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7293                                   (and btgs  (bmkp-some
7294                                               #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7295                                               btgs))))))
7296       (find-file (read-file-name "Find file: " nil nil t nil pred)))))
7297
7298 ;;;###autoload
7299 (when (> emacs-major-version 21)        ; Needs `read-file-name' with a PREDICATE arg.
7300   (defun bmkp-find-file-some-tags-regexp-other-window (regexp) ; `C-x 4 j t a % +'
7301     "`bmkp-find-file-some-tags-regexp', but in another window."
7302     (interactive (list (read-string "Regexp for tags: ")))
7303     (let ((use-file-dialog  nil)
7304           (pred             #'(lambda (ff)
7305                                 (let* ((bmk   (bmkp-get-autofile-bookmark ff))
7306                                        (btgs  (and bmk (bmkp-get-tags bmk))))
7307                                   (and btgs  (bmkp-some
7308                                               #'(lambda (tag) (string-match regexp (bmkp-tag-name tag)))
7309                                               btgs))))))
7310       (find-file-other-window (read-file-name "Find file: " nil nil t nil pred)))))
7311
7312 ;;;###autoload
7313 (defun bmkp-jump-in-navlist (bookmark-name &optional use-region-p) ; `C-x j N'
7314   "Jump to a bookmark, choosing from those in the navigation list."
7315   (interactive
7316    (progn (unless bmkp-nav-alist
7317             (bookmark-maybe-load-default-file)
7318             (setq bmkp-nav-alist  bookmark-alist)
7319             (unless bmkp-nav-alist (error "No bookmarks"))
7320             (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist))
7321             (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7322           (let ((bookmark-alist  bmkp-nav-alist))
7323             (list (bookmark-completing-read "Jump to bookmark (in another window)"
7324                                             (bmkp-default-bookmark-name))
7325                   current-prefix-arg))))
7326   (bmkp-jump-1 bookmark-name 'switch-to-buffer use-region-p))
7327
7328 ;;;###autoload
7329 (defun bmkp-jump-in-navlist-other-window (bookmark-name &optional use-region-p) ; `C-x 4 j N'
7330   "Same as `bmkp-jump-in-navlist', but use another window."
7331   (interactive
7332    (progn (unless bmkp-nav-alist
7333             (bookmark-maybe-load-default-file)
7334             (setq bmkp-nav-alist  bookmark-alist)
7335             (unless bmkp-nav-alist (error "No bookmarks"))
7336             (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist))
7337             (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7338           (let ((bookmark-alist  bmkp-nav-alist))
7339             (list (bookmark-completing-read "Jump to bookmark (in another window)"
7340                                             (bmkp-default-bookmark-name))
7341                   current-prefix-arg))))
7342   (bmkp-jump-1 bookmark-name 'bmkp-select-buffer-other-window use-region-p))
7343
7344 ;;;###autoload
7345 (defun bmkp-cycle (increment &optional other-window startoverp)
7346   "Cycle through bookmarks in the navlist by INCREMENT (default: 1).
7347 Positive INCREMENT cycles forward.  Negative INCREMENT cycles backward.
7348 Interactively, the prefix arg determines INCREMENT:
7349  Plain `C-u': 1
7350  otherwise: the numeric prefix arg value
7351
7352 Plain `C-u' also means start over at first bookmark.
7353
7354 You can set the navigation list using commands
7355  `bmkp-choose-navlist-from-bookmark-list' and
7356  `bmkp-choose-navlist-of-type'.
7357
7358 You can cycle among bookmarks in the current buffer using
7359  `bmkp-cycle-this-buffer' and
7360  `bmkp-cycle-this-buffer-other-window.'
7361
7362 In Lisp code:
7363  Non-nil OTHER-WINDOW means jump to the bookmark in another window.
7364  Non-nil STARTOVERP means reset `bmkp-current-nav-bookmark' to the
7365  first bookmark in the navlist."
7366   (interactive (let ((startovr  (consp current-prefix-arg)))
7367                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) nil startovr)))
7368   (unless bmkp-nav-alist
7369     (bookmark-maybe-load-default-file)
7370     (setq bmkp-nav-alist  bookmark-alist)
7371     (unless bmkp-nav-alist (error "No bookmarks"))
7372     (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist))
7373     (message "Bookmark navigation list is now the global bookmark list") (sit-for 2))
7374   (unless (and bmkp-current-nav-bookmark (not startoverp)
7375                (bookmark-get-bookmark bmkp-current-nav-bookmark 'NOERROR))
7376     (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist)))
7377   (if (bmkp-cycle-1 increment other-window startoverp)
7378       (unless (or (bmkp-sequence-bookmark-p bmkp-current-nav-bookmark)
7379                   (bmkp-function-bookmark-p bmkp-current-nav-bookmark))
7380         (message "Position: %9d, Bookmark: `%s'"
7381                  (point) (bookmark-name-from-full-record bmkp-current-nav-bookmark)))
7382     (message "Invalid bookmark: `%s'" (bookmark-name-from-full-record bmkp-current-nav-bookmark))))
7383
7384 ;;;###autoload
7385 (defun bmkp-cycle-other-window (increment &optional startoverp)
7386   "Same as `bmkp-cycle' but uses another window."
7387   (interactive "p")
7388   (bmkp-cycle increment 'OTHER-WINDOW startoverp))
7389
7390 ;;;###autoload
7391 (defun bmkp-cycle-this-buffer (increment &optional other-window startoverp)
7392   "Cycle through bookmarks in this buffer by INCREMENT (default: 1).
7393 Positive INCREMENT cycles forward.  Negative INCREMENT cycles backward.
7394 Interactively, the prefix arg determines INCREMENT:
7395  Plain `C-u': 1
7396  otherwise: the numeric prefix arg value 
7397
7398 Plain `C-u' also means start over at first bookmark.
7399
7400 You can cycle among bookmarks beyond the current buffer using
7401 `bmkp-cycle' and `bmkp-cycle-other-window.'
7402
7403 You can set your preferred sort order for this-buffer bookmarks by
7404 customizing option `bmkp-this-buffer-cycle-sort-comparer'.
7405
7406 To change the sort order without customizing, you can use \
7407 `\\[bmkp-this-buffer-bmenu-list]' to
7408 show the `*Bookmark List*' with only this buffer's bookmarks, sort
7409 them there, and use `\\[bmkp-choose-navlist-from-bookmark-list]', choosing \
7410 `CURRENT *Bookmark List*' as
7411 the navigation list.
7412
7413 Then you can cycle the bookmarks using `bmkp-cycle'
7414 \(`\\[bmkp-next-bookmark-repeat]' etc.), instead of `bmkp-cycle-this-buffer'.
7415
7416 In Lisp code:
7417  Non-nil OTHER-WINDOW means jump to the bookmark in another window.
7418  Non-nil STARTOVERP means reset `bmkp-current-nav-bookmark' to the
7419  first bookmark in the navlist."
7420   (interactive (let ((startovr  (consp current-prefix-arg)))
7421                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) nil startovr)))
7422   (bookmark-maybe-load-default-file)
7423   (let ((bmkp-sort-comparer  bmkp-this-buffer-cycle-sort-comparer))
7424     (setq bmkp-nav-alist  (bmkp-sort-omit (bmkp-this-buffer-alist-only))))
7425   (unless bmkp-nav-alist (error "No bookmarks in this buffer"))
7426   (unless (and bmkp-current-nav-bookmark (not startoverp)
7427                (bookmark-get-bookmark bmkp-current-nav-bookmark 'NOERROR)
7428                (bmkp-this-buffer-p bmkp-current-nav-bookmark)) ; Exclude desktops etc.
7429     (setq bmkp-current-nav-bookmark  (car bmkp-nav-alist)))
7430   (if (bmkp-cycle-1 increment other-window startoverp)
7431       (unless (or (bmkp-sequence-bookmark-p bmkp-current-nav-bookmark)
7432                   (bmkp-function-bookmark-p bmkp-current-nav-bookmark))
7433         (message "Position: %9d, Bookmark: `%s'"
7434                  (point) (bookmark-name-from-full-record bmkp-current-nav-bookmark)))
7435     (message "Invalid bookmark: `%s'" (bookmark-name-from-full-record bmkp-current-nav-bookmark))))
7436
7437 ;;;###autoload
7438 (defun bmkp-cycle-this-buffer-other-window (increment &optional startoverp)
7439   "Same as `bmkp-cycle-this-buffer' but use other window."
7440   (interactive (let ((startovr  (consp current-prefix-arg)))
7441                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7442   (bmkp-cycle-this-buffer increment 'OTHER-WINDOW startoverp))
7443
7444 (defun bmkp-cycle-1 (increment &optional other-window startoverp)
7445   "Helper for `bmkp-cycle' and `bmkp-cycle-this-buffer'.
7446 Do nothing if `bmkp-current-nav-bookmark' is an invalid bookmark.
7447 Return `bmkp-current-nav-bookmark', or nil if invalid.
7448
7449 NOTE: If `pop-up-frames' is non-nil, then cycling inhibits automatic
7450 showing of annotations (`bookmark-automatically-show-annotations').
7451 This is to prevent change of frame focus, so cycling can continue
7452 properly.
7453
7454 See `bmkp-cycle' for descriptions of the arguments."
7455   (let ((bookmark-alist   bmkp-nav-alist)
7456         (bookmark         (bookmark-get-bookmark bmkp-current-nav-bookmark 'no-error))
7457         (bmkp-use-region  (eq 'cycling-too bmkp-use-region)))
7458     (unless bookmark-alist (error "No bookmarks for cycling"))
7459     (when bookmark                      ; Skip bookmarks with bad names.
7460       (setq bmkp-current-nav-bookmark
7461             (if startoverp
7462                 (car bookmark-alist)
7463               (let ((index  (bmkp-list-position bookmark bookmark-alist #'eq)))
7464                 (if index
7465                     (nth (mod (+ increment index) (length bookmark-alist)) bookmark-alist)
7466                   (message "bmkp-cycle-1: Bookmark `%s' is not in navlist"
7467                            (bookmark-name-from-full-record bmkp-current-nav-bookmark))
7468                   (car bookmark-alist)))))
7469       (let ((bookmark-automatically-show-annotations ; Prevent possible frame focus change.
7470              (and bookmark-automatically-show-annotations (not pop-up-frames))))
7471         (if other-window
7472             (bookmark-jump-other-window (bookmark-name-from-full-record bmkp-current-nav-bookmark))
7473           (save-selected-window (bookmark-name-from-full-record
7474                                  (bookmark-jump bmkp-current-nav-bookmark))))))
7475     (and bookmark bmkp-current-nav-bookmark))) ; Return nil if not a valid bookmark.
7476
7477 (defun bmkp-list-position (item items &optional test)
7478   "Find the first occurrence of ITEM in list ITEMS.
7479 Return the index of the matching item, or nil if not found.
7480 Items are compared using binary predicate TEST, or `equal' if TEST is
7481 nil."
7482   (unless test (setq test  'equal))
7483   (let ((pos  0))
7484     (catch 'bmkp-list-position
7485       (dolist (itm  items)
7486         (when (funcall test item itm) (throw 'bmkp-list-position pos))
7487         (setq pos  (1+ pos)))
7488       nil)))
7489
7490 ;;;###autoload
7491 (defun bmkp-next-bookmark (n &optional startoverp) ; You can bind this to a repeatable key
7492   "Jump to the Nth next bookmark in the bookmark navigation list.
7493 N defaults to 1, meaning the next bookmark.
7494 Plain `C-u' means start over at first bookmark.
7495 See also `bmkp-cycle'."
7496   (interactive (let ((startovr  (consp current-prefix-arg)))
7497                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7498   (bmkp-cycle n nil startoverp))
7499
7500 ;;;###autoload
7501 (defun bmkp-previous-bookmark (n &optional startoverp) ; You can bind this to a repeatable key
7502   "Jump to the Nth previous bookmark in the bookmark navigation list.
7503 See `bmkp-next-bookmark'."
7504   (interactive (let ((startovr  (consp current-prefix-arg)))
7505                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7506   (bmkp-cycle (- n) nil startoverp))
7507
7508 ;;;###autoload
7509 (defun bmkp-next-bookmark-repeat (arg)  ; `C-x p right', `C-x p f', `C-x p C-f'
7510   "Jump to the Nth-next bookmark in the bookmark navigation list.
7511 This is a repeatable version of `bmkp-next-bookmark'.
7512 N defaults to 1, meaning the next bookmark.
7513 Plain `C-u' means start over at the first bookmark (and no repeat)."
7514   (interactive "P")
7515   (require 'repeat)
7516   (bmkp-repeat-command 'bmkp-next-bookmark))
7517
7518 ;;;###autoload
7519 (defun bmkp-previous-bookmark-repeat (arg) ; `C-x p left', `C-x p b', `C-x p C-b'
7520   "Jump to the Nth-previous bookmark in the bookmark navigation list.
7521 See `bmkp-next-bookmark-repeat'."
7522   (interactive "P")
7523   (require 'repeat)
7524   (bmkp-repeat-command 'bmkp-previous-bookmark))
7525
7526 ;;;###autoload
7527 (defun bmkp-next-bookmark-this-buffer (n &optional startoverp) ; Bind to repeatable key, e.g. `S-f2'
7528   "Jump to the Nth-next bookmark in the current buffer.
7529 N defaults to 1, meaning the next one.
7530 Plain `C-u' means start over at the first one.
7531 See also `bmkp-cycle-this-buffer'."
7532   (interactive (let ((startovr  (consp current-prefix-arg)))
7533                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7534   (bmkp-cycle-this-buffer n nil startoverp))
7535
7536 ;;;###autoload
7537 (defun bmkp-previous-bookmark-this-buffer (n &optional startoverp) ; Bind to repeatable key, e.g. `f2'
7538   "Jump to the Nth-previous bookmark in the current buffer.
7539 See `bmkp-next-bookmark-this-buffer'."
7540   (interactive (let ((startovr  (consp current-prefix-arg)))
7541                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7542   (bmkp-cycle-this-buffer (- n) nil startoverp))
7543
7544 ;;;###autoload
7545 (defun bmkp-next-bookmark-this-buffer-repeat (arg) ; `C-x p down', `C-x p n', `C-x p C-n'
7546   "Jump to the Nth next bookmark in the current buffer.
7547 This is a repeatable version of `bmkp-next-bookmark-this-buffer'.
7548 N defaults to 1, meaning the next one.
7549 Plain `C-u' means start over at the first one (and no repeat)."
7550   (interactive "P")
7551   (require 'repeat)
7552   (bmkp-repeat-command 'bmkp-next-bookmark-this-buffer))
7553
7554 ;;;###autoload
7555 (defun bmkp-previous-bookmark-this-buffer-repeat (arg) ; `C-x p up', `C-x p p', `C-x p C-p'
7556   "Jump to the Nth previous bookmark in the current buffer.
7557 See `bmkp-next-bookmark-this-buffer-repeat'."
7558   (interactive "P")
7559   (require 'repeat)
7560   (bmkp-repeat-command 'bmkp-previous-bookmark-this-buffer))
7561
7562 ;;;###autoload
7563 (defun bmkp-next-bookmark-w32 (n &optional startoverp)       ; You can bind this to a repeatable key
7564   "Windows `Open' the Nth next bookmark in the bookmark navigation list.
7565 MS Windows only.  Invokes the program associated with the file type.
7566 N defaults to 1, meaning the next one.
7567 Plain `C-u' means start over at the first one.
7568 See also `bmkp-cycle'."
7569   (interactive (let ((startovr  (consp current-prefix-arg)))
7570                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7571   (let ((bmkp-use-w32-browser-p  t))  (bmkp-cycle n nil startoverp)))
7572
7573 ;;;###autoload
7574 (defun bmkp-previous-bookmark-w32 (n &optional startoverp)   ; You can bind this to a repeatable key
7575   "Windows `Open' the Nth previous bookmark in the bookmark navlist.
7576 See `bmkp-next-bookmark-w32'."
7577   (interactive (let ((startovr  (consp current-prefix-arg)))
7578                  (list (if startovr 1 (prefix-numeric-value current-prefix-arg)) startovr)))
7579   (let ((bmkp-use-w32-browser-p  t))  (bmkp-cycle (- n) nil startoverp)))
7580
7581 ;;;###autoload
7582 (defun bmkp-next-bookmark-w32-repeat (arg) ; `C-x p next'
7583   "Windows `Open' the Nth next bookmark in the bookmark navigation list.
7584 This is a repeatable version of `bmkp-next-bookmark'.
7585 N defaults to 1, meaning the next bookmark.
7586 Plain `C-u' means start over at the first one (and no repeat)."
7587   (interactive "P")
7588   (require 'repeat)
7589   (let ((bmkp-use-w32-browser-p  t))  (bmkp-repeat-command 'bmkp-next-bookmark)))
7590
7591 ;;;###autoload
7592 (defun bmkp-previous-bookmark-w32-repeat (arg) ; `C-x p prior'
7593   "Windows `Open' the Nth previous bookmark in the bookmark navlist.
7594 See `bmkp-next-bookmark-w32-repeat'."
7595   (interactive "P")
7596   (require 'repeat)
7597   (let ((bmkp-use-w32-browser-p  t))  (bmkp-repeat-command 'bmkp-previous-bookmark)))
7598
7599 ;; In spite of their names, `bmkp-cycle-specific-(buffers|files)*' just cycle bookmarks in the
7600 ;; current buffer or file.  There is no way to choose multiple buffers or files.
7601 ;;
7602 ;; `bmkp-cycle-autonamed', `bmkp-cycle-autonamed-other-window',
7603 ;; `bmkp-cycle-bookmark-list', `bmkp-cycle-bookmark-list-other-window',
7604 ;; `bmkp-cycle-desktop',
7605 ;; `bmkp-cycle-dired', `bmkp-cycle-dired-other-window',
7606 ;; `bmkp-cycle-file', `bmkp-cycle-file-other-window',
7607 ;; `bmkp-cycle-gnus', `bmkp-cycle-gnus-other-window',
7608 ;; `bmkp-cycle-info', `bmkp-cycle-info-other-window',
7609 ;; `bmkp-cycle-lighted', `bmkp-cycle-lighted-other-window',
7610 ;; `bmkp-cycle-local-file', `bmkp-cycle-local-file-other-window',
7611 ;; `bmkp-cycle-man', `bmkp-cycle-man-other-window',
7612 ;; `bmkp-cycle-non-file', `bmkp-cycle-non-file-other-window',
7613 ;; `bmkp-cycle-remote-file', `bmkp-cycle-remote-file-other-window',
7614 ;; `bmkp-cycle-specific-buffers', `bmkp-cycle-specific-buffers-other-window',
7615 ;; `bmkp-cycle-specific-files', `bmkp-cycle-specific-files-other-window',
7616 ;; `bmkp-cycle-variable-list',
7617 ;; `bmkp-cycle-url', `bmkp-cycle-url-other-window',
7618 ;; `bmkp-next-autonamed-bookmark', `bmkp-next-autonamed-bookmark-repeat',
7619 ;; `bmkp-next-bookmark-list-bookmark', `bmkp-next-bookmark-list-bookmark-repeat',
7620 ;; `bmkp-next-desktop-bookmark', `bmkp-next-desktop-bookmark-repeat',
7621 ;; `bmkp-next-dired-bookmark', `bmkp-next-dired-bookmark-repeat',
7622 ;; `bmkp-next-file-bookmark', `bmkp-next-file-bookmark-repeat',
7623 ;; `bmkp-next-gnus-bookmark', `bmkp-next-gnus-bookmark-repeat',
7624 ;; `bmkp-next-info-bookmark', `bmkp-next-info-bookmark-repeat',
7625 ;; `bmkp-next-lighted-bookmark', `bmkp-next-lighted-bookmark-repeat',
7626 ;; `bmkp-next-local-file-bookmark', `bmkp-next-local-file-bookmark-repeat',
7627 ;; `bmkp-next-man-bookmark', `bmkp-next-man-bookmark-repeat',
7628 ;; `bmkp-next-non-file-bookmark', `bmkp-next-non-file-bookmark-repeat',
7629 ;; `bmkp-next-remote-file-bookmark', `bmkp-next-remote-file-bookmark-repeat',
7630 ;; `bmkp-next-specific-buffers-bookmark', `bmkp-next-specific-buffers-bookmark-repeat',
7631 ;; `bmkp-next-specific-files-bookmark', `bmkp-next-specific-files-bookmark-repeat',
7632 ;; `bmkp-next-variable-list-bookmark', `bmkp-next-variable-list-bookmark-repeat',
7633 ;; `bmkp-next-url-bookmark', `bmkp-next-url-bookmark-repeat'.
7634 ;;
7635 (bmkp-define-cycle-command "autonamed")
7636 (bmkp-define-cycle-command "autonamed" 'OTHER-WINDOW)
7637 (bmkp-define-cycle-command "bookmark-list") ; No other-window version needed
7638 (bmkp-define-cycle-command "desktop")   ; No other-window version needed
7639 (bmkp-define-cycle-command "dired")
7640 (bmkp-define-cycle-command "dired" 'OTHER-WINDOW)
7641 (bmkp-define-cycle-command "file")
7642 (bmkp-define-cycle-command "file" 'OTHER-WINDOW)
7643 (bmkp-define-cycle-command "gnus")
7644 (bmkp-define-cycle-command "gnus" 'OTHER-WINDOW)
7645 (bmkp-define-cycle-command "info")
7646 (bmkp-define-cycle-command "info" 'OTHER-WINDOW)
7647 (when (featurep 'bookmark+-lit)
7648   (bmkp-define-cycle-command "lighted")
7649   (bmkp-define-cycle-command "lighted" 'OTHER-WINDOW))
7650 (bmkp-define-cycle-command "local-file")
7651 (bmkp-define-cycle-command "local-file" 'OTHER-WINDOW)
7652 (bmkp-define-cycle-command "man")
7653 (bmkp-define-cycle-command "man" 'OTHER-WINDOW)
7654 (bmkp-define-cycle-command "non-file")
7655 (bmkp-define-cycle-command "non-file" 'OTHER-WINDOW)
7656 (bmkp-define-cycle-command "remote-file")
7657 (bmkp-define-cycle-command "remote-file" 'OTHER-WINDOW)
7658 (bmkp-define-cycle-command "specific-buffers")
7659 (bmkp-define-cycle-command "specific-buffers" 'OTHER-WINDOW)
7660 (bmkp-define-cycle-command "specific-files")
7661 (bmkp-define-cycle-command "specific-files" 'OTHER-WINDOW)
7662 (bmkp-define-cycle-command "variable-list") ; No other-window version needed
7663 (bmkp-define-cycle-command "url")
7664 (bmkp-define-cycle-command "url" 'OTHER-WINDOW)
7665
7666 (bmkp-define-next+prev-cycle-commands "autonamed")
7667 (bmkp-define-next+prev-cycle-commands "bookmark-list")
7668 (bmkp-define-next+prev-cycle-commands "desktop")
7669 (bmkp-define-next+prev-cycle-commands "dired")
7670 (bmkp-define-next+prev-cycle-commands "file")
7671 (bmkp-define-next+prev-cycle-commands "gnus")
7672 (bmkp-define-next+prev-cycle-commands "info")
7673 (bmkp-define-next+prev-cycle-commands "lighted")
7674 (bmkp-define-next+prev-cycle-commands "local-file")
7675 (bmkp-define-next+prev-cycle-commands "man")
7676 (bmkp-define-next+prev-cycle-commands "non-file")
7677 (bmkp-define-next+prev-cycle-commands "remote-file")
7678 (bmkp-define-next+prev-cycle-commands "specific-buffers")
7679 (bmkp-define-next+prev-cycle-commands "specific-files")
7680 (bmkp-define-next+prev-cycle-commands "variable-list")
7681 (bmkp-define-next+prev-cycle-commands "url")
7682
7683 ;;;###autoload
7684 (defun bmkp-toggle-autonamed-bookmark-set/delete (position &optional allp) ; Bound to `C-x p RET'
7685   "If there is an autonamed bookmark at point, delete it, else create one.
7686 The bookmark created has no region.  Its name is formatted according
7687 to option `bmkp-autoname-bookmark-function'.
7688
7689 With a prefix arg, delete *ALL* autonamed bookmarks for this buffer.
7690
7691 Non-interactively, act at POSITION, not point."
7692   (interactive "d\nP")
7693   (if allp
7694       (bmkp-delete-all-autonamed-for-this-buffer)
7695     (let ((bmk-name  (funcall bmkp-autoname-bookmark-function position)))
7696       (if (not (bookmark-get-bookmark bmk-name 'noerror))
7697           (let ((mark-active  nil))     ; Do not set a region bookmark.
7698             (bookmark-set bmk-name)
7699             (message "Set bookmark `%s'" bmk-name))
7700         (bookmark-delete bmk-name)
7701         (message "Deleted bookmark `%s'" bmk-name)))))
7702
7703 ;;;###autoload
7704 (defun bmkp-set-autonamed-bookmark (position &optional msgp)
7705   "Set an autonamed bookmark at point.
7706 The bookmark created has no region.  Its name is formatted according
7707 to option `bmkp-autoname-bookmark-function'.
7708 Non-interactively, act at POSITION, not point."
7709   (interactive (list (point) t))
7710   (let ((bmk-name     (funcall bmkp-autoname-bookmark-function position))
7711         (mark-active  nil))             ; Do not set a region bookmark.
7712     (bookmark-set bmk-name)
7713     (when msgp (message "Set bookmark `%s'" bmk-name))))
7714
7715 ;;;###autoload
7716 (defun bmkp-set-autonamed-bookmark-at-line (number)
7717   "Set an autonamed bookmark at the beginning of the given line NUMBER."
7718   (interactive "nSet bookmark on line: ")
7719   (save-excursion
7720     (goto-char (point-min))
7721     (unless (zerop (forward-line (1- number)))
7722       (error "No such line: %d (%d lines total)" number (1+ (count-lines (point-min) (point-max)))))
7723     (bmkp-set-autonamed-bookmark (point))))
7724
7725 (when (> emacs-major-version 21)
7726   (defun bmkp-occur-create-autonamed-bookmarks ( &optional msgp)
7727     "Create an autonamed bookmark for each `occur' hit.
7728 You can use this only in `Occur' mode (commands such as `occur' and
7729 `multi-occur')."
7730     (interactive (list 'MSG))
7731     (unless (eq major-mode 'occur-mode) (error "You must be in `occur-mode'"))
7732     (let ((count  0))
7733       (save-excursion
7734         (goto-char (point-min))
7735         (while (condition-case nil (progn (occur-next) t) (error nil))
7736           (let* ((pos   (get-text-property (point) 'occur-target))
7737                  (buf   (and pos (marker-buffer pos))))
7738             (when buf
7739               (with-current-buffer buf
7740                 (goto-char pos)
7741                 (bmkp-set-autonamed-bookmark (point)))
7742               (setq count  (1+ count))))))
7743       (when msgp (message "Created %d autonamed bookmarks" count)))))
7744
7745 ;;;###autoload
7746 (defun bmkp-set-autonamed-regexp-buffer (regexp &optional msgp)
7747   "Set autonamed bookmarks at matches for REGEXP in the buffer."
7748   (interactive (list (read-string "Regexp: " nil 'regexp-history)
7749                      t))
7750   (bmkp-set-autonamed-regexp-region regexp (point-min) (point-max) 'MSG))
7751
7752 ;;;###autoload
7753 (defun bmkp-set-autonamed-regexp-region (regexp beg end &optional msgp)
7754   "Set autonamed bookmarks at matches for REGEXP in the region."
7755   (interactive (list (read-string "Regexp: " nil 'regexp-history)
7756                      (region-beginning) (region-end)
7757                      t))
7758   (let ((count  0))
7759     (save-excursion
7760       (goto-char beg)
7761       (while (re-search-forward regexp end t)
7762         (bmkp-set-autonamed-bookmark (point))
7763         (setq count  (1+ count))
7764         (forward-line 1)))
7765     (when msgp (message "Set %d autonamed bookmarks" count))))
7766
7767 (defun bmkp-autoname-bookmark (position)
7768   "Return a bookmark name using POSITION and the current buffer name.
7769 The name is composed as follows:
7770  POSITION followed by a space and then the buffer name.
7771  The position value is prefixed with zeros to comprise 9 characters.
7772  For example, for POSITION value 31416 and current buffer `my-buffer',
7773  the name returned would be `000031416 my-buffer'"
7774   (format "%09d %s" (abs position) (buffer-name)))
7775
7776 ;;;###autoload
7777 (defun bmkp-delete-all-autonamed-for-this-buffer ()
7778   "Delete all autonamed bookmarks for the current buffer.
7779 To be deleted, a bookmark name must be an autonamed bookmark whose
7780 buffer part names the current buffer."
7781   (interactive)
7782   (let ((bmks-to-delete  (mapcar #'bookmark-name-from-full-record
7783                                  (bmkp-autonamed-this-buffer-alist-only))))
7784     (if (null bmks-to-delete)
7785         (message "No autonamed bookmarks for buffer `%s'" (buffer-name))
7786       (when (y-or-n-p (format "Delete ALL autonamed bookmarks for buffer `%s'? " (buffer-name)))
7787         (dolist (bmk  bmks-to-delete)  (bookmark-delete bmk))
7788         (message "Deleted all bookmarks for buffer `%s'" (buffer-name))))))
7789
7790 ;; You can use this in `kill-buffer-hook'.
7791 (defun bmkp-delete-autonamed-this-buffer-no-confirm ()
7792   "Delete all autonamed bookmarks for this buffer, without confirmation."
7793   (when (and bookmarks-already-loaded bookmark-alist)
7794     (let ((bmks-to-delete  (mapcar #'bookmark-name-from-full-record
7795                                    (bmkp-autonamed-this-buffer-alist-only))))
7796       (dolist (bmk  bmks-to-delete)  (bookmark-delete bmk)))))
7797
7798 ;; You can use this in `kill-emacs-hook'.
7799 (defun bmkp-delete-autonamed-no-confirm ()
7800   "Delete all autonamed bookmarks for all buffers, without confirmation."
7801   (when (and bookmarks-already-loaded bookmark-alist)
7802     (dolist (buf  (buffer-list))
7803       (with-current-buffer buf (bmkp-delete-autonamed-this-buffer-no-confirm)))))
7804
7805 ;;;###autoload
7806 (defun bmkp-delete-bookmarks (position allp &optional alist) ; Bound to `C-x p delete'
7807   "Delete some bookmarks at point or all bookmarks in the buffer.
7808 With no prefix argument, delete some bookmarks at point.
7809 If there is more than one, require confirmation for each.
7810
7811 With a prefix argument, delete *ALL* bookmarks in the current buffer.
7812
7813 Non-interactively, delete at POSITION.
7814 Optional arg ALIST is the alist of bookmarks.  It defaults to
7815 `bookmark-alist'."
7816   (interactive "d\nP")
7817   (let ((bmks-to-delete  (and allp (mapcar #'bookmark-name-from-full-record
7818                                            (bmkp-this-buffer-alist-only))))
7819         (bmks-deleted    ())
7820         bmk-pos)
7821     (cond ((and bmks-to-delete  (y-or-n-p (format "Delete ALL bookmarks in buffer `%s'? "
7822                                                   (buffer-name))))
7823            (dolist (bmk bmks-to-delete) (bookmark-delete bmk))
7824            (message "Deleted all bookmarks in buffer `%s'" (buffer-name)))
7825           (bmks-to-delete (message "Canceled - nothing deleted"))
7826           (allp (message "No bookmarks in buffer `%s' to delete" (buffer-name)))
7827           (t
7828            (dolist (bmk  (or alist bookmark-alist))
7829              (when (eq (setq bmk-pos  (bookmark-get-position bmk)) position)
7830                (add-to-list 'bmks-to-delete (bookmark-name-from-full-record bmk))))
7831            (if bmks-to-delete
7832                (cond ((cadr bmks-to-delete)
7833                       (dolist (bmk  bmks-to-delete)
7834                         (when (y-or-n-p (format "Delete bookmark `%s'? " bmk))
7835                           (bookmark-delete bmk)
7836                           (add-to-list 'bmks-deleted bmk)))
7837                       (message (if bmks-deleted
7838                                    (format "Deleted bookmarks: %s" bmks-deleted)
7839                                  "No bookmarks deleted")))
7840                      (t
7841                       (bookmark-delete (car bmks-to-delete))
7842                       (message "Deleted bookmark `%s'" (car bmks-to-delete))))
7843              (when (interactive-p) (message "No bookmarks at point to delete")))))))
7844
7845
7846 ;;;;;;;;;;;;;;;;;;;;;;;
7847
7848 (provide 'bookmark+-1)
7849
7850 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7851 ;;; bookmark+-1.el ends here