(tooltip-frame-parameters): Remove colors.
[bpt/emacs.git] / lisp / tooltip.el
CommitLineData
7840ced1
GM
1;;; tooltip.el --- Show tooltip windows
2
c39e8983 3;; Copyright (C) 1997, 1999, 2000 Free Software Foundation, Inc.
7840ced1
GM
4
5;; Author: Gerd Moellmann <gerd@acm.org>
6;; Keywords: help c mouse tools
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
25;;; Commentary:
26
27;; Put into your `.emacs'
28
29;; (require 'tooltip)
30;; (tooltip-mode 1)
31
32
33\f
34;;; Code:
35
36(eval-when-compile
37 (require 'cl)
38 (require 'comint)
39 (require 'gud))
40
41(provide 'tooltip)
42
43\f
44;;; Customizable settings
45
46(defgroup tooltip nil
47 "Customization group for the `tooltip' package."
48 :group 'help
49 :group 'c
50 :group 'mouse
51 :group 'tools
01b23b99 52 :version "21.1"
7840ced1
GM
53 :tag "Tool Tips")
54
01b23b99 55(defvar tooltip-mode)
7840ced1
GM
56
57(defcustom tooltip-delay 1.0
58 "Seconds to wait before displaying a tooltip the first time."
59 :tag "Delay"
60 :type 'number
61 :group 'tooltip)
62
63
64(defcustom tooltip-short-delay 0.1
65 "Seconds to wait between subsequent tooltips on different items."
66 :tag "Short delay"
67 :type 'number
68 :group 'tooltip)
69
70
71(defcustom tooltip-recent-seconds 1
01b23b99
DL
72 "Display tooltips if changing tip items within this many seconds.
73Do so after `tooltip-short-delay'."
7840ced1
GM
74 :tag "Recent seconds"
75 :type 'number
76 :group 'tooltip)
77
78
e4df9b40
GM
79(defcustom tooltip-x-offset nil
80 "Specify an X offset for the display of tooltips.
81The offset is relative to the position of the mouse. It must
82be chosen so that the tooltip window doesn't contain the mouse
83when it pops up."
84 :tag "X offset"
85 :type '(choice (const :tag "Default" nil)
86 (integer :tag "Offset" :value 1))
87 :group 'tooltip)
88
89
90(defcustom tooltip-y-offset nil
91 "Specify an Y offset for the display of tooltips.
92The offset is relative to the position of the mouse. It must
93be chosen so that the tooltip window doesn't contain the mouse
94when it pops up."
95 :tag "Y offset"
96 :type '(choice (const :tag "Default" nil)
97 (integer :tag "Offset" :value 1))
98 :group 'tooltip)
99
100
7840ced1
GM
101(defcustom tooltip-frame-parameters
102 '((name . "tooltip")
7840ced1 103 (internal-border-width . 5)
7840ced1
GM
104 (border-width . 1))
105 "Frame parameters used for tooltips."
106 :type 'sexp
107 :tag "Frame Parameters"
108 :group 'tooltip)
109
110
2621f5a9
GM
111(defface tooltip
112 '((((class color))
113 (:background "lightyellow" :foreground "black"))
114 (t ()))
115 "Face for tooltips."
116 :group 'tooltip)
117
118
7840ced1 119(defcustom tooltip-gud-tips-p nil
024e5da6 120 "*Non-nil means show tooltips in GUD sessions."
7840ced1
GM
121 :type 'boolean
122 :tag "GUD"
c39e8983
MB
123 :set #'(lambda (symbol on)
124 (setq tooltip-gud-tips-p on)
125 (if on (tooltip-gud-tips-setup)))
7840ced1
GM
126 :group 'tooltip)
127
128
129(defcustom tooltip-gud-modes '(gud-mode c-mode c++-mode)
130 "List of modes for which to enable GUD tips."
131 :type 'sexp
132 :tag "GUD modes"
133 :group 'tooltip)
134
a1f84f6d 135
7840ced1
GM
136(defcustom tooltip-gud-display
137 '((eq (tooltip-event-buffer tooltip-gud-event)
138 (marker-buffer overlay-arrow-position)))
139 "List of forms determining where GUD tooltips are displayed.
140
141Forms in the list are combined with AND. The default is to display
142only tooltips in the buffer containing the overlay arrow."
143 :type 'sexp
144 :tag "GUD buffers predicate"
145 :group 'tooltip)
146
147
72200f89 148(defcustom tooltip-use-echo-area nil
8be44f9d
DL
149 "Use the echo area instead of tooltip frames.
150This is only relevant GUD display, since otherwise it is equivalent to
151turning off Tooltip mode."
72200f89 152 :type 'boolean
e775fcc4 153 :tag "Use echo area"
72200f89
SS
154 :group 'tooltip)
155
7840ced1
GM
156\f
157;;; Variables that are not customizable.
158
159(defvar tooltip-hook nil
160 "Functions to call to display tooltips.
161Each function is called with one argument EVENT which is a copy of
162the last mouse movement event that occurred.")
163
164
165(defvar tooltip-timeout-id nil
166 "The id of the timeout started when Emacs becomes idle.")
167
168
169(defvar tooltip-last-mouse-motion-event nil
170 "A copy of the last mouse motion event seen.")
171
172
173(defvar tooltip-hide-time nil
174 "Time when the last tooltip was hidden.")
175
176
7840ced1
GM
177(defvar tooltip-gud-debugger nil
178 "The debugger for which we show tooltips.")
179
180
181\f
182;;; Event accessors
183
184(defun tooltip-event-buffer (event)
185 "Return the buffer over which event EVENT occurred.
186This might return nil if the event did not occur over a buffer."
187 (let ((window (posn-window (event-end event))))
188 (and window (window-buffer window))))
189
190
191\f
192;;; Switching tooltips on/off
193
194;; We don't set track-mouse globally because this is a big redisplay
195;; problem in buffers having a pre-command-hook or such installed,
196;; which does a set-buffer, like the summary buffer of Gnus. Calling
197;; set-buffer prevents redisplay optimizations, so every mouse motion
198;; would be accompanied by a full redisplay.
199
200;;;###autoload
201(defun tooltip-mode (&optional arg)
202 "Mode for tooltip display.
203With ARG, turn tooltip mode on if and only if ARG is positive."
204 (interactive "P")
c6da4eb4
GM
205 (unless (fboundp 'x-show-tip)
206 (error "Sorry, tooltips are not yet available on this system"))
7840ced1
GM
207 (let* ((on (if arg
208 (> (prefix-numeric-value arg) 0)
209 (not tooltip-mode)))
210 (hook-fn (if on 'add-hook 'remove-hook)))
211 (setq tooltip-mode on)
212 (funcall hook-fn 'change-major-mode-hook 'tooltip-change-major-mode)
213 (tooltip-activate-mouse-motions-if-enabled)
214 (funcall hook-fn 'pre-command-hook 'tooltip-hide)
215 (funcall hook-fn 'tooltip-hook 'tooltip-gud-tips)
216 (funcall hook-fn 'tooltip-hook 'tooltip-help-tips)
217 (setq show-help-function (if on 'tooltip-show-help-function nil))
218 ;; `ignore' is the default binding for mouse movements.
219 (define-key global-map [mouse-movement]
220 (if on 'tooltip-mouse-motion 'ignore))
c39e8983
MB
221 (tooltip-gud-tips-setup)))
222
223(defun tooltip-gud-tips-setup ()
224 "Setup debugger mode-hooks for tooltips."
225 (when (and tooltip-mode tooltip-gud-tips-p)
226 (global-set-key [S-mouse-3] 'tooltip-gud-toggle-dereference)
227 (add-hook 'gdb-mode-hook
228 #'(lambda () (setq tooltip-gud-debugger 'gdb)))
229 (add-hook 'sdb-mode-hook
230 #'(lambda () (setq tooltip-gud-debugger 'sdb)))
231 (add-hook 'dbx-mode-hook
232 #'(lambda () (setq tooltip-gud-debugger 'dbx)))
233 (add-hook 'xdb-mode-hook
234 #'(lambda () (setq tooltip-gud-debugger 'xdb)))
235 (add-hook 'perldb-mode-hook
236 #'(lambda () (setq tooltip-gud-debugger 'perldb)))))
7840ced1
GM
237\f
238;;; Timeout for tooltip display
239
7840ced1
GM
240(defun tooltip-delay ()
241 "Return the delay in seconds for the next tooltip."
242 (let ((delay tooltip-delay)
a1f84f6d 243 (now (float-time)))
7840ced1
GM
244 (when (and tooltip-hide-time
245 (< (- now tooltip-hide-time) tooltip-recent-seconds))
246 (setq delay tooltip-short-delay))
247 delay))
248
249
250(defun tooltip-disable-timeout ()
251 "Disable the tooltip timeout."
252 (when tooltip-timeout-id
253 (disable-timeout tooltip-timeout-id)
254 (setq tooltip-timeout-id nil)))
255
256
257(defun tooltip-add-timeout ()
258 "Add a one-shot timeout to call function tooltip-timeout."
259 (setq tooltip-timeout-id
260 (add-timeout (tooltip-delay) 'tooltip-timeout nil)))
261
262
263(defun tooltip-timeout (object)
264 "Function called when timer with id tooltip-timeout-id fires."
265 (run-hook-with-args-until-success 'tooltip-hook
266 tooltip-last-mouse-motion-event))
267
268
269\f
270;;; Reacting on mouse movements
271
272(defun tooltip-change-major-mode ()
273 "Function added to `change-major-mode-hook' when tooltip mode is on."
274 (add-hook 'post-command-hook 'tooltip-activate-mouse-motions-if-enabled))
275
276
277(defun tooltip-activate-mouse-motions-if-enabled ()
278 "Reconsider for all buffers whether mouse motion events are desired."
279 (remove-hook 'post-command-hook 'tooltip-activate-mouse-motions-if-enabled)
280 (let ((buffers (buffer-list)))
281 (save-excursion
282 (while buffers
283 (set-buffer (car buffers))
284 (if (and tooltip-mode
285 tooltip-gud-tips-p
286 (memq major-mode tooltip-gud-modes))
287 (tooltip-activate-mouse-motions t)
288 (tooltip-activate-mouse-motions nil))
289 (setq buffers (cdr buffers))))))
290
291
292(defun tooltip-activate-mouse-motions (activatep)
293 "Activate/deactivate mouse motion events for the current buffer.
294ACTIVATEP non-nil means activate mouse motion events."
295 (if activatep
296 (progn
297 (make-local-variable 'track-mouse)
298 (setq track-mouse t))
299 (kill-local-variable 'track-mouse)))
300
301
302(defun tooltip-mouse-motion (event)
303 "Command handler for mouse movement events in `global-map'."
304 (interactive "e")
305 (tooltip-hide)
306 (when (car (mouse-pixel-position))
307 (setq tooltip-last-mouse-motion-event (copy-sequence event))
308 (tooltip-add-timeout)))
309
310
311\f
312;;; Displaying tips
313
2621f5a9
GM
314(defun tooltip-set-param (alist key value)
315 "Change the value of KEY in alist ALIAS to VALUE.
316If there's no association for KEY in ALIST, add one, otherwise
317change the existing association. Value is the resulting alist."
318 (let ((param (assq key alist)))
319 (if (consp param)
320 (setcdr param value)
321 (push (cons key value) alist))
322 alist))
323
324
7840ced1
GM
325(defun tooltip-show (text)
326 "Show a tooltip window at the current mouse position displaying TEXT."
72200f89
SS
327 (if tooltip-use-echo-area
328 (message "%s" text)
e5603149 329 (condition-case error
2621f5a9
GM
330 (let ((params (copy-sequence tooltip-frame-parameters))
331 (fg (face-attribute 'tooltip :foreground))
332 (bg (face-attribute 'tooltip :background)))
333 (unless (eq 'unspecified fg)
334 (tooltip-set-param params 'foreground-color fg))
335 (unless (eq 'unspecified bg)
336 (tooltip-set-param params 'background-color bg)
337 (tooltip-set-param params 'border-color bg))
338 (x-show-tip (propertize text 'face 'tooltip)
339 (selected-frame)
340 tooltip-frame-parameters
341 nil
342 tooltip-x-offset
343 tooltip-y-offset))
e5603149
GM
344 (error
345 (message "Error while displaying tooltip: %s" error)
346 (sit-for 1)
347 (message "%s" text)))))
348
7840ced1
GM
349
350(defun tooltip-hide (&optional ignored-arg)
351 "Hide a tooltip, if one is displayed.
352Value is non-nil if tooltip was open."
353 (tooltip-disable-timeout)
354 (when (x-hide-tip)
a1f84f6d 355 (setq tooltip-hide-time (float-time))))
7840ced1
GM
356
357
358\f
359;;; Debugger-related functions
360
361(defun tooltip-identifier-from-point (point)
362 "Extract the identifier at POINT, if any.
363Value is nil if no identifier exists at point. Identifier extraction
364is based on the current syntax table."
365 (save-excursion
366 (goto-char point)
367 (let ((start (progn (skip-syntax-backward "w_") (point))))
368 (unless (looking-at "[0-9]")
369 (skip-syntax-forward "w_")
370 (when (> (point) start)
371 (buffer-substring start (point)))))))
372
373
374(defmacro tooltip-region-active-p ()
375 "Value is non-nil if the region is currently active."
376 (if (string-match "^GNU" (emacs-version))
377 `(and transient-mark-mode mark-active)
378 `(region-active-p)))
379
380
381(defun tooltip-expr-to-print (event)
382 "Return an expression that should be printed for EVENT.
383If a region is active and the mouse is inside the region, print
384the region. Otherwise, figure out the identifier around the point
385where the mouse is."
386 (save-excursion
387 (set-buffer (tooltip-event-buffer event))
388 (let ((point (posn-point (event-end event))))
389 (if (tooltip-region-active-p)
390 (when (and (<= (region-beginning) point) (<= point (region-end)))
391 (buffer-substring (region-beginning) (region-end)))
392 (tooltip-identifier-from-point point)))))
393
394
395(defun tooltip-process-prompt-regexp (process)
396 "Return regexp matching the prompt of PROCESS at the end of a string.
397The prompt is taken from the value of COMINT-PROMPT-REGEXP in the buffer
398of PROCESS."
399 (let ((prompt-regexp (save-excursion
400 (set-buffer (process-buffer process))
401 comint-prompt-regexp)))
402 ;; Most start with `^' but the one for `sdb' cannot be easily
403 ;; stripped. Code the prompt for `sdb' fixed here.
404 (if (= (aref prompt-regexp 0) ?^)
405 (setq prompt-regexp (substring prompt-regexp 1))
406 (setq prompt-regexp "\\*"))
407 (concat "\n*" prompt-regexp "$")))
408
409
410(defun tooltip-strip-prompt (process output)
411 "Return OUTPUT with any prompt of PROCESS stripped from its end."
412 (let ((prompt-regexp (tooltip-process-prompt-regexp process)))
413 (save-match-data
414 (when (string-match prompt-regexp output)
415 (setq output (substring output 0 (match-beginning 0)))))
416 output))
417
418
419\f
420;;; Tips for `gud'
421
422(defvar tooltip-gud-original-filter nil
423 "Process filter to restore after GUD output has been received.")
424
425
426(defvar tooltip-gud-dereference nil
427 "Non-nil means print expressions with a `*' in front of them.
428For C this would dereference a pointer expression.")
429
430
431(defvar tooltip-gud-event nil
432 "The mouse movement event that led to a tooltip display.
433This event can be examined by forms in TOOLTIP-GUD-DISPLAY.")
434
435
436(defvar tooltip-gud-debugger nil
437 "A symbol describing the debugger running under GUD.")
438
439
440(defun tooltip-gud-toggle-dereference ()
024e5da6 441 "Toggle whether tooltips should show `* expr' or `expr'."
7840ced1
GM
442 (interactive)
443 (setq tooltip-gud-dereference (not tooltip-gud-dereference))
444 (when (interactive-p)
445 (message "Dereferencing is now %s."
446 (if tooltip-gud-dereference "on" "off"))))
447
448
449(defun tooltip-gud-process-output (process output)
450 "Process debugger output and show it in a tooltip window."
451 (set-process-filter process tooltip-gud-original-filter)
452 (tooltip-show (tooltip-strip-prompt process output)))
453
454
455(defun tooltip-gud-print-command (expr)
456 "Return a suitable command to print the expression EXPR.
457If TOOLTIP-GUD-DEREFERENCE is t, also prepend a `*' to EXPR."
458 (when tooltip-gud-dereference
459 (setq expr (concat "*" expr)))
460 (case tooltip-gud-debugger
461 ((gdb dbx) (concat "print " expr))
462 (xdb (concat "p " expr))
463 (sdb (concat expr "/"))
464 (perldb expr)))
a1f84f6d 465
7840ced1
GM
466
467(defun tooltip-gud-tips (event)
01b23b99
DL
468 "Show tip for identifier or selection under the mouse.
469The mouse must either point at an identifier or inside a selected
470region for the tip window to be shown. If tooltip-gud-dereference is t,
471add a `*' in front of the printed expression.
7840ced1
GM
472
473This function must return nil if it doesn't handle EVENT."
474 (let (gud-buffer process)
475 (when (and (eventp event)
476 tooltip-gud-tips-p
477 (boundp 'gud-comint-buffer)
478 (setq gud-buffer gud-comint-buffer)
479 (setq process (get-buffer-process gud-buffer))
480 (posn-point (event-end event))
481 (progn (setq tooltip-gud-event event)
482 (eval (cons 'and tooltip-gud-display))))
483 (let ((expr (tooltip-expr-to-print event)))
484 (when expr
c39e8983
MB
485 (let ((cmd (tooltip-gud-print-command expr)))
486 (unless (null cmd) ; CMD can be nil if unknown debugger
487 (setq tooltip-gud-original-filter (process-filter process))
488 (set-process-filter process 'tooltip-gud-process-output)
489 (gud-basic-call cmd)
490 expr)))))))
7840ced1
GM
491
492\f
493;;; Tooltip help.
494
495(defvar tooltip-help-message nil
496 "The last help message received via `tooltip-show-help-function'.")
497
498
499(defun tooltip-show-help-function (msg)
500 "Function installed as `show-help-function'.
501MSG is either a help string to display, or nil to cancel the display."
502 (let ((previous-help tooltip-help-message))
503 (setq tooltip-help-message msg)
504 (cond ((null msg)
505 (tooltip-hide))
506 ((or (not (stringp previous-help))
507 (not (string= msg previous-help)))
508 (tooltip-hide)
509 (tooltip-add-timeout))
510 (t
511 (tooltip-disable-timeout)
512 (tooltip-add-timeout)))))
513
514
515(defun tooltip-help-tips (event)
516 "Hook function to display a help tooltip.
517Value is non-nil if this function handled the tip."
518 (when (stringp tooltip-help-message)
519 (tooltip-show tooltip-help-message)
520 (setq tooltip-help-message nil)
521 t))
522
523
524\f
90e69ae5
GM
525;;; Do this after all functions have been defined that are called from
526;;; `tooltip-mode'. The actual default value of `tooltip-mode' is set
527;;; in startup.el.
01b23b99
DL
528
529;;;###autoload
530(defcustom tooltip-mode nil
531 "Toggle tooltip-mode.
532Setting this variable directly does not take effect;
533use either \\[customize] or the function `tooltip-mode'."
534 :set (lambda (symbol value)
535 (tooltip-mode (or value 0)))
536 :initialize 'custom-initialize-default
7840ced1 537 :type 'boolean
7840ced1
GM
538 :require 'tooltip
539 :group 'tooltip)
540
541
542;;; tooltip.el ends here