blob: 90bef8b50c9fb73f54d37c9893329ec9c7afedba [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * Visual Workshop integration by Gordon Prieur
5 *
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10
11#include "vim.h"
12
13#if defined(FEAT_BEVAL) || defined(PROTO)
14
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000015/*
16 * Common code, invoked when the mouse is resting for a moment.
17 */
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000018 void
19general_beval_cb(beval, state)
20 BalloonEval *beval;
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +000021 int state UNUSED;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000022{
Bram Moolenaar56be9502010-06-06 14:20:26 +020023#ifdef FEAT_EVAL
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000024 win_T *wp;
25 int col;
Bram Moolenaarb71eaae2006-01-20 23:10:18 +000026 int use_sandbox;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000027 linenr_T lnum;
28 char_u *text;
29 static char_u *result = NULL;
30 long winnr = 0;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000031 char_u *bexpr;
32 buf_T *save_curbuf;
Bram Moolenaar56be9502010-06-06 14:20:26 +020033# ifdef FEAT_WINDOWS
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000034 win_T *cw;
Bram Moolenaar56be9502010-06-06 14:20:26 +020035# endif
Bram Moolenaar33aec762006-01-22 23:30:12 +000036#endif
Bram Moolenaar54a709e2006-05-04 21:57:11 +000037 static int recursive = FALSE;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000038
39 /* Don't do anything when 'ballooneval' is off, messages scrolled the
40 * windows up or we have no beval area. */
41 if (!p_beval || balloonEval == NULL || msg_scrolled > 0)
42 return;
43
Bram Moolenaar54a709e2006-05-04 21:57:11 +000044 /* Don't do this recursively. Happens when the expression evaluation
45 * takes a long time and invokes something that checks for CTRL-C typed. */
46 if (recursive)
47 return;
48 recursive = TRUE;
49
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000050#ifdef FEAT_EVAL
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000051 if (get_beval_info(balloonEval, TRUE, &wp, &lnum, &text, &col) == OK)
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000052 {
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000053 bexpr = (*wp->w_buffer->b_p_bexpr == NUL) ? p_bexpr
54 : wp->w_buffer->b_p_bexpr;
55 if (*bexpr != NUL)
56 {
Bram Moolenaar33aec762006-01-22 23:30:12 +000057# ifdef FEAT_WINDOWS
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000058 /* Convert window pointer to number. */
59 for (cw = firstwin; cw != wp; cw = cw->w_next)
60 ++winnr;
Bram Moolenaar33aec762006-01-22 23:30:12 +000061# endif
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000062
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000063 set_vim_var_nr(VV_BEVAL_BUFNR, (long)wp->w_buffer->b_fnum);
64 set_vim_var_nr(VV_BEVAL_WINNR, winnr);
65 set_vim_var_nr(VV_BEVAL_LNUM, (long)lnum);
66 set_vim_var_nr(VV_BEVAL_COL, (long)(col + 1));
67 set_vim_var_string(VV_BEVAL_TEXT, text, -1);
68 vim_free(text);
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000069
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000070 /*
71 * Temporarily change the curbuf, so that we can determine whether
Bram Moolenaarbae0c162007-05-10 19:30:25 +000072 * the buffer-local balloonexpr option was set insecurely.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000073 */
74 save_curbuf = curbuf;
75 curbuf = wp->w_buffer;
76 use_sandbox = was_set_insecurely((char_u *)"balloonexpr",
77 *curbuf->b_p_bexpr == NUL ? 0 : OPT_LOCAL);
78 curbuf = save_curbuf;
79 if (use_sandbox)
80 ++sandbox;
81 ++textlock;
Bram Moolenaarb71eaae2006-01-20 23:10:18 +000082
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000083 vim_free(result);
84 result = eval_to_string(bexpr, NULL, TRUE);
Bram Moolenaarb71eaae2006-01-20 23:10:18 +000085
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000086 if (use_sandbox)
87 --sandbox;
88 --textlock;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000089
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000090 set_vim_var_string(VV_BEVAL_TEXT, NULL, -1);
91 if (result != NULL && result[0] != NUL)
92 {
93 gui_mch_post_balloon(beval, result);
Bram Moolenaar54a709e2006-05-04 21:57:11 +000094 recursive = FALSE;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +000095 return;
96 }
Bram Moolenaare2ac10d2005-03-07 23:26:06 +000097 }
98 }
99#endif
100#ifdef FEAT_NETBEANS_INTG
101 if (bevalServers & BEVAL_NETBEANS)
102 netbeans_beval_cb(beval, state);
103#endif
104#ifdef FEAT_SUN_WORKSHOP
105 if (bevalServers & BEVAL_WORKSHOP)
106 workshop_beval_cb(beval, state);
107#endif
Bram Moolenaar54a709e2006-05-04 21:57:11 +0000108
109 recursive = FALSE;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000110}
111
112/* on Win32 only get_beval_info() is required */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113#if !defined(FEAT_GUI_W32) || defined(PROTO)
114
115#ifdef FEAT_GUI_GTK
116# include <gdk/gdkkeysyms.h>
117# include <gtk/gtk.h>
118#else
119# include <X11/keysym.h>
120# ifdef FEAT_GUI_MOTIF
121# include <Xm/PushB.h>
122# include <Xm/Separator.h>
123# include <Xm/List.h>
124# include <Xm/Label.h>
125# include <Xm/AtomMgr.h>
126# include <Xm/Protocols.h>
127# else
128 /* Assume Athena */
129# include <X11/Shell.h>
Bram Moolenaar238a5642006-02-21 22:12:05 +0000130# ifdef FEAT_GUI_NEXTAW
131# include <X11/neXtaw/Label.h>
132# else
133# include <X11/Xaw/Label.h>
134# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135# endif
136#endif
137
138#include "gui_beval.h"
139
140#ifndef FEAT_GUI_GTK
141extern Widget vimShell;
142
143/*
144 * Currently, we assume that there can be only one BalloonEval showing
145 * on-screen at any given moment. This variable will hold the currently
146 * showing BalloonEval or NULL if none is showing.
147 */
148static BalloonEval *current_beval = NULL;
149#endif
150
151#ifdef FEAT_GUI_GTK
152static void addEventHandler __ARGS((GtkWidget *, BalloonEval *));
153static void removeEventHandler __ARGS((BalloonEval *));
154static gint target_event_cb __ARGS((GtkWidget *, GdkEvent *, gpointer));
155static gint mainwin_event_cb __ARGS((GtkWidget *, GdkEvent *, gpointer));
156static void pointer_event __ARGS((BalloonEval *, int, int, unsigned));
157static void key_event __ARGS((BalloonEval *, unsigned, int));
158static gint timeout_cb __ARGS((gpointer));
159static gint balloon_expose_event_cb __ARGS((GtkWidget *, GdkEventExpose *, gpointer));
160# ifndef HAVE_GTK2
161static void balloon_draw_cb __ARGS((GtkWidget *, GdkRectangle *, gpointer));
162# endif
163#else
164static void addEventHandler __ARGS((Widget, BalloonEval *));
165static void removeEventHandler __ARGS((BalloonEval *));
166static void pointerEventEH __ARGS((Widget, XtPointer, XEvent *, Boolean *));
167static void pointerEvent __ARGS((BalloonEval *, XEvent *));
168static void timerRoutine __ARGS((XtPointer, XtIntervalId *));
169#endif
170static void cancelBalloon __ARGS((BalloonEval *));
171static void requestBalloon __ARGS((BalloonEval *));
172static void drawBalloon __ARGS((BalloonEval *));
173static void undrawBalloon __ARGS((BalloonEval *beval));
174static void createBalloonEvalWindow __ARGS((BalloonEval *));
175
176
177
178/*
179 * Create a balloon-evaluation area for a Widget.
180 * There can be either a "mesg" for a fixed string or "mesgCB" to generate a
181 * message by calling this callback function.
182 * When "mesg" is not NULL it must remain valid for as long as the balloon is
183 * used. It is not freed here.
184 * Returns a pointer to the resulting object (NULL when out of memory).
185 */
186 BalloonEval *
187gui_mch_create_beval_area(target, mesg, mesgCB, clientData)
188 void *target;
189 char_u *mesg;
190 void (*mesgCB)__ARGS((BalloonEval *, int));
191 void *clientData;
192{
193#ifndef FEAT_GUI_GTK
194 char *display_name; /* get from gui.dpy */
195 int screen_num;
196 char *p;
197#endif
198 BalloonEval *beval;
199
200 if (mesg != NULL && mesgCB != NULL)
201 {
202 EMSG(_("E232: Cannot create BalloonEval with both message and callback"));
203 return NULL;
204 }
205
206 beval = (BalloonEval *)alloc(sizeof(BalloonEval));
207 if (beval != NULL)
208 {
209#ifdef FEAT_GUI_GTK
210 beval->target = GTK_WIDGET(target);
211 beval->balloonShell = NULL;
212 beval->timerID = 0;
213#else
214 beval->target = (Widget)target;
215 beval->balloonShell = NULL;
216 beval->timerID = (XtIntervalId)NULL;
217 beval->appContext = XtWidgetToApplicationContext((Widget)target);
218#endif
219 beval->showState = ShS_NEUTRAL;
220 beval->x = 0;
221 beval->y = 0;
222 beval->msg = mesg;
223 beval->msgCB = mesgCB;
224 beval->clientData = clientData;
225
226 /*
227 * Set up event handler which will keep its eyes on the pointer,
228 * and when the pointer rests in a certain spot for a given time
229 * interval, show the beval.
230 */
231 addEventHandler(beval->target, beval);
232 createBalloonEvalWindow(beval);
233
234#ifndef FEAT_GUI_GTK
235 /*
236 * Now create and save the screen width and height. Used in drawing.
237 */
238 display_name = DisplayString(gui.dpy);
239 p = strrchr(display_name, '.');
240 if (p != NULL)
241 screen_num = atoi(++p);
242 else
243 screen_num = 0;
244 beval->screen_width = DisplayWidth(gui.dpy, screen_num);
245 beval->screen_height = DisplayHeight(gui.dpy, screen_num);
246#endif
247 }
248
249 return beval;
250}
251
252#if defined(FEAT_BEVAL_TIP) || defined(PROTO)
253/*
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000254 * Destroy a balloon-eval and free its associated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 */
256 void
257gui_mch_destroy_beval_area(beval)
258 BalloonEval *beval;
259{
260 cancelBalloon(beval);
261 removeEventHandler(beval);
262 /* Children will automatically be destroyed */
263# ifdef FEAT_GUI_GTK
264 gtk_widget_destroy(beval->balloonShell);
265# else
266 XtDestroyWidget(beval->balloonShell);
267# endif
268 vim_free(beval);
269}
270#endif
271
272 void
273gui_mch_enable_beval_area(beval)
274 BalloonEval *beval;
275{
276 if (beval != NULL)
277 addEventHandler(beval->target, beval);
278}
279
280 void
281gui_mch_disable_beval_area(beval)
282 BalloonEval *beval;
283{
284 if (beval != NULL)
285 removeEventHandler(beval);
286}
287
288#if defined(FEAT_BEVAL_TIP) || defined(PROTO)
289/*
290 * This function returns the BalloonEval * associated with the currently
291 * displayed tooltip. Returns NULL if there is no tooltip currently showing.
292 *
293 * Assumption: Only one tooltip can be shown at a time.
294 */
295 BalloonEval *
296gui_mch_currently_showing_beval()
297{
298 return current_beval;
299}
300#endif
301#endif /* !FEAT_GUI_W32 */
302
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000303#if defined(FEAT_SUN_WORKSHOP) || defined(FEAT_NETBEANS_INTG) \
304 || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000305/*
306 * Get the text and position to be evaluated for "beval".
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000307 * If "getword" is true the returned text is not the whole line but the
308 * relevant word in allocated memory.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000309 * Returns OK or FAIL.
310 */
311 int
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000312get_beval_info(beval, getword, winp, lnump, textp, colp)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000313 BalloonEval *beval;
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000314 int getword;
315 win_T **winp;
316 linenr_T *lnump;
317 char_u **textp;
318 int *colp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000319{
320 win_T *wp;
321 int row, col;
322 char_u *lbuf;
323 linenr_T lnum;
324
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000325 *textp = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326 row = Y_2_ROW(beval->y);
327 col = X_2_COL(beval->x);
Bram Moolenaar33aec762006-01-22 23:30:12 +0000328#ifdef FEAT_WINDOWS
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329 wp = mouse_find_win(&row, &col);
Bram Moolenaar33aec762006-01-22 23:30:12 +0000330#else
331 wp = firstwin;
332#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000333 if (wp != NULL && row < wp->w_height && col < W_WIDTH(wp))
334 {
335 /* Found a window and the cursor is in the text. Now find the line
336 * number. */
337 if (!mouse_comp_pos(wp, &row, &col, &lnum))
338 {
339 /* Not past end of the file. */
340 lbuf = ml_get_buf(wp->w_buffer, lnum, FALSE);
341 if (col <= win_linetabsize(wp, lbuf, (colnr_T)MAXCOL))
342 {
343 /* Not past end of line. */
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000344 if (getword)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345 {
346 /* For Netbeans we get the relevant part of the line
347 * instead of the whole line. */
348 int len;
349 pos_T *spos = NULL, *epos = NULL;
350
351 if (VIsual_active)
352 {
353 if (lt(VIsual, curwin->w_cursor))
354 {
355 spos = &VIsual;
356 epos = &curwin->w_cursor;
357 }
358 else
359 {
360 spos = &curwin->w_cursor;
361 epos = &VIsual;
362 }
363 }
364
365 col = vcol2col(wp, lnum, col) - 1;
366
367 if (VIsual_active
368 && wp->w_buffer == curwin->w_buffer
369 && (lnum == spos->lnum
370 ? col >= (int)spos->col
371 : lnum > spos->lnum)
372 && (lnum == epos->lnum
373 ? col <= (int)epos->col
374 : lnum < epos->lnum))
375 {
376 /* Visual mode and pointing to the line with the
377 * Visual selection: return selected text, with a
378 * maximum of one line. */
379 if (spos->lnum != epos->lnum || spos->col == epos->col)
380 return FAIL;
381
382 lbuf = ml_get_buf(curwin->w_buffer, VIsual.lnum, FALSE);
383 lbuf = vim_strnsave(lbuf + spos->col,
384 epos->col - spos->col + (*p_sel != 'e'));
385 lnum = spos->lnum;
386 col = spos->col;
387 }
388 else
389 {
390 /* Find the word under the cursor. */
391 ++emsg_off;
392 len = find_ident_at_pos(wp, lnum, (colnr_T)col, &lbuf,
393 FIND_IDENT + FIND_STRING + FIND_EVAL);
394 --emsg_off;
395 if (len == 0)
396 return FAIL;
397 lbuf = vim_strnsave(lbuf, len);
398 }
399 }
Bram Moolenaare2ac10d2005-03-07 23:26:06 +0000400
401 *winp = wp;
402 *lnump = lnum;
403 *textp = lbuf;
404 *colp = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000405 beval->ts = wp->w_buffer->b_p_ts;
406 return OK;
407 }
408 }
409 }
410
411 return FAIL;
412}
413
414# if !defined(FEAT_GUI_W32) || defined(PROTO)
415
416/*
417 * Show a balloon with "mesg".
418 */
419 void
420gui_mch_post_balloon(beval, mesg)
421 BalloonEval *beval;
422 char_u *mesg;
423{
424 beval->msg = mesg;
425 if (mesg != NULL)
426 drawBalloon(beval);
427 else
428 undrawBalloon(beval);
429}
430# endif /* FEAT_GUI_W32 */
431#endif /* FEAT_SUN_WORKSHOP || FEAT_NETBEANS_INTG || PROTO */
432
433#if !defined(FEAT_GUI_W32) || defined(PROTO)
434#if defined(FEAT_BEVAL_TIP) || defined(PROTO)
435/*
436 * Hide the given balloon.
437 */
438 void
439gui_mch_unpost_balloon(beval)
440 BalloonEval *beval;
441{
442 undrawBalloon(beval);
443}
444#endif
445
446#ifdef FEAT_GUI_GTK
447/*
448 * We can unconditionally use ANSI-style prototypes here since
449 * GTK+ requires an ANSI C compiler anyway.
450 */
451 static void
452addEventHandler(GtkWidget *target, BalloonEval *beval)
453{
454 /*
455 * Connect to the generic "event" signal instead of the individual
456 * signals for each event type, because the former is emitted earlier.
457 * This allows us to catch events independently of the signal handlers
458 * in gui_gtk_x11.c.
459 */
460 /* Should use GTK_OBJECT() here, but that causes a lint warning... */
461 gtk_signal_connect((GtkObject*)(target), "event",
462 GTK_SIGNAL_FUNC(target_event_cb),
463 beval);
464 /*
465 * Nasty: Key press events go to the main window thus the drawing area
466 * will never see them. This means we have to connect to the main window
467 * as well in order to catch those events.
468 */
469 if (gtk_socket_id == 0 && gui.mainwin != NULL
470 && gtk_widget_is_ancestor(target, gui.mainwin))
471 {
472 gtk_signal_connect((GtkObject*)(gui.mainwin), "event",
473 GTK_SIGNAL_FUNC(mainwin_event_cb),
474 beval);
475 }
476}
477
478 static void
479removeEventHandler(BalloonEval *beval)
480{
Bram Moolenaar33570922005-01-25 22:26:29 +0000481 /* LINTED: avoid warning: dubious operation on enum */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000482 gtk_signal_disconnect_by_func((GtkObject*)(beval->target),
483 GTK_SIGNAL_FUNC(target_event_cb),
484 beval);
485
486 if (gtk_socket_id == 0 && gui.mainwin != NULL
487 && gtk_widget_is_ancestor(beval->target, gui.mainwin))
488 {
Bram Moolenaar33570922005-01-25 22:26:29 +0000489 /* LINTED: avoid warning: dubious operation on enum */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 gtk_signal_disconnect_by_func((GtkObject*)(gui.mainwin),
491 GTK_SIGNAL_FUNC(mainwin_event_cb),
492 beval);
493 }
494}
495
496 static gint
497target_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
498{
499 BalloonEval *beval = (BalloonEval *)data;
500
501 switch (event->type)
502 {
503 case GDK_ENTER_NOTIFY:
504 pointer_event(beval, (int)event->crossing.x,
505 (int)event->crossing.y,
506 event->crossing.state);
507 break;
508 case GDK_MOTION_NOTIFY:
509 if (event->motion.is_hint)
510 {
511 int x;
512 int y;
513 GdkModifierType state;
514 /*
515 * GDK_POINTER_MOTION_HINT_MASK is set, thus we cannot obtain
516 * the coordinates from the GdkEventMotion struct directly.
517 */
518 gdk_window_get_pointer(widget->window, &x, &y, &state);
519 pointer_event(beval, x, y, (unsigned int)state);
520 }
521 else
522 {
523 pointer_event(beval, (int)event->motion.x,
524 (int)event->motion.y,
525 event->motion.state);
526 }
527 break;
528 case GDK_LEAVE_NOTIFY:
529 /*
530 * Ignore LeaveNotify events that are not "normal".
531 * Apparently we also get it when somebody else grabs focus.
532 */
533 if (event->crossing.mode == GDK_CROSSING_NORMAL)
534 cancelBalloon(beval);
535 break;
536 case GDK_BUTTON_PRESS:
537# ifdef HAVE_GTK2
538 case GDK_SCROLL:
539# endif
540 cancelBalloon(beval);
541 break;
542 case GDK_KEY_PRESS:
543 key_event(beval, event->key.keyval, TRUE);
544 break;
545 case GDK_KEY_RELEASE:
546 key_event(beval, event->key.keyval, FALSE);
547 break;
548 default:
549 break;
550 }
551
552 return FALSE; /* continue emission */
553}
554
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000556mainwin_event_cb(GtkWidget *widget UNUSED, GdkEvent *event, gpointer data)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557{
558 BalloonEval *beval = (BalloonEval *)data;
559
560 switch (event->type)
561 {
562 case GDK_KEY_PRESS:
563 key_event(beval, event->key.keyval, TRUE);
564 break;
565 case GDK_KEY_RELEASE:
566 key_event(beval, event->key.keyval, FALSE);
567 break;
568 default:
569 break;
570 }
571
572 return FALSE; /* continue emission */
573}
574
575 static void
576pointer_event(BalloonEval *beval, int x, int y, unsigned state)
577{
578 int distance;
579
580 distance = ABS(x - beval->x) + ABS(y - beval->y);
581
582 if (distance > 4)
583 {
584 /*
585 * Moved out of the balloon location: cancel it.
586 * Remember button state
587 */
588 beval->state = state;
589 cancelBalloon(beval);
590
591 /* Mouse buttons are pressed - no balloon now */
592 if (!(state & ((int)GDK_BUTTON1_MASK | (int)GDK_BUTTON2_MASK
593 | (int)GDK_BUTTON3_MASK)))
594 {
595 beval->x = x;
596 beval->y = y;
597
598 if (state & (int)GDK_MOD1_MASK)
599 {
600 /*
601 * Alt is pressed -- enter super-evaluate-mode,
602 * where there is no time delay
603 */
604 if (beval->msgCB != NULL)
605 {
606 beval->showState = ShS_PENDING;
607 (*beval->msgCB)(beval, state);
608 }
609 }
610 else
611 {
612 beval->timerID = gtk_timeout_add((guint32)p_bdlay,
613 &timeout_cb, beval);
614 }
615 }
616 }
617}
618
619 static void
620key_event(BalloonEval *beval, unsigned keyval, int is_keypress)
621{
622 if (beval->showState == ShS_SHOWING && beval->msgCB != NULL)
623 {
624 switch (keyval)
625 {
626 case GDK_Shift_L:
627 case GDK_Shift_R:
628 beval->showState = ShS_UPDATE_PENDING;
629 (*beval->msgCB)(beval, (is_keypress)
630 ? (int)GDK_SHIFT_MASK : 0);
631 break;
632 case GDK_Control_L:
633 case GDK_Control_R:
634 beval->showState = ShS_UPDATE_PENDING;
635 (*beval->msgCB)(beval, (is_keypress)
636 ? (int)GDK_CONTROL_MASK : 0);
637 break;
638 default:
Bram Moolenaar1d2ba7f2006-02-14 22:29:30 +0000639 /* Don't do this for key release, we apparently get these with
640 * focus changes in some GTK version. */
641 if (is_keypress)
642 cancelBalloon(beval);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000643 break;
644 }
645 }
646 else
647 cancelBalloon(beval);
648}
649
650 static gint
651timeout_cb(gpointer data)
652{
653 BalloonEval *beval = (BalloonEval *)data;
654
655 beval->timerID = 0;
656 /*
657 * If the timer event happens then the mouse has stopped long enough for
658 * a request to be started. The request will only send to the debugger if
659 * there the mouse is pointing at real data.
660 */
661 requestBalloon(beval);
662
663 return FALSE; /* don't call me again */
664}
665
Bram Moolenaar071d4272004-06-13 20:20:40 +0000666 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000667balloon_expose_event_cb(GtkWidget *widget,
668 GdkEventExpose *event,
669 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000670{
671 gtk_paint_flat_box(widget->style, widget->window,
672 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
673 &event->area, widget, "tooltip",
674 0, 0, -1, -1);
675
676 return FALSE; /* continue emission */
677}
678
679# ifndef HAVE_GTK2
Bram Moolenaar071d4272004-06-13 20:20:40 +0000680 static void
681balloon_draw_cb(GtkWidget *widget, GdkRectangle *area, gpointer data)
682{
683 GtkWidget *child;
684 GdkRectangle child_area;
685
686 gtk_paint_flat_box(widget->style, widget->window,
687 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
688 area, widget, "tooltip",
689 0, 0, -1, -1);
690
691 child = GTK_BIN(widget)->child;
692
693 if (gtk_widget_intersect(child, area, &child_area))
694 gtk_widget_draw(child, &child_area);
695}
696# endif
697
698#else /* !FEAT_GUI_GTK */
699
700 static void
701addEventHandler(target, beval)
702 Widget target;
703 BalloonEval *beval;
704{
705 XtAddEventHandler(target,
706 PointerMotionMask | EnterWindowMask |
707 LeaveWindowMask | ButtonPressMask | KeyPressMask |
708 KeyReleaseMask,
709 False,
710 pointerEventEH, (XtPointer)beval);
711}
712
713 static void
714removeEventHandler(beval)
715 BalloonEval *beval;
716{
717 XtRemoveEventHandler(beval->target,
718 PointerMotionMask | EnterWindowMask |
719 LeaveWindowMask | ButtonPressMask | KeyPressMask |
720 KeyReleaseMask,
721 False,
722 pointerEventEH, (XtPointer)beval);
723}
724
725
726/*
727 * The X event handler. All it does is call the real event handler.
728 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729 static void
730pointerEventEH(w, client_data, event, unused)
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000731 Widget w UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000732 XtPointer client_data;
733 XEvent *event;
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000734 Boolean *unused UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735{
736 BalloonEval *beval = (BalloonEval *)client_data;
737 pointerEvent(beval, event);
738}
739
740
741/*
742 * The real event handler. Called by pointerEventEH() whenever an event we are
Bram Moolenaarbae0c162007-05-10 19:30:25 +0000743 * interested in occurs.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744 */
745
746 static void
747pointerEvent(beval, event)
748 BalloonEval *beval;
749 XEvent *event;
750{
751 Position distance; /* a measure of how much the ponter moved */
752 Position delta; /* used to compute distance */
753
754 switch (event->type)
755 {
756 case EnterNotify:
757 case MotionNotify:
758 delta = event->xmotion.x - beval->x;
759 if (delta < 0)
760 delta = -delta;
761 distance = delta;
762 delta = event->xmotion.y - beval->y;
763 if (delta < 0)
764 delta = -delta;
765 distance += delta;
766 if (distance > 4)
767 {
768 /*
769 * Moved out of the balloon location: cancel it.
770 * Remember button state
771 */
772 beval->state = event->xmotion.state;
773 if (beval->state & (Button1Mask|Button2Mask|Button3Mask))
774 {
775 /* Mouse buttons are pressed - no balloon now */
776 cancelBalloon(beval);
777 }
778 else if (beval->state & (Mod1Mask|Mod2Mask|Mod3Mask))
779 {
780 /*
781 * Alt is pressed -- enter super-evaluate-mode,
782 * where there is no time delay
783 */
784 beval->x = event->xmotion.x;
785 beval->y = event->xmotion.y;
786 beval->x_root = event->xmotion.x_root;
787 beval->y_root = event->xmotion.y_root;
788 cancelBalloon(beval);
789 if (beval->msgCB != NULL)
790 {
791 beval->showState = ShS_PENDING;
792 (*beval->msgCB)(beval, beval->state);
793 }
794 }
795 else
796 {
797 beval->x = event->xmotion.x;
798 beval->y = event->xmotion.y;
799 beval->x_root = event->xmotion.x_root;
800 beval->y_root = event->xmotion.y_root;
801 cancelBalloon(beval);
802 beval->timerID = XtAppAddTimeOut( beval->appContext,
803 (long_u)p_bdlay, timerRoutine, beval);
804 }
805 }
806 break;
807
808 /*
809 * Motif and Athena version: Keystrokes will be caught by the
810 * "textArea" widget, and handled in gui_x11_key_hit_cb().
811 */
812 case KeyPress:
813 if (beval->showState == ShS_SHOWING && beval->msgCB != NULL)
814 {
815 Modifiers modifier;
816 KeySym keysym;
817
818 XtTranslateKeycode(gui.dpy,
819 event->xkey.keycode, event->xkey.state,
820 &modifier, &keysym);
821 if (keysym == XK_Shift_L || keysym == XK_Shift_R)
822 {
823 beval->showState = ShS_UPDATE_PENDING;
824 (*beval->msgCB)(beval, ShiftMask);
825 }
826 else if (keysym == XK_Control_L || keysym == XK_Control_R)
827 {
828 beval->showState = ShS_UPDATE_PENDING;
829 (*beval->msgCB)(beval, ControlMask);
830 }
831 else
832 cancelBalloon(beval);
833 }
834 else
835 cancelBalloon(beval);
836 break;
837
838 case KeyRelease:
839 if (beval->showState == ShS_SHOWING && beval->msgCB != NULL)
840 {
841 Modifiers modifier;
842 KeySym keysym;
843
844 XtTranslateKeycode(gui.dpy, event->xkey.keycode,
845 event->xkey.state, &modifier, &keysym);
846 if ((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) {
847 beval->showState = ShS_UPDATE_PENDING;
848 (*beval->msgCB)(beval, 0);
849 }
850 else if ((keysym == XK_Control_L) || (keysym == XK_Control_R))
851 {
852 beval->showState = ShS_UPDATE_PENDING;
853 (*beval->msgCB)(beval, 0);
854 }
855 else
856 cancelBalloon(beval);
857 }
858 else
859 cancelBalloon(beval);
860 break;
861
862 case LeaveNotify:
863 /* Ignore LeaveNotify events that are not "normal".
864 * Apparently we also get it when somebody else grabs focus.
865 * Happens for me every two seconds (some clipboard tool?) */
866 if (event->xcrossing.mode == NotifyNormal)
867 cancelBalloon(beval);
868 break;
869
870 case ButtonPress:
871 cancelBalloon(beval);
872 break;
873
874 default:
875 break;
876 }
877}
878
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879 static void
880timerRoutine(dx, id)
881 XtPointer dx;
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +0000882 XtIntervalId *id UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000883{
884 BalloonEval *beval = (BalloonEval *)dx;
885
886 beval->timerID = (XtIntervalId)NULL;
887
888 /*
889 * If the timer event happens then the mouse has stopped long enough for
890 * a request to be started. The request will only send to the debugger if
891 * there the mouse is pointing at real data.
892 */
893 requestBalloon(beval);
894}
895
896#endif /* !FEAT_GUI_GTK */
897
898 static void
899requestBalloon(beval)
900 BalloonEval *beval;
901{
902 if (beval->showState != ShS_PENDING)
903 {
904 /* Determine the beval to display */
905 if (beval->msgCB != NULL)
906 {
907 beval->showState = ShS_PENDING;
908 (*beval->msgCB)(beval, beval->state);
909 }
910 else if (beval->msg != NULL)
911 drawBalloon(beval);
912 }
913}
914
915#ifdef FEAT_GUI_GTK
916
917# ifdef HAVE_GTK2
918/*
919 * Convert the string to UTF-8 if 'encoding' is not "utf-8".
920 * Replace any non-printable characters and invalid bytes sequences with
921 * "^X" or "<xx>" escapes, and apply SpecialKey highlighting to them.
922 * TAB and NL are passed through unscathed.
923 */
924# define IS_NONPRINTABLE(c) (((c) < 0x20 && (c) != TAB && (c) != NL) \
925 || (c) == DEL)
926 static void
Bram Moolenaar89d40322006-08-29 15:30:07 +0000927set_printable_label_text(GtkLabel *label, char_u *text)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928{
929 char_u *convbuf = NULL;
930 char_u *buf;
931 char_u *p;
932 char_u *pdest;
933 unsigned int len;
934 int charlen;
935 int uc;
936 PangoAttrList *attr_list;
937
938 /* Convert to UTF-8 if it isn't already */
939 if (output_conv.vc_type != CONV_NONE)
940 {
Bram Moolenaar89d40322006-08-29 15:30:07 +0000941 convbuf = string_convert(&output_conv, text, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000942 if (convbuf != NULL)
Bram Moolenaar89d40322006-08-29 15:30:07 +0000943 text = convbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000944 }
945
946 /* First let's see how much we need to allocate */
947 len = 0;
Bram Moolenaar89d40322006-08-29 15:30:07 +0000948 for (p = text; *p != NUL; p += charlen)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949 {
950 if ((*p & 0x80) == 0) /* be quick for ASCII */
951 {
952 charlen = 1;
953 len += IS_NONPRINTABLE(*p) ? 2 : 1; /* nonprintable: ^X */
954 }
955 else
956 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +0000957 charlen = utf_ptr2len(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 uc = utf_ptr2char(p);
959
960 if (charlen != utf_char2len(uc))
961 charlen = 1; /* reject overlong sequences */
962
963 if (charlen == 1 || uc < 0xa0) /* illegal byte or */
964 len += 4; /* control char: <xx> */
965 else if (!utf_printable(uc))
966 /* Note: we assume here that utf_printable() doesn't
967 * care about characters outside the BMP. */
968 len += 6; /* nonprintable: <xxxx> */
969 else
970 len += charlen;
971 }
972 }
973
974 attr_list = pango_attr_list_new();
975 buf = alloc(len + 1);
976
977 /* Now go for the real work */
978 if (buf != NULL)
979 {
980 attrentry_T *aep;
981 PangoAttribute *attr;
982 guicolor_T pixel;
983 GdkColor color = { 0, 0, 0, 0 };
984
985 /* Look up the RGB values of the SpecialKey foreground color. */
986 aep = syn_gui_attr2entry(hl_attr(HLF_8));
987 pixel = (aep != NULL) ? aep->ae_u.gui.fg_color : INVALCOLOR;
988 if (pixel != INVALCOLOR)
989 gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
990 (unsigned long)pixel, &color);
991
992 pdest = buf;
Bram Moolenaar89d40322006-08-29 15:30:07 +0000993 p = text;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994 while (*p != NUL)
995 {
996 /* Be quick for ASCII */
997 if ((*p & 0x80) == 0 && !IS_NONPRINTABLE(*p))
998 {
999 *pdest++ = *p++;
1000 }
1001 else
1002 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00001003 charlen = utf_ptr2len(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004 uc = utf_ptr2char(p);
1005
1006 if (charlen != utf_char2len(uc))
1007 charlen = 1; /* reject overlong sequences */
1008
1009 if (charlen == 1 || uc < 0xa0 || !utf_printable(uc))
1010 {
1011 int outlen;
1012
1013 /* Careful: we can't just use transchar_byte() here,
1014 * since 'encoding' is not necessarily set to "utf-8". */
1015 if (*p & 0x80 && charlen == 1)
1016 {
1017 transchar_hex(pdest, *p); /* <xx> */
1018 outlen = 4;
1019 }
1020 else if (uc >= 0x80)
1021 {
1022 /* Note: we assume here that utf_printable() doesn't
1023 * care about characters outside the BMP. */
1024 transchar_hex(pdest, uc); /* <xx> or <xxxx> */
1025 outlen = (uc < 0x100) ? 4 : 6;
1026 }
1027 else
1028 {
1029 transchar_nonprint(pdest, *p); /* ^X */
1030 outlen = 2;
1031 }
1032 if (pixel != INVALCOLOR)
1033 {
1034 attr = pango_attr_foreground_new(
1035 color.red, color.green, color.blue);
1036 attr->start_index = pdest - buf;
1037 attr->end_index = pdest - buf + outlen;
1038 pango_attr_list_insert(attr_list, attr);
1039 }
1040 pdest += outlen;
1041 p += charlen;
1042 }
1043 else
1044 {
1045 do
1046 *pdest++ = *p++;
1047 while (--charlen != 0);
1048 }
1049 }
1050 }
1051 *pdest = NUL;
1052 }
1053
1054 vim_free(convbuf);
1055
1056 gtk_label_set_text(label, (const char *)buf);
1057 vim_free(buf);
1058
1059 gtk_label_set_attributes(label, attr_list);
1060 pango_attr_list_unref(attr_list);
1061}
1062# undef IS_NONPRINTABLE
1063# endif /* HAVE_GTK2 */
1064
1065/*
1066 * Draw a balloon.
1067 */
1068 static void
1069drawBalloon(BalloonEval *beval)
1070{
1071 if (beval->msg != NULL)
1072 {
1073 GtkRequisition requisition;
1074 int screen_w;
1075 int screen_h;
1076 int x;
1077 int y;
1078 int x_offset = EVAL_OFFSET_X;
1079 int y_offset = EVAL_OFFSET_Y;
1080# ifdef HAVE_GTK2
1081 PangoLayout *layout;
1082# endif
1083# ifdef HAVE_GTK_MULTIHEAD
1084 GdkScreen *screen;
1085
1086 screen = gtk_widget_get_screen(beval->target);
1087 gtk_window_set_screen(GTK_WINDOW(beval->balloonShell), screen);
1088 screen_w = gdk_screen_get_width(screen);
1089 screen_h = gdk_screen_get_height(screen);
1090# else
1091 screen_w = gdk_screen_width();
1092 screen_h = gdk_screen_height();
1093# endif
1094 gtk_widget_ensure_style(beval->balloonShell);
1095 gtk_widget_ensure_style(beval->balloonLabel);
1096
1097# ifdef HAVE_GTK2
1098 set_printable_label_text(GTK_LABEL(beval->balloonLabel), beval->msg);
1099 /*
1100 * Dirty trick: Enable wrapping mode on the label's layout behind its
1101 * back. This way GtkLabel won't try to constrain the wrap width to a
1102 * builtin maximum value of about 65 Latin characters.
1103 */
1104 layout = gtk_label_get_layout(GTK_LABEL(beval->balloonLabel));
1105# ifdef PANGO_WRAP_WORD_CHAR
1106 pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
1107# else
1108 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
1109# endif
1110 pango_layout_set_width(layout,
1111 /* try to come up with some reasonable width */
1112 PANGO_SCALE * CLAMP(gui.num_cols * gui.char_width,
1113 screen_w / 2,
1114 MAX(20, screen_w - 20)));
1115
1116 /* Calculate the balloon's width and height. */
1117 gtk_widget_size_request(beval->balloonShell, &requisition);
1118# else
1119 gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), FALSE);
1120 gtk_label_set_text(GTK_LABEL(beval->balloonLabel),
1121 (const char *)beval->msg);
1122
1123 /* Calculate the balloon's width and height. */
1124 gtk_widget_size_request(beval->balloonShell, &requisition);
1125 /*
1126 * Unfortunately, the dirty trick used above to get around the builtin
1127 * maximum wrap width of GtkLabel doesn't work with GTK+ 1. Thus if
1128 * and only if it's absolutely necessary to avoid drawing off-screen,
1129 * do enable wrapping now and recalculate the size request.
1130 */
1131 if (requisition.width > screen_w)
1132 {
1133 gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), TRUE);
1134 gtk_widget_size_request(beval->balloonShell, &requisition);
1135 }
1136# endif
1137
1138 /* Compute position of the balloon area */
1139 gdk_window_get_origin(beval->target->window, &x, &y);
1140 x += beval->x;
1141 y += beval->y;
1142
1143 /* Get out of the way of the mouse pointer */
1144 if (x + x_offset + requisition.width > screen_w)
1145 y_offset += 15;
1146 if (y + y_offset + requisition.height > screen_h)
1147 y_offset = -requisition.height - EVAL_OFFSET_Y;
1148
1149 /* Sanitize values */
1150 x = CLAMP(x + x_offset, 0, MAX(0, screen_w - requisition.width));
1151 y = CLAMP(y + y_offset, 0, MAX(0, screen_h - requisition.height));
1152
1153 /* Show the balloon */
1154 gtk_widget_set_uposition(beval->balloonShell, x, y);
1155 gtk_widget_show(beval->balloonShell);
1156
1157 beval->showState = ShS_SHOWING;
1158 }
1159}
1160
1161/*
1162 * Undraw a balloon.
1163 */
1164 static void
1165undrawBalloon(BalloonEval *beval)
1166{
1167 if (beval->balloonShell != NULL)
1168 gtk_widget_hide(beval->balloonShell);
1169 beval->showState = ShS_NEUTRAL;
1170}
1171
1172 static void
1173cancelBalloon(BalloonEval *beval)
1174{
1175 if (beval->showState == ShS_SHOWING
1176 || beval->showState == ShS_UPDATE_PENDING)
1177 undrawBalloon(beval);
1178
1179 if (beval->timerID != 0)
1180 {
1181 gtk_timeout_remove(beval->timerID);
1182 beval->timerID = 0;
1183 }
1184 beval->showState = ShS_NEUTRAL;
1185}
1186
1187 static void
1188createBalloonEvalWindow(BalloonEval *beval)
1189{
1190 beval->balloonShell = gtk_window_new(GTK_WINDOW_POPUP);
1191
1192 gtk_widget_set_app_paintable(beval->balloonShell, TRUE);
1193 gtk_window_set_policy(GTK_WINDOW(beval->balloonShell), FALSE, FALSE, TRUE);
1194 gtk_widget_set_name(beval->balloonShell, "gtk-tooltips");
1195 gtk_container_border_width(GTK_CONTAINER(beval->balloonShell), 4);
1196
1197 gtk_signal_connect((GtkObject*)(beval->balloonShell), "expose_event",
1198 GTK_SIGNAL_FUNC(balloon_expose_event_cb), NULL);
1199# ifndef HAVE_GTK2
1200 gtk_signal_connect((GtkObject*)(beval->balloonShell), "draw",
1201 GTK_SIGNAL_FUNC(balloon_draw_cb), NULL);
1202# endif
1203 beval->balloonLabel = gtk_label_new(NULL);
1204
1205 gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), FALSE);
1206 gtk_label_set_justify(GTK_LABEL(beval->balloonLabel), GTK_JUSTIFY_LEFT);
1207 gtk_misc_set_alignment(GTK_MISC(beval->balloonLabel), 0.5f, 0.5f);
1208 gtk_widget_set_name(beval->balloonLabel, "vim-balloon-label");
1209 gtk_widget_show(beval->balloonLabel);
1210
1211 gtk_container_add(GTK_CONTAINER(beval->balloonShell), beval->balloonLabel);
1212}
1213
1214#else /* !FEAT_GUI_GTK */
1215
1216/*
1217 * Draw a balloon.
1218 */
1219 static void
1220drawBalloon(beval)
1221 BalloonEval *beval;
1222{
1223 Dimension w;
1224 Dimension h;
1225 Position tx;
1226 Position ty;
1227
1228 if (beval->msg != NULL)
1229 {
1230 /* Show the Balloon */
1231
1232 /* Calculate the label's width and height */
1233#ifdef FEAT_GUI_MOTIF
1234 XmString s;
1235
1236 /* For the callback function we parse NL characters to create a
1237 * multi-line label. This doesn't work for all languages, but
1238 * XmStringCreateLocalized() doesn't do multi-line labels... */
1239 if (beval->msgCB != NULL)
1240 s = XmStringCreateLtoR((char *)beval->msg, XmFONTLIST_DEFAULT_TAG);
1241 else
1242 s = XmStringCreateLocalized((char *)beval->msg);
1243 {
1244 XmFontList fl;
1245
1246 fl = gui_motif_fontset2fontlist(&gui.tooltip_fontset);
1247 if (fl != NULL)
1248 {
1249 XmStringExtent(fl, s, &w, &h);
1250 XmFontListFree(fl);
1251 }
1252 }
1253 w += gui.border_offset << 1;
1254 h += gui.border_offset << 1;
1255 XtVaSetValues(beval->balloonLabel, XmNlabelString, s, NULL);
1256 XmStringFree(s);
1257#else /* Athena */
1258 /* Assume XtNinternational == True */
1259 XFontSet fset;
1260 XFontSetExtents *ext;
1261
1262 XtVaGetValues(beval->balloonLabel, XtNfontSet, &fset, NULL);
1263 ext = XExtentsOfFontSet(fset);
1264 h = ext->max_ink_extent.height;
1265 w = XmbTextEscapement(fset,
1266 (char *)beval->msg,
1267 (int)STRLEN(beval->msg));
1268 w += gui.border_offset << 1;
1269 h += gui.border_offset << 1;
1270 XtVaSetValues(beval->balloonLabel, XtNlabel, beval->msg, NULL);
1271#endif
1272
1273 /* Compute position of the balloon area */
1274 tx = beval->x_root + EVAL_OFFSET_X;
1275 ty = beval->y_root + EVAL_OFFSET_Y;
1276 if ((tx + w) > beval->screen_width)
1277 tx = beval->screen_width - w;
1278 if ((ty + h) > beval->screen_height)
1279 ty = beval->screen_height - h;
1280#ifdef FEAT_GUI_MOTIF
1281 XtVaSetValues(beval->balloonShell,
1282 XmNx, tx,
1283 XmNy, ty,
1284 NULL);
1285#else
1286 /* Athena */
1287 XtVaSetValues(beval->balloonShell,
1288 XtNx, tx,
1289 XtNy, ty,
1290 NULL);
1291#endif
Bram Moolenaar8281f442009-03-18 11:22:25 +00001292 /* Set tooltip colors */
1293 {
1294 Arg args[2];
1295
1296#ifdef FEAT_GUI_MOTIF
1297 args[0].name = XmNbackground;
1298 args[0].value = gui.tooltip_bg_pixel;
1299 args[1].name = XmNforeground;
1300 args[1].value = gui.tooltip_fg_pixel;
1301#else /* Athena */
1302 args[0].name = XtNbackground;
1303 args[0].value = gui.tooltip_bg_pixel;
1304 args[1].name = XtNforeground;
1305 args[1].value = gui.tooltip_fg_pixel;
1306#endif
1307 XtSetValues(beval->balloonLabel, &args[0], XtNumber(args));
1308 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309
1310 XtPopup(beval->balloonShell, XtGrabNone);
1311
1312 beval->showState = ShS_SHOWING;
1313
1314 current_beval = beval;
1315 }
1316}
1317
1318/*
1319 * Undraw a balloon.
1320 */
1321 static void
1322undrawBalloon(beval)
1323 BalloonEval *beval;
1324{
1325 if (beval->balloonShell != (Widget)0)
1326 XtPopdown(beval->balloonShell);
1327 beval->showState = ShS_NEUTRAL;
1328
1329 current_beval = NULL;
1330}
1331
1332 static void
1333cancelBalloon(beval)
1334 BalloonEval *beval;
1335{
1336 if (beval->showState == ShS_SHOWING
1337 || beval->showState == ShS_UPDATE_PENDING)
1338 undrawBalloon(beval);
1339
1340 if (beval->timerID != (XtIntervalId)NULL)
1341 {
1342 XtRemoveTimeOut(beval->timerID);
1343 beval->timerID = (XtIntervalId)NULL;
1344 }
1345 beval->showState = ShS_NEUTRAL;
1346}
1347
1348
1349 static void
1350createBalloonEvalWindow(beval)
1351 BalloonEval *beval;
1352{
1353 Arg args[12];
1354 int n;
1355
1356 n = 0;
1357#ifdef FEAT_GUI_MOTIF
1358 XtSetArg(args[n], XmNallowShellResize, True); n++;
1359 beval->balloonShell = XtAppCreateShell("balloonEval", "BalloonEval",
1360 overrideShellWidgetClass, gui.dpy, args, n);
1361#else
1362 /* Athena */
1363 XtSetArg(args[n], XtNallowShellResize, True); n++;
1364 beval->balloonShell = XtAppCreateShell("balloonEval", "BalloonEval",
1365 overrideShellWidgetClass, gui.dpy, args, n);
1366#endif
1367
1368 n = 0;
1369#ifdef FEAT_GUI_MOTIF
1370 {
1371 XmFontList fl;
1372
1373 fl = gui_motif_fontset2fontlist(&gui.tooltip_fontset);
1374 XtSetArg(args[n], XmNforeground, gui.tooltip_fg_pixel); n++;
1375 XtSetArg(args[n], XmNbackground, gui.tooltip_bg_pixel); n++;
1376 XtSetArg(args[n], XmNfontList, fl); n++;
1377 XtSetArg(args[n], XmNalignment, XmALIGNMENT_BEGINNING); n++;
1378 beval->balloonLabel = XtCreateManagedWidget("balloonLabel",
1379 xmLabelWidgetClass, beval->balloonShell, args, n);
1380 }
1381#else /* FEAT_GUI_ATHENA */
1382 XtSetArg(args[n], XtNforeground, gui.tooltip_fg_pixel); n++;
1383 XtSetArg(args[n], XtNbackground, gui.tooltip_bg_pixel); n++;
1384 XtSetArg(args[n], XtNinternational, True); n++;
1385 XtSetArg(args[n], XtNfontSet, gui.tooltip_fontset); n++;
1386 beval->balloonLabel = XtCreateManagedWidget("balloonLabel",
1387 labelWidgetClass, beval->balloonShell, args, n);
1388#endif
1389}
1390
1391#endif /* !FEAT_GUI_GTK */
1392#endif /* !FEAT_GUI_W32 */
1393
1394#endif /* FEAT_BEVAL */