comparison lisp/msb.el @ 10371:629821e2b42e

Better format of files-by-directory menus. Split big menus into sub-menus. (msb-max-menu-items): Changed default value. This variable now depicts the maximum number of items in a sub-menu. (msb-display-most-recently-used): Changed default value. (mouse-select-buffer): Now handles several levels of sub-menus. New format on return value.
author Richard M. Stallman <rms@gnu.org>
date Mon, 09 Jan 1995 22:16:23 +0000
parents 52a1e5ef144c
children c0e27466fb3f
comparison
equal deleted inserted replaced
10370:4b1c8dc724e6 10371:629821e2b42e
1 ;;; msb.el --- Customizable buffer-selection with multiple menus. 1 ;;; msb.el --- Customizable buffer-selection with multiple menus.
2 ;; Copyright (C) 1993, 1994 Lars Lindberg <Lars.Lindberg@sypro.cap.se> 2 ;; Copyright (C) 1993, 1994 Lars Lindberg <Lars.Lindberg@sypro.cap.se>
3 ;; 3 ;;
4 ;; Author: Lars Lindberg <Lars.Lindberg@sypro.cap.se> 4 ;; Author: Lars Lindberg <Lars.Lindberg@sypro.cap.se>
5 ;; Created: 8 Oct 1993 5 ;; Created: 8 Oct 1993
6 ;; Lindberg's last update version: 3.27
6 ;; Keywords: mouse buffer menu 7 ;; Keywords: mouse buffer menu
7 ;; 8 ;;
8 ;; This program is free software; you can redistribute it and/or modify 9 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by 10 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2 of the License, or 11 ;; the Free Software Foundation; either version 2 of the License, or
49 ;; sorting - you will get latest used buffer first. 50 ;; sorting - you will get latest used buffer first.
50 ;; 51 ;;
51 ;; Also check out the variable `msb-display-invisible-buffers-p'. 52 ;; Also check out the variable `msb-display-invisible-buffers-p'.
52 53
53 ;; Known bugs: 54 ;; Known bugs:
54 ;; - `msb' does not work on a non-X-toolkit Emacs. 55 ;; - Files-by-directory
56 ;; + No possibility to show client/changed buffers separately
55 ;; Future enhancements: 57 ;; Future enhancements:
56 ;; - [Mattes] had a suggestion about sorting files by extension. 58 ;; - [Mattes] had a suggestion about sorting files by extension.
57 ;; I (Lars Lindberg) think this case could be solved if msb.el was 59 ;; I (Lars Lindberg) think this case could be solved if msb.el was
58 ;; rewritten to handle more dynamic splitting. It's now completely 60 ;; rewritten to handle more dynamic splitting. It's now completely
59 ;; static, depending on the menu-cond. If the splitting could also 61 ;; static, depending on the menu-cond. If the splitting could also
209 The separators will appear between all menus that have a sorting key that differs by this value or more.") 211 The separators will appear between all menus that have a sorting key that differs by this value or more.")
210 212
211 (defvar msb-files-by-directory-sort-key 0 213 (defvar msb-files-by-directory-sort-key 0
212 "*The sort key for files sorted by directory") 214 "*The sort key for files sorted by directory")
213 215
214 (defvar msb-max-menu-items 25 216 (defvar msb-max-menu-items 15
215 "*The maximum number of items in a menu. 217 "*The maximum number of items in a menu.
216 If this variable is set to 15 for instance, then the 15 latest used 218 If this variable is set to 15 for instance, then the submenu will be split up in minor parts, 15 items each.
217 buffer that fits in a certain submenu will appear in that submenu.
218 Nil means no limit.") 219 Nil means no limit.")
219 220
220 (defvar msb-max-file-menu-items 10 221 (defvar msb-max-file-menu-items 10
221 "*The maximum number of items from different directories. 222 "*The maximum number of items from different directories.
222 223
223 When the menu is of type `file by directory', this is the maximum 224 When the menu is of type `file by directory', this is the maximum
224 number of buffers that are clumped togehter from different 225 number of buffers that are clumped togehter from different
225 directories. 226 directories.
226 227
228 Set this to 1 if you want one menu per directory instead of clumping
229 them together.
230
227 If the value is not a number, then the value 10 is used.") 231 If the value is not a number, then the value 10 is used.")
228 232
229 (defvar msb-most-recently-used-sort-key -1010 233 (defvar msb-most-recently-used-sort-key -1010
230 "*Where should the menu with the most recently used buffers be placed?") 234 "*Where should the menu with the most recently used buffers be placed?")
231 235
232 (defvar msb-display-most-recently-used t 236 (defvar msb-display-most-recently-used 15
233 "*How many buffers should be in the most-recently-used menu. 237 "*How many buffers should be in the most-recently-used menu.
234 No buffers at all if less than 1 or nil. 238 No buffers at all if less than 1 or nil (or any non-number).")
235 T means use the value of `msb-max-menu-items' in the way it is defined.")
236 239
237 (defvar msb-most-recently-used-title "Most recently used (%d)" 240 (defvar msb-most-recently-used-title "Most recently used (%d)"
238 "*The title for the most-recently-used menu.") 241 "*The title for the most-recently-used menu.")
239 242
240 (defvar msb-horizontal-shift-function '(lambda () 0) 243 (defvar msb-horizontal-shift-function '(lambda () 0)
250 "*The appearance of a buffer menu. 253 "*The appearance of a buffer menu.
251 254
252 The default function to call for handling the appearance of a menu 255 The default function to call for handling the appearance of a menu
253 item. It should take to arguments, BUFFER and MAX-BUFFER-NAME-LENGTH, 256 item. It should take to arguments, BUFFER and MAX-BUFFER-NAME-LENGTH,
254 where the latter is the max length of all buffer names. 257 where the latter is the max length of all buffer names.
258
259 The function should return the string to use in the menu.
260
255 When the function is called, BUFFER is the current buffer. 261 When the function is called, BUFFER is the current buffer.
256 This function is called for items in the variable `msb-menu-cond' that 262 This function is called for items in the variable `msb-menu-cond' that
257 have nil as ITEM-HANDLING-FUNCTION. See `msb-menu-cond' for more 263 have nil as ITEM-HANDLING-FUNCTION. See `msb-menu-cond' for more
258 information.") 264 information.")
259 265
329 335
330 ;; If this is non-nil, then it is a string that describes the error. 336 ;; If this is non-nil, then it is a string that describes the error.
331 (defvar msb--error nil) 337 (defvar msb--error nil)
332 338
333 ;;; 339 ;;;
334 ;;; Some example function to be used for `msb-item-sort-function'. 340 ;;; Some example function to be used for `msb-item-handling-function'.
335 ;;; 341 ;;;
336 (defun msb-item-handler (buffer &optional maxbuf) 342 (defun msb-item-handler (buffer &optional maxbuf)
337 "Create one string item, concerning BUFFER, for the buffer menu. 343 "Create one string item, concerning BUFFER, for the buffer menu.
338 The item looks like: 344 The item looks like:
339 *% <buffer-name> 345 *% <buffer-name>
384 (if buffer-read-only "%" " ") 390 (if buffer-read-only "%" " ")
385 (if (and (boundp 'vc-mode) vc-mode) "#" " ") 391 (if (and (boundp 'vc-mode) vc-mode) "#" " ")
386 (or buffer-file-name ""))) 392 (or buffer-file-name "")))
387 393
388 ;;; 394 ;;;
389 ;;; Some example function to be used for `msb-item-handling-function'. 395 ;;; Some example function to be used for `msb-item-sort-function'.
390 ;;; 396 ;;;
391 (defun msb-sort-by-name (item1 item2) 397 (defun msb-sort-by-name (item1 item2)
392 "Sorts the items depending on their buffer-name 398 "Sorts the items depending on their buffer-name
393 An item look like (NAME . BUFFER)." 399 An item look like (NAME . BUFFER)."
394 (string-lessp (buffer-name (cdr item1)) 400 (string-lessp (buffer-name (cdr item1))
415 See the function `mouse-select-buffer' and the variable 421 See the function `mouse-select-buffer' and the variable
416 `msb-menu-cond' for more information about how the menus are split." 422 `msb-menu-cond' for more information about how the menus are split."
417 (interactive "e") 423 (interactive "e")
418 (let ((buffer (mouse-select-buffer event)) 424 (let ((buffer (mouse-select-buffer event))
419 (window (posn-window (event-start event)))) 425 (window (posn-window (event-start event))))
420 (cond 426 (when buffer
421 (buffer 427 (unless (framep window) (select-window window))
422 (or (framep window) (select-window window)) 428 (switch-to-buffer buffer)))
423 (switch-to-buffer (car (cdr buffer))))))
424 nil) 429 nil)
425 430
426 ;;; 431 ;;;
427 ;;; Some supportive functions 432 ;;; Some supportive functions
428 ;;; 433 ;;;
461 (append 466 (append
462 (mapcan (function 467 (mapcan (function
463 (lambda (item) 468 (lambda (item)
464 (cond 469 (cond
465 ((and path 470 ((and path
466 msb-max-menu-items
467 (< (length buffers) msb-max-menu-items)
468 (string= path (car item))) 471 (string= path (car item)))
469 (push (cdr item) buffers) 472 (push (cdr item) buffers)
470 nil) 473 nil)
471 (t 474 (t
472 (when path 475 (when path
505 (setq tmp-rest (cdr tmp-rest)) 508 (setq tmp-rest (cdr tmp-rest))
506 (setq item (car tmp-rest))) 509 (setq item (car tmp-rest)))
507 (cond 510 (cond
508 ((> (length buffers) max-clumped-together) 511 ((> (length buffers) max-clumped-together)
509 (setq last-path (car first)) 512 (setq last-path (car first))
510 (when top-found-p 513 (setq first
511 (setq first (cons (concat (car first) "/...") 514 (cons (format (if top-found-p
512 (cdr first))) 515 "%s/... (%d)"
513 (setq top-found-p nil)) 516 "%s (%d)")
517 (car first)
518 (length (cdr first)))
519 (cdr first)))
520 (setq top-found-p nil)
514 (push first final-list) 521 (push first final-list)
515 (setq first (car rest) 522 (setq first (car rest)
516 rest (cdr rest)) 523 rest (cdr rest))
517 (setq path (car first) 524 (setq path (car first)
518 buffers (cdr first))) 525 buffers (cdr first)))
529 (substring path 0 (length last-path)))) 536 (substring path 0 (length last-path))))
530 (and (< (length path) (length last-path)) 537 (and (< (length path) (length last-path))
531 (string= path 538 (string= path
532 (substring last-path 0 (length path)))))) 539 (substring last-path 0 (length path))))))
533 540
534 (when top-found-p 541 (setq first
535 (setq first (cons (concat (car first) "/...") 542 (cons (format (if top-found-p
536 (cdr first))) 543 "%s/... (%d)"
537 (setq top-found-p nil)) 544 "%s (%d)")
545 (car first)
546 (length (cdr first)))
547 (cdr first)))
548 (setq top-found-p nil)
538 (push first final-list) 549 (push first final-list)
539 (setq first (car rest) 550 (setq first (car rest)
540 rest (cdr rest)) 551 rest (cdr rest))
541 (setq path (car first) 552 (setq path (car first)
542 buffers (cdr first))))))) 553 buffers (cdr first)))))))
543 (when top-found-p 554 (setq first
544 (setq first (cons (concat (car first) 555 (cons (format (if top-found-p
545 (if (string-match "/$" (car first)) 556 "%s/... (%d)"
546 "..." 557 "%s (%d)")
547 "/...")) 558 (car first)
548 (cdr first))) 559 (length (cdr first)))
549 (setq top-found-p nil)) 560 (cdr first)))
561 (setq top-found-p nil)
550 (push first final-list) 562 (push first final-list)
551 (nreverse final-list))) 563 (nreverse final-list)))
552 564
553 ;; Create a vector as: 565 ;; Create a vector as:
554 ;; [BUFFER-LIST-VARIABLE CONDITION MENU-SORT-KEY MENU-TITLE ITEM-HANDLER SORTER) 566 ;; [BUFFER-LIST-VARIABLE CONDITION MENU-SORT-KEY MENU-TITLE ITEM-HANDLER SORTER)
602 (eval (aref fi 1))) ;Test CONDITION 614 (eval (aref fi 1))) ;Test CONDITION
603 (not (and (eq result 'no-multi) 615 (not (and (eq result 'no-multi)
604 multi-flag)) 616 multi-flag))
605 (progn (when (eq result 'multi) 617 (progn (when (eq result 'multi)
606 (setq multi-flag t)) 618 (setq multi-flag t))
607 t) 619 t))
608 (or (not msb-max-menu-items)
609 (< (length (eval (aref fi 0)))
610 msb-max-menu-items)))
611 collect fi 620 collect fi
612 until (and result 621 until (and result
613 (not (eq result 'multi))))) 622 (not (eq result 'multi)))))
614 (when (and (not function-info-list) 623 (when (and (not function-info-list)
615 (not result)) 624 (not result))
670 (sort buffer-list sorter)))))))))) 679 (sort buffer-list sorter))))))))))
671 680
672 ;; Returns a list on the form ((TITLE . BUFFER-LIST)) for 681 ;; Returns a list on the form ((TITLE . BUFFER-LIST)) for
673 ;; the most recently used buffers. 682 ;; the most recently used buffers.
674 (defun msb--most-recently-used-menu (max-buffer-name-length) 683 (defun msb--most-recently-used-menu (max-buffer-name-length)
675 (when (and msb-display-most-recently-used 684 (when (and (numberp msb-display-most-recently-used)
676 (or (not (numberp msb-display-most-recently-used)) 685 (> msb-display-most-recently-used 0))
677 (> msb-display-most-recently-used 0))) 686 (let* ((most-recently-used
678 (let* ((max-in-menu
679 (if (numberp msb-display-most-recently-used)
680 msb-display-most-recently-used
681 msb-max-menu-items))
682
683 (most-recently-used
684 (loop with n = 0 687 (loop with n = 0
685 for buffer in (cdr (buffer-list)) 688 for buffer in (cdr (buffer-list))
686 if (save-excursion 689 if (save-excursion
687 (set-buffer buffer) 690 (set-buffer buffer)
688 (and (not (msb-invisible-buffer-p)) 691 (and (not (msb-invisible-buffer-p))
692 (cons (funcall msb-item-handling-function 695 (cons (funcall msb-item-handling-function
693 buffer 696 buffer
694 max-buffer-name-length) 697 max-buffer-name-length)
695 buffer)) 698 buffer))
696 and do (incf n) 699 and do (incf n)
697 until (and max-in-menu (>= n max-in-menu))))) 700 until (>= n msb-display-most-recently-used))))
698 (cons (if (stringp msb-most-recently-used-title) 701 (cons (if (stringp msb-most-recently-used-title)
699 (format msb-most-recently-used-title 702 (format msb-most-recently-used-title
700 (length most-recently-used)) 703 (length most-recently-used))
701 (signal 'wrong-type-argument (list msb-most-recently-used-title))) 704 (signal 'wrong-type-argument (list msb-most-recently-used-title)))
702 most-recently-used)))) 705 most-recently-used))))
746 (cons msb-files-by-directory-sort-key 749 (cons msb-files-by-directory-sort-key
747 (cons (car buffer-list) 750 (cons (car buffer-list)
748 (sort 751 (sort
749 (mapcar (function 752 (mapcar (function
750 (lambda (buffer) 753 (lambda (buffer)
751 (cons (buffer-name buffer) 754 (cons (save-excursion
755 (set-buffer buffer)
756 (funcall msb-item-handling-function
757 buffer
758 max-buffer-name-length))
752 buffer))) 759 buffer)))
753 (cdr buffer-list)) 760 (cdr buffer-list))
754 (function 761 (function
755 (lambda (item1 item2) 762 (lambda (item1 item2)
756 (string< (car item1) (car item2))))))))) 763 (string< (car item1) (car item2)))))))))
757 (msb--choose-file-menu file-buffers)))) 764 (msb--choose-file-menu file-buffers))))
758 ;; Now make the menu - a list of (TITLE . BUFFER-LIST) 765 ;; Now make the menu - a list of (TITLE . BUFFER-LIST)
759 (let* ((buffers (buffer-list)) 766 (let* (menu
760 menu
761 (most-recently-used 767 (most-recently-used
762 (msb--most-recently-used-menu max-buffer-name-length)) 768 (msb--most-recently-used-menu max-buffer-name-length))
763 (others (append file-buffers 769 (others (append file-buffers
764 (loop for elt 770 (loop for elt
765 across function-info-vector 771 across function-info-vector
766 for value = (msb--create-sort-item elt) 772 for value = (msb--create-sort-item elt)
767 if value collect value)))) 773 if value collect value))))
768 (setq menu 774 (setq menu
769 (mapcar 'cdr ;Remove the SORT-KEY 775 (mapcar 'cdr ;Remove the SORT-KEY
770 ;; Sort the menus - not the items. 776 ;; Sort the menus - not the items.
771 (msb--add-separators 777 (msb--add-separators
772 (sort 778 (sort
809 815
810 (defun mouse-select-buffer (event) 816 (defun mouse-select-buffer (event)
811 "Pop up several menus of buffers, for selection with the mouse. 817 "Pop up several menus of buffers, for selection with the mouse.
812 Returns the selected buffer or nil if no buffer is selected. 818 Returns the selected buffer or nil if no buffer is selected.
813 819
814 The way the buffers are splitted is conveniently handled with the 820 The way the buffers are split is conveniently handled with the
815 variable `msb-menu-cond'." 821 variable `msb-menu-cond'."
816 ;; Popup the menu and return the selected buffer. 822 ;; Popup the menu and return the selected buffer.
817 (when (or msb--error 823 (when (or msb--error
818 (not msb--last-buffer-menu) 824 (not msb--last-buffer-menu)
819 (not (fboundp 'frame-or-buffer-changed-p)) 825 (not (fboundp 'frame-or-buffer-changed-p))
820 (frame-or-buffer-changed-p)) 826 (frame-or-buffer-changed-p))
821 (setq msb--error nil) 827 (setq msb--error nil)
822 (setq msb--last-buffer-menu (msb--create-buffer-menu))) 828 (setq msb--last-buffer-menu (msb--create-buffer-menu)))
823 (let ((position event)) 829 (let ((position event)
830 choice)
824 (when (and (fboundp 'posn-x-y) 831 (when (and (fboundp 'posn-x-y)
825 (fboundp 'posn-window)) 832 (fboundp 'posn-window))
826 (let ((posX (car (posn-x-y (event-start event)))) 833 (let ((posX (car (posn-x-y (event-start event))))
827 (posY (cdr (posn-x-y (event-start event)))) 834 (posY (cdr (posn-x-y (event-start event))))
828 (posWind (posn-window (event-start event))) 835 (posWind (posn-window (event-start event))))
829 name)
830 ;; adjust position 836 ;; adjust position
831 (setq posX (- posX (funcall msb-horizontal-shift-function)) 837 (setq posX (- posX (funcall msb-horizontal-shift-function))
832 position (list (list posX posY) posWind)))) 838 position (list (list posX posY) posWind))))
833 (setq name (x-popup-menu position msb--last-buffer-menu)) 839 (setq choice (x-popup-menu position msb--last-buffer-menu))
834 ;; If toggle bring up the
835 (cond 840 (cond
836 ((eq (car name) 'toggle) 841 ((eq (car choice) 'toggle)
837 (msb--toggle-menu-type) 842 ;; Bring up the menu again with type toggled.
838 (mouse-select-buffer event)) 843 (msb--toggle-menu-type)
839 ((and (numberp (car name)) 844 (mouse-select-buffer event))
840 (null (cdr name))) 845 ((and (numberp (car choice))
841 (let ((msb--last-buffer-menu (nthcdr 3 (assq (car name) msb--last-buffer-menu)))) 846 (null (cdr choice)))
847 (let ((msb--last-buffer-menu (nthcdr 3 (assq (car choice) msb--last-buffer-menu))))
842 (mouse-select-buffer event))) 848 (mouse-select-buffer event)))
843 ((and (stringp (car name)) 849 ((while (numberp (car choice))
844 (null (cdr name))) 850 (setq choice (cdr choice))))
845 (cons nil name)) 851 ((and (stringp (car choice))
846 (t 852 (null (cdr choice)))
847 name)))) 853 (car choice))
854 (t
855 (error "Unknown form for buffer: %s" choice)))))
848 856
849 ;; Add separators 857 ;; Add separators
850 (defun msb--add-separators (sorted-list) 858 (defun msb--add-separators (sorted-list)
851 (cond 859 (cond
852 ((or (not msb-separator-diff) 860 ((or (not msb-separator-diff)
868 (t 876 (t
869 (setq last-key (car item)) 877 (setq last-key (car item))
870 (list item))))) 878 (list item)))))
871 sorted-list))))) 879 sorted-list)))))
872 880
881 (defun msb--split-menus-2 (list mcount result)
882 (cond
883 ((> (length list) msb-max-menu-items)
884 (let ((count 0)
885 sub-name
886 (tmp-list nil))
887 (while (< count msb-max-menu-items)
888 (push (pop list) tmp-list)
889 (incf count))
890 (setq tmp-list (nreverse tmp-list))
891 (setq sub-name (concat (car (car tmp-list)) "..."))
892 (push (append (list mcount sub-name
893 'keymap sub-name)
894 tmp-list)
895 result))
896 (msb--split-menus-2 list (1+ mcount) result))
897 ((null result)
898 list)
899 (t
900 (let (sub-name)
901 (setq sub-name (concat (car (car list)) "..."))
902 (push (append (list mcount sub-name
903 'keymap sub-name)
904 list)
905 result))
906 (nreverse result))))
907
908 (defun msb--split-menus (list)
909 (msb--split-menus-2 list 0 nil))
910
911
873 (defun msb--make-keymap-menu (raw-menu) 912 (defun msb--make-keymap-menu (raw-menu)
874 (let ((end (cons '(nil) 'menu-bar-select-buffer)) 913 (let ((end (cons '(nil) 'menu-bar-select-buffer))
875 (mcount 0)) 914 (mcount 0))
876 (mapcar 915 (mapcar
877 (function 916 (function
878 (lambda (sub-menu) 917 (lambda (sub-menu)
879 (cond 918 (cond
880 ((eq 'separator sub-menu) 919 ((eq 'separator sub-menu)
881 (list 'separator "---")) 920 (list 'separator "---"))
882 (t 921 (t
883 (append (list (incf mcount) (car sub-menu) 922 (let ((buffers (mapcar (function
884 'keymap (car sub-menu)) 923 (lambda (item)
885 (mapcar (function 924 (let ((string (car item))
886 (lambda (item) 925 (buffer (cdr item)))
887 (let ((string (car item)) 926 (cons (buffer-name buffer)
888 (buffer (cdr item))) 927 (cons string end)))))
889 (cons (buffer-name buffer) 928 (cdr sub-menu))))
890 (cons string end))))) 929 (append (list (incf mcount) (car sub-menu)
891 (cdr sub-menu))))))) 930 'keymap (car sub-menu))
931 (msb--split-menus buffers)))))))
892 raw-menu))) 932 raw-menu)))
893 933
894 (defun menu-bar-update-buffers (&optional arg) 934 (defun menu-bar-update-buffers (&optional arg)
895 ;; If user discards the Buffers item, play along. 935 ;; If user discards the Buffers item, play along.
896 (when (and (lookup-key (current-global-map) [menu-bar buffer]) 936 (when (and (lookup-key (current-global-map) [menu-bar buffer])
949 (substitute-key-definition 'mouse-buffer-menu 'msb (current-global-map))) 989 (substitute-key-definition 'mouse-buffer-menu 'msb (current-global-map)))
950 990
951 (provide 'msb) 991 (provide 'msb)
952 (eval-after-load 'msb (run-hooks 'msb-after-load-hooks)) 992 (eval-after-load 'msb (run-hooks 'msb-after-load-hooks))
953 ;;; msb.el ends here 993 ;;; msb.el ends here
954