]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinyhotlist.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinyhotlist.el
1 ;;; tinyhotlist.el --- Hot-list of important buffers, files(ange-ftp), dired
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C) 1995-2007 Jari Aalto
8 ;; Keywords:     tools
9 ;; Author:       Jari Aalto
10 ;; Maintainer:   Jari Aalto
11 ;;
12 ;; To get information on this program, call M-x tinyhotlist-version
13 ;; Look at the code with folding.el
14
15 ;; COPYRIGHT NOTICE
16 ;;
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
20 ;; any later version.
21 ;;
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 ;; for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
31 ;;
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
33
34 ;;}}}
35 ;;{{{ Install
36
37 ;;; Install:
38
39 ;; ........................................................ &t-install ...
40 ;; Put this file on your Emacs-Lisp load path, add following into your
41 ;; ~/.emacs startup file. Rip code with tinylib.el/ti::package-rip-magic
42 ;;
43 ;;      (add-hook 'tinyhotlist-:load-hook 'tinyhotlist-load-hotlist)
44 ;;      (require 'tinyhotlist)
45 ;;
46 ;; or use autoload, preferred because your emacs starts up faster
47 ;;
48 ;;      (autoload 'tinyhotlist-control          "tinyhotlist" "" t)
49 ;;      (autoload 'tinyhotlist-load-hotlist     "tinyhotlist" "" t)
50 ;;      (autoload 'tinyhotlist-save-hotlist     "tinyhotlist" "" t)
51 ;;
52 ;; Suggested key bindings
53 ;;
54 ;;      ;;  for windowed system. In XEmacs, use event `button3'.
55 ;;      (global-set-key [(control shift mouse-3)] 'tinyhotlist-control)
56 ;;
57 ;;      ;;  for non-windowed, close to C-x b , switch-to-buffer
58 ;;      (global-set-key "\C-cb" 'tinyhotlist-control-kbd)
59 ;;
60 ;; Before you can use hot list, read the documentation of function
61 ;; `tinyhotlist-control'. Example setup is at the end of file.
62 ;;
63 ;; If you have any questions, use this function
64 ;;
65 ;;      M-x tinyhotlist-submit-bug-report
66
67 ;;}}}
68 ;;{{{ Briefly
69
70 ;;; .................................................... &t-commentary ...
71
72 ;;; Commentary:
73
74 ;;}}}
75 ;;{{{ Documentation
76
77 ;;  Preface, may 1995
78 ;;
79 ;;      There is excellent utility 'msb.el', but when it comes to having
80 ;;      most important files at hand, It needs some companion with it. An
81 ;;      emacs session can easily have 20 C++ files, user may start news
82 ;;      while the compile it going on and try some lisp code found from the
83 ;;      gnu.emacs.help articles, load couple of emacs configuration files
84 ;;      for editing and then realize that there is mail coming, because
85 ;;      some biff utility tells so. User switches to mail reader and starts
86 ;;      reading the latest messages... within short period of time emacs is
87 ;;      full of buffers and to use MSB to navigate through them all may be
88 ;;      get one frustrated: "Where was that buffer again, do I need to step
89 ;;      3 panes before I can see that file...?"
90 ;;
91 ;;      The navigation is especially problem if user is working only with
92 ;;      handful of source files actively, while he may still have 40+ files
93 ;;      loaded.
94 ;;
95 ;;      What would help the situation? A simple hot list for my most used
96 ;;      files, where one can put and remove items very easily. No more
97 ;;      searching like in msb.el.
98 ;;
99 ;;      This package does not intend to replace `msb', it's superb in class
100 ;;      of its own, but there may be also need for a hot list, because the
101 ;;      most used files page in `msb' changes dynamically whenever buffers
102 ;;      are changed. Hot list in the other hand stays the same from session
103 ;;      to session.
104 ;;
105 ;;  Overview of features
106 ;;
107 ;;      o   Provides pop-up menu where you can add and remove current buffer:
108 ;;          "most important work file list". In non-windowed system,
109 ;;          standard completion feature is used instead of pop-up menu.
110 ;;      o   Any persistent files can be kept in hot list, even ange-ftp files or
111 ;;          dired buffers.
112 ;;      o   Hot list can be saved and read on startup.
113 ;;      o   This is not "last visited files" list, but persistent list of
114 ;;          files. When you select item from hot list, the file is displayed
115 ;;          (if it is in Emacs) or loaded (by using ange-ftp if necessary).
116 ;;
117 ;;  How to use the hotlist
118 ;;
119 ;;      When you load this package, it defines hot list cache to store the
120 ;;      items. The list will be empty at first, but after you
121 ;;      have added an entry to it, you can display the hot list. To add
122 ;;      or remove entries from hot list, is explained in function:
123 ;;
124 ;;          C-h f tinyhotlist-control
125 ;;
126 ;;      If you use add and remove commands often, it might be useful to
127 ;;      to define some custom key bindings. The alternative way is to use
128 ;;      prefix arguments to functions `tinyhotlist-control'
129 ;;
130 ;;          (global-set-key [(shift f3)]   'tinyhotlist-add)
131 ;;          (global-set-key [(control f3)] 'tinyhotlist-remove)
132 ;;
133 ;;      In non-windowed environment hot list is is managed through completion menu.
134 ;;      Admittedly, this is not as nice as the pop-up menu.,
135 ;;      To use keyboard, use function:
136 ;;
137 ;;          tinyhotlist-control-kbd
138 ;;
139 ;;      Here is an example of the displayed hot list in pop-up. The second
140 ;;      string to the right is abbreviation name of the directory, e.g. `~ftp1'
141 ;;      is a short name for /user@site.com:~user/project/this/. The `txt' is
142 ;;      short name for $HOME/doc/txt/
143 ;;
144 ;;          +-------------------+
145 ;;          |hotlist            |
146 ;;          |===================|
147 ;;          |*Backtrace*        |
148 ;;          |*VC-log*           |
149 ;;          |.emacs             |
150 ;;          |.procmailrc        |
151 ;;          |ChangeLog          |
152 ;;          |RMAIL              |
153 ;;          |file.txt     txt   |
154 ;;          |other.txt    txt   |
155 ;;          |remote.cc    ~ftp1 |
156 ;;          |remote.cc    ~ftp2 |
157 ;;          +-------------------+
158 ;;
159 ;;      Note about the pop-up display: The font used in pop-up may not be
160 ;;      proportionally spaced, like Courier, so the entries may therefore
161 ;;      show as ragged. That is, the directory names are not nicely lined
162 ;;      up.
163 ;;
164 ;;  Shortening long filenames
165 ;;
166 ;;      The typical menu item is quite long, because there is buffer name
167 ;;      and filename part. The default rule shortens the home directory
168 ;;      names to "" but if your file is elsewhere, you have to modify the
169 ;;      `tinyhotlist-:abbreviate-file-name-table'. There is examples how to use it
170 ;;      at the end of source file. Like:
171 ;;
172 ;;          /user@site.com:~user/project/this/  --> ~ftp1
173 ;;
174 ;;      If you make changes to this variable after the hot list has been
175 ;;      made, the new abbreviations will take effect on at creation of new
176 ;;      items. To rebuild everything from defaults (this destroys you
177 ;;      previous content), call function `tinyhotlist-build-default-hotlist'.
178 ;;
179 ;;  Hooks: saving hot list after each cache update
180 ;;
181 ;;      The buffers are stored into variable `tinyhotlist-:cache' and there
182 ;;      is two hooks that run after the entry is deleted or added to the
183 ;;      cache. The hooks are `tinyhotlist-:add-hook' and
184 ;;      `tinyhotlist-:remove-hook'. They contain default value
185 ;;      `tinyhotlist-save-hotlist' which updates the cache on disk after
186 ;;      each change. You can set these hooks to nil if you want to manually
187 ;;      control when to save cache. (Maybe you load BASE cache every time
188 ;;      and modify it during Emacs session, but you don't want to save
189 ;;      this "session" hot list).
190 ;;
191 ;;          (add-hook 'tinyhotlist-:load-hook 'my-tinyhotlist-load-hook)
192 ;;
193 ;;          (defun my-tinyhotlist-load-hook ()
194 ;;            "My hotlist settings"
195 ;;            (setq tinyhotlist-save-hotlist nil)
196 ;;            (setq tinyhotlist-:remove-hook nil))
197 ;;
198 ;;  Saving and restoring the hot list
199 ;;
200 ;;      When you're satisfied with the hot list, save it to file with command:
201 ;;
202 ;;            M-x tinyhotlist-save-hotlist
203 ;;
204 ;;      To automatically restore the hot list when package loads:
205 ;;
206 ;;          (add-hook 'tinyhotlist-:load-hook 'tinyhotlist-load-hotlist)
207 ;;
208 ;;      To save the _current_ hot list automatically when Emacs exists:
209 ;;
210 ;;          (add-hook 'kill-emacs-hook 'tinyhotlist-save-hotlist)
211 ;;
212 ;;  Example
213 ;;
214 ;;      Here is complete example setup how you could configure this package.
215 ;;
216 ;;          (autoload  'tinyhotlist-control "tinyhotlist" "" t)
217 ;;          (ti::add-hooks 'tinyhotlist-:load-hook
218 ;;               '(tinyhotlist-load-hotlist my-tinyhotlist-init))
219 ;;
220 ;;          (defun my-tinyhotlist-init ()
221 ;;            "Sets defaults for hotlist"
222 ;;            (setq tinyhotlist-:default-function       'my-tinyhotlist-defaults)
223 ;;            (global-set-key [(control shift mouse-3)] 'tinyhotlist-control))
224 ;;
225 ;;            (defconst tinyhotlist-:abbreviate-file-name-table
226 ;;              (list
227 ;;               ;;   Remember: the substitution order must be _BIGGEST_
228 ;;               ;;   substitution first.
229 ;;               ;;
230 ;;               ;;  Shorten ange ftp references
231 ;;               (list
232 ;;               "/foo@example.com:/home/foo"
233 ;;               "~foo")
234 ;;
235 ;;               (list txt    "~t")
236 ;;               (list wtxt   "~wt")
237 ;;               (list elisp  "")   ;; and wont show this either
238 ;;               (list h        ""))))   ;; don't display the home
239 ;;
240 ;;          (defconst tinyhotlist-:default-regexp
241 ;;            (concat
242 ;;             "^RMAIL$\\|scratc\\|diff\\|buffer menu\\|diff\\|Messages"
243 ;;
244 ;;             ;; Procmail
245 ;;             "\\|procmailrc\\|pm-.*\\(hdr\\|log\\|rc\\|txt\\)"
246 ;;
247 ;;             ;; text
248 ;;             "\\|elisp.txt\\|ssjaaa.txt"
249 ;;
250 ;;             ;; perl
251 ;;             "\\|\\.pls"
252 ;;
253 ;;             "\\|.mak"
254 ;;
255 ;;             ;; emacs project files
256 ;;             "\\|emacrs\\|funcs.ja.el\\|tinylibm.el\\|tinylib.el"
257 ;;
258 ;;             ;;  C++ project files
259 ;;             "\\|wmpmea.*cc"
260 ;;
261 ;;             ;; Gnus
262 ;;             "\\|article\\|newsgroup\\|Summary\\|MIME-out"))
263 ;;
264 ;;            ;; ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ ^^^ window-system ^ ^
265 ;;            )
266
267 ;;}}}
268
269 ;;; Change Log:
270
271 ;;; Code:
272
273 ;;{{{ setup: require
274
275 (require 'tinylibm)
276
277 (eval-when-compile (ti::package-use-dynamic-compilation))
278
279 (ti::package-defgroup-tiny TinyHotlist tinyhotlist-: tools
280   "Hotlist of important buffers and files, easy add, easy remove")
281
282 ;;}}}
283 ;;{{{ setup: private
284 ;;; .......................................................... &v-bind ...
285
286 (defvar tinyhotlist-:history-keymap nil
287   "Keymap for history.")
288
289 ;; completion keymap unused currently, #todo someday
290
291 (if tinyhotlist-:history-keymap
292     nil
293   (setq tinyhotlist-:history-keymap (make-sparse-keymap))
294   (define-key  tinyhotlist-:history-keymap      [(up)]   'ignore)
295   (define-key  tinyhotlist-:history-keymap      [(down)] 'ignore))
296
297 ;;; ......................................................... &v-hooks ...
298
299 (defcustom tinyhotlist-:load-hook '(tinyhotlist-load-hotlist)
300   "*Hook run when file is loaded."
301   :type  'hook
302   :group 'TinyHotlist)
303
304 (defcustom tinyhotlist-:add-hook '(tinyhotlist-save-hotlist)
305   "*Hook run when new buffer is added with `tinyhotlist-add-internal'.
306 Functions in hook are called with two arguments:
307
308   BUFFER ADD-STATUS
309
310 Default value is `tinyhotlist-save-hotlist' which saves cache after every change."
311   :type  'hook
312   :group 'TinyHotlist)
313
314 (defcustom tinyhotlist-:remove-hook '(tinyhotlist-save-hotlist)
315   "*Hook run when new buffer is added with `tinyhotlist-remove-internal'.
316 Functions in hook are called with two arguments:
317
318   BUFFER REMOVE-STATUS
319
320 Default value is `tinyhotlist-save-hotlist' which saves cache after every change."
321   :type  'hook
322   :group 'TinyHotlist)
323
324 ;;; ....................................................... &v-private ...
325
326 (defvar tinyhotlist-:cache nil
327   "Hotlist cache.
328 Format:
329   '((\"BUFFER-NAME[ DIRECTORY]\" . [FILE-NAME])
330     ...)
331
332 The BUFFER-NAME is the actual name of the buffer. It may contains
333 <2> in the name too indicatin second buffer with the same name.
334 The DIRECTORY part is only included if buffer is readlly connected to file.
335 the DIRECTORY contains leading space if the directory part is included
336
337   'buffer'     -- single entry
338   'buffer2 ~/' -- buffer and filename.")
339
340 (defvar tinyhotlist-:history nil
341   "History for completion.")
342
343 (defcustom tinyhotlist-:hotlist-file
344   (ti::package-config-file-prefix "tinyhotlist.el")
345   "*Default hotlist configuration file. You can edit as you want.
346 If you edit the order of this file, set `tinyhotlist-:cache-sort-flag' to nil."
347   :type  'file
348   :group 'TinyHotlist)
349
350 (defcustom tinyhotlist-:cache-sort-flag t
351   "Non-nil means Sort the entries in hotlist after adding a buffer to it.
352 If you want to edit by hand the order of the hotlist file
353 `tinyhotlist-:hotlist-file', then set this variable to nil, and the raw
354 order is preserved."
355   :type  'boolean
356   :group 'TinyHotlist)
357
358 ;;}}}
359 ;;{{{ setup: user config
360
361 ;;; ........................................................ &v-public ...
362
363 (defcustom tinyhotlist-:list-max 40
364   "*Maximum members in hotlist."
365   :type  'integer
366   :group 'TinyHotlist)
367
368 (defcustom tinyhotlist-:title "     hotlist     "
369   "*Title of menu."
370   :type  'string
371   :group 'TinyHotlist)
372
373 ;;  handy if you want to call from non-mouse, e.g. pressing key.
374 ;;  --> set event parameter to nil when calling func  tinyhotlist-control
375
376 (defcustom tinyhotlist-:x-coord 170
377   "*Default menu coordinate."
378   :type  'integer
379   :group 'TinyHotlist)
380
381 (defcustom tinyhotlist-:y-coord 170
382   "*Default menu coordinate."
383   :type  'integer
384   :group 'TinyHotlist)
385
386 (defcustom tinyhotlist-:use-x-popup t
387   "*If non-nil, don't use popups.
388 If you prefer not to use popup-like dialog box for hotlist items,
389 then set ths variable to nil. This variable is valid only if you're
390 running in X-windowed system."
391   :type  'boolean
392   :group 'TinyHotlist)
393
394 (defcustom tinyhotlist-:default-regexp nil
395   "*Regexp to match buffers when initialising hotlist menu.
396 See `tinyhotlist-control'."
397   :type  'string
398   :group 'TinyHotlist)
399
400 (defcustom tinyhotlist-:abbreviate-file-name-table
401   (list
402    (list
403     (or (and (getenv "HOME")
404              ;;   The path names are seen as lowercase in Emacs in Win32
405              (if (ti::win32-p)
406                  (downcase (getenv "HOME"))
407                (getenv "HOME")))
408         (error "TinyHotlist: no HOME env variable"))
409     "~"))
410   "How to substitute absolute path names. The PATH value is case sensitive.
411 Changes in this variable will only affect the new buffers added to the
412 hotlist. If you want to rebuild the whole hotlist using the
413 `tinyhotlist-:default-regexp', call `tinyhotlist-build-default-hotlist'
414
415 Format:
416  '((PATH  SUBSTITUTE) (P S) ..)
417
418 Example:
419
420   ;;  Remember to put _longest_ substitutionmatches first.
421   ;;
422   '((\"/etc/daemons/interalarms/\" \"ALARM\")
423     (\"/users/foo/\" \"\"))   ;; Don't show my home at all
424
425 Please look at the many examples that are in the end of tinyhotlist.el"
426   :type '(repeat
427           (list
428            (string :tag "path")
429            (string :tag "alias")))
430
431   :group 'TinyHotlist)
432
433 ;;}}}
434
435 ;;{{{ version
436
437 (eval-and-compile
438   (ti::macrof-version-bug-report
439    "tinyhotlist.el"
440    "tinyhotlist"
441    tinyhotlist-:version-id
442    "$Id: tinyhotlist.el,v 2.49 2007/05/01 17:20:43 jaalto Exp $"
443    '(tinyhotlist-:version-id
444      tinyhotlist-:list-max
445      tinyhotlist-:use-x-popup
446      tinyhotlist-:default-regexp
447      tinyhotlist-:abbreviate-file-name-table)))
448
449 ;;}}}
450
451 ;;{{{ menu handle
452
453 ;;; ----------------------------------------------------------------------
454 ;;;
455 (defun tinyhotlist-find-buffer (item &optional no-confirm)
456   "Find buffer for corresponding menu ITEM.
457 The buffer is loaded from disk if it does not exist in Emacs.
458 NO-CONFIRM suppresses confirm of loading ange-ftp files."
459   (let* (buffer
460          file
461          elt
462          ptr)
463     (setq elt (assoc item tinyhotlist-:cache))
464     (setq buffer (car elt)
465           file   (cdr elt))
466
467     (cond
468      (file
469       ;; Find ange-ftp dired buffer
470       (when (string-match "@.*:" file)
471         (dolist (buffer (buffer-list))
472           (with-current-buffer buffer
473             (when (and (eq major-mode 'dired-mode)
474                        (string=
475                         file
476                         (symbol-value 'dired-directory)))
477               (setq ptr (current-buffer))
478               (return)))))
479       (setq ptr
480             (or ptr
481                 (get-file-buffer file)
482
483                 ;;  This would call file-attributes, which will call
484                 ;;  ange ftp for remote buffers.
485                 ;;
486                 ;;  ange-ftp-hook-function(file-attributes ...
487
488                 (and (not (string-match "@.*:" file))
489                      (find-buffer-visiting file))))
490       (if ptr
491           (switch-to-buffer ptr)
492         (cond
493          ((and (string-match "@.*:" file)
494                (y-or-n-p (format "Load %s " file)))
495           (find-file file))
496          ((not (file-exists-p file))
497           (message "TinyHotlist: file not found [%s]." (or file buffer))
498           (sleep-for 2))
499          (t
500           (find-file file)))))
501      ((setq ptr (get-buffer buffer))
502       ;;   Temporary buffer, which is not a file, like  *Messages*
503       (switch-to-buffer ptr))
504      (t
505       (message "TinyHotlist: Can't find buffer [%s]" buffer)))))
506
507 ;;; ----------------------------------------------------------------------
508 ;;;
509 (defun tinyhotlist-abbreviate-file-name (file &optional restore)
510   "Abbreviate FILE by looking at `tinyhotlist-:abbreviate-file-name-table'.
511 If RESTORE is passed, the convert abbreviated FILE into absolute path
512 using `tinyhotlist-:abbreviate-file-name-table'."
513   (let* (case-fold-search
514          str
515          substitute
516          match
517          replace)
518     (dolist (elt tinyhotlist-:abbreviate-file-name-table)
519       (setq str (nth 0 elt)  substitute (nth 1 elt))
520       (setq match (if restore substitute str)
521             replace
522             (if restore
523                 (file-name-as-directory str)
524               substitute))
525
526       (when (string-match (concat "^" (regexp-quote match)) file)
527         (setq file (ti::replace-match 0 replace file))
528         (return)))
529     file))
530
531 ;;; ----------------------------------------------------------------------
532 ;;;
533 (defun tinyhotlist-add-internal (buffer)
534   "Add BUFFER or list of buffers to hotlist. Arg must be STRING [list].
535
536 Returns:
537    t or nil if added."
538   (let* (buffer-file
539          ptr
540          exist
541          ret)
542     (dolist (buffer (ti::list-make buffer))
543       ;;  We have to check if it exists already...
544       ;;  this is a  bit inefficent way to check list, but because
545       ;;  list is small, this is the shortest way.
546       (setq buffer-file
547             (and (setq ptr (get-buffer buffer))
548                  (with-current-buffer ptr
549                    (or (buffer-file-name) ;; 1) regular file
550                        ;; 2) User may be in dired
551                        ;;    VC renames dired mode, so we can't just 'memq
552                        ;;    `major-mode'
553                        (if (string-match "dired" (symbol-name major-mode))
554                            (symbol-value 'dired-directory))))))
555       ;; ................................................ check buffer ...
556       ;;  - If buffer has filename check the CDR of cache
557       ;;  - if buffer has no filename, then check CAR of the cache.
558       (cond
559        (buffer-file
560         (setq exist
561               (member buffer-file (mapcar (function cdr) tinyhotlist-:cache))))
562        (t
563         (setq exist (ti::list-find tinyhotlist-:cache (regexp-quote buffer)))))
564       ;; ............................................. push to hotlist ...
565       (unless exist
566         (when buffer-file ;;  Get the directory name
567           (let* ((abbrev (abbreviate-file-name
568                           (tinyhotlist-abbreviate-file-name
569                            (file-name-directory buffer-file))))
570                  (total  (+ (length buffer)
571                             (length abbrev)))
572                  elt)
573             (if (< total 80)
574                 (setq elt (format "%-25s %s" buffer abbrev))
575               (setq elt (concat buffer " " abbrev)))
576             (push (cons elt (abbreviate-file-name buffer-file))
577                   tinyhotlist-:cache)))))
578     ;;  Keep it in sorted order.
579     (when tinyhotlist-:cache-sort-flag
580       (setq
581        tinyhotlist-:cache
582        (sort
583         tinyhotlist-:cache
584         (function
585          (lambda (a b)
586            (string-lessp (car b) (car a)))))))
587     (setq ret (not exist)) ;; buffer was added if didn't exist
588     (run-hook-with-args 'tinyhotlist-:add-hook buffer ret)
589     ret))
590
591 ;;; ----------------------------------------------------------------------
592 ;;;
593 (defun tinyhotlist-remove-internal (arg type)
594   "Remove according to ARG and MODE a item from `tinyhotlist-:cache'.
595
596 Input:
597
598   ARG    Depends on mode.
599   TYPE   what type the arg is: 'menu-item   'buffer  'file
600
601 Return
602
603  nil t   if removed."
604   (let* (list
605          func
606          ret)
607     (cond
608      ((eq type 'menu-item)
609       (when (and (stringp arg)
610                  (setq arg (assoc arg tinyhotlist-:cache)))
611         (setq ret t)
612         (setq tinyhotlist-:cache (delete arg tinyhotlist-:cache))))
613      ((eq type 'file)
614       (setq func 'cdr))
615      ((eq type 'buffer)
616       (cond
617        ((get-buffer arg)                ;Buffer is filename
618         (setq func 'cdr))
619        (t
620         (setq func 'car)))))
621     (when func
622       (dolist (elt tinyhotlist-:cache)
623         (if (string-match (regexp-quote arg) (or (funcall func elt) "" ))
624             (setq ret t)
625           (push elt list))
626         (setq tinyhotlist-:cache list))
627       (setq tinyhotlist-:cache list))
628     (run-hook-with-args 'tinyhotlist-:remove-hook arg ret)
629     ret))
630
631 ;;; ----------------------------------------------------------------------
632 ;;;
633 (defun tinyhotlist-add-by-regexp (regexp &optional temp-buf)
634   "Add all buffers matchig REGEXP to hotlist.
635 If optional TEMP-BUF prefix arg is non-nil the mach is made
636 against temporary buffers too. Otherwise they are not counted."
637   (interactive "sAdd buffers matching: \nP")
638   (tinyhotlist-add-internal
639    (ti::dolist-buffer-list
640     (string-match regexp (buffer-name))
641     temp-buf)))
642
643 ;;; ----------------------------------------------------------------------
644 ;;;
645 (defun tinyhotlist-kill (&optional default)
646   "Kill hotlist or initialise with defaults if DEFAULT flag is non-nil.
647 References:
648    `tinyhotlist-:default-regexp'."
649   (interactive)
650   (setq tinyhotlist-:cache nil)
651   (if (and default (stringp tinyhotlist-:default-regexp))
652       (tinyhotlist-add-by-regexp tinyhotlist-:default-regexp)))
653
654 ;;; ----------------------------------------------------------------------
655 ;;;
656 (defun tinyhotlist-set-defaults ()
657   "Initialise hotlist according to `tinyhotlist-:default-regexp'."
658   (tinyhotlist-kill 'init))
659
660 ;;; ----------------------------------------------------------------------
661 ;;;
662 (defun tinyhotlist-build-default-hotlist ()
663   "Delete existing hotlist and build with `tinyhotlist-:default-regexp'.
664 See variable `tinyhotlist-:abbreviate-file-name-table'."
665   (interactive)
666   (setq tinyhotlist-:cache nil)
667   (tinyhotlist-set-defaults))
668
669 ;;; ----------------------------------------------------------------------
670 ;;;
671 ;;;###autoload
672 (defun tinyhotlist-save-hotlist (&rest ARGS)
673   "Call `tinyhotlist-load-hotlist' with arg to save hotlist. ARGS are ignored."
674   (interactive)
675   (tinyhotlist-load-hotlist 'save))
676
677 ;;; ----------------------------------------------------------------------
678 ;;;
679 ;;;###autoload
680 (defun tinyhotlist-load-hotlist (&optional save)
681   "Load or SAVE hotlist configuration from `tinyhotlist-:hotlist-file'.
682 When the hotlist file is loaded, only valid entries from there
683 are selected to `tinyhotlist-:cache': If File does not exist, it is dropped.
684
685 Return:
686
687  nil t"
688   (interactive "P")
689   (let* ((file  tinyhotlist-:hotlist-file)
690          buffer
691          list)
692     (cond
693      ;; ......................................................... load ...
694      ((null save)
695       (when (file-exists-p file)
696         (load-file file)
697         (dolist (elt tinyhotlist-:cache)
698           (setq buffer (car elt)
699                 file   (cdr elt))
700           ;;  Drop away non-existing files.
701           ;;  The Temp buffers *scratch* may not be in emacs, but
702           ;;  they can be in hotlist.
703
704           (when (or (null file)
705                     (and file
706                          (or
707                           ;;  Let ange-ftp fies go through
708                           (string-match "@" file)
709                           ;;  Check regular files.
710                           (file-exists-p file))))
711             (push (cons buffer file) list)))
712         ;;  Reverse must be used due to push.
713         (setq tinyhotlist-:cache (nreverse list))
714         t))
715      ;; ......................................................... save ...
716      (t
717       (let* ((file tinyhotlist-:hotlist-file)
718              (dir  (file-name-directory file)))
719         (if (not (stringp dir))
720             (error (concat "TinyHotlist: `tinyhotlist-:hotlist-file'"
721                            " must be absolute path [%s]")
722                    file)
723           ;;  Make sure that file can be saved to a directory
724           (or (file-directory-p dir)
725               (and (y-or-n-p (format "TinyHotlist: [SAVE] Create %s? " dir))
726                    (make-directory dir t)))
727           (ti::write-file-variable-state
728            file
729            "Emacs TinyHotlist.el cache file."
730            '(tinyhotlist-:cache))))))))
731
732 ;;}}}
733 ;;{{{ X menu
734
735 ;;; ----------------------------------------------------------------------
736 ;;;
737 (defun tinyhotlist-complete (list)
738   "Show LIST in completion menu.
739 Return:
740  buffer or nil"
741   (let* ((menu  (ti::list-to-assoc-menu list))
742          (def   (car-safe tinyhotlist-:history))
743          ret)
744     (setq ret (completing-read "hot item: " menu nil t def 'tinyhotlist-:history))
745     (if (ti::nil-p ret)                 ;really selected ?
746         nil
747       ret)))
748
749 ;;; ----------------------------------------------------------------------
750 ;;;
751 (defun tinyhotlist-show-menu (event &optional title)
752   "Pop the menu and select the buffer.
753 If EVENT is nil, use default coordinates to display the menu and TITLE.
754
755 Return:
756   menu item or nil."
757   (interactive "e")
758   (let* ((list   (mapcar (function car) tinyhotlist-:cache))
759          (title  (or title tinyhotlist-:title))
760          (x      (cond
761                   ((and tinyhotlist-:use-x-popup ;; permits use of popup
762                         (ti::compat-window-system))
763                    t)
764                   (t
765                    nil)))) ;; no X available...
766     (if x
767         (ti::compat-popup list event nil title)
768       (tinyhotlist-complete list))))
769
770 ;;; ----------------------------------------------------------------------
771 ;;;
772 ;;;###autoload
773 (defun tinyhotlist-control-kbd (&optional arg)
774   "Same as `tinyhotlist-control' with ARG, but you can call this from keyboard."
775   (interactive "P")
776   (tinyhotlist-control
777    (ti::compat-make-fake-event tinyhotlist-:x-coord tinyhotlist-:y-coord) arg))
778
779 ;;; ----------------------------------------------------------------------
780 ;;;
781 ;;;###autoload
782 (defun tinyhotlist-control (event &optional arg)
783   "Control center of hotlist. Use mouse EVENT to position popup.
784
785 Optional ARG can be:
786
787   nil           show the hotlist
788   0             kill all members from hotlist.
789   9             kill all, but initalize with defaults.
790   nbr           any number, add current active buffer to hotlist
791   -             negative number, remove item from hotlist. E.g. \\[universal-argument]  -
792   1 x \\[universal-argument]       remove current buffer from hotlist
793   2 x \\[universal-argument]       Save hotlist
794   3 x \\[universal-argument]       load hotlist."
795   (interactive "e\nP")
796   (let* ((buffer (buffer-name))
797          (menu   (or tinyhotlist-:cache
798                      ;;  See if there is any buffers matching user's
799                      ;;  regexp to make the initial hotlist.
800                      (and tinyhotlist-:default-regexp
801                           (tinyhotlist-set-defaults)
802                           tinyhotlist-:cache)))
803          ret)
804     (cond
805      ;; ...................................................... display ...
806      ((null arg)
807       (cond
808        ((null menu)
809         (message  "TinyHotlist: Empty hotlist.")
810         (sleep-for 1))
811        (t
812         (when (setq ret (tinyhotlist-show-menu event))
813           (tinyhotlist-find-buffer ret)))))
814      ;; ................................................... remove/add ...
815      ((or (integerp arg)
816           (memq arg '(-)))
817       (cond
818        ((eq 0 arg)
819         (tinyhotlist-kill)
820         (message "TinyHotlist: Hotlist killed.")
821         (sleep-for 1))
822        ;;  "Why number 9??" --   Because it's next to number 0
823        ((eq 9 arg)
824         (tinyhotlist-set-defaults)
825         (message "TinyHotlist: Hotlist killed/initalized.")
826         (sleep-for 1))
827        ((and (integerp arg)
828              (> arg 0))
829         (cond
830          ((tinyhotlist-add-internal buffer)
831           (message "TinyHotlist: Added to hotlist [%s]" buffer)
832           (sleep-for 1))
833          (t
834           (message "TinyHotlist: Already in hotlist."))))
835
836        (t                               ;Negative
837         (if (null menu)
838             (message "TinyHotlist: Empty hotlist.")
839           (when (setq ret (tinyhotlist-show-menu event "--Remove item--"))
840             (tinyhotlist-remove-internal ret 'menu-item)
841             (message "TinyHotlist: Removed. [%s]" ret)
842             (sleep-for 1))))))
843      ;; ............................................... remove current ...
844      ((equal '(4) arg)
845       (if (if (buffer-file-name)
846               (tinyhotlist-remove-internal (buffer-file-name) 'file )
847             (tinyhotlist-remove-internal buffer 'buffer))
848           (message "TinyHotlist: Removed [%s]" buffer)
849         (message "TinyHotlist: Nothing to remove, [%s] wasn't in hotlist."
850                  buffer)))
851      ((equal '(16) arg)
852       (tinyhotlist-save-hotlist)
853       (message "TinyHotlist: saved")
854       (sit-for 1.5))
855      ((equal '(64) arg)
856       (if (tinyhotlist-load-hotlist)
857           (message "TinyHotlist: loaded")
858         (message "TinyHotlist: Can't load %s" tinyhotlist-:hotlist-file))
859       (sleep-for 2)))))
860
861 ;;; ----------------------------------------------------------------------
862 ;;;
863 ;;;
864 ;;;###autoload
865 (defun tinyhotlist-add ()
866   "Add current buffer to hotlist."
867   (interactive)
868   (tinyhotlist-control nil 1))
869
870 ;;; ----------------------------------------------------------------------
871 ;;;
872 ;;;
873 ;;;###autoload
874 (defun tinyhotlist-remove ()
875   "Remove current buffer from hotlist."
876   (interactive)
877   (tinyhotlist-control nil -1))
878
879 ;;}}}
880
881 (provide   'tinyhotlist)
882 (run-hooks 'tinyhotlist-:load-hook)
883
884 ;;; tinyhotlist.el ends here