Emacs config utilizing prelude as a base
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
10 KiB

  1. ;;; dropdown-list.el --- Drop-down menu interface
  2. ;;
  3. ;; Filename: dropdown-list.el
  4. ;; Description: Drop-down menu interface
  5. ;; Author: Jaeyoun Chung [jay.chung@gmail.com]
  6. ;; Maintainer:
  7. ;; Copyright (C) 2008 Jaeyoun Chung
  8. ;; Created: Sun Mar 16 11:20:45 2008 (Pacific Daylight Time)
  9. ;; Version:
  10. ;; Last-Updated: Sun Mar 16 12:19:49 2008 (Pacific Daylight Time)
  11. ;; By: dradams
  12. ;; Update #: 43
  13. ;; URL: http://www.emacswiki.org/cgi-bin/wiki/dropdown-list.el
  14. ;; Keywords: convenience menu
  15. ;; Compatibility: GNU Emacs 21.x, GNU Emacs 22.x
  16. ;;
  17. ;; Features that might be required by this library:
  18. ;;
  19. ;; `cl'.
  20. ;;
  21. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  22. ;;
  23. ;;; Commentary:
  24. ;;
  25. ;; According to Jaeyoun Chung, "overlay code stolen from company-mode.el."
  26. ;;
  27. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  28. ;;
  29. ;;; Change log:
  30. ;;
  31. ;; 2008/03/16 dadams
  32. ;; Clean-up - e.g. use char-to-string for control chars removed by email posting.
  33. ;; Moved example usage code (define-key*, command-selector) inside the library.
  34. ;; Require cl.el at byte-compile time.
  35. ;; Added GPL statement.
  36. ;; 2008/01/06 Jaeyoun Chung
  37. ;; Posted to gnu-emacs-sources@gnu.org at 9:10 p.m.
  38. ;;
  39. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  40. ;;
  41. ;; This program is free software; you can redistribute it and/or
  42. ;; modify it under the terms of the GNU General Public License as
  43. ;; published by the Free Software Foundation; either version 3, or
  44. ;; (at your option) any later version.
  45. ;;
  46. ;; This program is distributed in the hope that it will be useful,
  47. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  48. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  49. ;; General Public License for more details.
  50. ;;
  51. ;; You should have received a copy of the GNU General Public License
  52. ;; along with this program; see the file COPYING. If not, write to
  53. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  54. ;; Floor, Boston, MA 02110-1301, USA.
  55. ;;
  56. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  57. ;;
  58. ;;; Code:
  59. (eval-when-compile (require 'cl)) ;; decf, fourth, incf, loop, mapcar*
  60. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  61. (defface dropdown-list-face
  62. '((t :inherit default :background "lightyellow" :foreground "black"))
  63. "*Bla." :group 'dropdown-list)
  64. (defface dropdown-list-selection-face
  65. '((t :inherit dropdown-list-face :background "purple"))
  66. "*Bla." :group 'dropdown-list)
  67. (defvar dropdown-list-overlays nil)
  68. (defun dropdown-list-hide ()
  69. (while dropdown-list-overlays
  70. (delete-overlay (pop dropdown-list-overlays))))
  71. (defun dropdown-list-put-overlay (beg end &optional prop value prop2 value2)
  72. (let ((ov (make-overlay beg end)))
  73. (overlay-put ov 'window t)
  74. (when prop
  75. (overlay-put ov prop value)
  76. (when prop2 (overlay-put ov prop2 value2)))
  77. ov))
  78. (defun dropdown-list-line (start replacement &optional no-insert)
  79. ;; start might be in the middle of a tab, which means we need to hide the
  80. ;; tab and add spaces
  81. (let ((end (+ start (length replacement)))
  82. beg-point end-point
  83. before-string after-string)
  84. (goto-char (point-at-eol))
  85. (if (< (current-column) start)
  86. (progn (setq before-string (make-string (- start (current-column)) ? ))
  87. (setq beg-point (point)))
  88. (goto-char (point-at-bol)) ;; Emacs bug, move-to-column is wrong otherwise
  89. (move-to-column start)
  90. (setq beg-point (point))
  91. (when (> (current-column) start)
  92. (goto-char (1- (point)))
  93. (setq beg-point (point))
  94. (setq before-string (make-string (- start (current-column)) ? ))))
  95. (move-to-column end)
  96. (setq end-point (point))
  97. (let ((end-offset (- (current-column) end)))
  98. (when (> end-offset 0) (setq after-string (make-string end-offset ?b))))
  99. (when no-insert
  100. ;; prevent inheriting of faces
  101. (setq before-string (when before-string (propertize before-string 'face 'default)))
  102. (setq after-string (when after-string (propertize after-string 'face 'default))))
  103. (let ((string (concat before-string replacement after-string)))
  104. (if no-insert
  105. string
  106. (push (dropdown-list-put-overlay beg-point end-point 'invisible t
  107. 'after-string string)
  108. dropdown-list-overlays)))))
  109. (defun dropdown-list-start-column (display-width)
  110. (let ((column (mod (current-column) (window-width)))
  111. (width (window-width)))
  112. (cond ((<= (+ column display-width) width) column)
  113. ((> column display-width) (- column display-width))
  114. ((>= width display-width) (- width display-width))
  115. (t nil))))
  116. (defun dropdown-list-move-to-start-line (candidate-count)
  117. (decf candidate-count)
  118. (let ((above-line-count (save-excursion (- (vertical-motion (- candidate-count)))))
  119. (below-line-count (save-excursion (vertical-motion candidate-count))))
  120. (cond ((= below-line-count candidate-count)
  121. t)
  122. ((= above-line-count candidate-count)
  123. (vertical-motion (- candidate-count))
  124. t)
  125. ((>= (+ below-line-count above-line-count) candidate-count)
  126. (vertical-motion (- (- candidate-count below-line-count)))
  127. t)
  128. (t nil))))
  129. (defun dropdown-list-at-point (candidates &optional selidx)
  130. (dropdown-list-hide)
  131. (let* ((lengths (mapcar #'length candidates))
  132. (max-length (apply #'max lengths))
  133. (start (dropdown-list-start-column (+ max-length 3)))
  134. (i -1)
  135. (candidates (mapcar* (lambda (candidate length)
  136. (let ((diff (- max-length length)))
  137. (propertize
  138. (concat (if (> diff 0)
  139. (concat candidate (make-string diff ? ))
  140. (substring candidate 0 max-length))
  141. (format "%3d" (+ 2 i)))
  142. 'face (if (eql (incf i) selidx)
  143. 'dropdown-list-selection-face
  144. 'dropdown-list-face))))
  145. candidates
  146. lengths)))
  147. (save-excursion
  148. (and start
  149. (dropdown-list-move-to-start-line (length candidates))
  150. (loop initially (vertical-motion 0)
  151. for candidate in candidates
  152. do (dropdown-list-line (+ (current-column) start) candidate)
  153. while (/= (vertical-motion 1) 0)
  154. finally return t)))))
  155. (defun dropdown-list (candidates)
  156. (let ((selection)
  157. (temp-buffer))
  158. (save-window-excursion
  159. (unwind-protect
  160. (let ((candidate-count (length candidates))
  161. done key (selidx 0))
  162. (while (not done)
  163. (unless (dropdown-list-at-point candidates selidx)
  164. (switch-to-buffer (setq temp-buffer (get-buffer-create "*selection*"))
  165. 'norecord)
  166. (delete-other-windows)
  167. (delete-region (point-min) (point-max))
  168. (insert (make-string (length candidates) ?\n))
  169. (goto-char (point-min))
  170. (dropdown-list-at-point candidates selidx))
  171. (setq key (read-key-sequence ""))
  172. (cond ((and (stringp key)
  173. (>= (aref key 0) ?1)
  174. (<= (aref key 0) (+ ?0 (min 9 candidate-count))))
  175. (setq selection (- (aref key 0) ?1)
  176. done t))
  177. ((member key `(,(char-to-string ?\C-p) [up] "p"))
  178. (setq selidx (mod (+ candidate-count (1- (or selidx 0)))
  179. candidate-count)))
  180. ((member key `(,(char-to-string ?\C-n) [down] "n"))
  181. (setq selidx (mod (1+ (or selidx -1)) candidate-count)))
  182. ((member key `(,(char-to-string ?\f))))
  183. ((member key `(,(char-to-string ?\r) [return]))
  184. (setq selection selidx
  185. done t))
  186. (t (setq done t)))))
  187. (dropdown-list-hide)
  188. (and temp-buffer (kill-buffer temp-buffer)))
  189. ;; (when selection
  190. ;; (message "your selection => %d: %s" selection (nth selection candidates))
  191. ;; (sit-for 1))
  192. selection)))
  193. (defun define-key* (keymap key command)
  194. "Add COMMAND to the multiple-command binding of KEY in KEYMAP.
  195. Use multiple times to bind different COMMANDs to the same KEY."
  196. (define-key keymap key (combine-command command (lookup-key keymap key))))
  197. (defun combine-command (command defs)
  198. "$$$$$ FIXME - no doc string"
  199. (cond ((null defs) command)
  200. ((and (listp defs)
  201. (eq 'lambda (car defs))
  202. (= (length defs) 4)
  203. (listp (fourth defs))
  204. (eq 'command-selector (car (fourth defs))))
  205. (unless (member `',command (cdr (fourth defs)))
  206. (setcdr (fourth defs) (nconc (cdr (fourth defs)) `(',command))))
  207. defs)
  208. (t
  209. `(lambda () (interactive) (command-selector ',defs ',command)))))
  210. (defvar command-selector-last-command nil "$$$$$ FIXME - no doc string")
  211. (defun command-selector (&rest candidates)
  212. "$$$$$ FIXME - no doc string"
  213. (if (and (eq last-command this-command) command-selector-last-command)
  214. (call-interactively command-selector-last-command)
  215. (let* ((candidate-strings
  216. (mapcar (lambda (candidate)
  217. (format "%s" (if (symbolp candidate)
  218. candidate
  219. (let ((s (format "%s" candidate)))
  220. (if (>= (length s) 7)
  221. (concat (substring s 0 7) "...")
  222. s)))))
  223. candidates))
  224. (selection (dropdown-list candidate-strings)))
  225. (when selection
  226. (let ((cmd (nth selection candidates)))
  227. (call-interactively cmd)
  228. (setq command-selector-last-command cmd))))))
  229. ;;;;;;;;;;;;;;;;;;;;
  230. (provide 'dropdown-list)
  231. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  232. ;;; dropdown-list.el ends here