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