Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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