Path: blob/master/elisp/emacs-for-python/auto-complete/popup.el
990 views
;;; popup.el --- Visual popup interface12;; Copyright (C) 2009, 2010 Tomohiro Matsuyama34;; Author: Tomohiro Matsuyama <[email protected]>5;; Keywords: lisp6;; Version: 0.478;; This program is free software; you can redistribute it and/or modify9;; it under the terms of the GNU General Public License as published by10;; the Free Software Foundation, either version 3 of the License, or11;; (at your option) any later version.1213;; This program is distributed in the hope that it will be useful,14;; but WITHOUT ANY WARRANTY; without even the implied warranty of15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16;; GNU General Public License for more details.1718;; You should have received a copy of the GNU General Public License19;; along with this program. If not, see <http://www.gnu.org/licenses/>.2021;;; Commentary:2223;;2425;;; Code:2627(eval-when-compile28(require 'cl))29303132;; Utilities3334(defvar popup-use-optimized-column-computation t35"Use optimized column computation routine.36If there is a problem, please set it to nil.")3738;; Borrowed from anything.el39(defmacro popup-aif (test-form then-form &rest else-forms)40"Anaphoric if. Temporary variable `it' is the result of test-form."41(declare (indent 2))42`(let ((it ,test-form))43(if it ,then-form ,@else-forms)))4445(defun popup-x-to-string (x)46"Convert any object to string effeciently.47This is faster than prin1-to-string in many cases."48(typecase x49(string x)50(symbol (symbol-name x))51(integer (number-to-string x))52(float (number-to-string x))53(t (format "%s" x))))5455(defun popup-substring-by-width (string width)56"Return cons of substring and remaining string by `WIDTH'."57;; Expand tabs with 4 spaces58(setq string (replace-regexp-in-string "\t" " " string))59(loop with len = (length string)60with w = 061for l from 062for c in (append string nil)63while (<= (incf w (char-width c)) width)64finally return65(if (< l len)66(cons (substring string 0 l) (substring string l))67(list string))))6869(defun popup-fill-string (string &optional width max-width justify squeeze)70"Split STRING into fixed width strings and return a cons cell like71\(WIDTH . ROWS). Here, the car WIDTH indicates the actual maxim width of ROWS.7273The argument WIDTH specifies the width of filling each paragraph. WIDTH nil74means don't perform any justification and word wrap. Note that this function75doesn't add any padding characters at the end of each row.7677MAX-WIDTH, if WIDTH is nil, specifies the maximum number of columns.7879The optional fourth argument JUSTIFY specifies which kind of justification80to do: `full', `left', `right', `center', or `none' (equivalent to nil).81A value of t means handle each paragraph as specified by its text properties.8283SQUEEZE nil means leave whitespaces other than line breaks untouched."84(if (eq width 0)85(error "Can't fill string with 0 width"))86(if width87(setq max-width width))88(with-temp-buffer89(let ((tab-width 4)90(fill-column width)91(left-margin 0)92(kinsoku-limit 1)93indent-tabs-mode94row rows)95(insert string)96(untabify (point-min) (point-max))97(if width98(fill-region (point-min) (point-max) justify (not squeeze)))99(goto-char (point-min))100(setq width 0)101(while (prog2102(let ((line (buffer-substring103(point) (progn (end-of-line) (point)))))104(if max-width105(while (progn106(setq row (truncate-string-to-width line max-width)107width (max width (string-width row)))108(push row rows)109(if (not (= (length row) (length line)))110(setq line (substring line (length row))))))111(setq width (max width (string-width line)))112(push line rows)))113(< (point) (point-max))114(beginning-of-line 2)))115(cons width (nreverse rows)))))116117(defmacro popup-save-buffer-state (&rest body)118(declare (indent 0))119`(save-excursion120(let ((buffer-undo-list t)121(buffer-read-only nil)122(modified (buffer-modified-p)))123(unwind-protect124(progn ,@body)125(set-buffer-modified-p modified)))))126127(defun popup-preferred-width (list)128"Return preferred width of popup to show `LIST' beautifully."129(loop with tab-width = 4130for item in list131for summary = (popup-item-summary item)132maximize (string-width (popup-x-to-string item)) into width133if (stringp summary)134maximize (+ (string-width summary) 2) into summary-width135finally return (* (ceiling (/ (+ (or width 0) (or summary-width 0)) 10.0)) 10)))136137;; window-full-width-p is not defined in Emacs 22.1138(defun popup-window-full-width-p (&optional window)139(if (fboundp 'window-full-width-p)140(window-full-width-p window)141(= (window-width window) (frame-width (window-frame (or window (selected-window)))))))142143;; truncated-partial-width-window-p is not defined in Emacs 22144(defun popup-truncated-partial-width-window-p (&optional window)145(unless window146(setq window (selected-window)))147(unless (popup-window-full-width-p window)148(let ((t-p-w-w (buffer-local-value 'truncate-partial-width-windows149(window-buffer window))))150(if (integerp t-p-w-w)151(< (window-width window) t-p-w-w)152t-p-w-w))))153154(defun popup-current-physical-column ()155(or (when (and popup-use-optimized-column-computation156(eq (window-hscroll) 0))157(let ((current-column (current-column)))158(if (or (popup-truncated-partial-width-window-p)159truncate-lines160(< current-column (window-width)))161current-column)))162(car (posn-col-row (posn-at-point)))))163164(defun popup-last-line-of-buffer-p ()165(save-excursion (end-of-line) (/= (forward-line) 0)))166167(defun popup-lookup-key-by-event (function event)168(or (funcall function (vector event))169(if (symbolp event)170(popup-aif (get event 'event-symbol-element-mask)171(funcall function (vector (logior (or (get (car it) 'ascii-character) 0)172(cadr it))))))))173174175176;; Popup common177178(defgroup popup nil179"Visual popup interface"180:group 'lisp181:prefix "popup-")182183(defface popup-face184'((t (:background "lightgray" :foreground "black")))185"Face for popup."186:group 'popup)187188(defface popup-scroll-bar-foreground-face189'((t (:background "black")))190"Foreground face for scroll-bar."191:group 'popup)192193(defface popup-scroll-bar-background-face194'((t (:background "gray")))195"Background face for scroll-bar."196:group 'popup)197198(defvar popup-instances nil199"Popup instances.")200201(defvar popup-scroll-bar-foreground-char202(propertize " " 'face 'popup-scroll-bar-foreground-face)203"Foreground character for scroll-bar.")204205(defvar popup-scroll-bar-background-char206(propertize " " 'face 'popup-scroll-bar-background-face)207"Background character for scroll-bar.")208209(defstruct popup210point row column width height min-height direction overlays211parent depth212face selection-face213margin-left margin-right margin-left-cancel scroll-bar symbol214cursor offset scroll-top current-height list newlines215pattern original-list)216217(defun popup-item-propertize (item &rest properties)218"Same to `propertize` but this avoids overriding existed value with `nil` property."219(let (props)220(while properties221(when (cadr properties)222(push (car properties) props)223(push (cadr properties) props))224(setq properties (cddr properties)))225(apply 'propertize226(popup-x-to-string item)227(nreverse props))))228229(defun popup-item-property (item property)230(if (stringp item)231(get-text-property 0 property item)))232233(defun* popup-make-item (name234&key235value236popup-face237selection-face238sublist239document240symbol241summary)242"Utility function to make popup item.243See also `popup-item-propertize'."244(popup-item-propertize name245'value value246'popup-face popup-face247'selection-face selection-face248'document document249'symbol symbol250'summary summary251'sublist sublist))252253(defsubst popup-item-value (item) (popup-item-property item 'value))254(defsubst popup-item-value-or-self (item) (or (popup-item-value item) item))255(defsubst popup-item-popup-face (item) (popup-item-property item 'popup-face))256(defsubst popup-item-selection-face (item) (popup-item-property item 'selection-face))257(defsubst popup-item-document (item) (popup-item-property item 'document))258(defsubst popup-item-summary (item) (popup-item-property item 'summary))259(defsubst popup-item-symbol (item) (popup-item-property item 'symbol))260(defsubst popup-item-sublist (item) (popup-item-property item 'sublist))261262(defun popup-item-documentation (item)263(let ((doc (popup-item-document item)))264(if (functionp doc)265(setq doc (funcall doc (popup-item-value-or-self item))))266doc))267268(defun popup-item-show-help-1 (item)269(let ((doc (popup-item-documentation item)))270(when doc271(with-current-buffer (get-buffer-create " *Popup Help*")272(erase-buffer)273(insert doc)274(goto-char (point-min))275(display-buffer (current-buffer)))276t)))277278(defun popup-item-show-help (item &optional persist)279(when item280(if (not persist)281(save-window-excursion282(when (popup-item-show-help-1 item)283(block nil284(while t285(clear-this-command-keys)286(let ((key (read-key-sequence-vector nil)))287(case (key-binding key)288('scroll-other-window289(scroll-other-window))290('scroll-other-window-down291(scroll-other-window-down nil))292(t293(setq unread-command-events (append key unread-command-events))294(return))))))))295(popup-item-show-help-1 item))))296297(defun popup-set-list (popup list)298(popup-set-filtered-list popup list)299(setf (popup-pattern popup) nil)300(setf (popup-original-list popup) list))301302(defun popup-set-filtered-list (popup list)303(setf (popup-list popup) list304(popup-offset popup) (if (> (popup-direction popup) 0)3050306(max (- (popup-height popup) (length list)) 0))))307308(defun popup-selected-item (popup)309(nth (popup-cursor popup) (popup-list popup)))310311(defun popup-selected-line (popup)312(- (popup-cursor popup) (popup-scroll-top popup)))313314(defun popup-line-overlay (popup line)315(aref (popup-overlays popup) line))316317(defun popup-selected-line-overlay (popup)318(popup-line-overlay popup (popup-selected-line popup)))319320(defun popup-hide-line (popup line)321(let ((overlay (popup-line-overlay popup line)))322(overlay-put overlay 'display nil)323(overlay-put overlay 'after-string nil)))324325(defun popup-line-hidden-p (popup line)326(let ((overlay (popup-line-overlay popup line)))327(and (eq (overlay-get overlay 'display) nil)328(eq (overlay-get overlay 'after-string) nil))))329330(defun popup-set-line-item (popup line item face margin-left margin-right scroll-bar-char symbol summary)331(let* ((overlay (popup-line-overlay popup line))332(content (popup-create-line-string popup (popup-x-to-string item) margin-left margin-right symbol summary))333(start 0)334(prefix (overlay-get overlay 'prefix))335(postfix (overlay-get overlay 'postfix))336end)337;; Overlap face properties338(if (get-text-property start 'face content)339(setq start (next-single-property-change start 'face content)))340(while (and start (setq end (next-single-property-change start 'face content)))341(put-text-property start end 'face face content)342(setq start (next-single-property-change end 'face content)))343(if start344(put-text-property start (length content) 'face face content))345(unless (overlay-get overlay 'dangle)346(overlay-put overlay 'display (concat prefix (substring content 0 1)))347(setq prefix nil348content (concat (substring content 1))))349(overlay-put overlay350'after-string351(concat prefix352content353scroll-bar-char354postfix))))355356(defun popup-create-line-string (popup string margin-left margin-right symbol summary)357(let* ((popup-width (popup-width popup))358(summary-width (string-width summary))359(string (car (popup-substring-by-width string360(- popup-width361(if (> summary-width 0)362(+ summary-width 2)3630)))))364(string-width (string-width string)))365(concat margin-left366string367(make-string (max (- popup-width string-width summary-width) 0) ? )368summary369symbol370margin-right)))371372(defun popup-live-p (popup)373(and popup (popup-overlays popup) t))374375(defun popup-child-point (popup &optional offset)376(overlay-end (popup-line-overlay popup377(or offset378(popup-selected-line popup)))))379380(defun* popup-create (point381width382height383&key384min-height385around386(face 'popup-face)387(selection-face face)388scroll-bar389margin-left390margin-right391symbol392parent393parent-offset)394(or margin-left (setq margin-left 0))395(or margin-right (setq margin-right 0))396(unless point397(setq point398(if parent (popup-child-point parent parent-offset) (point))))399400(save-excursion401(goto-char point)402(let* ((row (line-number-at-pos))403(column (popup-current-physical-column))404(overlays (make-vector height nil))405(popup-width (+ width406(if scroll-bar 1 0)407margin-left408margin-right409(if symbol 2 0)))410margin-left-cancel411(window (selected-window))412(window-start (window-start))413(window-hscroll (window-hscroll))414(window-width (window-width))415(right (+ column popup-width))416(overflow (and (> right window-width)417(>= right popup-width)))418(foldable (and (null parent)419(>= column popup-width)))420(direction (or421;; Currently the direction of cascade popup won't be changed422(and parent (popup-direction parent))423424;; Calculate direction425(if (and (> row height)426(> height (- (max 1 (- (window-height)427(if mode-line-format 1 0)428(if header-line-format 1 0)))429(count-lines window-start (point)))))430-14311)))432(depth (if parent (1+ (popup-depth parent)) 0))433(newlines (max 0 (+ (- height (count-lines point (point-max))) (if around 1 0))))434current-column)435(when (> newlines 0)436(popup-save-buffer-state437(goto-char (point-max))438(insert (make-string newlines ?\n))))439440(if overflow441(if foldable442(progn443(decf column (- popup-width margin-left margin-right))444(unless around (move-to-column column)))445(when (not truncate-lines)446;; Cut out overflow447(let ((d (1+ (- popup-width (- window-width column)))))448(decf popup-width d)449(decf width d)))450(decf column margin-left))451(decf column margin-left))452(when (and (null parent)453(< column 0))454;; Cancel margin left455(setq column 0)456(decf popup-width margin-left)457(setq margin-left-cancel t))458459(dotimes (i height)460(let (overlay begin w (dangle t) (prefix "") (postfix ""))461(when around462(if (>= emacs-major-version 23)463(vertical-motion (cons column direction))464(vertical-motion direction)465(move-to-column (+ (current-column) column))))466(setq around t467current-column (popup-current-physical-column))468469(when (> current-column column)470(backward-char)471(setq current-column (popup-current-physical-column)))472(when (< current-column column)473;; Extend short buffer lines by popup prefix (line of spaces)474(setq prefix (make-string (+ (if (= current-column 0)475(- window-hscroll (current-column))4760)477(- column current-column))478? )))479480(setq begin (point))481(setq w (+ popup-width (length prefix)))482(while (and (not (eolp)) (> w 0))483(setq dangle nil)484(decf w (char-width (char-after)))485(forward-char))486(if (< w 0)487(setq postfix (make-string (- w) ? )))488489(setq overlay (make-overlay begin (point)))490(overlay-put overlay 'window window)491(overlay-put overlay 'dangle dangle)492(overlay-put overlay 'prefix prefix)493(overlay-put overlay 'postfix postfix)494(overlay-put overlay 'width width)495(aset overlays496(if (> direction 0) i (- height i 1))497overlay)))498(loop for p from (- 10000 (* depth 1000))499for overlay in (nreverse (append overlays nil))500do (overlay-put overlay 'priority p))501(let ((it (make-popup :point point502:row row503:column column504:width width505:height height506:min-height min-height507:direction direction508:parent parent509:depth depth510:face face511:selection-face selection-face512:margin-left margin-left513:margin-right margin-right514:margin-left-cancel margin-left-cancel515:scroll-bar scroll-bar516:symbol symbol517:cursor 0518:scroll-top 0519:current-height 0520:list nil521:newlines newlines522:overlays overlays)))523(push it popup-instances)524it))))525526(defun popup-delete (popup)527(when (popup-live-p popup)528(popup-hide popup)529(mapc 'delete-overlay (popup-overlays popup))530(setf (popup-overlays popup) nil)531(setq popup-instances (delq popup popup-instances))532(let ((newlines (popup-newlines popup)))533(when (> newlines 0)534(popup-save-buffer-state535(goto-char (point-max))536(dotimes (i newlines)537(if (= (char-before) ?\n)538(delete-char -1)))))))539nil)540541(defun popup-draw (popup)542(loop with height = (popup-height popup)543with min-height = (popup-min-height popup)544with popup-face = (popup-face popup)545with selection-face = (popup-selection-face popup)546with list = (popup-list popup)547with length = (length list)548with thum-size = (max (/ (* height height) (max length 1)) 1)549with page-size = (/ (+ 0.0 (max length 1)) height)550with scroll-bar = (popup-scroll-bar popup)551with margin-left = (make-string (if (popup-margin-left-cancel popup) 0 (popup-margin-left popup)) ? )552with margin-right = (make-string (popup-margin-right popup) ? )553with symbol = (popup-symbol popup)554with cursor = (popup-cursor popup)555with scroll-top = (popup-scroll-top popup)556with offset = (popup-offset popup)557for o from offset558for i from scroll-top559while (< o height)560for item in (nthcdr scroll-top list)561for page-index = (* thum-size (/ o thum-size))562for face = (if (= i cursor)563(or (popup-item-selection-face item) selection-face)564(or (popup-item-popup-face item) popup-face))565for empty-char = (propertize " " 'face face)566for scroll-bar-char = (if scroll-bar567(cond568((<= page-size 1)569empty-char)570((and (> page-size 1)571(>= cursor (* page-index page-size))572(< cursor (* (+ page-index thum-size) page-size)))573popup-scroll-bar-foreground-char)574(t575popup-scroll-bar-background-char))576"")577for sym = (if symbol578(concat " " (or (popup-item-symbol item) " "))579"")580for summary = (or (popup-item-summary item) "")581582do583;; Show line and set item to the line584(popup-set-line-item popup o item face margin-left margin-right scroll-bar-char sym summary)585586finally587;; Remember current height588(setf (popup-current-height popup) (- o offset))589590;; Hide remaining lines591(let ((scroll-bar-char (if scroll-bar (propertize " " 'face popup-face) ""))592(symbol (if symbol " " "")))593(if (> (popup-direction popup) 0)594(progn595(when min-height596(while (< o min-height)597(popup-set-line-item popup o "" popup-face margin-left margin-right scroll-bar-char symbol "")598(incf o)))599(while (< o height)600(popup-hide-line popup o)601(incf o)))602(loop with h = (if min-height (- height min-height) offset)603for o from 0 below offset604if (< o h)605do (popup-hide-line popup o)606if (>= o h)607do (popup-set-line-item popup o "" popup-face margin-left margin-right scroll-bar-char symbol ""))))))608609(defun popup-hide (popup)610(dotimes (i (popup-height popup))611(popup-hide-line popup i)))612613(defun popup-hidden-p (popup)614(let ((hidden t))615(when (popup-live-p popup)616(dotimes (i (popup-height popup))617(unless (popup-line-hidden-p popup i)618(setq hidden nil))))619hidden))620621(defun popup-select (popup i)622(setq i (+ i (popup-offset popup)))623(when (and (<= 0 i) (< i (popup-height popup)))624(setf (popup-cursor popup) i)625(popup-draw popup)626t))627628(defun popup-next (popup)629(let ((height (popup-height popup))630(cursor (1+ (popup-cursor popup)))631(scroll-top (popup-scroll-top popup))632(length (length (popup-list popup))))633(cond634((>= cursor length)635;; Back to first page636(setq cursor 0637scroll-top 0))638((= cursor (+ scroll-top height))639;; Go to next page640(setq scroll-top (min (1+ scroll-top) (max (- length height) 0)))))641(setf (popup-cursor popup) cursor642(popup-scroll-top popup) scroll-top)643(popup-draw popup)))644645(defun popup-previous (popup)646(let ((height (popup-height popup))647(cursor (1- (popup-cursor popup)))648(scroll-top (popup-scroll-top popup))649(length (length (popup-list popup))))650(cond651((< cursor 0)652;; Go to last page653(setq cursor (1- length)654scroll-top (max (- length height) 0)))655((= cursor (1- scroll-top))656;; Go to previous page657(decf scroll-top)))658(setf (popup-cursor popup) cursor659(popup-scroll-top popup) scroll-top)660(popup-draw popup)))661662(defun popup-scroll-down (popup &optional n)663(let ((scroll-top (min (+ (popup-scroll-top popup) (or n 1))664(- (length (popup-list popup)) (popup-height popup)))))665(setf (popup-cursor popup) scroll-top666(popup-scroll-top popup) scroll-top)667(popup-draw popup)))668669(defun popup-scroll-up (popup &optional n)670(let ((scroll-top (max (- (popup-scroll-top popup) (or n 1))6710)))672(setf (popup-cursor popup) scroll-top673(popup-scroll-top popup) scroll-top)674(popup-draw popup)))675676677678;; Popup isearch679680(defface popup-isearch-match681'((t (:background "sky blue")))682"Popup isearch match face."683:group 'popup)684685(defvar popup-isearch-cursor-color "blue")686687(defvar popup-isearch-keymap688(let ((map (make-sparse-keymap)))689;(define-key map "\r" 'popup-isearch-done)690(define-key map "\C-g" 'popup-isearch-cancel)691(define-key map "\C-h" 'popup-isearch-delete)692(define-key map (kbd "DEL") 'popup-isearch-delete)693map))694695(defsubst popup-isearch-char-p (char)696(and (integerp char)697(<= 32 char)698(<= char 126)))699700(defun popup-isearch-filter-list (pattern list)701(loop with regexp = (regexp-quote pattern)702for item in list703do704(unless (stringp item)705(setq item (popup-item-propertize (popup-x-to-string item)706'value item)))707if (string-match regexp item)708collect (let ((beg (match-beginning 0))709(end (match-end 0)))710(alter-text-property 0 (length item) 'face711(lambda (prop)712(unless (eq prop 'popup-isearch-match)713prop))714item)715(put-text-property beg end716'face 'popup-isearch-match717item)718item)))719720(defun popup-isearch-prompt (popup pattern)721(format "Pattern: %s" (if (= (length (popup-list popup)) 0)722(propertize pattern 'face 'isearch-fail)723pattern)))724725(defun popup-isearch-update (popup pattern &optional callback)726(setf (popup-cursor popup) 0727(popup-scroll-top popup) 0728(popup-pattern popup) pattern)729(let ((list (popup-isearch-filter-list pattern (popup-original-list popup))))730(popup-set-filtered-list popup list)731(if callback732(funcall callback list)))733(popup-draw popup))734735(defun* popup-isearch (popup736&key737(cursor-color popup-isearch-cursor-color)738(keymap popup-isearch-keymap)739callback740help-delay)741(let ((list (popup-original-list popup))742(pattern (or (popup-pattern popup) ""))743(old-cursor-color (frame-parameter (selected-frame) 'cursor-color))744prompt key binding done)745(unwind-protect746(unless (block nil747(if cursor-color748(set-cursor-color cursor-color))749(while t750(setq prompt (popup-isearch-prompt popup pattern))751(setq key (popup-menu-read-key-sequence keymap prompt help-delay))752(if (null key)753(unless (funcall popup-menu-show-quick-help-function popup nil :prompt prompt)754(clear-this-command-keys)755(push (read-event prompt) unread-command-events))756(setq binding (lookup-key keymap key))757(cond758((and (stringp key)759(popup-isearch-char-p (aref key 0)))760(setq pattern (concat pattern key)))761((eq binding 'popup-isearch-done)762(return t))763((eq binding 'popup-isearch-cancel)764(return nil))765((eq binding 'popup-isearch-delete)766(if (> (length pattern) 0)767(setq pattern (substring pattern 0 (1- (length pattern))))))768(t769(setq unread-command-events770(append (listify-key-sequence key) unread-command-events))771(return t)))772(popup-isearch-update popup pattern callback))))773(popup-isearch-update popup "" callback)774t) ; Return non-nil if isearch is cancelled775(if old-cursor-color776(set-cursor-color old-cursor-color)))))777778779780;; Popup tip781782(defface popup-tip-face783'((t (:background "khaki1" :foreground "black")))784"Face for popup tip."785:group 'popup)786787(defvar popup-tip-max-width 80)788789(defun* popup-tip (string790&key791point792(around t)793width794(height 15)795min-height796truncate797margin798margin-left799margin-right800scroll-bar801parent802parent-offset803nowait804prompt805&aux tip lines)806(if (bufferp string)807(setq string (with-current-buffer string (buffer-string))))808;; TODO strip text (mainly face) properties809(setq string (substring-no-properties string))810811(and (eq margin t) (setq margin 1))812(or margin-left (setq margin-left margin))813(or margin-right (setq margin-right margin))814815(let ((it (popup-fill-string string width popup-tip-max-width)))816(setq width (car it)817lines (cdr it)))818819(setq tip (popup-create point width height820:min-height min-height821:around around822:margin-left margin-left823:margin-right margin-right824:scroll-bar scroll-bar825:face 'popup-tip-face826:parent parent827:parent-offset parent-offset))828829(unwind-protect830(when (> (popup-width tip) 0) ; not to be corrupted831(when (and (not (eq width (popup-width tip))) ; truncated832(not truncate))833;; Refill once again to lines be fitted to popup width834(setq width (popup-width tip))835(setq lines (cdr (popup-fill-string string width width))))836837(popup-set-list tip lines)838(popup-draw tip)839(if nowait840tip841(clear-this-command-keys)842(push (read-event prompt) unread-command-events)843t))844(unless nowait845(popup-delete tip))))846847848849;; Popup menu850851(defface popup-menu-face852'((t (:background "lightgray" :foreground "black")))853"Face for popup menu."854:group 'popup)855856(defface popup-menu-selection-face857'((t (:background "steelblue" :foreground "white")))858"Face for popup menu selection."859:group 'popup)860861(defvar popup-menu-show-tip-function 'popup-tip862"Function used for showing tooltip by `popup-menu-show-quick-help'.")863864(defvar popup-menu-show-quick-help-function 'popup-menu-show-quick-help865"Function used for showing quick help by `popup-menu*'.")866867(defun popup-menu-show-help (menu &optional persist item)868(popup-item-show-help (or item (popup-selected-item menu)) persist))869870(defun popup-menu-documentation (menu &optional item)871(popup-item-documentation (or item (popup-selected-item menu))))872873(defun popup-menu-show-quick-help (menu &optional item &rest args)874(let* ((point (plist-get args :point))875(height (or (plist-get args :height) (popup-height menu)))876(min-height (min height (popup-current-height menu)))877(around nil)878(parent-offset (popup-offset menu))879(doc (popup-menu-documentation menu item)))880(when (stringp doc)881(if (popup-hidden-p menu)882(setq around t883menu nil884parent-offset nil)885(setq point nil))886(let ((popup-use-optimized-column-computation nil)) ; To avoid wrong positioning887(apply popup-menu-show-tip-function888doc889:point point890:height height891:min-height min-height892:around around893:parent menu894:parent-offset parent-offset895args)))))896897(defun popup-menu-read-key-sequence (keymap &optional prompt timeout)898(catch 'timeout899(let ((timer (and timeout900(run-with-timer timeout nil901(lambda ()902(if (zerop (length (this-command-keys)))903(throw 'timeout nil))))))904(old-global-map (current-global-map))905(temp-global-map (make-sparse-keymap))906(overriding-terminal-local-map (make-sparse-keymap)))907(substitute-key-definition 'keyboard-quit 'keyboard-quit908temp-global-map old-global-map)909(define-key temp-global-map [menu-bar] (lookup-key old-global-map [menu-bar]))910(define-key temp-global-map [tool-bar] (lookup-key old-global-map [tool-bar]))911(set-keymap-parent overriding-terminal-local-map keymap)912(if (current-local-map)913(define-key overriding-terminal-local-map [menu-bar]914(lookup-key (current-local-map) [menu-bar])))915(unwind-protect916(progn917(use-global-map temp-global-map)918(clear-this-command-keys)919(with-temp-message prompt920(read-key-sequence nil)))921(use-global-map old-global-map)922(if timer (cancel-timer timer))))))923924(defun popup-menu-fallback (event default))925926(defun* popup-menu-event-loop (menu keymap fallback &optional prompt help-delay isearch isearch-cursor-color isearch-keymap isearch-callback &aux key binding)927(block nil928(while (popup-live-p menu)929(and isearch930(popup-isearch menu931:cursor-color isearch-cursor-color932:keymap isearch-keymap933:callback isearch-callback934:help-delay help-delay)935(keyboard-quit))936(setq key (popup-menu-read-key-sequence keymap prompt help-delay))937(if (null key)938(unless (funcall popup-menu-show-quick-help-function menu nil :prompt prompt)939(clear-this-command-keys)940(push (read-event prompt) unread-command-events))941(if (eq (lookup-key (current-global-map) key) 'keyboard-quit)942(keyboard-quit))943(setq binding (lookup-key keymap key))944(cond945((eq binding 'popup-close)946(if (popup-parent menu)947(return)))948((memq binding '(popup-select popup-open))949(let* ((item (popup-selected-item menu))950(sublist (popup-item-sublist item)))951(if sublist952(popup-aif (popup-cascade-menu sublist953:around nil954:parent menu955:margin-left (popup-margin-left menu)956:margin-right (popup-margin-right menu)957:scroll-bar (popup-scroll-bar menu))958(and it (return it)))959(if (eq binding 'popup-select)960(return (popup-item-value-or-self item))))))961((eq binding 'popup-next)962(popup-next menu))963((eq binding 'popup-previous)964(popup-previous menu))965((eq binding 'popup-help)966(popup-menu-show-help menu))967((eq binding 'popup-isearch)968(popup-isearch menu969:cursor-color isearch-cursor-color970:keymap isearch-keymap971:callback isearch-callback972:help-delay help-delay))973((commandp binding)974(call-interactively binding))975(t976(funcall fallback key (key-binding key))))))))977978;; popup-menu is used by mouse.el unfairly...979(defun* popup-menu* (list980&key981point982(around t)983(width (popup-preferred-width list))984(height 15)985margin986margin-left987margin-right988scroll-bar989symbol990parent991parent-offset992(keymap popup-menu-keymap)993(fallback 'popup-menu-fallback)994help-delay995prompt996isearch997(isearch-cursor-color popup-isearch-cursor-color)998(isearch-keymap popup-isearch-keymap)999isearch-callback1000&aux menu event)1001(and (eq margin t) (setq margin 1))1002(or margin-left (setq margin-left margin))1003(or margin-right (setq margin-right margin))1004(if (and scroll-bar1005(integerp margin-right)1006(> margin-right 0))1007;; Make scroll-bar space as margin-right1008(decf margin-right))1009(setq menu (popup-create point width height1010:around around1011:face 'popup-menu-face1012:selection-face 'popup-menu-selection-face1013:margin-left margin-left1014:margin-right margin-right1015:scroll-bar scroll-bar1016:symbol symbol1017:parent parent))1018(unwind-protect1019(progn1020(popup-set-list menu list)1021(popup-draw menu)1022(popup-menu-event-loop menu keymap fallback prompt help-delay isearch1023isearch-cursor-color isearch-keymap isearch-callback))1024(popup-delete menu)))10251026(defun popup-cascade-menu (list &rest args)1027"Same to `popup-menu', but an element of `LIST' can be1028list of submenu."1029(apply 'popup-menu*1030(mapcar (lambda (item)1031(if (consp item)1032(popup-make-item (car item)1033:sublist (cdr item)1034:symbol ">")1035item))1036list)1037:symbol t1038args))10391040(defvar popup-menu-keymap1041(let ((map (make-sparse-keymap)))1042(define-key map "\r" 'popup-select)1043(define-key map "\C-f" 'popup-open)1044(define-key map [right] 'popup-open)1045(define-key map "\C-b" 'popup-close)1046(define-key map [left] 'popup-close)10471048(define-key map "\C-n" 'popup-next)1049(define-key map [down] 'popup-next)1050(define-key map "\C-p" 'popup-previous)1051(define-key map [up] 'popup-previous)10521053(define-key map [f1] 'popup-help)1054(define-key map (kbd "\C-?") 'popup-help)10551056(define-key map "\C-s" 'popup-isearch)1057map))10581059(provide 'popup)1060;;; popup.el ends here106110621063