Personal emacs config
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.

1435 lines
54 KiB

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