Klimi's new dotfiles with stow.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

1432 řádky
54 KiB

před 4 roky
  1. ;;; popup.el --- Visual Popup User Interface
  2. ;; Copyright (C) 2009-2015 Tomohiro Matsuyama
  3. ;; Author: Tomohiro Matsuyama <m2ym.pub@gmail.com>
  4. ;; Keywords: lisp
  5. ;; Package-Version: 20160709.1429
  6. ;; Version: 0.5.3
  7. ;; Package-Requires: ((cl-lib "0.5"))
  8. ;; 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. ;; the Free Software Foundation, either version 3 of the License, or
  11. ;; (at your option) any later version.
  12. ;; This program is distributed in the hope that it will be useful,
  13. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. ;; GNU General Public License for more details.
  16. ;; You should have received a copy of the GNU General Public License
  17. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. ;;; Commentary:
  19. ;; popup.el is a visual popup user interface library for Emacs. This
  20. ;; provides a basic API and common UI widgets such as popup tooltips
  21. ;; and popup menus.
  22. ;; See README.markdown for more information.
  23. ;;; Code:
  24. (require 'cl-lib)
  25. (defconst popup-version "0.5.3")
  26. ;;; Utilities
  27. (defun popup-calculate-max-width (max-width)
  28. "Determines whether the width desired is
  29. character or window proportion based, And returns the result."
  30. (cl-typecase max-width
  31. (integer max-width)
  32. (float (* (ceiling (/ (round (* max-width (window-width))) 10.0)) 10))))
  33. (defvar popup-use-optimized-column-computation t
  34. "Use the optimized column computation routine.
  35. If there is a problem, please set it nil.")
  36. (defmacro popup-aif (test then &rest else)
  37. "Anaphoric if."
  38. (declare (indent 2))
  39. `(let ((it ,test))
  40. (if it ,then ,@else)))
  41. (defmacro popup-awhen (test &rest body)
  42. "Anaphoric when."
  43. (declare (indent 1))
  44. `(let ((it ,test))
  45. (when it ,@body)))
  46. (defun popup-x-to-string (x)
  47. "Convert any object to string effeciently.
  48. This is faster than `prin1-to-string' in many cases."
  49. (cl-typecase x
  50. (string x)
  51. (symbol (symbol-name x))
  52. (integer (number-to-string x))
  53. (float (number-to-string x))
  54. (t (format "%s" x))))
  55. (defun popup-substring-by-width (string width)
  56. "Return a cons cell of substring and remaining string by
  57. splitting with WIDTH."
  58. ;; Expand tabs into 4 spaces
  59. (setq string (replace-regexp-in-string "\t" " " string))
  60. (cl-loop with len = (length string)
  61. with w = 0
  62. for l from 0
  63. for c in (append string nil)
  64. while (<= (cl-incf w (char-width c)) width)
  65. finally return
  66. (if (< l len)
  67. (cons (substring string 0 l) (substring string l))
  68. (list string))))
  69. (defun popup-fill-string (string &optional width max-width justify squeeze)
  70. "Split STRING into fixed width strings and return a cons cell
  71. like \(WIDTH . ROWS). Here, the car WIDTH indicates the actual
  72. maxim width of ROWS.
  73. The argument WIDTH specifies the width of filling each
  74. paragraph. WIDTH nil means don't perform any justification and
  75. word wrap. Note that this function doesn't add any padding
  76. characters at the end of each row.
  77. MAX-WIDTH, if WIDTH is nil, specifies the maximum number of
  78. columns.
  79. The optional fourth argument JUSTIFY specifies which kind of
  80. justification to do: `full', `left', `right', `center', or
  81. `none' (equivalent to nil). A value of t means handle each
  82. paragraph as specified by its text properties.
  83. SQUEEZE nil means leave whitespaces other than line breaks
  84. untouched."
  85. (if (eq width 0)
  86. (error "Can't fill string with 0 width"))
  87. (if width
  88. (setq max-width width))
  89. (with-temp-buffer
  90. (let ((tab-width 4)
  91. (fill-column width)
  92. (left-margin 0)
  93. (kinsoku-limit 1)
  94. indent-tabs-mode
  95. row rows)
  96. (insert string)
  97. (untabify (point-min) (point-max))
  98. (if width
  99. (fill-region (point-min) (point-max) justify (not squeeze)))
  100. (goto-char (point-min))
  101. (setq width 0)
  102. (while (prog2
  103. (let ((line (buffer-substring
  104. (point) (progn (end-of-line) (point)))))
  105. (if max-width
  106. (while (progn
  107. (setq row (truncate-string-to-width line max-width)
  108. width (max width (string-width row)))
  109. (push row rows)
  110. (if (not (= (length row) (length line)))
  111. (setq line (substring line (length row))))))
  112. (setq width (max width (string-width line)))
  113. (push line rows)))
  114. (< (point) (point-max))
  115. (beginning-of-line 2)))
  116. (cons width (nreverse rows)))))
  117. (defmacro popup-save-buffer-state (&rest body)
  118. (declare (indent 0))
  119. `(save-excursion
  120. (let ((buffer-undo-list t)
  121. (inhibit-read-only t)
  122. (modified (buffer-modified-p)))
  123. (unwind-protect
  124. (progn ,@body)
  125. (set-buffer-modified-p modified)))))
  126. (defun popup-vertical-motion (column direction)
  127. "A portable version of `vertical-motion'."
  128. (if (>= emacs-major-version 23)
  129. (vertical-motion (cons column direction))
  130. (vertical-motion direction)
  131. (move-to-column (+ (current-column) column))))
  132. (defun popup-last-line-of-buffer-p ()
  133. "Return non-nil if the cursor is at the last line of the
  134. buffer."
  135. (save-excursion (end-of-line) (/= (forward-line) 0)))
  136. (defun popup-lookup-key-by-event (function event)
  137. (or (funcall function (vector event))
  138. (if (symbolp event)
  139. (popup-aif (get event 'event-symbol-element-mask)
  140. (funcall function
  141. (vector (logior (or (get (car it) 'ascii-character)
  142. 0)
  143. (cadr it))))))))
  144. ;;; Core
  145. (defgroup popup nil
  146. "Visual Popup User Interface"
  147. :group 'lisp
  148. :prefix "popup-")
  149. (defface popup-face
  150. '((t (:inherit default :background "lightgray" :foreground "black")))
  151. "Face for popup."
  152. :group 'popup)
  153. (defface popup-summary-face
  154. '((t (:inherit popup-face :foreground "dimgray")))
  155. "Face for popup summary."
  156. :group 'popup)
  157. (defface popup-scroll-bar-foreground-face
  158. '((t (:background "black")))
  159. "Foreground face for scroll-bar."
  160. :group 'popup)
  161. (defface popup-scroll-bar-background-face
  162. '((t (:background "gray")))
  163. "Background face for scroll-bar."
  164. :group 'popup)
  165. (defvar popup-instances nil
  166. "Popup instances.")
  167. (defvar popup-scroll-bar-foreground-char
  168. (propertize " " 'face 'popup-scroll-bar-foreground-face)
  169. "Foreground character for scroll-bar.")
  170. (defvar popup-scroll-bar-background-char
  171. (propertize " " 'face 'popup-scroll-bar-background-face)
  172. "Background character for scroll-bar.")
  173. (cl-defstruct popup
  174. point row column width height min-height direction overlays keymap
  175. parent depth
  176. face mouse-face selection-face summary-face
  177. margin-left margin-right margin-left-cancel scroll-bar symbol
  178. cursor offset scroll-top current-height list newlines
  179. pattern original-list invis-overlays)
  180. (defun popup-item-propertize (item &rest properties)
  181. "Same as `propertize' except that this avoids overriding
  182. existed value with `nil' property."
  183. (cl-loop for (k v) on properties by 'cddr
  184. if v append (list k v) into props
  185. finally return
  186. (apply 'propertize
  187. (popup-x-to-string item)
  188. props)))
  189. (defun popup-item-property (item property)
  190. "Same as `get-text-property' except that this returns nil if
  191. ITEM is not string."
  192. (if (stringp item)
  193. (get-text-property 0 property item)))
  194. (cl-defun popup-make-item (name
  195. &key
  196. value
  197. face
  198. mouse-face
  199. selection-face
  200. sublist
  201. document
  202. symbol
  203. summary)
  204. "Utility function to make popup item. See also
  205. `popup-item-propertize'."
  206. (popup-item-propertize name
  207. 'value value
  208. 'popup-face face
  209. 'popup-mouse-face mouse-face
  210. 'selection-face selection-face
  211. 'document document
  212. 'symbol symbol
  213. 'summary summary
  214. 'sublist sublist))
  215. (defsubst popup-item-value (item) (popup-item-property item 'value))
  216. (defsubst popup-item-value-or-self (item) (or (popup-item-value item) item))
  217. (defsubst popup-item-face (item) (popup-item-property item 'popup-face))
  218. (defsubst popup-item-mouse-face (item) (popup-item-property item 'popup-mouse-face))
  219. (defsubst popup-item-selection-face (item) (popup-item-property item 'selection-face))
  220. (defsubst popup-item-document (item) (popup-item-property item 'document))
  221. (defsubst popup-item-summary (item) (popup-item-property item 'summary))
  222. (defsubst popup-item-symbol (item) (popup-item-property item 'symbol))
  223. (defsubst popup-item-sublist (item) (popup-item-property item 'sublist))
  224. (defun popup-item-documentation (item)
  225. (let ((doc (popup-item-document item)))
  226. (if (functionp doc)
  227. (setq doc (funcall doc (popup-item-value-or-self item))))
  228. doc))
  229. (defun popup-item-show-help-1 (item)
  230. (let ((doc (popup-item-documentation item)))
  231. (when doc
  232. (with-current-buffer (get-buffer-create " *Popup Help*")
  233. (erase-buffer)
  234. (insert doc)
  235. (goto-char (point-min))
  236. (display-buffer (current-buffer)))
  237. t)))
  238. (defun popup-item-show-help-with-event-loop (item)
  239. (save-window-excursion
  240. (when (popup-item-show-help-1 item)
  241. (cl-loop do (clear-this-command-keys)
  242. for key = (read-key-sequence-vector nil)
  243. do
  244. (cl-case (key-binding key)
  245. (scroll-other-window
  246. (scroll-other-window))
  247. (scroll-other-window-down
  248. (scroll-other-window-down nil))
  249. (otherwise
  250. (setq unread-command-events (append key unread-command-events))
  251. (cl-return)))))))
  252. (defun popup-item-show-help (item &optional persist)
  253. "Display the documentation of ITEM with `display-buffer'. If
  254. PERSIST is nil, the documentation buffer will be closed
  255. automatically, meaning interal event loop ensures the buffer to
  256. be closed. Otherwise, the buffer will be just displayed as
  257. usual."
  258. (when item
  259. (if (not persist)
  260. (popup-item-show-help-with-event-loop item)
  261. (popup-item-show-help-1 item))))
  262. (defun popup-set-list (popup list)
  263. (popup-set-filtered-list popup list)
  264. (setf (popup-pattern popup) nil)
  265. (setf (popup-original-list popup) list))
  266. (defun popup-set-filtered-list (popup list)
  267. (let ((offset
  268. (if (> (popup-direction popup) 0)
  269. 0
  270. (max (- (popup-height popup) (length list)) 0))))
  271. (setf (popup-list popup) list
  272. (popup-offset popup) offset)))
  273. (defun popup-selected-item (popup)
  274. (nth (popup-cursor popup) (popup-list popup)))
  275. (defun popup-selected-line (popup)
  276. (- (popup-cursor popup) (popup-scroll-top popup)))
  277. (defun popup-line-overlay (popup line)
  278. (aref (popup-overlays popup) line))
  279. (defun popup-selected-line-overlay (popup)
  280. (popup-line-overlay popup (popup-selected-line popup)))
  281. (defun popup-hide-line (popup line)
  282. (let ((overlay (popup-line-overlay popup line)))
  283. (overlay-put overlay 'display nil)
  284. (overlay-put overlay 'after-string nil)))
  285. (defun popup-line-hidden-p (popup line)
  286. (let ((overlay (popup-line-overlay popup line)))
  287. (and (eq (overlay-get overlay 'display) nil)
  288. (eq (overlay-get overlay 'after-string) nil))))
  289. (cl-defun popup-set-line-item (popup
  290. line
  291. &key
  292. item
  293. face
  294. mouse-face
  295. margin-left
  296. margin-right
  297. scroll-bar-char
  298. symbol
  299. summary
  300. summary-face
  301. keymap)
  302. (let* ((overlay (popup-line-overlay popup line))
  303. (content (popup-create-line-string popup (popup-x-to-string item)
  304. :margin-left margin-left
  305. :margin-right margin-right
  306. :symbol symbol
  307. :summary summary
  308. :summary-face summary-face))
  309. (start 0)
  310. (prefix (overlay-get overlay 'prefix))
  311. (postfix (overlay-get overlay 'postfix))
  312. end)
  313. (put-text-property 0 (length content) 'popup-item item content)
  314. (put-text-property 0 (length content) 'keymap keymap content)
  315. ;; Overlap face properties
  316. (when (get-text-property start 'face content)
  317. (setq start (next-single-property-change start 'face content)))
  318. (while (and start (setq end (next-single-property-change start 'face content)))
  319. (put-text-property start end 'face face content)
  320. (setq start (next-single-property-change end 'face content)))
  321. (when start
  322. (put-text-property start (length content) 'face face content))
  323. (when mouse-face
  324. (put-text-property 0 (length content) 'mouse-face mouse-face content))
  325. (let ((prop (if (overlay-get overlay 'dangle)
  326. 'after-string
  327. 'display)))
  328. (overlay-put overlay
  329. prop
  330. (concat prefix
  331. content
  332. scroll-bar-char
  333. postfix)))))
  334. (cl-defun popup-create-line-string (popup
  335. string
  336. &key
  337. margin-left
  338. margin-right
  339. symbol
  340. summary
  341. summary-face)
  342. (let* ((popup-width (popup-width popup))
  343. (summary-width (string-width summary))
  344. (content-width (max
  345. (min popup-width (string-width string))
  346. (- popup-width
  347. (if (> summary-width 0)
  348. (+ summary-width 2)
  349. 0))))
  350. (string (car (popup-substring-by-width string content-width)))
  351. (string-width (string-width string))
  352. (spacing (max (- popup-width string-width summary-width)
  353. (if (> popup-width string-width) 1 0)))
  354. (truncated-summary
  355. (car (popup-substring-by-width
  356. summary (max (- popup-width string-width spacing) 0)))))
  357. (when summary-face
  358. (put-text-property 0 (length truncated-summary)
  359. 'face summary-face truncated-summary))
  360. (concat margin-left
  361. string
  362. (make-string spacing ? )
  363. truncated-summary
  364. symbol
  365. margin-right)))
  366. (defun popup-live-p (popup)
  367. "Return non-nil if POPUP is alive."
  368. (and popup (popup-overlays popup) t))
  369. (defun popup-child-point (popup &optional offset)
  370. (overlay-end
  371. (popup-line-overlay
  372. popup
  373. (or offset
  374. (popup-selected-line popup)))))
  375. (defun popup-calculate-direction (height row)
  376. "Return a proper direction when displaying a popup on this
  377. window. HEIGHT is the a height of the popup, and ROW is a line
  378. number at the point."
  379. (let* ((remaining-rows (- (max 1 (- (window-height)
  380. (if mode-line-format 1 0)
  381. (if header-line-format 1 0)))
  382. (count-lines (window-start) (point))))
  383. (enough-space-above (> row height))
  384. (enough-space-below (<= height remaining-rows)))
  385. (if (and enough-space-above
  386. (not enough-space-below))
  387. -1
  388. 1)))
  389. (cl-defun popup-create (point
  390. width
  391. height
  392. &key
  393. min-height
  394. max-width
  395. around
  396. (face 'popup-face)
  397. mouse-face
  398. (selection-face face)
  399. (summary-face 'popup-summary-face)
  400. scroll-bar
  401. margin-left
  402. margin-right
  403. symbol
  404. parent
  405. parent-offset
  406. keymap)
  407. "Create a popup instance at POINT with WIDTH and HEIGHT.
  408. MIN-HEIGHT is a minimal height of the popup. The default value is
  409. 0.
  410. MAX-WIDTH is the maximum width of the popup. The default value is
  411. nil (no limit). If a floating point, the value refers to the ratio of
  412. the window. If an integer, limit is in characters.
  413. If AROUND is non-nil, the popup will be displayed around the
  414. point but not at the point.
  415. FACE is a background face of the popup. The default value is POPUP-FACE.
  416. SELECTION-FACE is a foreground (selection) face of the popup The
  417. default value is POPUP-FACE.
  418. If SCROLL-BAR is non-nil, the popup will have a scroll bar at the
  419. right.
  420. If MARGIN-LEFT is non-nil, the popup will have a margin at the
  421. left.
  422. If MARGIN-RIGHT is non-nil, the popup will have a margin at the
  423. right.
  424. SYMBOL is a single character which indicates a kind of the item.
  425. PARENT is a parent popup instance. If PARENT is omitted, the
  426. popup will be a root instance.
  427. PARENT-OFFSET is a row offset from the parent popup.
  428. KEYMAP is a keymap that will be put on the popup contents."
  429. (or margin-left (setq margin-left 0))
  430. (or margin-right (setq margin-right 0))
  431. (unless point
  432. (setq point
  433. (if parent (popup-child-point parent parent-offset) (point))))
  434. (when max-width
  435. (setq width (min width (popup-calculate-max-width max-width))))
  436. (save-excursion
  437. (goto-char point)
  438. (let* ((col-row (posn-col-row (posn-at-point)))
  439. (row (cdr col-row))
  440. (column (car col-row))
  441. (overlays (make-vector height nil))
  442. (popup-width (+ width
  443. (if scroll-bar 1 0)
  444. margin-left
  445. margin-right
  446. (if symbol 2 0)))
  447. margin-left-cancel
  448. (window (selected-window))
  449. (window-start (window-start))
  450. (window-hscroll (window-hscroll))
  451. (window-width (window-width))
  452. (right (+ column popup-width))
  453. (overflow (and (> right window-width)
  454. (>= right popup-width)))
  455. (foldable (and (null parent)
  456. (>= column popup-width)))
  457. (direction (or
  458. ;; Currently the direction of cascade popup won't be changed
  459. (and parent (popup-direction parent))
  460. ;; Calculate direction
  461. (popup-calculate-direction height row)))
  462. (depth (if parent (1+ (popup-depth parent)) 0))
  463. (newlines (max 0 (+ (- height (count-lines point (point-max))) (if around 1 0))))
  464. invis-overlays
  465. current-column)
  466. ;; Case: no newlines at the end of the buffer
  467. (when (> newlines 0)
  468. (popup-save-buffer-state
  469. (goto-char (point-max))
  470. (insert (make-string newlines ?\n))))
  471. ;; Case: the popup overflows
  472. (if overflow
  473. (if foldable
  474. (progn
  475. (cl-decf column (- popup-width margin-left margin-right))
  476. (unless around (move-to-column column)))
  477. (when (not truncate-lines)
  478. ;; Truncate.
  479. (let ((d (1+ (- popup-width (- window-width column)))))
  480. (cl-decf popup-width d)
  481. (cl-decf width d)))
  482. (cl-decf column margin-left))
  483. (cl-decf column margin-left))
  484. ;; Case: no space at the left
  485. (when (and (null parent)
  486. (< column 0))
  487. ;; Cancel margin left
  488. (setq column 0)
  489. (cl-decf popup-width margin-left)
  490. (setq margin-left-cancel t))
  491. (dotimes (i height)
  492. (let (overlay begin w (dangle t) (prefix "") (postfix ""))
  493. (when around
  494. (popup-vertical-motion column direction))
  495. (cl-loop for ov in (overlays-in (save-excursion
  496. (beginning-of-visual-line)
  497. (point))
  498. (save-excursion
  499. (end-of-visual-line)
  500. (point)))
  501. when (and (not (overlay-get ov 'popup))
  502. (not (overlay-get ov 'popup-item))
  503. (or (overlay-get ov 'invisible)
  504. (overlay-get ov 'display)))
  505. do (progn
  506. (push (list ov (overlay-get ov 'display)) invis-overlays)
  507. (overlay-put ov 'display "")))
  508. (setq around t)
  509. (setq current-column (car (posn-col-row (posn-at-point))))
  510. (when (< current-column column)
  511. ;; Extend short buffer lines by popup prefix (line of spaces)
  512. (setq prefix (make-string
  513. (+ (if (= current-column 0)
  514. (- window-hscroll current-column)
  515. 0)
  516. (- column current-column))
  517. ? )))
  518. (setq begin (point))
  519. (setq w (+ popup-width (length prefix)))
  520. (while (and (not (eolp)) (> w 0))
  521. (setq dangle nil)
  522. (cl-decf w (char-width (char-after)))
  523. (forward-char))
  524. (if (< w 0)
  525. (setq postfix (make-string (- w) ? )))
  526. (setq overlay (make-overlay begin (point)))
  527. (overlay-put overlay 'popup t)
  528. (overlay-put overlay 'window window)
  529. (overlay-put overlay 'dangle dangle)
  530. (overlay-put overlay 'prefix prefix)
  531. (overlay-put overlay 'postfix postfix)
  532. (overlay-put overlay 'width width)
  533. (aset overlays
  534. (if (> direction 0) i (- height i 1))
  535. overlay)))
  536. (cl-loop for p from (- 10000 (* depth 1000))
  537. for overlay in (nreverse (append overlays nil))
  538. do (overlay-put overlay 'priority p))
  539. (let ((it (make-popup :point point
  540. :row row
  541. :column column
  542. :width width
  543. :height height
  544. :min-height min-height
  545. :direction direction
  546. :parent parent
  547. :depth depth
  548. :face face
  549. :mouse-face mouse-face
  550. :selection-face selection-face
  551. :summary-face summary-face
  552. :margin-left margin-left
  553. :margin-right margin-right
  554. :margin-left-cancel margin-left-cancel
  555. :scroll-bar scroll-bar
  556. :symbol symbol
  557. :cursor 0
  558. :offset 0
  559. :scroll-top 0
  560. :current-height 0
  561. :list nil
  562. :newlines newlines
  563. :overlays overlays
  564. :invis-overlays invis-overlays
  565. :keymap keymap)))
  566. (push it popup-instances)
  567. it))))
  568. (defun popup-delete (popup)
  569. "Delete POPUP instance."
  570. (when (popup-live-p popup)
  571. (popup-hide popup)
  572. (mapc 'delete-overlay (popup-overlays popup))
  573. (setf (popup-overlays popup) nil)
  574. (setq popup-instances (delq popup popup-instances))
  575. ;; Restore newlines state
  576. (let ((newlines (popup-newlines popup)))
  577. (when (> newlines 0)
  578. (popup-save-buffer-state
  579. (goto-char (point-max))
  580. (dotimes (i newlines)
  581. (if (and (char-before)
  582. (= (char-before) ?\n))
  583. (delete-char -1)))))))
  584. nil)
  585. (defun popup-draw (popup)
  586. "Draw POPUP."
  587. (cl-loop for (ov olddisplay) in (popup-invis-overlays popup)
  588. do (overlay-put ov 'display ""))
  589. (cl-loop with height = (popup-height popup)
  590. with min-height = (popup-min-height popup)
  591. with popup-face = (popup-face popup)
  592. with mouse-face = (popup-mouse-face popup)
  593. with selection-face = (popup-selection-face popup)
  594. with summary-face-0 = (popup-summary-face popup)
  595. with list = (popup-list popup)
  596. with length = (length list)
  597. with thum-size = (max (/ (* height height) (max length 1)) 1)
  598. with page-size = (/ (+ 0.0 (max length 1)) height)
  599. with scroll-bar = (popup-scroll-bar popup)
  600. with margin-left = (make-string (if (popup-margin-left-cancel popup) 0 (popup-margin-left popup)) ? )
  601. with margin-right = (make-string (popup-margin-right popup) ? )
  602. with symbol = (popup-symbol popup)
  603. with cursor = (popup-cursor popup)
  604. with scroll-top = (popup-scroll-top popup)
  605. with offset = (popup-offset popup)
  606. with keymap = (popup-keymap popup)
  607. for o from offset
  608. for i from scroll-top
  609. while (< o height)
  610. for item in (nthcdr scroll-top list)
  611. for page-index = (* thum-size (/ o thum-size))
  612. for face = (if (= i cursor)
  613. (or (popup-item-selection-face item) selection-face)
  614. (or (popup-item-face item) popup-face))
  615. for summary-face = (unless (= i cursor) summary-face-0)
  616. for empty-char = (propertize " " 'face face)
  617. for scroll-bar-char = (if scroll-bar
  618. (cond
  619. ((and (not (eq scroll-bar :always))
  620. (<= page-size 1))
  621. empty-char)
  622. ((and (> page-size 1)
  623. (>= cursor (* page-index page-size))
  624. (< cursor (* (+ page-index thum-size) page-size)))
  625. popup-scroll-bar-foreground-char)
  626. (t
  627. popup-scroll-bar-background-char))
  628. "")
  629. for sym = (if symbol
  630. (concat " " (or (popup-item-symbol item) " "))
  631. "")
  632. for summary = (or (popup-item-summary item) "")
  633. do
  634. ;; Show line and set item to the line
  635. (popup-set-line-item popup o
  636. :item item
  637. :face face
  638. :mouse-face mouse-face
  639. :margin-left margin-left
  640. :margin-right margin-right
  641. :scroll-bar-char scroll-bar-char
  642. :symbol sym
  643. :summary summary
  644. :summary-face summary-face
  645. :keymap keymap)
  646. finally
  647. ;; Remember current height
  648. (setf (popup-current-height popup) (- o offset))
  649. ;; Hide remaining lines
  650. (let ((scroll-bar-char (if scroll-bar (propertize " " 'face popup-face) ""))
  651. (symbol (if symbol " " "")))
  652. (if (> (popup-direction popup) 0)
  653. (progn
  654. (when min-height
  655. (while (< o min-height)
  656. (popup-set-line-item popup o
  657. :item ""
  658. :face popup-face
  659. :margin-left margin-left
  660. :margin-right margin-right
  661. :scroll-bar-char scroll-bar-char
  662. :symbol symbol
  663. :summary "")
  664. (cl-incf o)))
  665. (while (< o height)
  666. (popup-hide-line popup o)
  667. (cl-incf o)))
  668. (cl-loop with h = (if min-height (- height min-height) offset)
  669. for o from 0 below offset
  670. if (< o h)
  671. do (popup-hide-line popup o)
  672. if (>= o h)
  673. do (popup-set-line-item popup o
  674. :item ""
  675. :face popup-face
  676. :margin-left margin-left
  677. :margin-right margin-right
  678. :scroll-bar-char scroll-bar-char
  679. :symbol symbol
  680. :summary ""))))))
  681. (defun popup-hide (popup)
  682. "Hide POPUP."
  683. (cl-loop for (ov olddisplay) in (popup-invis-overlays popup)
  684. do (overlay-put ov 'display olddisplay))
  685. (dotimes (i (popup-height popup))
  686. (popup-hide-line popup i)))
  687. (defun popup-hidden-p (popup)
  688. "Return non-nil if POPUP is hidden."
  689. (let ((hidden t))
  690. (when (popup-live-p popup)
  691. (dotimes (i (popup-height popup))
  692. (unless (popup-line-hidden-p popup i)
  693. (setq hidden nil))))
  694. hidden))
  695. (defun popup-jump (popup cursor)
  696. "Jump to a position specified by CURSOR of POPUP and draw."
  697. (let ((scroll-top (popup-scroll-top popup)))
  698. ;; Do not change page as much as possible.
  699. (unless (and (<= scroll-top cursor)
  700. (< cursor (+ scroll-top (popup-height popup))))
  701. (setf (popup-scroll-top popup) cursor))
  702. (setf (popup-cursor popup) cursor)
  703. (popup-draw popup)))
  704. (defun popup-select (popup i)
  705. "Select the item at I of POPUP and draw."
  706. (setq i (+ i (popup-offset popup)))
  707. (when (and (<= 0 i) (< i (popup-height popup)))
  708. (setf (popup-cursor popup) i)
  709. (popup-draw popup)
  710. t))
  711. (defun popup-next (popup)
  712. "Select the next item of POPUP and draw."
  713. (let ((height (popup-height popup))
  714. (cursor (1+ (popup-cursor popup)))
  715. (scroll-top (popup-scroll-top popup))
  716. (length (length (popup-list popup))))
  717. (cond
  718. ((>= cursor length)
  719. ;; Back to first page
  720. (setq cursor 0
  721. scroll-top 0))
  722. ((= cursor (+ scroll-top height))
  723. ;; Go to next page
  724. (setq scroll-top (min (1+ scroll-top) (max (- length height) 0)))))
  725. (setf (popup-cursor popup) cursor
  726. (popup-scroll-top popup) scroll-top)
  727. (popup-draw popup)))
  728. (defun popup-previous (popup)
  729. "Select the previous item of POPUP and draw."
  730. (let ((height (popup-height popup))
  731. (cursor (1- (popup-cursor popup)))
  732. (scroll-top (popup-scroll-top popup))
  733. (length (length (popup-list popup))))
  734. (cond
  735. ((< cursor 0)
  736. ;; Go to last page
  737. (setq cursor (1- length)
  738. scroll-top (max (- length height) 0)))
  739. ((= cursor (1- scroll-top))
  740. ;; Go to previous page
  741. (cl-decf scroll-top)))
  742. (setf (popup-cursor popup) cursor
  743. (popup-scroll-top popup) scroll-top)
  744. (popup-draw popup)))
  745. (defun popup-page-next (popup)
  746. "Select next item of POPUP per `popup-height' range.
  747. Pages down through POPUP."
  748. (dotimes (counter (1- (popup-height popup)))
  749. (popup-next popup)))
  750. (defun popup-page-previous (popup)
  751. "Select previous item of POPUP per `popup-height' range.
  752. Pages up through POPUP."
  753. (dotimes (counter (1- (popup-height popup)))
  754. (popup-previous popup)))
  755. (defun popup-scroll-down (popup &optional n)
  756. "Scroll down N of POPUP and draw."
  757. (let ((scroll-top (min (+ (popup-scroll-top popup) (or n 1))
  758. (- (length (popup-list popup)) (popup-height popup)))))
  759. (setf (popup-cursor popup) scroll-top
  760. (popup-scroll-top popup) scroll-top)
  761. (popup-draw popup)))
  762. (defun popup-scroll-up (popup &optional n)
  763. "Scroll up N of POPUP and draw."
  764. (let ((scroll-top (max (- (popup-scroll-top popup) (or n 1))
  765. 0)))
  766. (setf (popup-cursor popup) scroll-top
  767. (popup-scroll-top popup) scroll-top)
  768. (popup-draw popup)))
  769. ;;; Popup Incremental Search
  770. (defface popup-isearch-match
  771. '((t (:inherit default :background "sky blue")))
  772. "Popup isearch match face."
  773. :group 'popup)
  774. (defvar popup-isearch-cursor-color "blue")
  775. (defvar popup-isearch-keymap
  776. (let ((map (make-sparse-keymap)))
  777. ;(define-key map "\r" 'popup-isearch-done)
  778. (define-key map "\C-g" 'popup-isearch-cancel)
  779. (define-key map "\C-b" 'popup-isearch-close)
  780. (define-key map [left] 'popup-isearch-close)
  781. (define-key map "\C-h" 'popup-isearch-delete)
  782. (define-key map (kbd "DEL") 'popup-isearch-delete)
  783. (define-key map (kbd "C-y") 'popup-isearch-yank)
  784. map))
  785. (defvar popup-menu-show-quick-help-function 'popup-menu-show-quick-help
  786. "Function used for showing quick help by `popup-menu*'.")
  787. (defcustom popup-isearch-regexp-builder-function #'regexp-quote
  788. "Function used to construct a regexp from a pattern. You may for instance
  789. provide a function that replaces spaces by '.+' if you like helm or ivy style
  790. of completion."
  791. :type 'function)
  792. (defsubst popup-isearch-char-p (char)
  793. (and (integerp char)
  794. (<= 32 char)
  795. (<= char 126)))
  796. (defun popup-isearch-filter-list (pattern list)
  797. (cl-loop with regexp = (funcall popup-isearch-regexp-builder-function pattern)
  798. for item in list
  799. do
  800. (unless (stringp item)
  801. (setq item (popup-item-propertize (popup-x-to-string item)
  802. 'value item)))
  803. if (string-match regexp item)
  804. collect
  805. (let ((beg (match-beginning 0))
  806. (end (match-end 0)))
  807. (alter-text-property 0 (length item) 'face
  808. (lambda (prop)
  809. (unless (eq prop 'popup-isearch-match)
  810. prop))
  811. item)
  812. (put-text-property beg end
  813. 'face 'popup-isearch-match
  814. item)
  815. item)))
  816. (defun popup-isearch-prompt (popup pattern)
  817. (format "Pattern: %s" (if (= (length (popup-list popup)) 0)
  818. (propertize pattern 'face 'isearch-fail)
  819. pattern)))
  820. (defun popup-isearch-update (popup filter pattern &optional callback)
  821. (setf (popup-cursor popup) 0
  822. (popup-scroll-top popup) 0
  823. (popup-pattern popup) pattern)
  824. (let ((list (funcall filter pattern (popup-original-list popup))))
  825. (popup-set-filtered-list popup list)
  826. (if callback
  827. (funcall callback list)))
  828. (popup-draw popup))
  829. (cl-defun popup-isearch (popup
  830. &key
  831. (filter 'popup-isearch-filter-list)
  832. (cursor-color popup-isearch-cursor-color)
  833. (keymap popup-isearch-keymap)
  834. callback
  835. help-delay)
  836. "Start isearch on POPUP. This function is synchronized, meaning
  837. event loop waits for quiting of isearch.
  838. FILTER is function with two argumenst to perform popup items filtering.
  839. CURSOR-COLOR is a cursor color during isearch. The default value
  840. is `popup-isearch-cursor-color'.
  841. KEYMAP is a keymap which is used when processing events during
  842. event loop. The default value is `popup-isearch-keymap'.
  843. CALLBACK is a function taking one argument. `popup-isearch' calls
  844. CALLBACK, if specified, after isearch finished or isearch
  845. canceled. The arguments is whole filtered list of items.
  846. HELP-DELAY is a delay of displaying helps."
  847. (let ((list (popup-original-list popup))
  848. (pattern (or (popup-pattern popup) ""))
  849. (old-cursor-color (frame-parameter (selected-frame) 'cursor-color))
  850. prompt key binding)
  851. (unwind-protect
  852. (cl-block nil
  853. (if cursor-color
  854. (set-cursor-color cursor-color))
  855. (while t
  856. (setq prompt (popup-isearch-prompt popup pattern))
  857. (setq key (popup-menu-read-key-sequence keymap prompt help-delay))
  858. (if (null key)
  859. (unless (funcall popup-menu-show-quick-help-function popup nil :prompt prompt)
  860. (clear-this-command-keys)
  861. (push (read-event prompt) unread-command-events))
  862. (setq binding (lookup-key keymap key))
  863. (cond
  864. ((and (stringp key)
  865. (popup-isearch-char-p (aref key 0)))
  866. (setq pattern (concat pattern key)))
  867. ((eq binding 'popup-isearch-done)
  868. (cl-return nil))
  869. ((eq binding 'popup-isearch-cancel)
  870. (popup-isearch-update popup filter "" callback)
  871. (cl-return t))
  872. ((eq binding 'popup-isearch-close)
  873. (popup-isearch-update popup filter "" callback)
  874. (setq unread-command-events
  875. (append (listify-key-sequence key) unread-command-events))
  876. (cl-return nil))
  877. ((eq binding 'popup-isearch-delete)
  878. (if (> (length pattern) 0)
  879. (setq pattern (substring pattern 0 (1- (length pattern))))))
  880. ((eq binding 'popup-isearch-yank)
  881. (popup-isearch-update popup filter (car kill-ring) callback)
  882. (cl-return nil))
  883. (t
  884. (setq unread-command-events
  885. (append (listify-key-sequence key) unread-command-events))
  886. (cl-return nil)))
  887. (popup-isearch-update popup filter pattern callback))))
  888. (if old-cursor-color
  889. (set-cursor-color old-cursor-color)))))
  890. ;;; Popup Tip
  891. (defface popup-tip-face
  892. '((t (:background "khaki1" :foreground "black")))
  893. "Face for popup tip."
  894. :group 'popup)
  895. (defvar popup-tip-max-width 80)
  896. (cl-defun popup-tip (string
  897. &key
  898. point
  899. (around t)
  900. width
  901. (height 15)
  902. min-height
  903. max-width
  904. truncate
  905. margin
  906. margin-left
  907. margin-right
  908. scroll-bar
  909. parent
  910. parent-offset
  911. nowait
  912. nostrip
  913. prompt
  914. &aux tip lines)
  915. "Show a tooltip of STRING at POINT. This function is
  916. synchronized unless NOWAIT specified. Almost all arguments are
  917. the same as in `popup-create', except for TRUNCATE, NOWAIT, and
  918. PROMPT.
  919. If TRUNCATE is non-nil, the tooltip can be truncated.
  920. If NOWAIT is non-nil, this function immediately returns the
  921. tooltip instance without entering event loop.
  922. If `NOSTRIP` is non-nil, `STRING` properties are not stripped.
  923. PROMPT is a prompt string when reading events during event loop."
  924. (if (bufferp string)
  925. (setq string (with-current-buffer string (buffer-string))))
  926. (unless nostrip
  927. ;; TODO strip text (mainly face) properties
  928. (setq string (substring-no-properties string)))
  929. (and (eq margin t) (setq margin 1))
  930. (or margin-left (setq margin-left margin))
  931. (or margin-right (setq margin-right margin))
  932. (let ((it (popup-fill-string string width popup-tip-max-width)))
  933. (setq width (car it)
  934. lines (cdr it)))
  935. (setq tip (popup-create point width height
  936. :min-height min-height
  937. :max-width max-width
  938. :around around
  939. :margin-left margin-left
  940. :margin-right margin-right
  941. :scroll-bar scroll-bar
  942. :face 'popup-tip-face
  943. :parent parent
  944. :parent-offset parent-offset))
  945. (unwind-protect
  946. (when (> (popup-width tip) 0) ; not to be corrupted
  947. (when (and (not (eq width (popup-width tip))) ; truncated
  948. (not truncate))
  949. ;; Refill once again to lines be fitted to popup width
  950. (setq width (popup-width tip))
  951. (setq lines (cdr (popup-fill-string string width width))))
  952. (popup-set-list tip lines)
  953. (popup-draw tip)
  954. (if nowait
  955. tip
  956. (clear-this-command-keys)
  957. (push (read-event prompt) unread-command-events)
  958. t))
  959. (unless nowait
  960. (popup-delete tip))))
  961. ;;; Popup Menu
  962. (defface popup-menu-face
  963. '((t (:inherit popup-face)))
  964. "Face for popup menu."
  965. :group 'popup)
  966. (defface popup-menu-mouse-face
  967. '((t (:background "blue" :foreground "white")))
  968. "Face for popup menu."
  969. :group 'popup)
  970. (defface popup-menu-selection-face
  971. '((t (:inherit default :background "steelblue" :foreground "white")))
  972. "Face for popup menu selection."
  973. :group 'popup)
  974. (defface popup-menu-summary-face
  975. '((t (:inherit popup-summary-face)))
  976. "Face for popup summary."
  977. :group 'popup)
  978. (defvar popup-menu-show-tip-function 'popup-tip
  979. "Function used for showing tooltip by `popup-menu-show-quick-help'.")
  980. (defun popup-menu-show-help (menu &optional persist item)
  981. (popup-item-show-help (or item (popup-selected-item menu)) persist))
  982. (defun popup-menu-documentation (menu &optional item)
  983. (popup-item-documentation (or item (popup-selected-item menu))))
  984. (defun popup-menu-show-quick-help (menu &optional item &rest args)
  985. (let* ((point (plist-get args :point))
  986. (height (or (plist-get args :height) (popup-height menu)))
  987. (min-height (min height (popup-current-height menu)))
  988. (around nil)
  989. (parent-offset (popup-offset menu))
  990. (doc (popup-menu-documentation menu item)))
  991. (when (stringp doc)
  992. (if (popup-hidden-p menu)
  993. (setq around t
  994. menu nil
  995. parent-offset nil)
  996. (setq point nil))
  997. (let ((popup-use-optimized-column-computation nil)) ; To avoid wrong positioning
  998. (apply popup-menu-show-tip-function
  999. doc
  1000. :point point
  1001. :height height
  1002. :min-height min-height
  1003. :around around
  1004. :parent menu
  1005. :parent-offset parent-offset
  1006. args)))))
  1007. (defun popup-menu-item-of-mouse-event (event)
  1008. (when (and (consp event)
  1009. (memq (cl-first event) '(mouse-1 mouse-2 mouse-3 mouse-4 mouse-5)))
  1010. (let* ((position (cl-second event))
  1011. (object (elt position 4)))
  1012. (when (consp object)
  1013. (get-text-property (cdr object) 'popup-item (car object))))))
  1014. (defun popup-menu-read-key-sequence (keymap &optional prompt timeout)
  1015. (catch 'timeout
  1016. (let ((timer (and timeout
  1017. (run-with-timer timeout nil
  1018. (lambda ()
  1019. (if (zerop (length (this-command-keys)))
  1020. (throw 'timeout nil))))))
  1021. (old-global-map (current-global-map))
  1022. (temp-global-map (make-sparse-keymap))
  1023. (overriding-terminal-local-map (make-sparse-keymap)))
  1024. (substitute-key-definition 'keyboard-quit 'keyboard-quit
  1025. temp-global-map old-global-map)
  1026. (define-key temp-global-map [menu-bar] (lookup-key old-global-map [menu-bar]))
  1027. (define-key temp-global-map [tool-bar] (lookup-key old-global-map [tool-bar]))
  1028. (set-keymap-parent overriding-terminal-local-map keymap)
  1029. (if (current-local-map)
  1030. (define-key overriding-terminal-local-map [menu-bar]
  1031. (lookup-key (current-local-map) [menu-bar])))
  1032. (unwind-protect
  1033. (progn
  1034. (use-global-map temp-global-map)
  1035. (clear-this-command-keys)
  1036. (with-temp-message prompt
  1037. (read-key-sequence nil)))
  1038. (use-global-map old-global-map)
  1039. (if timer (cancel-timer timer))))))
  1040. (defun popup-menu-fallback (event default))
  1041. (cl-defun popup-menu-event-loop (menu
  1042. keymap
  1043. fallback
  1044. &key
  1045. prompt
  1046. help-delay
  1047. isearch
  1048. isearch-filter
  1049. isearch-cursor-color
  1050. isearch-keymap
  1051. isearch-callback
  1052. &aux key binding)
  1053. (cl-block nil
  1054. (while (popup-live-p menu)
  1055. (and isearch
  1056. (popup-isearch menu
  1057. :filter isearch-filter
  1058. :cursor-color isearch-cursor-color
  1059. :keymap isearch-keymap
  1060. :callback isearch-callback
  1061. :help-delay help-delay)
  1062. (keyboard-quit))
  1063. (setq key (popup-menu-read-key-sequence keymap prompt help-delay))
  1064. (setq binding (and key (lookup-key keymap key)))
  1065. (cond
  1066. ((or (null key) (zerop (length key)))
  1067. (unless (funcall popup-menu-show-quick-help-function menu nil :prompt prompt)
  1068. (clear-this-command-keys)
  1069. (push (read-event prompt) unread-command-events)))
  1070. ((eq (lookup-key (current-global-map) key) 'keyboard-quit)
  1071. (keyboard-quit)
  1072. (cl-return))
  1073. ((eq binding 'popup-close)
  1074. (if (popup-parent menu)
  1075. (cl-return)))
  1076. ((memq binding '(popup-select popup-open))
  1077. (let* ((item (or (popup-menu-item-of-mouse-event (elt key 0))
  1078. (popup-selected-item menu)))
  1079. (index (cl-position item (popup-list menu)))
  1080. (sublist (popup-item-sublist item)))
  1081. (unless index (cl-return))
  1082. (if sublist
  1083. (popup-aif (let (popup-use-optimized-column-computation)
  1084. (popup-cascade-menu sublist
  1085. :around nil
  1086. :margin-left (popup-margin-left menu)
  1087. :margin-right (popup-margin-right menu)
  1088. :scroll-bar (popup-scroll-bar menu)
  1089. :parent menu
  1090. :parent-offset index
  1091. :help-delay help-delay
  1092. :isearch isearch
  1093. :isearch-filter isearch-filter
  1094. :isearch-cursor-color isearch-cursor-color
  1095. :isearch-keymap isearch-keymap
  1096. :isearch-callback isearch-callback))
  1097. (and it (cl-return it)))
  1098. (if (eq binding 'popup-select)
  1099. (cl-return (popup-item-value-or-self item))))))
  1100. ((eq binding 'popup-next)
  1101. (popup-next menu))
  1102. ((eq binding 'popup-previous)
  1103. (popup-previous menu))
  1104. ((eq binding 'popup-page-next)
  1105. (popup-page-next menu))
  1106. ((eq binding 'popup-page-previous)
  1107. (popup-page-previous menu))
  1108. ((eq binding 'popup-help)
  1109. (popup-menu-show-help menu))
  1110. ((eq binding 'popup-isearch)
  1111. (popup-isearch menu
  1112. :filter isearch-filter
  1113. :cursor-color isearch-cursor-color
  1114. :keymap isearch-keymap
  1115. :callback isearch-callback
  1116. :help-delay help-delay))
  1117. ((commandp binding)
  1118. (call-interactively binding))
  1119. (t
  1120. (funcall fallback key (key-binding key)))))))
  1121. (defun popup-preferred-width (list)
  1122. "Return the preferred width to show LIST beautifully."
  1123. (cl-loop with tab-width = 4
  1124. for item in list
  1125. for summary = (popup-item-summary item)
  1126. maximize (string-width (popup-x-to-string item)) into width
  1127. if (stringp summary)
  1128. maximize (+ (string-width summary) 2) into summary-width
  1129. finally return
  1130. (let ((total (+ (or width 0) (or summary-width 0))))
  1131. (* (ceiling (/ total 10.0)) 10))))
  1132. (defvar popup-menu-keymap
  1133. (let ((map (make-sparse-keymap)))
  1134. (define-key map "\r" 'popup-select)
  1135. (define-key map "\C-f" 'popup-open)
  1136. (define-key map [right] 'popup-open)
  1137. (define-key map "\C-b" 'popup-close)
  1138. (define-key map [left] 'popup-close)
  1139. (define-key map "\C-n" 'popup-next)
  1140. (define-key map [down] 'popup-next)
  1141. (define-key map "\C-p" 'popup-previous)
  1142. (define-key map [up] 'popup-previous)
  1143. (define-key map [next] 'popup-page-next)
  1144. (define-key map [prior] 'popup-page-previous)
  1145. (define-key map [f1] 'popup-help)
  1146. (define-key map (kbd "\C-?") 'popup-help)
  1147. (define-key map "\C-s" 'popup-isearch)
  1148. (define-key map [mouse-1] 'popup-select)
  1149. (define-key map [mouse-4] 'popup-previous)
  1150. (define-key map [mouse-5] 'popup-next)
  1151. map))
  1152. (cl-defun popup-menu* (list
  1153. &key
  1154. point
  1155. (around t)
  1156. (width (popup-preferred-width list))
  1157. (height 15)
  1158. max-width
  1159. margin
  1160. margin-left
  1161. margin-right
  1162. scroll-bar
  1163. symbol
  1164. parent
  1165. parent-offset
  1166. cursor
  1167. (keymap popup-menu-keymap)
  1168. (fallback 'popup-menu-fallback)
  1169. help-delay
  1170. nowait
  1171. prompt
  1172. isearch
  1173. (isearch-filter 'popup-isearch-filter-list)
  1174. (isearch-cursor-color popup-isearch-cursor-color)
  1175. (isearch-keymap popup-isearch-keymap)
  1176. isearch-callback
  1177. initial-index
  1178. &aux menu event)
  1179. "Show a popup menu of LIST at POINT. This function returns a
  1180. value of the selected item. Almost all arguments are the same as in
  1181. `popup-create', except for KEYMAP, FALLBACK, HELP-DELAY, PROMPT,
  1182. ISEARCH, ISEARCH-FILTER, ISEARCH-CURSOR-COLOR, ISEARCH-KEYMAP, and
  1183. ISEARCH-CALLBACK.
  1184. If KEYMAP is a keymap which is used when processing events during
  1185. event loop.
  1186. If FALLBACK is a function taking two arguments; a key and a
  1187. command. FALLBACK is called when no special operation is found on
  1188. the key. The default value is `popup-menu-fallback', which does
  1189. nothing.
  1190. HELP-DELAY is a delay of displaying helps.
  1191. If NOWAIT is non-nil, this function immediately returns the menu
  1192. instance without entering event loop.
  1193. PROMPT is a prompt string when reading events during event loop.
  1194. If ISEARCH is non-nil, do isearch as soon as displaying the popup
  1195. menu.
  1196. ISEARCH-FILTER is a filtering function taking two arguments:
  1197. search pattern and list of items. Returns a list of matching items.
  1198. ISEARCH-CURSOR-COLOR is a cursor color during isearch. The
  1199. default value is `popup-isearch-cursor-color'.
  1200. ISEARCH-KEYMAP is a keymap which is used when processing events
  1201. during event loop. The default value is `popup-isearch-keymap'.
  1202. ISEARCH-CALLBACK is a function taking one argument. `popup-menu'
  1203. calls ISEARCH-CALLBACK, if specified, after isearch finished or
  1204. isearch canceled. The arguments is whole filtered list of items.
  1205. If `INITIAL-INDEX' is non-nil, this is an initial index value for
  1206. `popup-select'. Only positive integer is valid."
  1207. (and (eq margin t) (setq margin 1))
  1208. (or margin-left (setq margin-left margin))
  1209. (or margin-right (setq margin-right margin))
  1210. (if (and scroll-bar
  1211. (integerp margin-right)
  1212. (> margin-right 0))
  1213. ;; Make scroll-bar space as margin-right
  1214. (cl-decf margin-right))
  1215. (setq menu (popup-create point width height
  1216. :max-width max-width
  1217. :around around
  1218. :face 'popup-menu-face
  1219. :mouse-face 'popup-menu-mouse-face
  1220. :selection-face 'popup-menu-selection-face
  1221. :summary-face 'popup-menu-summary-face
  1222. :margin-left margin-left
  1223. :margin-right margin-right
  1224. :scroll-bar scroll-bar
  1225. :symbol symbol
  1226. :parent parent
  1227. :parent-offset parent-offset))
  1228. (unwind-protect
  1229. (progn
  1230. (popup-set-list menu list)
  1231. (if cursor
  1232. (popup-jump menu cursor)
  1233. (popup-draw menu))
  1234. (when initial-index
  1235. (dotimes (_i (min (- (length list) 1) initial-index))
  1236. (popup-next menu)))
  1237. (if nowait
  1238. menu
  1239. (popup-menu-event-loop menu keymap fallback
  1240. :prompt prompt
  1241. :help-delay help-delay
  1242. :isearch isearch
  1243. :isearch-filter isearch-filter
  1244. :isearch-cursor-color isearch-cursor-color
  1245. :isearch-keymap isearch-keymap
  1246. :isearch-callback isearch-callback)))
  1247. (unless nowait
  1248. (popup-delete menu))))
  1249. (defun popup-cascade-menu (list &rest args)
  1250. "Same as `popup-menu' except that an element of LIST can be
  1251. also a sub-menu if the element is a cons cell formed (ITEM
  1252. . SUBLIST) where ITEM is an usual item and SUBLIST is a list of
  1253. the sub menu."
  1254. (apply 'popup-menu*
  1255. (mapcar (lambda (item)
  1256. (if (consp item)
  1257. (popup-make-item (car item)
  1258. :sublist (cdr item)
  1259. :symbol ">")
  1260. item))
  1261. list)
  1262. :symbol t
  1263. args))
  1264. (provide 'popup)
  1265. ;;; popup.el ends here