1 ;;; tpum.el --- Popup menus in text mode.
3 ;; Copyright (C) 2003,2004,2005 by Zajcev Evgeny.
5 ;; Author: Zajcev Evgeny <zevlg@yandex.ru>
6 ;; Maintainer: none, if you want be a maintainer please e-mail me.
7 ;; Temporary maintainer is Zajcev Evgeny.
9 ;; Keywords: tools, menus
10 ;; X-CVS: $Id: tpum.el,v 1.1 2005-04-15 21:10:48 lg Exp $
12 ;; This file is NOT part of XEmacs.
14 ;; tpum.el is free software; you can redistribute it and/or modify it
15 ;; under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; tpum.el is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 ;; General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with XEmacs; see the file COPYING. If not, write to the Free
26 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29 ;;; Synched up with: Not in FSF
33 ;; This package aimed to bring popup menus to be more usable for
34 ;; keyboard friendly and mouse unfriendly people like me. On the one
35 ;; hand I like popup menus, because they just perfect and easy
36 ;; interface for selective task, but on the other hand I hate them,
37 ;; because I need to find my trackball, click ugly buttons, twiddle
38 ;; ball to access menu item. This pretty annoys. Also popup menus
39 ;; are not available in console. tpum.el package makes popup menu
40 ;; inlined in buffer, or creates transient XEmacs frame with temporary
41 ;; buffer, where menu will be inlined. So this makes possible to
42 ;; control popup menus using keyboard and makes possible to popup
43 ;; menus in console. To start using tpum.el just include in your
46 ;; To enable tpum globally:
49 ;; (tpum-global-mode 1)
51 ;; Or if you don't want tpum to be enable globally (default) do
54 ;; (autoload 'tpum-minor-mode "Toggle tpum minor mode." t)
56 ;; And then enable tpum minor mode by `M-x tpum-minor-mode RET'.
58 ;; To define new keys in order to emulate mouse events use something
61 ;; (define-tpum-key global-map (kbd "s-3") (kbd "<button3>"))
62 ;; (define-tpum-key global-map (kbd "C-s-3") (kbd "C-<button3>"))
63 ;; (define-tpum-key global-map (kbd "C-s-1") (kbd "C-<button1>"))
64 ;; (define-tpum-key global-map (kbd "s-!") (kbd "Sh-<button1>"))
65 ;; (define-tpum-key global-map (kbd "s-#") (kbd "Sh-<button3>"))
69 ;; To change tpum styles use:
71 ;; (setq tpum-cstyle 'tpum-style-pseudo)
72 ;; (setq tpum-menu-type 'inline)
74 ;; Best menu redering done if you are using fixed width fonts with
75 ;; pseudo-graphic characters and 'tpum-style-pseudo as tpum style.
79 ;; * With setnu-mode if poping up menu while point at the beginning
80 ;; of line, setnu's numbers gets invisible.
82 ;; * If some menu uses broken :suffix keyword, tpum may render menu
85 ;; * Control characters may temporaly change their displying type.
89 ;; - Document code more carefuly
90 ;; - Improve docstrings
91 ;; - If it is possible to avoid overriding local map - avoid it
100 "Popup menus in text mode."
104 (defcustom tpum-global-mode nil
105 "*Non-nil mean tpum is enabled globally."
109 (defgroup tpum-faces nil
110 "Faces used in tpum."
115 (defface tpum-active-face
116 '((((background light))
117 (:foreground "black"))
119 (:foreground "brightwhite")))
120 "*Face for displaying activated menu items."
123 (defface tpum-deactive-face
124 '((((background dark))
125 (:foreground "darkgrey"))
126 (((background light))
127 (:foreground "darkgrey")))
128 "*Face for displaying deactivated menu items."
131 (defface tpum-title-face
132 '((((type tty) (class color))
133 (:foreground "red" :bold t))
137 (((type x) (class color))
138 (:foreground "red4" :bold t))
141 "*Face to display title of menu."
144 (defface tpum-toggled-face
146 "*Face for toggle items, whos state is on."
149 (defcustom tpum-menu-type 'inline
150 "Type of `popup-menu' behaviour.
151 'inline - To inline in current buffer.
152 'frame - To pop Emacs frame."
153 :type '(choice (const :tag "Inline" inline)
154 (const :tag "Frame" frame))
157 (defcustom tpum-auto-submenu-mode nil
158 "*Non-nil mean auto opening submenus at point."
162 (defcustom tpum-isearch-global-scope t
163 "*Non-nil mean that isearch will be performed in any shown tpums."
167 (defcustom tpum-truncate-lines nil
168 "*Value of `truncate-lines' while in `tpum-mode'."
172 (defcustom tpum-cstyle 'tpum-style-plain
177 (unless (fboundp 'get-face)
178 (defalias 'get-face 'identity))
180 (defface tpum-plain-face1
182 (:foreground "blue" :bold t)))
183 "Border face for plain style."
186 (defvar tpum-plain-face (get-face 'tpum-plain-face1))
188 (defface tpum-pseudo-face1
190 (:foreground "blue" :bold t))
192 "Border face for pseudo style."
195 (defvar tpum-pseudo-face (get-face 'tpum-pseudo-face1))
197 (defvar tpum-frame-face (get-face 'blue))
199 (defcustom tpum-max-height 80
200 "*Maximum height of popup menu."
204 (defcustom tpum-search-ahead-mode t
205 "*Non-nil mean to use type ahead search mode."
209 (defcustom tpum-load-hook nil
210 "*Hooks to run after tpum loaded."
215 (unless (fboundp 'set-keymap-default-binding)
216 (defun set-keymap-default-binding (map cmd)
217 (define-key map [t] cmd)))
219 (defvar tpum-mode-map
220 (let ((map (make-sparse-keymap)))
221 (set-keymap-default-binding map 'tpum-default-command)
222 (define-key map (kbd "C-u") 'universal-argument)
223 (define-key map (kbd "C-1") 'digit-argument)
224 (define-key map (kbd "C-2") 'digit-argument)
225 (define-key map (kbd "C-3") 'digit-argument)
226 (define-key map (kbd "C-4") 'digit-argument)
227 (define-key map (kbd "C-5") 'digit-argument)
228 (define-key map (kbd "C-6") 'digit-argument)
229 (define-key map (kbd "C-7") 'digit-argument)
230 (define-key map (kbd "C-8") 'digit-argument)
231 (define-key map (kbd "C-9") 'digit-argument)
232 (define-key map (kbd "C-0") 'digit-argument)
233 (define-key map (kbd "M-1") 'digit-argument)
234 (define-key map (kbd "M-2") 'digit-argument)
235 (define-key map (kbd "M-3") 'digit-argument)
236 (define-key map (kbd "M-4") 'digit-argument)
237 (define-key map (kbd "M-5") 'digit-argument)
238 (define-key map (kbd "M-6") 'digit-argument)
239 (define-key map (kbd "M-7") 'digit-argument)
240 (define-key map (kbd "M-8") 'digit-argument)
241 (define-key map (kbd "M-9") 'digit-argument)
242 (define-key map (kbd "M-0") 'digit-argument)
244 (define-key map (kbd "C-g") 'tpum-quit)
245 (define-key map (kbd "C-G") 'tpum-global-quit)
247 (define-key map (kbd "C-l") 'recenter)
248 (define-key map (kbd "C-n") 'tpum-next)
249 (define-key map (kbd "<up>") 'tpum-prev)
250 (define-key map (kbd "C-p") 'tpum-prev)
251 (define-key map (kbd "<down>") 'tpum-next)
252 (define-key map (kbd "M->") 'tpum-goto-last)
253 (define-key map (kbd "M-<") 'tpum-goto-first)
255 (define-key map (kbd "M-RET") 'tpum-submenu-toggle)
256 (define-key map (kbd "C-RET") 'tpum-submenu-toggle)
257 (define-key map (kbd "M-t") 'tpum-auto-submenu-toggle)
258 (define-key map (kbd "RET") 'tpum-select)
259 ; (define-key map (kbd "M-s") 'tpum-submenu-show)
260 ; (define-key map (kbd "M-c") 'tpum-submenu-hide)
263 (define-key map (kbd "C-t") 'tpum-isearch-global-toggle)
264 (define-key map (kbd "C-s") 'isearch-forward)
265 (define-key map (kbd "C-r") 'isearch-backward)
268 (define-key map (kbd "<f1>") 'tpum-help)
269 (define-key map (kbd "C-h") 'tpum-help)
270 (define-key map (kbd "M-h") 'tpum-describe-bindings)
271 (define-key map (kbd "M-x") 'execute-extended-command)
272 (define-key map (kbd "M-:") 'eval-expression)
273 (define-key map (kbd "C-x 1") 'delete-other-windows)
275 "Keymap used while in tpum.")
278 (defun tpum-da-face (isactive)
279 "Return face according to ISACTIVE."
280 (if isactive 'tpum-active-face 'tpum-deactive-face))
282 (defstruct tpum-style
283 border-face ; face used to draw borders
284 toggle-dis toggle-act radio-dis radio-act submenu
285 title-sep title-sl title-sr
286 left right top bottom
288 right-top right-bottom
289 right-bot2 left-bot2 line-bot2 center-bot2 ;when menu is not fully displayed
292 single-line double-line
293 single-dashed-line double-dashed-line
295 right-sl right-dl right-sdl right-ddl right-nl
296 left-sl left-dl left-sdl left-ddl left-nl)
298 (defvar tpum-style-plain
300 :border-face tpum-plain-face
301 :toggle-dis "[ ] " :toggle-act "[x] " :radio-dis "( ) " :radio-act "(*) "
303 :title-sep "=" :title-sl "|" :title-sr "|"
304 :left "|" :right "|" :top "-" :bottom "-"
305 :left-top "." :left-bottom "`" :right-top "." :right-bottom "'"
306 :right-bot2 "|" :left-bot2 "|" :line-bot2 "." :center-bot2 "vvv"
308 :single-line "-" :double-line "=" :single-dashed-line "- -"
309 :double-dashed-line "= =" :no-line " "
310 :right-sl "|" :right-dl "|" :right-sdl "|" :right-ddl "|" :right-nl "|"
311 :left-sl "|" :left-dl "|" :left-sdl "|" :left-ddl "|" :left-nl "|")
314 (defvar tpum-style-pseudo
316 :border-face tpum-pseudo-face
317 :toggle-dis "[ ] " :toggle-act "[x] " :radio-dis "( ) " :radio-act "(*) "
319 :title-sep "=" :title-sl "\x15" :title-sr "\x16"
320 :left "\x19" :right "\x19" :top "\x12" :bottom "\x12"
321 :left-top "\x0d" :left-bottom "\x0e" :right-top "\x0c" :right-bottom "\x0b"
322 :right-bot2 "|" :left-bot2 "|" :line-bot2 "." :center-bot2 "vvv"
324 :single-line "\x12" :double-line "=" :single-dashed-line "- -"
325 :double-dashed-line "= =" :no-line " "
326 :right-sl "\x16" :right-dl "\x16" :right-sdl "|" :right-ddl "|" :right-nl "|"
327 :left-sl "\x15" :left-dl "\x15" :left-sdl "|" :left-ddl "|" :left-nl "|")
328 "Pseudo graphics style.")
330 (defvar tpum-style-frame
332 :border-face tpum-frame-face
333 :toggle-dis "[ ] " :toggle-act "[x] " :radio-dis "( ) " :radio-act "(*) "
335 :title-sep "=" :title-sl "" :title-sr ""
336 :left "" :right "" :top "-" :bottom "-"
337 :left-top "" :left-bottom "" :right-top "" :right-bottom ""
338 :right-bot2 "" :left-bot2 "" :line-bot2 "" :center-bot2 "vvv"
340 :single-line "-" :double-line "=" :single-dashed-line "- -"
341 :double-dashed-line "= =" :no-line " "
342 :right-sl "" :right-dl "" :right-sdl "" :right-ddl "" :right-nl ""
343 :left-sl "" :left-dl "" :left-sdl "" :left-ddl "" :left-nl "")
344 "Separate frame style.")
346 (defun tpum-style-toggle (&optional act)
347 "Return toggle button, if ACT non-nil toggle button is on."
349 (tpum-style-toggle-act (eval tpum-cstyle))
350 (tpum-style-toggle-dis (eval tpum-cstyle))))
352 (defun tpum-style-radio (&optional act)
353 "Return radio button, if ACT is non-nil - radio button is on."
355 (tpum-style-radio-act (eval tpum-cstyle))
356 (tpum-style-radio-dis (eval tpum-cstyle))))
358 (defmacro tpum-defmacro (new old)
359 "Define NEW accesor using OLD accessor."
361 (,old (eval tpum-cstyle))))
363 (tpum-defmacro tpum-st-bface tpum-style-border-face)
364 (tpum-defmacro tpum-st-td tpum-style-toggle-dis)
365 (tpum-defmacro tpum-st-ta tpum-style-toggle-act)
366 (tpum-defmacro tpum-st-rd tpum-style-radio-dis)
367 (tpum-defmacro tpum-st-ra tpum-style-radio-act)
368 (tpum-defmacro tpum-st-sub tpum-style-submenu)
369 (tpum-defmacro tpum-st-ts tpum-style-title-sep)
370 (tpum-defmacro tpum-st-tsl tpum-style-title-sl)
371 (tpum-defmacro tpum-st-tsr tpum-style-title-sr)
372 (tpum-defmacro tpum-st-l tpum-style-left)
373 (tpum-defmacro tpum-st-r tpum-style-right)
374 (tpum-defmacro tpum-st-t tpum-style-top)
375 (tpum-defmacro tpum-st-b tpum-style-bottom)
376 (tpum-defmacro tpum-st-lt tpum-style-left-top)
377 (tpum-defmacro tpum-st-lb tpum-style-left-bottom)
378 (tpum-defmacro tpum-st-lb2 tpum-style-left-bot2)
379 (tpum-defmacro tpum-st-rt tpum-style-right-top)
380 (tpum-defmacro tpum-st-rb tpum-style-right-bottom)
381 (tpum-defmacro tpum-st-rb2 tpum-style-right-bot2)
382 (tpum-defmacro tpum-st-lbot2 tpum-style-line-bot2)
383 (tpum-defmacro tpum-st-cbot2 tpum-style-center-bot2)
384 (tpum-defmacro tpum-st-sl tpum-style-single-line)
385 (tpum-defmacro tpum-st-dl tpum-style-double-line)
386 (tpum-defmacro tpum-st-sdl tpum-style-single-dashed-line)
387 (tpum-defmacro tpum-st-ddl tpum-style-double-dashed-line)
388 (tpum-defmacro tpum-st-noline tpum-style-no-line)
389 (tpum-defmacro tpum-st-rsl tpum-style-right-sl)
390 (tpum-defmacro tpum-st-rdl tpum-style-right-dl)
391 (tpum-defmacro tpum-st-rsdl tpum-style-right-sdl)
392 (tpum-defmacro tpum-st-rddl tpum-style-right-ddl)
393 (tpum-defmacro tpum-st-rnl tpum-style-right-nl)
394 (tpum-defmacro tpum-st-lsl tpum-style-left-sl)
395 (tpum-defmacro tpum-st-ldl tpum-style-left-dl)
396 (tpum-defmacro tpum-st-lsdl tpum-style-left-sdl)
397 (tpum-defmacro tpum-st-lddl tpum-style-left-ddl)
398 (tpum-defmacro tpum-st-lnl tpum-style-left-nl)
401 local-mode-map local-mode-name
403 recursive-p post-command-hooks truncate-lines
404 after-change-functions before-change-functions
405 undo-list selective-display
406 buffer point tmpoint column width offset height atomics-list todel-list
407 frame child-ctx parent-ctx
409 plist) ; user defined plist
412 (defun tpum-plist-put (ctx prop val)
413 "In CTX put property PROP with value VAL."
414 (setf (tpum-ctx-plist ctx) (plist-put (tpum-ctx-plist ctx) prop val)))
416 (defun tpum-plist-get (ctx prop)
417 "In CTX get value of property PROP."
418 (plist-get (tpum-ctx-plist ctx) prop))
420 (defun tpum-plist-rem (ctx prop)
421 "In CTX remove property PROP."
422 (setf (tpum-ctx-plist ctx) (plist-remprop (tpum-ctx-plist ctx) prop)))
424 (defun tpum-delete-region (tpum-ctx start end)
425 "In TPUM-CTX delete region from START to END.
426 Check for overlaps inside todel-list in TPUM-CTX."
427 (push (cons start end) (tpum-ctx-todel-list tpum-ctx)))
429 (defun tpum-get-context (&optional point)
430 "Return tpum context at POINT."
431 (get-text-property (or point (point)) 'tpum-ctx))
433 (defun tpum-get-global-context (&optional tpum-ctx)
434 "Return global tpum context.
435 Optionally local tpum context TPUM-CTX can be specified."
436 (let ((ctx (or tpum-ctx (tpum-get-context))))
437 (while (tpum-get-context (tpum-ctx-point ctx))
438 (setq ctx (tpum-get-context (tpum-ctx-point ctx))))
441 (defun tpum-line-num (&optional pnt)
442 "Get number of line at PNT.
443 PNT default to `(point)'."
444 (let ((p (or pnt (point))))
445 (count-lines (point-min) p)))
447 (defun tpum-current-column ()
448 "Return current column skiping invisible chars."
453 (while (< (point) spnt)
454 (unless (and (get-text-property (point) 'invisible)
455 (not (get-text-property (point) 'tpum-invisible)))
460 (defun tpum-forward-char (num)
461 "Forward NUM chars skiping invisible chars."
462 (let ((fcfun (if (>= num 0) 'forward-char 'backward-char)))
466 ;; First skip any invisible characters at point
467 (while (get-text-property (point) 'invisible)
472 (unless (and (get-text-property (point) 'invisible)
473 (not (get-text-property (point) 'tpum-invisible)))
474 (setq num (1- num))))))
476 (defun tpum-next-line (num)
477 "Move NUM next lines in tpum's CTX."
478 (let ((ccol (tpum-current-column)))
480 (tpum-forward-char ccol)))
482 (defun tpum-move-to-offset (&optional tpum-context)
483 "In TPUM-CONTEXT move point to offset column."
484 (let ((ctx (or tpum-context (tpum-get-context))))
485 (when (tpum-ctx-p ctx)
487 (tpum-forward-char (+ (tpum-ctx-column ctx) (tpum-ctx-offset ctx) -1)))))
489 (defun tpum-get-mi (&optional point)
490 "Get menu item at POINT."
491 (get-text-property (or point (point)) 'tpum-menu-item))
493 (defun tpum-mi-help (mi)
494 "Display menu item MI help string in minibuffer."
495 (let ((help (tpum-get-keyword mi :help)))
499 (defun tpum-move-point (arg &optional ctx)
500 "Move point ARG times down.
501 If CTX is ommited `tpum-get-context' will be used."
502 (let ((tpum-ctx (or ctx (tpum-get-context)))
510 (tpum-next-line (if (> sarg 0) 1 -1))
511 (t (throw 'lout (setq lines-to-go 0))))
513 (when (not (eq (tpum-get-context) tpum-ctx))
514 (throw 'lout (setq lines-to-go 0)))
516 (let ((mi (tpum-get-mi)))
517 (when (and mi (tpum-mi-active-p tpum-ctx mi))
518 (setq arg (if (> arg 0) (1- arg) (1+ arg)))
520 (setq not-break nil))))
521 (setq lines-to-go (1+ lines-to-go)))
524 (if (= lines-to-go 0)
525 (error "TPUM: can't move")
526 (tpum-next-line (if (> sarg 0) lines-to-go (- lines-to-go))))
529 ;;; Interactive commands
531 "Show tpum help in minibuffer."
534 (substitute-command-keys
536 "`\\[tpum-describe-bindings]':bindings `\\[tpum-help]':help "
537 "`\\[tpum-next]':next `\\[tpum-prev]':prev `\\[tpum-quit]':quit"))))
539 (defun tpum-describe-bindings ()
540 "Describe TPUM bindings."
543 (with-displaying-help-buffer
545 (let ((local (current-local-map)))
546 (set-buffer standard-output)
547 (insert "Key Bindings for TPUM:\n")
548 (describe-bindings-internal local)))
549 "bindings for TPUM mode"))
551 (defun tpum-next (arg)
552 "Goto ARG next visible items."
557 (tpum-move-point arg)
558 (t (call-interactively 'tpum-goto-first)))
559 (tpum-mi-help (tpum-get-mi))
560 (when tpum-auto-submenu-mode
561 (tpum-submenu-show)))
563 (defun tpum-prev (arg)
564 "Goto ARG previous visible items."
569 (tpum-move-point (- arg))
570 (t (call-interactively 'tpum-goto-last)))
571 (tpum-mi-help (tpum-get-mi))
572 (when tpum-auto-submenu-mode
573 (tpum-submenu-show)))
575 (defun tpum-goto-first ()
576 "Go to the first menu item."
580 (while (condition-case nil
581 (progn (tpum-move-point -1) t)
583 (when tpum-auto-submenu-mode
585 (tpum-mi-help (tpum-get-mi)))
587 (defun tpum-goto-last ()
588 "Go to the last menu item."
592 (while (condition-case nil
593 (progn (tpum-move-point 1) t)
596 (when tpum-auto-submenu-mode
598 (tpum-mi-help (tpum-get-mi)))
600 (defun tpum-auto-submenu-toggle ()
601 "Toggle `tpum-auto-submenu-mode'."
603 (setq tpum-auto-submenu-mode (not tpum-auto-submenu-mode))
605 (if tpum-auto-submenu-mode
606 (message "tpum: Auto submenus mode on.")
607 (message "tpum: Auto submenus mode off."))
609 (when (interactive-p)
610 (if tpum-auto-submenu-mode
612 (tpum-submenu-hide))))
614 (defun tpum-isearch-global-toggle ()
615 "Toggle `tpum-isearch-global-scope'."
617 (setq tpum-isearch-global-scope (not tpum-isearch-global-scope))
618 (if tpum-isearch-global-scope
619 (message "tpum: Global isearch mode on.")
620 (message "tpum: Global isearch mode off.")))
622 (defvar tpum-isearch-mode nil
623 "Non nil mean that isearch uses tpum's searcher.")
625 (defun tpum-search-data (searcher what &optional bound noerror count)
626 "Use SEARCHER to search WHAT in tpum menu after or before point.
627 If SEARCHER contain 'backward' search performed before point.
628 BOUND, NOERROR and COUNT are described in `search-forward'."
629 (let* ((cctx (tpum-get-context))
632 (step (cond ((= cnt 0) 0)
634 (t (setq cnt (- cnt)) -1)))
635 found next sstart send mi)
637 (while (and (not found)
638 (setq next (funcall searcher what bound t step)))
639 (setq sstart (match-beginning 0)
646 (when (and (if tpum-isearch-global-scope
648 (eq (tpum-get-context) cctx))
649 mi (tpum-mi-active-p cctx mi))
655 ((= step (if (string-match "backward" (symbol-name searcher)) 1 -1))
661 ;; Setup the returned value and the `match-data' or maybe fail!
662 (funcall searcher what send noerror step)))
664 (defun tpum-search-forward (what &optional bound noerror count)
665 "Search tpum menu item forward from point for string WHAT.
666 For optional BOUND, NOERROR and COUNT see description for `search-forward'."
667 (tpum-search-data #'search-forward what bound noerror count))
669 (defun tpum-search-backward (what &optional bound noerror count)
670 "Search tpum menu item backward from point for string WHAT.
671 For optional BOUND, NOERROR and COUNT see description for `search-backward'."
672 (tpum-search-data #'search-backward what bound noerror count))
674 (defmacro tpum-define-search-advice (searcher)
675 "Advice the built-in SEARCHER function to do tpum search.
676 That is to call the tpum searcher when variables
677 `isearch-mode' and `tpum-isearch-mode' are non-nil."
678 (let ((tpum-searcher (intern (format "tpum-%s" searcher))))
679 `(defadvice ,searcher (around unused activate)
680 (if (and isearch-mode tpum-isearch-mode
681 ;; The following condition ensure to do a tpum
682 ;; search on the `isearch-string' only!
683 (string-equal (ad-get-arg 0) isearch-string))
684 (let ((old-isearch-mode tpum-isearch-mode))
686 ;; Temporarily set `tpum-isearch-mode' to
687 ;; nil to avoid an infinite recursive call of the
688 ;; tpum search function!
689 (setq tpum-isearch-mode nil
691 (funcall #',tpum-searcher
692 (ad-get-arg 0) ; string
693 (ad-get-arg 1) ; bound
694 (ad-get-arg 2) ; no-error
695 (ad-get-arg 3))) ; count
696 (setq tpum-isearch-mode old-isearch-mode)))
699 (tpum-define-search-advice search-forward)
700 (tpum-define-search-advice search-backward)
702 (defun tpum-isearch-end-hook ()
703 "To be used in `isearch-mode-end-hook'."
704 (when tpum-isearch-mode
705 ;; Hide submenu if needed
706 (when (eq (tpum-get-context)
708 (goto-char isearch-opoint)
711 (goto-char isearch-opoint)
712 (tpum-submenu-hide)))
714 (tpum-move-to-offset)
715 (when tpum-auto-submenu-mode
717 (tpum-mi-help (tpum-get-mi))))
719 (defun tpum-search-symbol (chr)
720 "Begin to search for CHR."
721 (let ((executing-kbd-macro t)) ; do not make isearch to be modal
722 (isearch-mode t nil nil (not (interactive-p)))
723 (isearch-process-search-string
725 (isearch-text-char-description chr))))
727 (defun tpum-default-command (keys)
728 "Search ahead after KEYS presses."
729 (interactive (list (this-command-keys)))
730 (let* ((evk (and (= (length keys) 1)
731 (key-press-event-p (aref keys 0))
732 (null (event-modifiers (aref keys 0)))
733 (event-key (aref keys 0))))
735 (if (and tpum-search-ahead-mode (characterp kchr))
736 (tpum-search-symbol kchr)
738 (if (eq (event-type (aref keys 0)) 'button-release)
739 ;; Button release event are ok
741 (signal 'undefined-keystroke-sequence (list keys))))))
743 (defun tpum-quit (&optional ctx)
744 "Exit tpum mode, using tpum CTX."
746 (let ((tpum-ctx (or ctx (tpum-get-context)))
747 (buffer-read-only nil))
749 (unless (tpum-ctx-p ctx)
752 (error "No tpum context at point")
754 (if (tpum-ctx-frame tpum-ctx)
755 (select-frame (tpum-ctx-frame tpum-ctx))
758 (tpum-restore-todel-atomics tpum-ctx))
760 (setq tpum-isearch-mode (cdr tpum-isearch-mode))
762 (setq post-command-hook (tpum-ctx-post-command-hooks tpum-ctx))
763 (setq truncate-lines (tpum-ctx-truncate-lines tpum-ctx))
764 (setq after-change-functions (tpum-ctx-after-change-functions tpum-ctx))
765 (setq before-change-functions (tpum-ctx-before-change-functions tpum-ctx))
766 (setq buffer-undo-list (tpum-ctx-undo-list tpum-ctx))
767 (setq selective-display (tpum-ctx-selective-display tpum-ctx))
769 (setq overriding-local-map
770 (tpum-ctx-overriding-local-map tpum-ctx))
771 (use-local-map (tpum-ctx-local-mode-map tpum-ctx))
772 (setq mode-name (tpum-ctx-local-mode-name tpum-ctx))
774 (goto-char (tpum-ctx-point tpum-ctx))
775 (set-buffer-modified-p nil)
777 ;; ctl-arrow used by pseudo style
778 (when (tpum-plist-get tpum-ctx 'ctl-arrow)
779 (setq ctl-arrow (tpum-plist-get tpum-ctx 'ctl-arrow)))
781 (when (tpum-ctx-recursive-p tpum-ctx)
782 (exit-recursive-edit))
784 ;; Generate fake misc-user event, to make imenu work
785 (setq unread-command-event
786 (make-event 'misc-user '(x 0 y 0 button 1 function ignore)))
788 (when (tpum-ctx-frame tpum-ctx)
789 (delete-frame (tpum-ctx-frame tpum-ctx)))
791 ;; Select parent frame if any
792 (let ((pctx (tpum-ctx-parent-ctx tpum-ctx)))
793 (when (tpum-ctx-p pctx)
794 ;; Unset child ctx and select parent's frame
795 (setf (tpum-ctx-child-ctx pctx) nil)
796 (when (tpum-ctx-frame pctx)
797 (focus-frame (tpum-ctx-frame pctx)))))
799 (when (interactive-p)
800 (message "TPUM exit.")))))
802 (defun tpum-global-quit ()
803 "Globally quit tpum."
805 (while (tpum-get-context)
808 (defun tpum-submenu-show ()
809 "Show submenu if any."
811 (let ((tmi (tpum-get-mi))
812 (tpum-auto-submenu-mode nil)
814 (when (and tmi (consp tmi))
817 (setq nctx (tpum-get-context)))
818 (setq pctx (tpum-ctx-parent-ctx nctx))
819 (when (and (tpum-ctx-p pctx) (tpum-ctx-frame pctx))
820 (focus-frame (tpum-ctx-frame pctx))))))
822 (defun tpum-submenu-hide ()
823 "Hide submenu if any."
825 (let* ((cctx (tpum-get-context))
826 (smctx (tpum-ctx-child-ctx cctx)))
827 (when (tpum-ctx-p smctx)
829 (tpum-quit smctx)))))
831 (defun tpum-submenu-toggle ()
832 "Show or hide submenu."
834 (if (tpum-ctx-child-ctx (tpum-get-context))
836 (tpum-submenu-show)))
838 (defun tpum-submenu-select ()
841 (let ((smctx (tpum-ctx-child-ctx (tpum-get-context))))
842 (when (tpum-ctx-p smctx)
843 (if (tpum-ctx-frame smctx)
844 (focus-frame (tpum-ctx-frame smctx))
846 (goto-char (tpum-ctx-tmpoint smctx))
847 (tpum-move-point 1 smctx)
848 (tpum-move-to-offset smctx))
850 (when tpum-auto-submenu-mode
851 (tpum-submenu-show)))))
853 (defun tpum-apply-callback (cbfun)
854 "Apply menuitem's call back function CBFUN."
855 (cond ((listp cbfun) (eval cbfun))
856 ((commandp cbfun) (command-execute cbfun))
857 ((symbolp cbfun) (funcall cbfun))
860 (defvar tpum-frame-y-offset 42 "Hack")
861 (defvar tpum-frame-x-offset 6 "Hack")
863 (defun tpum-select ()
864 "Select current menu item."
866 (let ((tpum-ctx (tpum-get-context))
872 (message "TPUM: No menu item under cursor."))
874 (cond ((listp mi) ;submenu
875 (if (tpum-ctx-child-ctx tpum-ctx)
876 (call-interactively 'tpum-submenu-select)
879 (if (tpum-ctx-frame tpum-ctx)
880 (let* ((tfr (tpum-ctx-frame tpum-ctx))
881 (tpum-frame-y-offset 0) ; hack
882 (tpum-frame-x-offset 2)) ; hack
885 (cons (+ (frame-property tfr 'left)
886 (frame-pixel-width tfr)
888 (+ (frame-property tfr 'top)
889 (cdr (tpum-frame-get-coord))))))
891 (tpum-do-menu mi (save-excursion
894 (- (tpum-ctx-width tpum-ctx)
895 (tpum-ctx-offset tpum-ctx) -1))
897 (setf (tpum-ctx-child-ctx tpum-ctx) nctx)
898 (setf (tpum-ctx-parent-ctx nctx) tpum-ctx)))
900 ((vectorp mi) ; Normal menu-item
902 (tpum-apply-callback (cadr (append mi nil))))
904 (t (message "Unknown menu item type."))))))
906 (defun tpum-mode (tpum-ctx)
907 "Enter tpum mode, using TPUM-CTX."
908 (setf (tpum-ctx-local-mode-map tpum-ctx) (current-local-map))
909 (setf (tpum-ctx-local-mode-name tpum-ctx) mode-name)
910 (setf (tpum-ctx-overriding-local-map tpum-ctx)
911 overriding-local-map)
913 (setq overriding-local-map tpum-mode-map)
914 (use-local-map tpum-mode-map)
915 (setq tpum-isearch-mode (cons t tpum-isearch-mode))
917 (set-buffer-modified-p nil)
919 (add-hook 'isearch-mode-end-hook 'tpum-isearch-end-hook))
921 (defun tpum-safe-delrec (tpum-ctx width height &optional startpoint)
922 "In TPUM-CTX safe delete rectangle with sizes WIDTH HEIGHT.
923 Starting from STARTPOINT, if ommited `point' will be used."
926 (and startpoint (goto-char startpoint))
927 (setq ccol (tpum-current-column))
930 (funcall #'(lambda (beg end)
932 (narrow-to-region beg end)
934 (while (re-search-forward "\t" nil t)
935 (let ((indent-tabs-mode nil))
936 (replace-match (make-string tab-width ?\x20))))
938 (progn (beginning-of-line) (point))
939 (progn (end-of-line) (point)))
941 ;; Now we are at the end of line
942 (setq ep (tpum-current-column))
943 (when (< (- ep ccol) width)
948 (progn (insert (make-string (- width (- ep ccol)) ?\x20))
952 (tpum-forward-char ccol)
953 (push (cons (point-marker)
957 (tpum-ctx-atomics-list tpum-ctx))
959 (add-text-properties (point) (+ (point) width) '(invisible t))
965 (point-marker) (progn (insert "\n") (point-marker))))
967 (setq height (1- height))))
968 (goto-char (+ (point) width))))
970 (defun tpum-restore-todel-atomics (ctx)
971 "Restore some stuff of tpum CTX."
973 (delete-region (marker-position (car el))
974 (marker-position (cdr el))))
975 (tpum-ctx-todel-list ctx))
977 (add-text-properties (marker-position (car el))
978 (marker-position (cdr el))
980 (tpum-ctx-atomics-list ctx)))
982 (defun tpum-insert-face (str faces)
983 "Insert STR with FACES."
984 (declare (special tpum-ctx))
986 (set-text-properties (point) (progn (insert str) (point))
990 (let ((width (/ (glyph-width str) (font-width (face-font 'default)))))
991 (tpum-delete-region tpum-ctx (point-marker)
992 (progn (insert (make-string width ?X))
994 (add-text-properties (- (point) width) (point)
995 '(tpum-invisible t invisible t))
996 (set-extent-begin-glyph (make-extent (point) (point)) str)))))
998 (defun tpum-insert-string (tpum-ctx tpum-string)
999 "Using TPUM-CTX insert TPUM-STRING.
1000 TPUM-STRING is list of cons cells where car is string and cdr is face
1002 (mapc #'(lambda (el)
1003 (let ((str (if (consp el) (car el) el))
1004 (face (if (consp el) (cdr el) 'default)))
1006 tpum-ctx (point-marker)
1007 (progn (tpum-insert-face str face)
1011 (defun tpum-insert-menu (tpum-menu tpum-ctx &optional startpoint)
1012 "Insert TPUM-MENU using TPUM-CTX at STARTPOINT.
1013 TPUM-MENU is list of conses which car is menu-item and cdr is tpum string."
1014 (let ((lines tpum-menu))
1016 (goto-char startpoint))
1020 (add-text-properties (point)
1021 (progn (tpum-insert-string tpum-ctx (cdar lines))
1023 (list 'tpum-ctx tpum-ctx 'tpum-menu-item
1025 (setq lines (cdr lines))
1027 (tpum-next-line 1)))))
1029 (defun tpum-get-keyword (menu-item keyword &optional defret)
1030 "From MENU-ITEM get KEYWORD value.
1031 If KEYWORD not found DEFRET returned."
1032 (let* ((mi-list (if (listp menu-item) menu-item (append menu-item nil)))
1033 (ckword (car mi-list))
1036 (if (eq ckword keyword)
1038 (setq kw-retval (cadr mi-list))
1039 (setq mi-list nil))) ; break
1040 (setq mi-list (cdr mi-list))
1041 (setq ckword (car mi-list)))
1045 (unless (fboundp 'keywordp)
1046 (defun keywordp (sym)
1047 (eq (aref (symbol-name sym) 0) ?:)))
1049 (defun tpum-get-suffix (menu-item)
1050 "Get suffix of MENU-ITEM."
1051 (if (vectorp menu-item)
1052 (let* ((mit (append menu-item nil))
1053 (sf1 (and (> (length mit) 3)
1054 (not (keywordp (nth 2 mit)))
1055 (not (keywordp (nth 3 mit)))
1057 (sf2 (tpum-get-keyword menu-item :suffix)))
1061 (defun tpum-mi-active-p (tpum-ctx menu-item)
1062 "Return non-nil if in TPUM-CTX, MENU-ITEM is active."
1063 ;; Note that if :active uses current point position, strange things
1064 ;; will happen, so there a little work around to redefine `point' to
1065 ;; return saved point position.
1067 (set-buffer (tpum-ctx-buffer (tpum-get-global-context tpum-ctx)))
1068 (goto-char (tpum-ctx-point (tpum-get-global-context tpum-ctx)))
1069 (cond ((stringp menu-item) nil) ; title or separator
1070 ((consp menu-item) t) ; submenu
1071 ((vectorp menu-item)
1072 (let ((mit (append menu-item nil))
1073 (at2 (tpum-get-keyword menu-item :active t)))
1075 (cond ((and (> (length mit) 2) (not (keywordp (nth 2 mit))))
1080 (defun tpum-setup-ctx (menuspec tpum-ctx)
1081 "According to MENUSPEC setup TPUM-CTX."
1082 (let ((fel (car menuspec))
1085 (ret-off 1)) ; XXX see tpum-move-to-offset
1087 (let* ((mistyle (cond ((vectorp fel) (tpum-get-keyword fel :style))
1090 (xpm-data (tpum-get-keyword fel :xpm-data))
1091 (glyph (and xpm-data (make-glyph xpm-data)))
1092 (offset (cond ((eq mistyle 'toggle)
1093 (length (tpum-st-td)))
1094 ((eq mistyle 'radio)
1095 (length (tpum-st-rd)))
1097 (1+ (/ (glyph-width glyph)
1098 (font-width (face-font 'default)))))
1101 (cond ((vectorp fel) (car (append fel nil)))
1102 ((consp fel) (concat (car fel) (tpum-st-sub)))
1105 (suffix (tpum-get-suffix fel))
1106 (suflen (if suffix (1+ (length (eval suffix))) 0)))
1108 (when (> (+ width suflen) ret-wid)
1109 (setq ret-wid (+ width suflen)))
1110 (setq ret-hei (1+ ret-hei))
1111 (when (> offset ret-off)
1112 (setq ret-off offset))
1114 (setq menuspec (cdr menuspec))
1115 (setq fel (car menuspec))))
1117 (setf (tpum-ctx-offset tpum-ctx) ret-off)
1118 (setf (tpum-ctx-width tpum-ctx) (+ ret-wid ret-off)) ; XXX
1119 (setf (tpum-ctx-height tpum-ctx) ret-hei)))
1121 (defun tpum-make-delim (menu-item width)
1122 "Make delimiter string using MENU-ITEM.
1123 Delimeter shold be WIDTH chars length."
1125 (cond ((string= menu-item "--:singleLine")
1126 (setq lb (tpum-st-lsl))
1127 (setq rb (tpum-st-rsl))
1128 (setq lin (tpum-st-sl)))
1129 ((string= menu-item "--:doubleLine")
1130 (setq lb (tpum-st-ldl))
1131 (setq rb (tpum-st-rdl))
1132 (setq lin (tpum-st-dl)))
1133 ((string= menu-item "--:singleDashedLine")
1134 (setq lb (tpum-st-lsdl))
1135 (setq rb (tpum-st-rsdl))
1136 (setq lin (tpum-st-sdl)))
1137 ((string= menu-item "--:doubleDashedLine")
1138 (setq lb (tpum-st-lddl))
1139 (setq rb (tpum-st-rddl))
1140 (setq lin (tpum-st-ddl)))
1142 ;; Default separator as is "--:singleLine"
1144 (setq lb (tpum-st-lsl))
1145 (setq rb (tpum-st-rsl))
1146 (setq lin (tpum-st-sl))))
1149 (cons lb (tpum-st-bface))
1151 (mapconcat 'identity
1152 (make-list width lin) "")
1154 (cons rb (tpum-st-bface)))))
1156 (defun tpum-fixate-name (name)
1157 "Remove accell stuff from NAME."
1158 (or (and (stringp name)
1159 (replace-in-string name "%_" ""))
1162 (defun tpum-mitotmi (menu-item tpum-ctx)
1163 "Convert MENU-ITEM to tpum menu item using TPUM-CTX."
1164 (let ((width (tpum-ctx-width tpum-ctx))
1165 ; (height (tpum-ctx-height tpum-ctx))
1166 (offset (tpum-ctx-offset tpum-ctx)))
1168 (cond ((listp menu-item) ;submenu
1169 (let ((iname (tpum-fixate-name (car menu-item))))
1170 (list (cons (tpum-st-l) (tpum-st-bface))
1171 (concat (make-string offset ?\ ) iname)
1172 (concat (make-string
1173 (- width (length iname) offset
1174 (length (tpum-st-sub))) ?\ ))
1176 (cons (tpum-st-r) (tpum-st-bface)))))
1178 ((vectorp menu-item)
1179 (let* ((mi-list (append menu-item nil))
1180 (iname (car mi-list))
1181 (accel (tpum-get-keyword menu-item :accelerator))
1182 (style (tpum-get-keyword menu-item :style))
1183 (active (tpum-mi-active-p tpum-ctx menu-item))
1184 (selected (eval (tpum-get-keyword menu-item :selected)))
1185 (xpm-img (eval (tpum-get-keyword menu-item :xpm-data)))
1186 (suffix (eval (tpum-get-suffix menu-item))))
1189 (setq iname (if (> (length iname) 0)
1190 (concat iname " " suffix)
1193 (setq xpm-img (make-glyph xpm-img))
1194 (when (or (not (= (glyph-height xpm-img)
1195 (font-height (face-font 'default))))
1196 (not (member (glyph-width xpm-img)
1197 (list (font-width (face-font 'default))
1199 (face-font 'default)))))))
1200 (error (format "Invalid xpm-img sizes %S"
1201 (cons (glyph-width xpm-img)
1202 (glyph-height xpm-img))))))
1204 ;; Deal with accel key stuff
1205 (when (string-match "%_\\(.\\)" iname)
1206 (setq accel (string-to-char (match-string 1 iname))))
1207 (setq iname (tpum-fixate-name iname))
1209 (cond ((eq style 'toggle)
1211 (cons (tpum-st-l) (tpum-st-bface))
1212 (cons (tpum-style-toggle selected) (tpum-da-face active))
1213 (cons iname (if selected
1214 (list (tpum-da-face active)
1216 (tpum-da-face active)))
1217 (make-string (- width (length iname) offset) ?\x20)
1218 (cons (tpum-st-r) (tpum-st-bface))))
1222 (cons (tpum-st-l) (tpum-st-bface))
1223 (cons (tpum-style-radio selected) (tpum-da-face active))
1224 (cons iname (if selected
1225 (list (tpum-da-face active)
1227 (tpum-da-face active)))
1228 (make-string (- width (length iname) offset) ?\ )
1229 (cons (tpum-st-r) (tpum-st-bface))))
1230 ;; TODO: radio, normal, etc
1233 (cons (tpum-st-l) (tpum-st-bface))
1238 (- offset (/ (glyph-width xpm-img)
1239 (font-width (face-font 'default))))
1242 (cons iname (tpum-da-face active))
1243 (make-string (- width (length iname) offset) ?\ )
1244 (cons (tpum-st-r) (tpum-st-bface)))))
1247 ((stringp menu-item)
1248 (cond ((and (> (length menu-item) 1)
1249 (string= "--" (substring menu-item 0 2)))
1250 (tpum-make-delim menu-item width))
1253 (cons (tpum-st-l) (tpum-st-bface))
1254 (make-string offset ?\ )
1255 (cons menu-item 'tpum-deactive-face)
1256 (make-string (- width (length menu-item) offset) ?\x20)
1257 (cons (tpum-st-r) (tpum-st-bface))))))
1259 ((symbolp menu-item)
1260 (cond ((eq menu-item 'menu-begin)
1262 (cons (tpum-st-lt) (tpum-st-bface))
1264 (mapconcat 'identity
1265 (make-list width (tpum-st-t)) "")
1266 0 width) (tpum-st-bface))
1267 (cons (tpum-st-rt) (tpum-st-bface))))
1268 ((eq menu-item 'menu-title)
1270 (cons (tpum-st-l) (tpum-st-bface))
1271 (cons (eval menu-item) 'tpum-title-face)
1272 (make-string (- width (length (eval menu-item))) ?\x20)
1273 (cons (tpum-st-r) (tpum-st-bface))))
1275 ((eq menu-item 'menu-separator)
1277 (cons (tpum-st-tsl) (tpum-st-bface))
1279 (mapconcat 'identity
1280 (make-list width (tpum-st-ts)) "")
1281 0 width) (tpum-st-bface))
1282 (cons (tpum-st-tsr) (tpum-st-bface))))
1284 ((eq menu-item 'menu-end)
1286 (cons (tpum-st-lb) (tpum-st-bface))
1288 (mapconcat 'identity
1289 (make-list width (tpum-st-b)) "")
1290 0 width) (tpum-st-bface))
1291 (cons (tpum-st-rb) (tpum-st-bface))))
1295 (cons (tpum-st-l) (tpum-st-bface))
1297 (cons (tpum-st-r) (tpum-st-bface)))))))
1299 (defun tpum-menu-title (menuspec)
1300 "Return title of MENUSPEC or nil, if there no title."
1301 (let ((postit (car menuspec)))
1302 (if (and (not (keywordp postit))
1303 (not (listp postit)))
1304 (tpum-fixate-name postit)
1307 (defun tpum-menu-process-keywords (menuspec)
1308 "Cut off all keywords in MENUSPEC."
1309 (while (keywordp (car menuspec))
1310 (setq menuspec (cddr menuspec)))
1313 (defun tpum-apply-filter (menuspec)
1314 "Apply filter function to MENUSPEC and return new MENU."
1315 (let* ((filter (tpum-get-keyword menuspec :filter))
1316 (nm (if filter (funcall filter menuspec) menuspec)))
1319 (defun tpum-do-modal (ctx)
1320 "Do modal mode using CTX."
1322 (setf (tpum-ctx-recursive-p ctx) t)
1325 (defun tpum-do-menu (menuspec &optional spoint frame-coords)
1326 "Insert menu specified by MENUSPEC at the SPOINT.
1327 If FRAME-COORDS is given, then popup Emacs frame at FRAME-COORDS.
1328 Return tpum context."
1329 ;; XXX TPUM-CTX-FRAME will hold frame coords temporary.
1330 (let* ((tpum-ctx (make-tpum-ctx :frame frame-coords))
1331 (filter (tpum-get-keyword menuspec :filter))
1332 (menu-title (tpum-menu-title menuspec))
1333 (desc (tpum-menu-process-keywords
1334 (if menu-title (cdr menuspec) menuspec)))
1336 (buffer-read-only nil))
1338 (setf (tpum-ctx-buffer tpum-ctx) (current-buffer))
1339 (setf (tpum-ctx-post-command-hooks tpum-ctx) post-command-hook)
1340 (setq post-command-hook nil)
1341 (setf (tpum-ctx-truncate-lines tpum-ctx) truncate-lines)
1342 (setq truncate-lines tpum-truncate-lines)
1343 (setf (tpum-ctx-after-change-functions tpum-ctx) after-change-functions)
1344 (setq after-change-functions nil)
1345 (setf (tpum-ctx-before-change-functions tpum-ctx) before-change-functions)
1346 (setq before-change-functions nil)
1347 (setf (tpum-ctx-undo-list tpum-ctx) buffer-undo-list)
1349 (setf (tpum-ctx-selective-display tpum-ctx) selective-display)
1350 (setq selective-display nil)
1352 ;; pseudo stlye uses ctl-arrow
1353 (when (eq tpum-cstyle 'tpum-style-pseudo)
1354 (tpum-plist-put tpum-ctx 'ctl-arrow ctl-arrow)
1355 (setq ctl-arrow 11))
1357 ;; Apply filter function
1359 (setq desc (funcall filter desc)))
1361 (tpum-setup-ctx (cons menu-title desc) tpum-ctx)
1363 ;; Save position before moving point
1364 (setf (tpum-ctx-point tpum-ctx) (point-marker))
1366 ;; Construct list of strings to insert
1367 (unless (tpum-ctx-frame tpum-ctx)
1368 (setq inrec (cons (cons nil (tpum-mitotmi 'menu-begin tpum-ctx)) inrec))
1369 (setf (tpum-ctx-height tpum-ctx) (+ 1 (tpum-ctx-height tpum-ctx))))
1371 (when popup-menu-titles
1372 (setq inrec (cons (cons nil (tpum-mitotmi 'menu-title tpum-ctx)) inrec)
1373 inrec (cons (cons nil
1374 (tpum-mitotmi 'menu-separator tpum-ctx)) inrec))
1375 (setf (tpum-ctx-height tpum-ctx) (+ 2 (tpum-ctx-height tpum-ctx))))
1377 ;; Construct tpum-menu
1379 (let ((inc (tpum-get-keyword (car desc) :included))
1380 (conf (tpum-get-keyword (car desc) :config)))
1381 (setq inc (if inc (eval inc) t))
1382 (setq inc (if conf (memq conf menubar-configuration) inc))
1385 (setq inrec (cons (cons (car desc)
1386 (tpum-mitotmi (car desc) tpum-ctx))
1388 (setq desc (cdr desc)))
1390 (unless (tpum-ctx-frame tpum-ctx)
1391 (setq inrec (cons (cons nil (tpum-mitotmi 'menu-end tpum-ctx)) inrec)))
1392 (setq inrec (nreverse inrec))
1394 ;; Configure frame if needed
1395 (when (tpum-ctx-frame tpum-ctx)
1396 (let ((coord (tpum-ctx-frame tpum-ctx))
1397 (blv (buffer-local-variables))
1399 (setf (tpum-ctx-frame tpum-ctx)
1400 (tpum-frame-make (car coord) (cdr coord)
1401 (+ 2 (tpum-ctx-width tpum-ctx))
1402 (tpum-ctx-height tpum-ctx)))
1403 (setq tcf (tpum-ctx-frame tpum-ctx))
1404 (set-frame-size tcf (+ 2 (tpum-ctx-width tpum-ctx))
1405 (tpum-ctx-height tpum-ctx))
1406 (setq buf (generate-new-buffer
1407 (concat "*tpum*" (if menu-title menu-title "empty*"))))
1408 (set-window-buffer (frame-selected-window tcf) buf)
1409 ;; Make it dedicated?
1410 (set-buffer-dedicated-frame buf tcf)
1411 (make-frame-visible tcf)
1413 (switch-to-buffer buf)
1414 ;; Setup buffer local variables
1415 (mapc #'(lambda (vv)
1417 (set (car vv) (cdr vv))))
1420 ;; Save position before moving point
1421 (setf (tpum-ctx-point tpum-ctx) (point-marker))
1423 ;; Move point to start location
1427 ;; XXX Delete rectangle
1428 (tpum-safe-delrec tpum-ctx (+ (length (concat (tpum-st-l) (tpum-st-r)))
1429 (tpum-ctx-width tpum-ctx))
1433 (tpum-insert-menu inrec tpum-ctx))
1435 ;; Save column and point
1436 (setf (tpum-ctx-tmpoint tpum-ctx) (point))
1437 (setf (tpum-ctx-column tpum-ctx)
1438 (+ (tpum-current-column) (length (tpum-st-l))))
1443 (tpum-move-to-offset tpum-ctx))
1446 (tpum-mode tpum-ctx)
1448 (when tpum-auto-submenu-mode
1449 (tpum-submenu-show))
1454 (defun tpum-popup-menu (menu &optional event)
1455 "Popup MENU in text mode.
1457 (tpum-do-menu menu))
1459 ;;; Ballooning support
1460 (defun tpum-frame-make (x y width height &optional buffer)
1461 "At X Y using WIDTH HEIGHT size make balloon frame to be used by tpum.
1462 Optionally you may specify BUFFER to select in newly created frame.
1463 Return Emacs frame."
1464 (let ((frame (make-frame `((top-toolbar-visible-p . nil)
1465 (left-toolbar-visible-p . nil)
1466 (right-toolbar-visible-p . nil)
1467 (bottom-toolbar-visible-p . nil)
1468 (has-modeline-p . nil)
1469 (modeline-shadow-thickness . 0)
1470 (vertical-scrollbar-visible-p . nil)
1471 (horizontal-scrollbar-visible-p . nil)
1472 (menubar-visible-p . nil)
1473 (text-cursor-visible-p . t)
1474 (left-margin-width . 1)
1475 (initially-unmapped . t)
1476 (internal-border-width . 1)
1477 (name . "*tpum frame*")
1479 (border-color . ,(face-foreground-name
1483 (popup . ,(selected-frame))
1486 (height . ,height)))))
1488 (set-window-buffer (frame-selected-window frame) buffer)
1489 (set-buffer-dedicated-frame buffer frame))
1493 (defun tpum-frame-get-coord ()
1494 "Get coordinates of point."
1495 (let ((edgs (window-pixel-edges))
1496 (lw (/ (window-text-area-pixel-width) (window-width)))
1497 (lh (/ (window-text-area-pixel-height) (window-height))))
1498 (cons (+ tpum-frame-x-offset (* lw (current-column)) (car edgs))
1499 (+ tpum-frame-y-offset
1500 (* lh (count-lines (window-start) (window-point)))
1503 (defun tpum-frame-do-menu (menu coor)
1504 "Popup MENU in separate frame at COOR.
1505 COOR is cons cell where car is X and cdr is Y."
1506 ; (let ((tfr (tpum-frame-make (car coor) (cdr coor) 3 1)))
1507 ; (tpum-do-menu menu nil tfr)))
1508 (tpum-do-menu menu nil coor))
1511 (defun tpum-frame-popup-menu (menu &optional event)
1512 "Popup MENU in separate frame.
1513 EVENT is some mouse event."
1517 (cons (event-x-pixel event) (event-y-pixel event))
1518 (tpum-frame-get-coord))))
1521 ;; Suggested by Uwe Brauer <oub@mat.ucm.es>
1523 (defvar tpum-minor-mode nil
1524 "Non-nil if tpum minor mode enabled.")
1525 (make-variable-buffer-local 'tpum-minor-mode)
1527 ;; Save old `popup-menu' function
1528 (defun old-popup-menu (menu &optional event))
1529 (fset 'old-popup-menu (symbol-function 'popup-menu))
1531 ;; Redefine standart `popup-menu'
1532 (defun popup-menu-redefined-by-tpum (menu &optional event)
1533 "Text mode `popup-menu' replacement.
1535 Optionally mouse EVENT can be specified."
1536 (if (or tpum-minor-mode tpum-global-mode)
1537 (if (eq tpum-menu-type 'inline)
1538 (tpum-popup-menu menu event)
1541 ;; - Maybe change `tpum-cstyle' to `tpum-style-frame'?
1542 (tpum-frame-popup-menu menu event))
1544 ;; Use old popup menu function
1545 (funcall #'old-popup-menu menu event)))
1547 (fset 'popup-menu (symbol-function 'popup-menu-redefined-by-tpum))
1550 (defun tpum-minor-mode (&optional arg)
1551 "Toggle tpum minor mode.
1552 With prefix ARG, turn on if positive, otherwise off."
1553 (interactive (list (or (and current-prefix-arg
1554 (prefix-numeric-value current-prefix-arg))
1555 (if tpum-minor-mode 0 1))))
1556 (setq tpum-minor-mode (> arg 0)))
1559 (defun tpum-global-mode (&optional arg)
1560 "Toggle tpum global mode.
1561 With prefix ARG, turn on if positive, otherwise off."
1562 (interactive (list (or (and current-prefix-arg
1563 (prefix-numeric-value current-prefix-arg))
1564 (if tpum-global-mode 0 1))))
1565 (setq tpum-global-mode (> arg 0)))
1567 (add-minor-mode 'tpum-minor-mode " Tpum")
1569 (defun define-tpum-key (map tkeys rkeys)
1570 "In keymap MAP define keys TKEYS that emulates press of RKEYS."
1571 (define-key map tkeys
1574 (let* ((nevents (aref (key-sequence-list-description ,rkeys) 0))
1575 (mods (and (> (length nevents) 0) (butlast nevents)))
1576 (buname (symbol-name (car (last nevents))))
1577 (button (- (char-to-int (aref buname (1- (length buname)))) 48)))
1578 (setq unread-command-events
1579 (append (list (make-event
1581 (list 'modifiers mods 'button button)))
1582 unread-command-events))))))
1587 ;; Finally run load hooks
1588 (run-hooks 'tpum-load-hook)
1590 ;;; tpum.el ends here