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