xref: /vim-8.2.3635/src/gui_gtk.c (revision 91acfffc)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * Porting to GTK+ was done by:
12  *
13  * (C) 1998,1999,2000 by Marcin Dalecki <[email protected]>
14  *
15  * With GREAT support and continuous encouragements by Andy Kahn and of
16  * course Bram Moolenaar!
17  *
18  * Support for GTK+ 2 was added by:
19  *
20  * (C) 2002,2003  Jason Hildebrand  <[email protected]>
21  *		  Daniel Elstner  <[email protected]>
22  *
23  * Best supporting actor (He helped somewhat, aesthetically speaking):
24  * Maxime Romano <[email protected]>
25  *
26  * Support for GTK+ 3 was added by:
27  *
28  * 2016  Kazunobu Kuriyama  <[email protected]>
29  *
30  * With the help of Marius Gedminas and the word of Bram Moolenaar,
31  * "Let's give this some time to mature."
32  */
33 
34 #include "vim.h"
35 
36 #ifdef FEAT_GUI_GTK
37 # include "gui_gtk_f.h"
38 #endif
39 
40 /* GTK defines MAX and MIN, but some system header files as well.  Undefine
41  * them and don't use them. */
42 #ifdef MIN
43 # undef MIN
44 #endif
45 #ifdef MAX
46 # undef MAX
47 #endif
48 
49 #ifdef FEAT_GUI_GNOME
50 /* Gnome redefines _() and N_().  Grrr... */
51 # ifdef _
52 #  undef _
53 # endif
54 # ifdef ngettext
55 #  undef ngettext
56 # endif
57 # ifdef N_
58 #  undef N_
59 # endif
60 # ifdef textdomain
61 #  undef textdomain
62 # endif
63 # ifdef bindtextdomain
64 #  undef bindtextdomain
65 # endif
66 # ifdef bind_textdomain_codeset
67 #  undef bind_textdomain_codeset
68 # endif
69 # if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
70 #  define ENABLE_NLS	/* so the texts in the dialog boxes are translated */
71 # endif
72 # include <gnome.h>
73 #endif
74 
75 #ifdef FEAT_GUI_GTK
76 # if GTK_CHECK_VERSION(3,0,0)
77 #  include <gdk/gdkkeysyms-compat.h>
78 # else
79 #  include <gdk/gdkkeysyms.h>
80 # endif
81 # include <gdk/gdk.h>
82 # ifdef WIN3264
83 #  include <gdk/gdkwin32.h>
84 # else
85 #  include <gdk/gdkx.h>
86 # endif
87 
88 # include <gtk/gtk.h>
89 #else
90 /* define these items to be able to generate prototypes without GTK */
91 typedef int GtkWidget;
92 # define gpointer int
93 # define guint8 int
94 # define GdkPixmap int
95 # define GdkBitmap int
96 # define GtkIconFactory int
97 # define GtkToolbar int
98 # define GtkAdjustment int
99 # define gboolean int
100 # define GdkEventKey int
101 # define CancelData int
102 #endif
103 
104 static void entry_activate_cb(GtkWidget *widget, gpointer data);
105 static void entry_changed_cb(GtkWidget *entry, GtkWidget *dialog);
106 static void find_replace_cb(GtkWidget *widget, gpointer data);
107 #if defined(FEAT_BROWSE) || defined(PROTO)
108 static void recent_func_log_func(
109 	const gchar *log_domain,
110 	GLogLevelFlags log_level,
111 	const gchar *message,
112 	gpointer user_data);
113 #endif
114 
115 #if defined(FEAT_TOOLBAR)
116 /*
117  * Table from BuiltIn## icon indices to GTK+ stock IDs.  Order must exactly
118  * match toolbar_names[] in menu.c!  All stock icons including the "vim-*"
119  * ones can be overridden in your gtkrc file.
120  */
121 # if GTK_CHECK_VERSION(3,10,0)
122 static const char * const menu_themed_names[] =
123 {
124     /* 00 */ "document-new",		/* sub. GTK_STOCK_NEW */
125     /* 01 */ "document-open",		/* sub. GTK_STOCK_OPEN */
126     /* 02 */ "document-save",		/* sub. GTK_STOCK_SAVE */
127     /* 03 */ "edit-undo",		/* sub. GTK_STOCK_UNDO */
128     /* 04 */ "edit-redo",		/* sub. GTK_STOCK_REDO */
129     /* 05 */ "edit-cut",		/* sub. GTK_STOCK_CUT */
130     /* 06 */ "edit-copy",		/* sub. GTK_STOCK_COPY */
131     /* 07 */ "edit-paste",		/* sub. GTK_STOCK_PASTE */
132     /* 08 */ "document-print",		/* sub. GTK_STOCK_PRINT */
133     /* 09 */ "help-browser",		/* sub. GTK_STOCK_HELP */
134     /* 10 */ "edit-find",		/* sub. GTK_STOCK_FIND */
135 #  if GTK_CHECK_VERSION(3,14,0)
136     /* Use the file names in gui_gtk_res.xml, cutting off the extension.
137      * Similar changes follow. */
138     /* 11 */ "stock_vim_save_all",
139     /* 12 */ "stock_vim_session_save",
140     /* 13 */ "stock_vim_session_new",
141     /* 14 */ "stock_vim_session_load",
142 #  else
143     /* 11 */ "vim-save-all",
144     /* 12 */ "vim-session-save",
145     /* 13 */ "vim-session-new",
146     /* 14 */ "vim-session-load",
147 #  endif
148     /* 15 */ "system-run",		/* sub. GTK_STOCK_EXECUTE */
149     /* 16 */ "edit-find-replace",	/* sub. GTK_STOCK_FIND_AND_REPLACE */
150     /* 17 */ "window-close",		/* sub. GTK_STOCK_CLOSE, FIXME: fuzzy */
151 #  if GTK_CHECK_VERSION(3,14,0)
152     /* 18 */ "stock_vim_window_maximize",
153     /* 19 */ "stock_vim_window_minimize",
154     /* 20 */ "stock_vim_window_split",
155     /* 21 */ "stock_vim_shell",
156 #  else
157     /* 18 */ "vim-window-maximize",
158     /* 19 */ "vim-window-minimize",
159     /* 20 */ "vim-window-split",
160     /* 21 */ "vim-shell",
161 #  endif
162     /* 22 */ "go-previous",		/* sub. GTK_STOCK_GO_BACK */
163     /* 23 */ "go-next",			/* sub. GTK_STOCK_GO_FORWARD */
164 #  if GTK_CHECK_VERSION(3,14,0)
165     /* 24 */ "stock_vim_find_help",
166 #  else
167     /* 24 */ "vim-find-help",
168 #  endif
169     /* 25 */ "gtk-convert",		/* sub. GTK_STOCK_CONVERT */
170     /* 26 */ "go-jump",			/* sub. GTK_STOCK_JUMP_TO */
171 #  if GTK_CHECK_VERSION(3,14,0)
172     /* 27 */ "stock_vim_build_tags",
173     /* 28 */ "stock_vim_window_split_vertical",
174     /* 29 */ "stock_vim_window_maximize_width",
175     /* 30 */ "stock_vim_window_minimize_width",
176 #  else
177     /* 27 */ "vim-build-tags",
178     /* 28 */ "vim-window-split-vertical",
179     /* 29 */ "vim-window-maximize-width",
180     /* 30 */ "vim-window-minimize-width",
181 #  endif
182     /* 31 */ "application-exit",	/* GTK_STOCK_QUIT */
183 };
184 # else /* !GTK_CHECK_VERSION(3,10,0) */
185 static const char * const menu_stock_ids[] =
186 {
187     /* 00 */ GTK_STOCK_NEW,
188     /* 01 */ GTK_STOCK_OPEN,
189     /* 02 */ GTK_STOCK_SAVE,
190     /* 03 */ GTK_STOCK_UNDO,
191     /* 04 */ GTK_STOCK_REDO,
192     /* 05 */ GTK_STOCK_CUT,
193     /* 06 */ GTK_STOCK_COPY,
194     /* 07 */ GTK_STOCK_PASTE,
195     /* 08 */ GTK_STOCK_PRINT,
196     /* 09 */ GTK_STOCK_HELP,
197     /* 10 */ GTK_STOCK_FIND,
198     /* 11 */ "vim-save-all",
199     /* 12 */ "vim-session-save",
200     /* 13 */ "vim-session-new",
201     /* 14 */ "vim-session-load",
202     /* 15 */ GTK_STOCK_EXECUTE,
203     /* 16 */ GTK_STOCK_FIND_AND_REPLACE,
204     /* 17 */ GTK_STOCK_CLOSE,		/* FIXME: fuzzy */
205     /* 18 */ "vim-window-maximize",
206     /* 19 */ "vim-window-minimize",
207     /* 20 */ "vim-window-split",
208     /* 21 */ "vim-shell",
209     /* 22 */ GTK_STOCK_GO_BACK,
210     /* 23 */ GTK_STOCK_GO_FORWARD,
211     /* 24 */ "vim-find-help",
212     /* 25 */ GTK_STOCK_CONVERT,
213     /* 26 */ GTK_STOCK_JUMP_TO,
214     /* 27 */ "vim-build-tags",
215     /* 28 */ "vim-window-split-vertical",
216     /* 29 */ "vim-window-maximize-width",
217     /* 30 */ "vim-window-minimize-width",
218     /* 31 */ GTK_STOCK_QUIT
219 };
220 # endif /* !GTK_CHECK_VERSION(3,10,0) */
221 
222 # ifdef USE_GRESOURCE
223 #  if !GTK_CHECK_VERSION(3,10,0)
224 typedef struct IconNames {
225     const char *icon_name;
226     const char *file_name;
227 } IconNames;
228 
229 static IconNames stock_vim_icons[] = {
230     { "vim-build-tags", "stock_vim_build_tags.png" },
231     { "vim-find-help", "stock_vim_find_help.png" },
232     { "vim-save-all", "stock_vim_save_all.png" },
233     { "vim-session-load", "stock_vim_session_load.png" },
234     { "vim-session-new", "stock_vim_session_new.png" },
235     { "vim-session-save", "stock_vim_session_save.png" },
236     { "vim-shell", "stock_vim_shell.png" },
237     { "vim-window-maximize", "stock_vim_window_maximize.png" },
238     { "vim-window-maximize-width", "stock_vim_window_maximize_width.png" },
239     { "vim-window-minimize", "stock_vim_window_minimize.png" },
240     { "vim-window-minimize-width", "stock_vim_window_minimize_width.png" },
241     { "vim-window-split", "stock_vim_window_split.png" },
242     { "vim-window-split-vertical", "stock_vim_window_split_vertical.png" },
243     { NULL, NULL }
244 };
245 #  endif
246 # endif /* USE_G_RESOURCE */
247 
248 # ifndef USE_GRESOURCE
249     static void
250 add_stock_icon(GtkIconFactory	*factory,
251 	       const char	*stock_id,
252 	       const guint8	*inline_data,
253 	       int		data_length)
254 {
255     GdkPixbuf	*pixbuf;
256     GtkIconSet	*icon_set;
257 
258     pixbuf = gdk_pixbuf_new_from_inline(data_length, inline_data, FALSE, NULL);
259     icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
260 
261     gtk_icon_factory_add(factory, stock_id, icon_set);
262 
263     gtk_icon_set_unref(icon_set);
264     g_object_unref(pixbuf);
265 }
266 # endif
267 
268     static int
269 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
270 {
271     expand_env(iconfile, dest, MAXPATHL);
272 
273     if (mch_isFullName(dest))
274     {
275 	return vim_fexists(dest);
276     }
277     else
278     {
279 	static const char   suffixes[][4] = {"png", "xpm", "bmp"};
280 	char_u		    buf[MAXPATHL];
281 	unsigned int	    i;
282 
283 	for (i = 0; i < G_N_ELEMENTS(suffixes); ++i)
284 	    if (gui_find_bitmap(dest, buf, (char *)suffixes[i]) == OK)
285 	    {
286 		STRCPY(dest, buf);
287 		return TRUE;
288 	    }
289 
290 	return FALSE;
291     }
292 }
293 
294     static GtkWidget *
295 load_menu_iconfile(char_u *name, GtkIconSize icon_size)
296 {
297     GtkWidget	    *image = NULL;
298 # if GTK_CHECK_VERSION(3,10,0)
299     int		     pixel_size = -1;
300 
301     switch (icon_size)
302     {
303 	case GTK_ICON_SIZE_MENU:
304 	    pixel_size = 16;
305 	    break;
306 	case GTK_ICON_SIZE_SMALL_TOOLBAR:
307 	    pixel_size = 16;
308 	    break;
309 	case GTK_ICON_SIZE_LARGE_TOOLBAR:
310 	    pixel_size = 24;
311 	    break;
312 	case GTK_ICON_SIZE_BUTTON:
313 	    pixel_size = 16;
314 	    break;
315 	case GTK_ICON_SIZE_DND:
316 	    pixel_size = 32;
317 	    break;
318 	case GTK_ICON_SIZE_DIALOG:
319 	    pixel_size = 48;
320 	    break;
321 	case GTK_ICON_SIZE_INVALID:
322 	    /* FALLTHROUGH */
323 	default:
324 	    pixel_size = 0;
325 	    break;
326     }
327 
328     if (pixel_size > 0 || pixel_size == -1)
329     {
330 	GdkPixbuf * const pixbuf
331 	    = gdk_pixbuf_new_from_file_at_scale((const char *)name,
332 		pixel_size, pixel_size, TRUE, NULL);
333 	if (pixbuf != NULL)
334 	{
335 	    image = gtk_image_new_from_pixbuf(pixbuf);
336 	    g_object_unref(pixbuf);
337 	}
338     }
339     if (image == NULL)
340 	image = gtk_image_new_from_icon_name("image-missing", icon_size);
341 
342     return image;
343 # else /* !GTK_CHECK_VERSION(3,10,0) */
344     GtkIconSet	    *icon_set;
345     GtkIconSource   *icon_source;
346 
347     /*
348      * Rather than loading the icon directly into a GtkImage, create
349      * a new GtkIconSet and put it in there.  This way we can easily
350      * scale the toolbar icons on the fly when needed.
351      */
352     icon_set = gtk_icon_set_new();
353     icon_source = gtk_icon_source_new();
354 
355     gtk_icon_source_set_filename(icon_source, (const char *)name);
356     gtk_icon_set_add_source(icon_set, icon_source);
357 
358     image = gtk_image_new_from_icon_set(icon_set, icon_size);
359 
360     gtk_icon_source_free(icon_source);
361     gtk_icon_set_unref(icon_set);
362 
363     return image;
364 # endif /* !GTK_CHECK_VERSION(3,10,0) */
365 }
366 
367     static GtkWidget *
368 create_menu_icon(vimmenu_T *menu, GtkIconSize icon_size)
369 {
370     GtkWidget	*image = NULL;
371     char_u	buf[MAXPATHL];
372 
373     /* First use a specified "icon=" argument. */
374     if (menu->iconfile != NULL && lookup_menu_iconfile(menu->iconfile, buf))
375 	image = load_menu_iconfile(buf, icon_size);
376 
377     /* If not found and not builtin specified try using the menu name. */
378     if (image == NULL && !menu->icon_builtin
379 				     && lookup_menu_iconfile(menu->name, buf))
380 	image = load_menu_iconfile(buf, icon_size);
381 
382     /* Still not found?  Then use a builtin icon, a blank one as fallback. */
383     if (image == NULL)
384     {
385 # if GTK_CHECK_VERSION(3,10,0)
386 	const char *icon_name = NULL;
387 	const int   n_names = G_N_ELEMENTS(menu_themed_names);
388 
389 	if (menu->iconidx >= 0 && menu->iconidx < n_names)
390 	    icon_name = menu_themed_names[menu->iconidx];
391 	if (icon_name == NULL)
392 	    icon_name = "image-missing";
393 
394 	image = gtk_image_new_from_icon_name(icon_name, icon_size);
395 # else
396 	const char  *stock_id;
397 	const int   n_ids = G_N_ELEMENTS(menu_stock_ids);
398 
399 	if (menu->iconidx >= 0 && menu->iconidx < n_ids)
400 	    stock_id = menu_stock_ids[menu->iconidx];
401 	else
402 	    stock_id = GTK_STOCK_MISSING_IMAGE;
403 
404 	image = gtk_image_new_from_stock(stock_id, icon_size);
405 # endif
406     }
407 
408     return image;
409 }
410 
411     static gint
412 toolbar_button_focus_in_event(GtkWidget *widget UNUSED,
413 			      GdkEventFocus *event UNUSED,
414 			      gpointer data UNUSED)
415 {
416     /* When we're in a GtkPlug, we don't have window focus events, only widget
417      * focus.  To emulate stand-alone gvim, if a button gets focus (e.g.,
418      * <Tab> into GtkPlug) immediately pass it to mainwin. */
419     if (gtk_socket_id != 0)
420 	gtk_widget_grab_focus(gui.drawarea);
421 
422     return TRUE;
423 }
424 #endif /* FEAT_TOOLBAR */
425 
426 #if defined(FEAT_TOOLBAR) || defined(PROTO)
427 
428     void
429 gui_gtk_register_stock_icons(void)
430 {
431 # ifndef USE_GRESOURCE
432 #  include "../pixmaps/stock_icons.h"
433     GtkIconFactory *factory;
434 
435     factory = gtk_icon_factory_new();
436 #  define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
437 
438     ADD_ICON("vim-build-tags",		  stock_vim_build_tags);
439     ADD_ICON("vim-find-help",		  stock_vim_find_help);
440     ADD_ICON("vim-save-all",		  stock_vim_save_all);
441     ADD_ICON("vim-session-load",	  stock_vim_session_load);
442     ADD_ICON("vim-session-new",		  stock_vim_session_new);
443     ADD_ICON("vim-session-save",	  stock_vim_session_save);
444     ADD_ICON("vim-shell",		  stock_vim_shell);
445     ADD_ICON("vim-window-maximize",	  stock_vim_window_maximize);
446     ADD_ICON("vim-window-maximize-width", stock_vim_window_maximize_width);
447     ADD_ICON("vim-window-minimize",	  stock_vim_window_minimize);
448     ADD_ICON("vim-window-minimize-width", stock_vim_window_minimize_width);
449     ADD_ICON("vim-window-split",	  stock_vim_window_split);
450     ADD_ICON("vim-window-split-vertical", stock_vim_window_split_vertical);
451 
452 #  undef ADD_ICON
453 
454     gtk_icon_factory_add_default(factory);
455     g_object_unref(factory);
456 # else /* defined(USE_GRESOURCE) */
457     const char * const path_prefix = "/org/vim/gui/icon";
458 #  if GTK_CHECK_VERSION(3,14,0)
459     GdkScreen    *screen = NULL;
460     GtkIconTheme *icon_theme = NULL;
461 
462     if (GTK_IS_WIDGET(gui.mainwin))
463 	screen = gtk_widget_get_screen(gui.mainwin);
464     else
465 	screen = gdk_screen_get_default();
466     icon_theme = gtk_icon_theme_get_for_screen(screen);
467     gtk_icon_theme_add_resource_path(icon_theme, path_prefix);
468 #  elif GTK_CHECK_VERSION(3,0,0)
469     IconNames *names;
470 
471     for (names = stock_vim_icons; names->icon_name != NULL; names++)
472     {
473 	char path[MAXPATHL];
474 
475 	vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
476 	GdkPixbuf *pixbuf = NULL;
477 	pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
478 	if (pixbuf != NULL)
479 	{
480 	    const gint size = MAX(gdk_pixbuf_get_width(pixbuf),
481 				  gdk_pixbuf_get_height(pixbuf));
482 	    if (size > 16)
483 	    {
484 		/* An icon theme is supposed to provide fixed-size
485 		 * image files for each size, e.g., 16, 22, 24, ...
486 		 * Naturally, in contrast to GtkIconSet, GtkIconTheme
487 		 * won't prepare size variants for us out of a single
488 		 * fixed-size image.
489 		 *
490 		 * Currently, Vim provides 24x24 images only while the
491 		 * icon size on the menu and the toolbar is set to 16x16
492 		 * by default.
493 		 *
494 		 * Resize them by ourselves until we have our own fully
495 		 * fledged icon theme. */
496 		GdkPixbuf *src = pixbuf;
497 		pixbuf = gdk_pixbuf_scale_simple(src,
498 						 16, 16,
499 						 GDK_INTERP_BILINEAR);
500 		if (pixbuf == NULL)
501 		    pixbuf = src;
502 		else
503 		    g_object_unref(src);
504 	    }
505 	    gtk_icon_theme_add_builtin_icon(names->icon_name, size, pixbuf);
506 	    g_object_unref(pixbuf);
507 	}
508     }
509 #  else /* !GTK_CHECK_VERSION(3,0.0) */
510     GtkIconFactory * const factory = gtk_icon_factory_new();
511     IconNames *names;
512 
513     for (names = stock_vim_icons; names->icon_name != NULL; names++)
514     {
515 	char path[MAXPATHL];
516 	GdkPixbuf *pixbuf;
517 
518 	vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
519 	pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
520 	if (pixbuf != NULL)
521 	{
522 	    GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
523 	    gtk_icon_factory_add(factory, names->icon_name, icon_set);
524 	    gtk_icon_set_unref(icon_set);
525 	    g_object_unref(pixbuf);
526 	}
527     }
528 
529     gtk_icon_factory_add_default(factory);
530     g_object_unref(factory);
531 #  endif /* !GTK_CHECK_VERSION(3,0,0) */
532 # endif /* defined(USE_GRESOURCE) */
533 }
534 
535 #endif /* FEAT_TOOLBAR */
536 
537 #if defined(FEAT_MENU) || defined(PROTO)
538 
539 /*
540  * Translate Vim's mnemonic tagging to GTK+ style and convert to UTF-8
541  * if necessary.  The caller must vim_free() the returned string.
542  *
543  *	Input	Output
544  *	_	__
545  *	&&	&
546  *	&	_	stripped if use_mnemonic == FALSE
547  *	<Tab>		end of menu label text
548  */
549     static char_u *
550 translate_mnemonic_tag(char_u *name, int use_mnemonic)
551 {
552     char_u  *buf;
553     char_u  *psrc;
554     char_u  *pdest;
555     int	    n_underscores = 0;
556 
557     name = CONVERT_TO_UTF8(name);
558     if (name == NULL)
559 	return NULL;
560 
561     for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
562 	if (*psrc == '_')
563 	    ++n_underscores;
564 
565     buf = alloc((unsigned)(psrc - name + n_underscores + 1));
566     if (buf != NULL)
567     {
568 	pdest = buf;
569 	for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
570 	{
571 	    if (*psrc == '_')
572 	    {
573 		*pdest++ = '_';
574 		*pdest++ = '_';
575 	    }
576 	    else if (*psrc != '&')
577 	    {
578 		*pdest++ = *psrc;
579 	    }
580 	    else if (*(psrc + 1) == '&')
581 	    {
582 		*pdest++ = *psrc++;
583 	    }
584 	    else if (use_mnemonic)
585 	    {
586 		*pdest++ = '_';
587 	    }
588 	}
589 	*pdest = NUL;
590     }
591 
592     CONVERT_TO_UTF8_FREE(name);
593     return buf;
594 }
595 
596     static void
597 menu_item_new(vimmenu_T *menu, GtkWidget *parent_widget)
598 {
599     GtkWidget	*box;
600     char_u	*text;
601     int		use_mnemonic;
602 
603     /* It would be neat to have image menu items, but that would require major
604      * changes to Vim's menu system.  Not to mention that all the translations
605      * had to be updated. */
606     menu->id = gtk_menu_item_new();
607 # if GTK_CHECK_VERSION(3,2,0)
608     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
609     gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
610 # else
611     box = gtk_hbox_new(FALSE, 20);
612 # endif
613 
614     use_mnemonic = (p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget));
615     text = translate_mnemonic_tag(menu->name, use_mnemonic);
616 
617     menu->label = gtk_label_new_with_mnemonic((const char *)text);
618     vim_free(text);
619 
620     gtk_box_pack_start(GTK_BOX(box), menu->label, FALSE, FALSE, 0);
621 
622     if (menu->actext != NULL && menu->actext[0] != NUL)
623     {
624 	text = CONVERT_TO_UTF8(menu->actext);
625 
626 	gtk_box_pack_end(GTK_BOX(box),
627 			 gtk_label_new((const char *)text),
628 			 FALSE, FALSE, 0);
629 
630 	CONVERT_TO_UTF8_FREE(text);
631     }
632 
633     gtk_container_add(GTK_CONTAINER(menu->id), box);
634     gtk_widget_show_all(menu->id);
635 }
636 
637     void
638 gui_mch_add_menu(vimmenu_T *menu, int idx)
639 {
640     vimmenu_T	*parent;
641     GtkWidget	*parent_widget;
642 
643     if (menu->name[0] == ']' || menu_is_popup(menu->name))
644     {
645 	menu->submenu_id = gtk_menu_new();
646 	return;
647     }
648 
649     parent = menu->parent;
650 
651     if ((parent != NULL && parent->submenu_id == NULL)
652 	    || !menu_is_menubar(menu->name))
653 	return;
654 
655     parent_widget = (parent != NULL) ? parent->submenu_id : gui.menubar;
656     menu_item_new(menu, parent_widget);
657 
658     /* since the tearoff should always appear first, increment idx */
659     if (parent != NULL && !menu_is_popup(parent->name))
660 	++idx;
661 
662     gtk_menu_shell_insert(GTK_MENU_SHELL(parent_widget), menu->id, idx);
663 
664     menu->submenu_id = gtk_menu_new();
665 
666     gtk_menu_set_accel_group(GTK_MENU(menu->submenu_id), gui.accel_group);
667     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->id), menu->submenu_id);
668 
669 # if !GTK_CHECK_VERSION(3,4,0)
670     menu->tearoff_handle = gtk_tearoff_menu_item_new();
671     if (vim_strchr(p_go, GO_TEAROFF) != NULL)
672 	gtk_widget_show(menu->tearoff_handle);
673 #  if GTK_CHECK_VERSION(3,0,0)
674     gtk_menu_shell_prepend(GTK_MENU_SHELL(menu->submenu_id),
675 	    menu->tearoff_handle);
676 #  else
677     gtk_menu_prepend(GTK_MENU(menu->submenu_id), menu->tearoff_handle);
678 #  endif
679 # endif
680 }
681 
682     static void
683 menu_item_activate(GtkWidget *widget UNUSED, gpointer data)
684 {
685     gui_menu_cb((vimmenu_T *)data);
686 }
687 
688     void
689 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
690 {
691     vimmenu_T *parent;
692 
693     parent = menu->parent;
694 
695 # ifdef FEAT_TOOLBAR
696     if (menu_is_toolbar(parent->name))
697     {
698 	GtkToolbar *toolbar;
699 
700 	toolbar = GTK_TOOLBAR(gui.toolbar);
701 	menu->submenu_id = NULL;
702 
703 	if (menu_is_separator(menu->name))
704 	{
705 #  if GTK_CHECK_VERSION(3,0,0)
706 	    GtkToolItem *item = gtk_separator_tool_item_new();
707 	    gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(item),
708 		    TRUE);
709 	    gtk_tool_item_set_expand(GTK_TOOL_ITEM(item), FALSE);
710 	    gtk_widget_show(GTK_WIDGET(item));
711 
712 	    gtk_toolbar_insert(toolbar, item, idx);
713 #  else
714 	    gtk_toolbar_insert_space(toolbar, idx);
715 #  endif
716 	    menu->id = NULL;
717 	}
718 	else
719 	{
720 	    char_u *text;
721 	    char_u *tooltip;
722 
723 	    text    = CONVERT_TO_UTF8(menu->dname);
724 	    tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
725 	    if (tooltip != NULL && !utf_valid_string(tooltip, NULL))
726 		/* Invalid text, can happen when 'encoding' is changed.  Avoid
727 		 * a nasty GTK error message, skip the tooltip. */
728 		CONVERT_TO_UTF8_FREE(tooltip);
729 
730 #  if GTK_CHECK_VERSION(3,0,0)
731 	    {
732 		GtkWidget *icon;
733 		GtkToolItem *item;
734 
735 		icon = create_menu_icon(menu,
736 			gtk_toolbar_get_icon_size(toolbar));
737 		item = gtk_tool_button_new(icon, (const gchar *)text);
738 		gtk_tool_item_set_tooltip_text(item, (const gchar *)tooltip);
739 		g_signal_connect(G_OBJECT(item), "clicked",
740 			G_CALLBACK(&menu_item_activate), menu);
741 		gtk_widget_show_all(GTK_WIDGET(item));
742 
743 		gtk_toolbar_insert(toolbar, item, idx);
744 
745 		menu->id = GTK_WIDGET(item);
746 	    }
747 #  else
748 	    menu->id = gtk_toolbar_insert_item(
749 		    toolbar,
750 		    (const char *)text,
751 		    (const char *)tooltip,
752 		    NULL,
753 		    create_menu_icon(menu, gtk_toolbar_get_icon_size(toolbar)),
754 		    G_CALLBACK(&menu_item_activate),
755 		    menu,
756 		    idx);
757 #  endif
758 
759 	    if (gtk_socket_id != 0)
760 #  if GTK_CHECK_VERSION(3,0,0)
761 		g_signal_connect(G_OBJECT(menu->id), "focus-in-event",
762 			G_CALLBACK(toolbar_button_focus_in_event), NULL);
763 #  else
764 		gtk_signal_connect(GTK_OBJECT(menu->id), "focus_in_event",
765 			GTK_SIGNAL_FUNC(toolbar_button_focus_in_event), NULL);
766 #  endif
767 
768 	    CONVERT_TO_UTF8_FREE(text);
769 	    CONVERT_TO_UTF8_FREE(tooltip);
770 	}
771     }
772     else
773 # endif /* FEAT_TOOLBAR */
774     {
775 	/* No parent, must be a non-menubar menu */
776 	if (parent == NULL || parent->submenu_id == NULL)
777 	    return;
778 
779 	/* Make place for the possible tearoff handle item.  Not in the popup
780 	 * menu, it doesn't have a tearoff item. */
781 	if (!menu_is_popup(parent->name))
782 	    ++idx;
783 
784 	if (menu_is_separator(menu->name))
785 	{
786 	    /* Separator: Just add it */
787 # if GTK_CHECK_VERSION(3,0,0)
788 	    menu->id = gtk_separator_menu_item_new();
789 # else
790 	    menu->id = gtk_menu_item_new();
791 	    gtk_widget_set_sensitive(menu->id, FALSE);
792 # endif
793 	    gtk_widget_show(menu->id);
794 # if GTK_CHECK_VERSION(3,0,0)
795 	    gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id),
796 		    menu->id, idx);
797 # else
798 	    gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
799 # endif
800 
801 	    return;
802 	}
803 
804 	/* Add textual menu item. */
805 	menu_item_new(menu, parent->submenu_id);
806 	gtk_widget_show(menu->id);
807 # if GTK_CHECK_VERSION(3,0,0)
808 	gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id),
809 		menu->id, idx);
810 # else
811 	gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
812 # endif
813 
814 	if (menu->id != NULL)
815 # if GTK_CHECK_VERSION(3,0,0)
816 	    g_signal_connect(G_OBJECT(menu->id), "activate",
817 			     G_CALLBACK(menu_item_activate), menu);
818 # else
819 	    gtk_signal_connect(GTK_OBJECT(menu->id), "activate",
820 			       GTK_SIGNAL_FUNC(menu_item_activate), menu);
821 # endif
822     }
823 }
824 #endif /* FEAT_MENU */
825 
826 
827     void
828 gui_mch_set_text_area_pos(int x, int y, int w, int h)
829 {
830     gtk_form_move_resize(GTK_FORM(gui.formwin), gui.drawarea, x, y, w, h);
831 }
832 
833 
834 #if defined(FEAT_MENU) || defined(PROTO)
835 /*
836  * Enable or disable accelerators for the toplevel menus.
837  */
838     void
839 gui_gtk_set_mnemonics(int enable)
840 {
841     vimmenu_T	*menu;
842     char_u	*name;
843 
844     for (menu = root_menu; menu != NULL; menu = menu->next)
845     {
846 	if (menu->id == NULL)
847 	    continue;
848 
849 	name = translate_mnemonic_tag(menu->name, enable);
850 	gtk_label_set_text_with_mnemonic(GTK_LABEL(menu->label),
851 					 (const char *)name);
852 	vim_free(name);
853     }
854 }
855 
856 # if !GTK_CHECK_VERSION(3,4,0)
857     static void
858 recurse_tearoffs(vimmenu_T *menu, int val)
859 {
860     for (; menu != NULL; menu = menu->next)
861     {
862 	if (menu->submenu_id != NULL && menu->tearoff_handle != NULL
863 		&& menu->name[0] != ']' && !menu_is_popup(menu->name))
864 	{
865 	    if (val)
866 		gtk_widget_show(menu->tearoff_handle);
867 	    else
868 		gtk_widget_hide(menu->tearoff_handle);
869 	}
870 	recurse_tearoffs(menu->children, val);
871     }
872 }
873 # endif
874 
875 # if GTK_CHECK_VERSION(3,4,0)
876     void
877 gui_mch_toggle_tearoffs(int enable UNUSED)
878 {
879     /* Do nothing */
880 }
881 # else
882     void
883 gui_mch_toggle_tearoffs(int enable)
884 {
885     recurse_tearoffs(root_menu, enable);
886 }
887 # endif
888 #endif /* FEAT_MENU */
889 
890 #if defined(FEAT_TOOLBAR)
891     static int
892 get_menu_position(vimmenu_T *menu)
893 {
894     vimmenu_T	*node;
895     int		idx = 0;
896 
897     for (node = menu->parent->children; node != menu; node = node->next)
898     {
899 	g_return_val_if_fail(node != NULL, -1);
900 	++idx;
901     }
902 
903     return idx;
904 }
905 #endif /* FEAT_TOOLBAR */
906 
907 
908 #if defined(FEAT_TOOLBAR) || defined(PROTO)
909     void
910 gui_mch_menu_set_tip(vimmenu_T *menu)
911 {
912     if (menu->id != NULL && menu->parent != NULL
913 	    && gui.toolbar != NULL && menu_is_toolbar(menu->parent->name))
914     {
915 	char_u *tooltip;
916 
917 	tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
918 	if (tooltip != NULL && utf_valid_string(tooltip, NULL))
919 # if GTK_CHECK_VERSION(3,0,0)
920 	    /* Only set the tooltip when it's valid utf-8. */
921 	    gtk_widget_set_tooltip_text(menu->id, (const gchar *)tooltip);
922 # else
923 	    /* Only set the tooltip when it's valid utf-8. */
924 	    gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
925 				 menu->id, (const char *)tooltip, NULL);
926 # endif
927 	CONVERT_TO_UTF8_FREE(tooltip);
928     }
929 }
930 #endif /* FEAT_TOOLBAR */
931 
932 
933 #if defined(FEAT_MENU) || defined(PROTO)
934 /*
935  * Destroy the machine specific menu widget.
936  */
937     void
938 gui_mch_destroy_menu(vimmenu_T *menu)
939 {
940     /* Don't let gtk_container_remove automatically destroy menu->id. */
941     if (menu->id != NULL)
942 	g_object_ref(menu->id);
943 
944     /* Workaround for a spurious gtk warning in Ubuntu: "Trying to remove
945      * a child that doesn't believe we're it's parent."
946      * Remove widget from gui.menubar before destroying it. */
947     if (menu->id != NULL && gui.menubar != NULL
948 			    && gtk_widget_get_parent(menu->id) == gui.menubar)
949 	gtk_container_remove(GTK_CONTAINER(gui.menubar), menu->id);
950 
951 # ifdef FEAT_TOOLBAR
952     if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
953     {
954 	if (menu_is_separator(menu->name))
955 #  if GTK_CHECK_VERSION(3,0,0)
956 	{
957 	    GtkToolItem *item = NULL;
958 
959 	    item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(gui.toolbar),
960 					    get_menu_position(menu));
961 	    if (item != NULL)
962 		gtk_container_remove(GTK_CONTAINER(gui.toolbar),
963 				     GTK_WIDGET(item));
964 	}
965 #  else
966 	    gtk_toolbar_remove_space(GTK_TOOLBAR(gui.toolbar),
967 				     get_menu_position(menu));
968 #  endif
969 	else if (menu->id != NULL)
970 	    gtk_widget_destroy(menu->id);
971     }
972     else
973 # endif /* FEAT_TOOLBAR */
974     {
975 	if (menu->submenu_id != NULL)
976 	    gtk_widget_destroy(menu->submenu_id);
977 
978 	if (menu->id != NULL)
979 	    gtk_widget_destroy(menu->id);
980     }
981 
982     if (menu->id != NULL)
983 	g_object_unref(menu->id);
984     menu->submenu_id = NULL;
985     menu->id = NULL;
986 }
987 #endif /* FEAT_MENU */
988 
989 
990 /*
991  * Scrollbar stuff.
992  */
993     void
994 gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max)
995 {
996     if (sb->id != NULL)
997     {
998 	GtkAdjustment *adjustment;
999 
1000 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
1001 
1002 #if GTK_CHECK_VERSION(3,0,0)
1003 	gtk_adjustment_set_lower(adjustment, 0.0);
1004 	gtk_adjustment_set_value(adjustment, val);
1005 	gtk_adjustment_set_upper(adjustment, max + 1);
1006 	gtk_adjustment_set_page_size(adjustment, size);
1007 	gtk_adjustment_set_page_increment(adjustment,
1008 					  size < 3L ? 1L : size - 2L);
1009 	gtk_adjustment_set_step_increment(adjustment, 1.0);
1010 #else
1011 	adjustment->lower = 0.0;
1012 	adjustment->value = val;
1013 	adjustment->upper = max + 1;
1014 	adjustment->page_size = size;
1015 	adjustment->page_increment = size < 3L ? 1L : size - 2L;
1016 	adjustment->step_increment = 1.0;
1017 #endif
1018 
1019 #if GTK_CHECK_VERSION(3,0,0)
1020 	g_signal_handler_block(G_OBJECT(adjustment),
1021 						      (gulong)sb->handler_id);
1022 #else
1023 	g_signal_handler_block(GTK_OBJECT(adjustment),
1024 						      (gulong)sb->handler_id);
1025 #endif
1026 
1027 #if !GTK_CHECK_VERSION(3,18,0)
1028 	gtk_adjustment_changed(adjustment);
1029 #endif
1030 
1031 #if GTK_CHECK_VERSION(3,0,0)
1032 	g_signal_handler_unblock(G_OBJECT(adjustment),
1033 						      (gulong)sb->handler_id);
1034 #else
1035 	g_signal_handler_unblock(GTK_OBJECT(adjustment),
1036 						      (gulong)sb->handler_id);
1037 #endif
1038     }
1039 }
1040 
1041     void
1042 gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h)
1043 {
1044     if (sb->id != NULL)
1045 	gtk_form_move_resize(GTK_FORM(gui.formwin), sb->id, x, y, w, h);
1046 }
1047 
1048 /*
1049  * Take action upon scrollbar dragging.
1050  */
1051     static void
1052 adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
1053 {
1054     scrollbar_T	*sb;
1055     long	value;
1056     int		dragging = FALSE;
1057 
1058 #ifdef FEAT_XIM
1059     /* cancel any preediting */
1060     if (im_is_preediting())
1061 	xim_reset();
1062 #endif
1063 
1064     sb = gui_find_scrollbar((long)data);
1065 #if GTK_CHECK_VERSION(3,0,0)
1066     value = gtk_adjustment_get_value(adjustment);
1067 #else
1068     value = (long)adjustment->value;
1069 #endif
1070 #if !GTK_CHECK_VERSION(3,0,0)
1071     /*
1072      * The dragging argument must be right for the scrollbar to work with
1073      * closed folds.  This isn't documented, hopefully this will keep on
1074      * working in later GTK versions.
1075      *
1076      * FIXME: Well, it doesn't work in GTK2. :)
1077      * HACK: Get the mouse pointer position, if it appears to be on an arrow
1078      * button set "dragging" to FALSE.  This assumes square buttons!
1079      */
1080     if (sb != NULL)
1081     {
1082 	dragging = TRUE;
1083 
1084 	if (sb->wp != NULL)
1085 	{
1086 	    int			x;
1087 	    int			y;
1088 	    GdkModifierType	state;
1089 	    int			width;
1090 	    int			height;
1091 
1092 	    /* vertical scrollbar: need to set "dragging" properly in case
1093 	     * there are closed folds. */
1094 	    gdk_window_get_pointer(sb->id->window, &x, &y, &state);
1095 	    gdk_window_get_size(sb->id->window, &width, &height);
1096 	    if (x >= 0 && x < width && y >= 0 && y < height)
1097 	    {
1098 		if (y < width)
1099 		{
1100 		    /* up arrow: move one (closed fold) line up */
1101 		    dragging = FALSE;
1102 		    value = sb->wp->w_topline - 2;
1103 		}
1104 		else if (y > height - width)
1105 		{
1106 		    /* down arrow: move one (closed fold) line down */
1107 		    dragging = FALSE;
1108 		    value = sb->wp->w_topline;
1109 		}
1110 	    }
1111 	}
1112     }
1113 #endif /* !GTK_CHECK_VERSION(3,0,0) */
1114     gui_drag_scrollbar(sb, value, dragging);
1115 }
1116 
1117 /* SBAR_VERT or SBAR_HORIZ */
1118     void
1119 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
1120 {
1121     if (orient == SBAR_HORIZ)
1122 #if GTK_CHECK_VERSION(3,2,0)
1123 	sb->id = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
1124 #else
1125 	sb->id = gtk_hscrollbar_new(NULL);
1126 #endif
1127     else if (orient == SBAR_VERT)
1128 #if GTK_CHECK_VERSION(3,2,0)
1129 	sb->id = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
1130 #else
1131 	sb->id = gtk_vscrollbar_new(NULL);
1132 #endif
1133 
1134     if (sb->id != NULL)
1135     {
1136 	GtkAdjustment *adjustment;
1137 
1138 #if GTK_CHECK_VERSION(3,0,0)
1139 	gtk_widget_set_can_focus(sb->id, FALSE);
1140 #else
1141 	GTK_WIDGET_UNSET_FLAGS(sb->id, GTK_CAN_FOCUS);
1142 #endif
1143 	gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
1144 
1145 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
1146 
1147 #if GTK_CHECK_VERSION(3,0,0)
1148 	sb->handler_id = g_signal_connect(
1149 			     G_OBJECT(adjustment), "value-changed",
1150 			     G_CALLBACK(adjustment_value_changed),
1151 			     GINT_TO_POINTER(sb->ident));
1152 #else
1153 	sb->handler_id = gtk_signal_connect(
1154 			     GTK_OBJECT(adjustment), "value_changed",
1155 			     GTK_SIGNAL_FUNC(adjustment_value_changed),
1156 			     GINT_TO_POINTER(sb->ident));
1157 #endif
1158 	gui_mch_update();
1159     }
1160 }
1161 
1162 #if defined(FEAT_WINDOWS) || defined(PROTO)
1163     void
1164 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1165 {
1166     if (sb->id != NULL)
1167     {
1168 	gtk_widget_destroy(sb->id);
1169 	sb->id = NULL;
1170     }
1171     gui_mch_update();
1172 }
1173 #endif
1174 
1175 #if defined(FEAT_BROWSE) || defined(PROTO)
1176 /*
1177  * Implementation of the file selector related stuff
1178  */
1179 
1180 #ifndef USE_FILE_CHOOSER
1181     static void
1182 browse_ok_cb(GtkWidget *widget UNUSED, gpointer cbdata)
1183 {
1184     gui_T *vw = (gui_T *)cbdata;
1185 
1186     if (vw->browse_fname != NULL)
1187 	g_free(vw->browse_fname);
1188 
1189     vw->browse_fname = (char_u *)g_strdup(gtk_file_selection_get_filename(
1190 					GTK_FILE_SELECTION(vw->filedlg)));
1191     gtk_widget_hide(vw->filedlg);
1192 }
1193 
1194     static void
1195 browse_cancel_cb(GtkWidget *widget UNUSED, gpointer cbdata)
1196 {
1197     gui_T *vw = (gui_T *)cbdata;
1198 
1199     if (vw->browse_fname != NULL)
1200     {
1201 	g_free(vw->browse_fname);
1202 	vw->browse_fname = NULL;
1203     }
1204     gtk_widget_hide(vw->filedlg);
1205 }
1206 
1207     static gboolean
1208 browse_destroy_cb(GtkWidget *widget UNUSED)
1209 {
1210     if (gui.browse_fname != NULL)
1211     {
1212 	g_free(gui.browse_fname);
1213 	gui.browse_fname = NULL;
1214     }
1215     gui.filedlg = NULL;
1216     gtk_main_quit();
1217     return FALSE;
1218 }
1219 #endif
1220 
1221 /*
1222  * Put up a file requester.
1223  * Returns the selected name in allocated memory, or NULL for Cancel.
1224  * saving,			select file to write
1225  * title			title for the window
1226  * dflt				default name
1227  * ext				not used (extension added)
1228  * initdir			initial directory, NULL for current dir
1229  * filter			not used (file name filter)
1230  */
1231     char_u *
1232 gui_mch_browse(int saving UNUSED,
1233 	       char_u *title,
1234 	       char_u *dflt,
1235 	       char_u *ext UNUSED,
1236 	       char_u *initdir,
1237 	       char_u *filter)
1238 {
1239 #ifdef USE_FILE_CHOOSER
1240     GtkWidget		*fc;
1241 #endif
1242     char_u		dirbuf[MAXPATHL];
1243     guint		log_handler;
1244     const gchar		*domain = "Gtk";
1245 
1246     title = CONVERT_TO_UTF8(title);
1247 
1248     /* GTK has a bug, it only works with an absolute path. */
1249     if (initdir == NULL || *initdir == NUL)
1250 	mch_dirname(dirbuf, MAXPATHL);
1251     else if (vim_FullName(initdir, dirbuf, MAXPATHL - 2, FALSE) == FAIL)
1252 	dirbuf[0] = NUL;
1253     /* Always need a trailing slash for a directory. */
1254     add_pathsep(dirbuf);
1255 
1256     /* If our pointer is currently hidden, then we should show it. */
1257     gui_mch_mousehide(FALSE);
1258 
1259     /* Hack: The GTK file dialog warns when it can't access a new file, this
1260      * makes it shut up. http://bugzilla.gnome.org/show_bug.cgi?id=664587 */
1261     log_handler = g_log_set_handler(domain, G_LOG_LEVEL_WARNING,
1262 						  recent_func_log_func, NULL);
1263 
1264 #ifdef USE_FILE_CHOOSER
1265     /* We create the dialog each time, so that the button text can be "Open"
1266      * or "Save" according to the action. */
1267     fc = gtk_file_chooser_dialog_new((const gchar *)title,
1268 	    GTK_WINDOW(gui.mainwin),
1269 	    saving ? GTK_FILE_CHOOSER_ACTION_SAVE
1270 					   : GTK_FILE_CHOOSER_ACTION_OPEN,
1271 # if GTK_CHECK_VERSION(3,10,0)
1272 	    _("_Cancel"), GTK_RESPONSE_CANCEL,
1273 	    saving ? _("_Save") : _("_Open"), GTK_RESPONSE_ACCEPT,
1274 # else
1275 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1276 	    saving ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
1277 # endif
1278 	    NULL);
1279     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),
1280 						       (const gchar *)dirbuf);
1281 
1282     if (filter != NULL && *filter != NUL)
1283     {
1284 	int     i = 0;
1285 	char_u  *patt;
1286 	char_u  *p = filter;
1287 	GtkFileFilter	*gfilter;
1288 
1289 	gfilter = gtk_file_filter_new();
1290 	patt = alloc(STRLEN(filter));
1291 	while (p != NULL && *p != NUL)
1292 	{
1293 	    if (*p == '\n' || *p == ';' || *p == '\t')
1294 	    {
1295 		STRNCPY(patt, filter, i);
1296 		patt[i] = '\0';
1297 		if (*p == '\t')
1298 		    gtk_file_filter_set_name(gfilter, (gchar *)patt);
1299 		else
1300 		{
1301 		    gtk_file_filter_add_pattern(gfilter, (gchar *)patt);
1302 		    if (*p == '\n')
1303 		    {
1304 			gtk_file_chooser_add_filter((GtkFileChooser *)fc,
1305 								     gfilter);
1306 			if (*(p + 1) != NUL)
1307 			    gfilter = gtk_file_filter_new();
1308 		    }
1309 		}
1310 		filter = ++p;
1311 		i = 0;
1312 	    }
1313 	    else
1314 	    {
1315 		p++;
1316 		i++;
1317 	    }
1318 	}
1319 	vim_free(patt);
1320     }
1321     if (saving && dflt != NULL && *dflt != NUL)
1322 	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), (char *)dflt);
1323 
1324     gui.browse_fname = NULL;
1325     if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT)
1326     {
1327 	char *filename;
1328 
1329 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1330 	gui.browse_fname = (char_u *)g_strdup(filename);
1331 	g_free(filename);
1332     }
1333     gtk_widget_destroy(GTK_WIDGET(fc));
1334 
1335 #else /* !USE_FILE_CHOOSER */
1336 
1337     if (gui.filedlg == NULL)
1338     {
1339 	GtkFileSelection	*fs;	/* shortcut */
1340 
1341 	gui.filedlg = gtk_file_selection_new((const gchar *)title);
1342 	gtk_window_set_modal(GTK_WINDOW(gui.filedlg), TRUE);
1343 	gtk_window_set_transient_for(GTK_WINDOW(gui.filedlg),
1344 						     GTK_WINDOW(gui.mainwin));
1345 	fs = GTK_FILE_SELECTION(gui.filedlg);
1346 
1347 	gtk_container_border_width(GTK_CONTAINER(fs), 4);
1348 
1349 	gtk_signal_connect(GTK_OBJECT(fs->ok_button),
1350 		"clicked", GTK_SIGNAL_FUNC(browse_ok_cb), &gui);
1351 	gtk_signal_connect(GTK_OBJECT(fs->cancel_button),
1352 		"clicked", GTK_SIGNAL_FUNC(browse_cancel_cb), &gui);
1353 	/* gtk_signal_connect() doesn't work for destroy, it causes a hang */
1354 	gtk_signal_connect_object(GTK_OBJECT(gui.filedlg),
1355 		"destroy", GTK_SIGNAL_FUNC(browse_destroy_cb),
1356 		GTK_OBJECT(gui.filedlg));
1357     }
1358     else
1359 	gtk_window_set_title(GTK_WINDOW(gui.filedlg), (const gchar *)title);
1360 
1361     /* Concatenate "initdir" and "dflt". */
1362     if (dflt != NULL && *dflt != NUL
1363 			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
1364 	STRCAT(dirbuf, dflt);
1365 
1366     gtk_file_selection_set_filename(GTK_FILE_SELECTION(gui.filedlg),
1367 						      (const gchar *)dirbuf);
1368 
1369     gtk_widget_show(gui.filedlg);
1370     gtk_main();
1371 #endif /* !USE_FILE_CHOOSER */
1372     g_log_remove_handler(domain, log_handler);
1373 
1374     CONVERT_TO_UTF8_FREE(title);
1375     if (gui.browse_fname == NULL)
1376 	return NULL;
1377 
1378     /* shorten the file name if possible */
1379     return vim_strsave(shorten_fname1(gui.browse_fname));
1380 }
1381 
1382 /*
1383  * Put up a directory selector
1384  * Returns the selected name in allocated memory, or NULL for Cancel.
1385  * title			title for the window
1386  * dflt				default name
1387  * initdir			initial directory, NULL for current dir
1388  */
1389     char_u *
1390 gui_mch_browsedir(
1391 	       char_u *title,
1392 	       char_u *initdir)
1393 {
1394 # if defined(GTK_FILE_CHOOSER)	    /* Only in GTK 2.4 and later. */
1395     char_u		dirbuf[MAXPATHL];
1396     char_u		*p;
1397     GtkWidget		*dirdlg;	    /* file selection dialog */
1398     char_u		*dirname = NULL;
1399 
1400     title = CONVERT_TO_UTF8(title);
1401 
1402     dirdlg = gtk_file_chooser_dialog_new(
1403 	    (const gchar *)title,
1404 	    GTK_WINDOW(gui.mainwin),
1405 	    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1406 #  if GTK_CHECK_VERSION(3,10,0)
1407 	    _("_Cancel"), GTK_RESPONSE_CANCEL,
1408 	    _("_OK"), GTK_RESPONSE_ACCEPT,
1409 #  else
1410 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1411 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1412 #  endif
1413 	    NULL);
1414 
1415     CONVERT_TO_UTF8_FREE(title);
1416 
1417     /* if our pointer is currently hidden, then we should show it. */
1418     gui_mch_mousehide(FALSE);
1419 
1420     /* GTK appears to insist on an absolute path. */
1421     if (initdir == NULL || *initdir == NUL
1422 	       || vim_FullName(initdir, dirbuf, MAXPATHL - 10, FALSE) == FAIL)
1423 	mch_dirname(dirbuf, MAXPATHL - 10);
1424 
1425     /* Always need a trailing slash for a directory.
1426      * Also add a dummy file name, so that we get to the directory. */
1427     add_pathsep(dirbuf);
1428     STRCAT(dirbuf, "@zd(*&1|");
1429     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dirdlg),
1430 						      (const gchar *)dirbuf);
1431 
1432     /* Run the dialog. */
1433     if (gtk_dialog_run(GTK_DIALOG(dirdlg)) == GTK_RESPONSE_ACCEPT)
1434 	dirname = (char_u *)gtk_file_chooser_get_filename(
1435 						    GTK_FILE_CHOOSER(dirdlg));
1436     gtk_widget_destroy(dirdlg);
1437     if (dirname == NULL)
1438 	return NULL;
1439 
1440     /* shorten the file name if possible */
1441     p = vim_strsave(shorten_fname1(dirname));
1442     g_free(dirname);
1443     return p;
1444 
1445 # else /* !defined(GTK_FILE_CHOOSER) */
1446     /* For GTK 2.2 and earlier: fall back to ordinary file selector. */
1447     return gui_mch_browse(0, title, NULL, NULL, initdir, NULL);
1448 # endif /* !defined(GTK_FILE_CHOOSER) */
1449 }
1450 
1451 
1452 #endif	/* FEAT_BROWSE */
1453 
1454 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
1455 
1456     static GtkWidget *
1457 create_message_dialog(int type, char_u *title, char_u *message)
1458 {
1459     GtkWidget	    *dialog;
1460     GtkMessageType  message_type;
1461 
1462     switch (type)
1463     {
1464 	case VIM_ERROR:	    message_type = GTK_MESSAGE_ERROR;	 break;
1465 	case VIM_WARNING:   message_type = GTK_MESSAGE_WARNING;	 break;
1466 	case VIM_QUESTION:  message_type = GTK_MESSAGE_QUESTION; break;
1467 	default:	    message_type = GTK_MESSAGE_INFO;	 break;
1468     }
1469 
1470     message = CONVERT_TO_UTF8(message);
1471     dialog  = gtk_message_dialog_new(GTK_WINDOW(gui.mainwin),
1472 				     GTK_DIALOG_DESTROY_WITH_PARENT,
1473 				     message_type,
1474 				     GTK_BUTTONS_NONE,
1475 				     "%s", (const char *)message);
1476     CONVERT_TO_UTF8_FREE(message);
1477 
1478     if (title != NULL)
1479     {
1480 	title = CONVERT_TO_UTF8(title);
1481 	gtk_window_set_title(GTK_WINDOW(dialog), (const char *)title);
1482 	CONVERT_TO_UTF8_FREE(title);
1483     }
1484     else if (type == VIM_GENERIC)
1485     {
1486 	gtk_window_set_title(GTK_WINDOW(dialog), "VIM");
1487     }
1488 
1489     return dialog;
1490 }
1491 
1492 /*
1493  * Split up button_string into individual button labels by inserting
1494  * NUL bytes.  Also replace the Vim-style mnemonic accelerator prefix
1495  * '&' with '_'.  button_string must point to allocated memory!
1496  * Return an allocated array of pointers into button_string.
1497  */
1498     static char **
1499 split_button_string(char_u *button_string, int *n_buttons)
1500 {
1501     char	    **array;
1502     char_u	    *p;
1503     unsigned int    count = 1;
1504 
1505     for (p = button_string; *p != NUL; ++p)
1506 	if (*p == DLG_BUTTON_SEP)
1507 	    ++count;
1508 
1509     array = (char **)alloc((count + 1) * sizeof(char *));
1510     count = 0;
1511 
1512     if (array != NULL)
1513     {
1514 	array[count++] = (char *)button_string;
1515 	for (p = button_string; *p != NUL; )
1516 	{
1517 	    if (*p == DLG_BUTTON_SEP)
1518 	    {
1519 		*p++ = NUL;
1520 		array[count++] = (char *)p;
1521 	    }
1522 	    else if (*p == DLG_HOTKEY_CHAR)
1523 		*p++ = '_';
1524 	    else
1525 		MB_PTR_ADV(p);
1526 	}
1527 	array[count] = NULL; /* currently not relied upon, but doesn't hurt */
1528     }
1529 
1530     *n_buttons = count;
1531     return array;
1532 }
1533 
1534     static char **
1535 split_button_translation(const char *message)
1536 {
1537     char    **buttons = NULL;
1538     char_u  *str;
1539     int	    n_buttons = 0;
1540     int	    n_expected = 1;
1541 
1542     for (str = (char_u *)message; *str != NUL; ++str)
1543 	if (*str == DLG_BUTTON_SEP)
1544 	    ++n_expected;
1545 
1546     str = (char_u *)_(message);
1547     if (str != NULL)
1548     {
1549 	if (output_conv.vc_type != CONV_NONE)
1550 	    str = string_convert(&output_conv, str, NULL);
1551 	else
1552 	    str = vim_strsave(str);
1553 
1554 	if (str != NULL)
1555 	    buttons = split_button_string(str, &n_buttons);
1556     }
1557     /*
1558      * Uh-oh... this should never ever happen.	But we don't wanna crash
1559      * if the translation is broken, thus fall back to the untranslated
1560      * buttons string in case of emergency.
1561      */
1562     if (buttons == NULL || n_buttons != n_expected)
1563     {
1564 	vim_free(buttons);
1565 	vim_free(str);
1566 	buttons = NULL;
1567 	str = vim_strsave((char_u *)message);
1568 
1569 	if (str != NULL)
1570 	    buttons = split_button_string(str, &n_buttons);
1571 	if (buttons == NULL)
1572 	    vim_free(str);
1573     }
1574 
1575     return buttons;
1576 }
1577 
1578     static int
1579 button_equal(const char *a, const char *b)
1580 {
1581     while (*a != '\0' && *b != '\0')
1582     {
1583 	if (*a == '_' && *++a == '\0')
1584 	    break;
1585 	if (*b == '_' && *++b == '\0')
1586 	    break;
1587 
1588 	if (g_unichar_tolower(g_utf8_get_char(a))
1589 		!= g_unichar_tolower(g_utf8_get_char(b)))
1590 	    return FALSE;
1591 
1592 	a = g_utf8_next_char(a);
1593 	b = g_utf8_next_char(b);
1594     }
1595 
1596     return (*a == '\0' && *b == '\0');
1597 }
1598 
1599     static void
1600 dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
1601 {
1602     char    **ok;
1603     char    **ync;  /* "yes no cancel" */
1604     char    **buttons;
1605     int	    n_buttons = 0;
1606     int	    idx;
1607 
1608     button_string = vim_strsave(button_string); /* must be writable */
1609     if (button_string == NULL)
1610 	return;
1611 
1612     /* Check 'v' flag in 'guioptions': vertical button placement. */
1613     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
1614     {
1615 # if GTK_CHECK_VERSION(3,0,0)
1616 	/* Add GTK+ 3 code if necessary. */
1617 	/* N.B. GTK+ 3 doesn't allow you to access vbox and action_area via
1618 	 * the C API. */
1619 # else
1620 	GtkWidget	*vbutton_box;
1621 
1622 	vbutton_box = gtk_vbutton_box_new();
1623 	gtk_widget_show(vbutton_box);
1624 	gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1625 						 vbutton_box, TRUE, FALSE, 0);
1626 	/* Overrule the "action_area" value, hopefully this works... */
1627 	GTK_DIALOG(dialog)->action_area = vbutton_box;
1628 # endif
1629     }
1630 
1631     /*
1632      * Yes this is ugly, I don't particularly like it either.  But doing it
1633      * this way has the compelling advantage that translations need not to
1634      * be touched at all.  See below what 'ok' and 'ync' are used for.
1635      */
1636     ok	    = split_button_translation(N_("&Ok"));
1637     ync     = split_button_translation(N_("&Yes\n&No\n&Cancel"));
1638     buttons = split_button_string(button_string, &n_buttons);
1639 
1640     /*
1641      * Yes, the buttons are in reversed order to match the GNOME 2 desktop
1642      * environment.  Don't hit me -- it's all about consistency.
1643      * Well, apparently somebody changed his mind: with GTK 2.2.4 it works the
1644      * other way around...
1645      */
1646     for (idx = 1; idx <= n_buttons; ++idx)
1647     {
1648 	char	*label;
1649 	char_u	*label8;
1650 
1651 	label = buttons[idx - 1];
1652 	/*
1653 	 * Perform some guesswork to find appropriate stock items for the
1654 	 * buttons.  We have to compare with a sample of the translated
1655 	 * button string to get things right.  Yes, this is hackish :/
1656 	 *
1657 	 * But even the common button labels aren't necessarily translated,
1658 	 * since anyone can create their own dialogs using Vim functions.
1659 	 * Thus we have to check for those too.
1660 	 */
1661 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
1662 	{
1663 # if GTK_CHECK_VERSION(3,10,0)
1664 	    if	    (button_equal(label, ok[0]))    label = _("OK");
1665 	    else if (button_equal(label, ync[0]))   label = _("Yes");
1666 	    else if (button_equal(label, ync[1]))   label = _("No");
1667 	    else if (button_equal(label, ync[2]))   label = _("Cancel");
1668 	    else if (button_equal(label, "Ok"))     label = _("OK");
1669 	    else if (button_equal(label, "Yes"))    label = _("Yes");
1670 	    else if (button_equal(label, "No"))     label = _("No");
1671 	    else if (button_equal(label, "Cancel")) label = _("Cancel");
1672 # else
1673 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
1674 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
1675 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
1676 	    else if (button_equal(label, ync[2]))   label = GTK_STOCK_CANCEL;
1677 	    else if (button_equal(label, "Ok"))     label = GTK_STOCK_OK;
1678 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
1679 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
1680 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
1681 # endif
1682 	}
1683 	label8 = CONVERT_TO_UTF8((char_u *)label);
1684 	gtk_dialog_add_button(dialog, (const gchar *)label8, idx);
1685 	CONVERT_TO_UTF8_FREE(label8);
1686     }
1687 
1688     if (ok != NULL)
1689 	vim_free(*ok);
1690     if (ync != NULL)
1691 	vim_free(*ync);
1692     vim_free(ok);
1693     vim_free(ync);
1694     vim_free(buttons);
1695     vim_free(button_string);
1696 }
1697 
1698 /*
1699  * Allow mnemonic accelerators to be activated without pressing <Alt>.
1700  * I'm not sure if it's a wise idea to do this.  However, the old GTK+ 1.2
1701  * GUI used to work this way, and I consider the impact on UI consistency
1702  * low enough to justify implementing this as a special Vim feature.
1703  */
1704 typedef struct _DialogInfo
1705 {
1706     int		ignore_enter;	    /* no default button, ignore "Enter" */
1707     int		noalt;		    /* accept accelerators without Alt */
1708     GtkDialog	*dialog;	    /* Widget of the dialog */
1709 } DialogInfo;
1710 
1711     static gboolean
1712 dialog_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1713 {
1714     DialogInfo *di = (DialogInfo *)data;
1715 
1716     /* Ignore hitting Enter (or Space) when there is no default button. */
1717     if (di->ignore_enter && (event->keyval == GDK_Return
1718 						     || event->keyval == ' '))
1719 	return TRUE;
1720     else    /* A different key was pressed, return to normal behavior */
1721 	di->ignore_enter = FALSE;
1722 
1723     /* Close the dialog when hitting "Esc". */
1724     if (event->keyval == GDK_Escape)
1725     {
1726 	gtk_dialog_response(di->dialog, GTK_RESPONSE_REJECT);
1727 	return TRUE;
1728     }
1729 
1730     if (di->noalt
1731 	      && (event->state & gtk_accelerator_get_default_mod_mask()) == 0)
1732     {
1733 	return gtk_window_mnemonic_activate(
1734 		   GTK_WINDOW(widget), event->keyval,
1735 		   gtk_window_get_mnemonic_modifier(GTK_WINDOW(widget)));
1736     }
1737 
1738     return FALSE; /* continue emission */
1739 }
1740 
1741     int
1742 gui_mch_dialog(int	type,	    /* type of dialog */
1743 	       char_u	*title,	    /* title of dialog */
1744 	       char_u	*message,   /* message text */
1745 	       char_u	*buttons,   /* names of buttons */
1746 	       int	def_but,    /* default button */
1747 	       char_u	*textfield, /* text for textfield or NULL */
1748 	       int	ex_cmd UNUSED)
1749 {
1750     GtkWidget	*dialog;
1751     GtkWidget	*entry = NULL;
1752     char_u	*text;
1753     int		response;
1754     DialogInfo  dialoginfo;
1755 
1756     dialog = create_message_dialog(type, title, message);
1757     dialoginfo.dialog = GTK_DIALOG(dialog);
1758     dialog_add_buttons(GTK_DIALOG(dialog), buttons);
1759 
1760     if (textfield != NULL)
1761     {
1762 	GtkWidget *alignment;
1763 
1764 	entry = gtk_entry_new();
1765 	gtk_widget_show(entry);
1766 
1767 	/* Make Enter work like pressing OK. */
1768 	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
1769 
1770 	text = CONVERT_TO_UTF8(textfield);
1771 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
1772 	CONVERT_TO_UTF8_FREE(text);
1773 
1774 # if GTK_CHECK_VERSION(3,14,0)
1775 	gtk_widget_set_halign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
1776 	gtk_widget_set_valign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
1777 	gtk_widget_set_hexpand(GTK_WIDGET(entry), TRUE);
1778 	gtk_widget_set_vexpand(GTK_WIDGET(entry), TRUE);
1779 
1780 	alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1781 # else
1782 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
1783 						      (float)1.0, (float)1.0);
1784 # endif
1785 	gtk_container_add(GTK_CONTAINER(alignment), entry);
1786 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
1787 	gtk_widget_show(alignment);
1788 
1789 # if GTK_CHECK_VERSION(3,0,0)
1790 	{
1791 	    GtkWidget * const vbox
1792 		= gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1793 	    gtk_box_pack_start(GTK_BOX(vbox),
1794 		    alignment, TRUE, FALSE, 0);
1795 	}
1796 # else
1797 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1798 			   alignment, TRUE, FALSE, 0);
1799 # endif
1800 	dialoginfo.noalt = FALSE;
1801     }
1802     else
1803 	dialoginfo.noalt = TRUE;
1804 
1805     /* Allow activation of mnemonic accelerators without pressing <Alt> when
1806      * there is no textfield.  Handle pressing Esc. */
1807     g_signal_connect(G_OBJECT(dialog), "key_press_event",
1808 			 G_CALLBACK(&dialog_key_press_event_cb), &dialoginfo);
1809 
1810     if (def_but > 0)
1811     {
1812 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), def_but);
1813 	dialoginfo.ignore_enter = FALSE;
1814     }
1815     else
1816 	/* No default button, ignore pressing Enter. */
1817 	dialoginfo.ignore_enter = TRUE;
1818 
1819     /* Show the mouse pointer if it's currently hidden. */
1820     gui_mch_mousehide(FALSE);
1821 
1822     response = gtk_dialog_run(GTK_DIALOG(dialog));
1823 
1824     /* GTK_RESPONSE_NONE means the dialog was programmatically destroyed. */
1825     if (response != GTK_RESPONSE_NONE)
1826     {
1827 	if (response == GTK_RESPONSE_ACCEPT)	    /* Enter pressed */
1828 	    response = def_but;
1829 	if (textfield != NULL)
1830 	{
1831 	    text = (char_u *)gtk_entry_get_text(GTK_ENTRY(entry));
1832 	    text = CONVERT_FROM_UTF8(text);
1833 
1834 	    vim_strncpy(textfield, text, IOSIZE - 1);
1835 
1836 	    CONVERT_FROM_UTF8_FREE(text);
1837 	}
1838 	gtk_widget_destroy(dialog);
1839     }
1840 
1841     return response > 0 ? response : 0;
1842 }
1843 
1844 #endif /* FEAT_GUI_DIALOG */
1845 
1846 
1847 #if defined(FEAT_MENU) || defined(PROTO)
1848 
1849     void
1850 gui_mch_show_popupmenu(vimmenu_T *menu)
1851 {
1852 # if defined(FEAT_XIM)
1853     /*
1854      * Append a submenu for selecting an input method.	This is
1855      * currently the only way to switch input methods at runtime.
1856      */
1857 #  if !GTK_CHECK_VERSION(3,10,0)
1858     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
1859 					 "vim-has-im-menu") == NULL)
1860     {
1861 	GtkWidget   *menuitem;
1862 	GtkWidget   *submenu;
1863 	char_u	    *name;
1864 
1865 	menuitem = gtk_separator_menu_item_new();
1866 	gtk_widget_show(menuitem);
1867 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
1868 
1869 	name = (char_u *)_("Input _Methods");
1870 	name = CONVERT_TO_UTF8(name);
1871 	menuitem = gtk_menu_item_new_with_mnemonic((const char *)name);
1872 	CONVERT_TO_UTF8_FREE(name);
1873 	gtk_widget_show(menuitem);
1874 
1875 	submenu = gtk_menu_new();
1876 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
1877 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
1878 
1879 	gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(xic),
1880 					     GTK_MENU_SHELL(submenu));
1881 	g_object_set_data(G_OBJECT(menu->submenu_id),
1882 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
1883     }
1884 #  endif
1885 # endif /* FEAT_XIM */
1886 
1887 # if GTK_CHECK_VERSION(3,22,2)
1888     {
1889 	GdkEventButton trigger;
1890 
1891 	/* A pseudo event to have gtk_menu_popup_at_pointer() work. Since the
1892 	 * function calculates the popup menu position on the basis of the
1893 	 * actual pointer position when it is invoked, the fields x, y, x_root
1894 	 * and y_root are set to zero for convenience. */
1895 	trigger.type       = GDK_BUTTON_PRESS;
1896 	trigger.window     = gtk_widget_get_window(gui.drawarea);
1897 	trigger.send_event = FALSE;
1898 	trigger.time       = gui.event_time;
1899 	trigger.x          = 0.0;
1900 	trigger.y          = 0.0;
1901 	trigger.axes       = NULL;
1902 	trigger.state      = 0;
1903 	trigger.button     = 3;
1904 	trigger.device     = NULL;
1905 	trigger.x_root     = 0.0;
1906 	trigger.y_root     = 0.0;
1907 
1908 	gtk_menu_popup_at_pointer(GTK_MENU(menu->submenu_id),
1909 				  (GdkEvent *)&trigger);
1910     }
1911 #else
1912     gtk_menu_popup(GTK_MENU(menu->submenu_id),
1913 		   NULL, NULL,
1914 		   (GtkMenuPositionFunc)NULL, NULL,
1915 		   3U, gui.event_time);
1916 #endif
1917 }
1918 
1919 /* Ugly global variable to pass "mouse_pos" flag from gui_make_popup() to
1920  * popup_menu_position_func(). */
1921 static int popup_mouse_pos;
1922 
1923 /*
1924  * Menu position callback; used by gui_make_popup() to place the menu
1925  * at the current text cursor position.
1926  *
1927  * Note: The push_in output argument seems to affect scrolling of huge
1928  * menus that don't fit on the screen.	Leave it at the default for now.
1929  */
1930     static void
1931 popup_menu_position_func(GtkMenu *menu UNUSED,
1932 			 gint *x, gint *y,
1933 			 gboolean *push_in UNUSED,
1934 			 gpointer user_data UNUSED)
1935 {
1936 # if GTK_CHECK_VERSION(3,0,0)
1937     gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), x, y);
1938 # else
1939     gdk_window_get_origin(gui.drawarea->window, x, y);
1940 # endif
1941 
1942     if (popup_mouse_pos)
1943     {
1944 	int	mx, my;
1945 
1946 	gui_mch_getmouse(&mx, &my);
1947 	*x += mx;
1948 	*y += my;
1949     }
1950 # if GTK_CHECK_VERSION(3,0,0)
1951     else if (curwin != NULL && gui.drawarea != NULL &&
1952 	     gtk_widget_get_window(gui.drawarea) != NULL)
1953 # else
1954     else if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
1955 # endif
1956     {
1957 	/* Find the cursor position in the current window */
1958 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
1959 	*y += FILL_Y(W_WINROW(curwin) + curwin->w_wrow + 1) + 1;
1960     }
1961 }
1962 
1963     void
1964 gui_make_popup(char_u *path_name, int mouse_pos)
1965 {
1966     vimmenu_T *menu;
1967 
1968     popup_mouse_pos = mouse_pos;
1969 
1970     menu = gui_find_menu(path_name);
1971 
1972     if (menu != NULL && menu->submenu_id != NULL)
1973     {
1974 # if GTK_CHECK_VERSION(3,22,2)
1975 	GdkWindow * const win = gtk_widget_get_window(gui.drawarea);
1976 	GdkEventButton trigger;
1977 
1978 	/* A pseudo event to have gtk_menu_popup_at_*() functions work. Since
1979 	 * the position where the menu pops up is automatically adjusted by
1980 	 * the functions, none of the fields x, y, x_root and y_root has to be
1981 	 * set to a specific value here; therefore, they are set to zero for
1982 	 * convenience.*/
1983 	trigger.type       = GDK_BUTTON_PRESS;
1984 	trigger.window     = win;
1985 	trigger.send_event = FALSE;
1986 	trigger.time       = GDK_CURRENT_TIME;
1987 	trigger.x          = 0.0;
1988 	trigger.y          = 0.0;
1989 	trigger.axes       = NULL;
1990 	trigger.state      = 0;
1991 	trigger.button     = 0;
1992 	trigger.device     = NULL;
1993 	trigger.x_root     = 0.0;
1994 	trigger.y_root     = 0.0;
1995 
1996 	if (mouse_pos)
1997 	    gtk_menu_popup_at_pointer(GTK_MENU(menu->submenu_id),
1998 				      (GdkEvent *)&trigger);
1999 	else
2000 	{
2001 	    gint origin_x, origin_y;
2002 	    GdkRectangle rect = { 0, 0, 0, 0 };
2003 
2004 	    gdk_window_get_origin(win, &origin_x, &origin_y);
2005 	    popup_menu_position_func(NULL, &rect.x, &rect.y, NULL, NULL);
2006 
2007 	    rect.x -= origin_x;
2008 	    rect.y -= origin_y;
2009 
2010 	    gtk_menu_popup_at_rect(GTK_MENU(menu->submenu_id),
2011 				   win,
2012 				   &rect,
2013 				   GDK_GRAVITY_SOUTH_EAST,
2014 				   GDK_GRAVITY_NORTH_WEST,
2015 				   (GdkEvent *)&trigger);
2016 	}
2017 # else
2018 	gtk_menu_popup(GTK_MENU(menu->submenu_id),
2019 		       NULL, NULL,
2020 		       &popup_menu_position_func, NULL,
2021 		       0U, (guint32)GDK_CURRENT_TIME);
2022 # endif
2023     }
2024 }
2025 
2026 #endif /* FEAT_MENU */
2027 
2028 
2029 /*
2030  * We don't create it twice.
2031  */
2032 
2033 typedef struct _SharedFindReplace
2034 {
2035     GtkWidget *dialog;	/* the main dialog widget */
2036     GtkWidget *wword;	/* 'Whole word only' check button */
2037     GtkWidget *mcase;	/* 'Match case' check button */
2038     GtkWidget *up;	/* search direction 'Up' radio button */
2039     GtkWidget *down;	/* search direction 'Down' radio button */
2040     GtkWidget *what;	/* 'Find what' entry text widget */
2041     GtkWidget *with;	/* 'Replace with' entry text widget */
2042     GtkWidget *find;	/* 'Find Next' action button */
2043     GtkWidget *replace;	/* 'Replace With' action button */
2044     GtkWidget *all;	/* 'Replace All' action button */
2045 } SharedFindReplace;
2046 
2047 static SharedFindReplace find_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
2048 static SharedFindReplace repl_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
2049 
2050     static int
2051 find_key_press_event(
2052 		GtkWidget	*widget UNUSED,
2053 		GdkEventKey	*event,
2054 		SharedFindReplace *frdp)
2055 {
2056     /* If the user is holding one of the key modifiers we will just bail out,
2057      * thus preserving the possibility of normal focus traversal.
2058      */
2059     if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
2060 	return FALSE;
2061 
2062     /* the Escape key synthesizes a cancellation action */
2063     if (event->keyval == GDK_Escape)
2064     {
2065 	gtk_widget_hide(frdp->dialog);
2066 
2067 	return TRUE;
2068     }
2069 
2070     /* It would be delightful if it where possible to do search history
2071      * operations on the K_UP and K_DOWN keys here.
2072      */
2073 
2074     return FALSE;
2075 }
2076 
2077     static GtkWidget *
2078 #if GTK_CHECK_VERSION(3,10,0)
2079 create_image_button(const char *stock_id UNUSED,
2080 		    const char *label)
2081 #else
2082 create_image_button(const char *stock_id,
2083 		    const char *label)
2084 #endif
2085 {
2086     char_u	*text;
2087     GtkWidget	*box;
2088     GtkWidget	*alignment;
2089     GtkWidget	*button;
2090 
2091     text = CONVERT_TO_UTF8((char_u *)label);
2092 
2093 #if GTK_CHECK_VERSION(3,2,0)
2094     box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
2095     gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
2096 #else
2097     box = gtk_hbox_new(FALSE, 3);
2098 #endif
2099 #if !GTK_CHECK_VERSION(3,10,0)
2100     if (stock_id != NULL)
2101 	gtk_box_pack_start(GTK_BOX(box),
2102 			   gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
2103 			   FALSE, FALSE, 0);
2104 #endif
2105     gtk_box_pack_start(GTK_BOX(box),
2106 		       gtk_label_new((const char *)text),
2107 		       FALSE, FALSE, 0);
2108 
2109     CONVERT_TO_UTF8_FREE(text);
2110 
2111 #if GTK_CHECK_VERSION(3,14,0)
2112     gtk_widget_set_halign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
2113     gtk_widget_set_valign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
2114     gtk_widget_set_hexpand(GTK_WIDGET(box), TRUE);
2115     gtk_widget_set_vexpand(GTK_WIDGET(box), TRUE);
2116 
2117     alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2118 #else
2119     alignment = gtk_alignment_new((float)0.5, (float)0.5,
2120 						      (float)0.0, (float)0.0);
2121 #endif
2122     gtk_container_add(GTK_CONTAINER(alignment), box);
2123     gtk_widget_show_all(alignment);
2124 
2125     button = gtk_button_new();
2126     gtk_container_add(GTK_CONTAINER(button), alignment);
2127 
2128     return button;
2129 }
2130 
2131 /*
2132  * This is currently only used by find_replace_dialog_create(), and
2133  * I'd really like to keep it at that.	In other words: don't spread
2134  * this nasty hack all over the code.  Think twice.
2135  */
2136     static const char *
2137 convert_localized_message(char_u **buffer, const char *message)
2138 {
2139     if (output_conv.vc_type == CONV_NONE)
2140 	return message;
2141 
2142     vim_free(*buffer);
2143     *buffer = string_convert(&output_conv, (char_u *)message, NULL);
2144 
2145     return (const char *)*buffer;
2146 }
2147 
2148     static void
2149 find_replace_dialog_create(char_u *arg, int do_replace)
2150 {
2151     GtkWidget	*hbox;		/* main top down box */
2152     GtkWidget	*actionarea;
2153     GtkWidget	*table;
2154     GtkWidget	*tmp;
2155     GtkWidget	*vbox;
2156     gboolean	sensitive;
2157     SharedFindReplace *frdp;
2158     char_u	*entry_text;
2159     int		wword = FALSE;
2160     int		mcase = !p_ic;
2161     char_u	*conv_buffer = NULL;
2162 #   define CONV(message) convert_localized_message(&conv_buffer, (message))
2163 
2164     frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
2165 
2166     /* Get the search string to use. */
2167     entry_text = get_find_dialog_text(arg, &wword, &mcase);
2168 
2169     if (entry_text != NULL && output_conv.vc_type != CONV_NONE)
2170     {
2171 	char_u *old_text = entry_text;
2172 	entry_text = string_convert(&output_conv, entry_text, NULL);
2173 	vim_free(old_text);
2174     }
2175 
2176     /*
2177      * If the dialog already exists, just raise it.
2178      */
2179     if (frdp->dialog)
2180     {
2181 	if (entry_text != NULL)
2182 	{
2183 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2184 #if GTK_CHECK_VERSION(3,0,0)
2185 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
2186 							     (gboolean)wword);
2187 	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
2188 							     (gboolean)mcase);
2189 #else
2190 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2191 							     (gboolean)wword);
2192 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2193 							     (gboolean)mcase);
2194 #endif
2195 	}
2196 	gtk_window_present(GTK_WINDOW(frdp->dialog));
2197 	vim_free(entry_text);
2198 	return;
2199     }
2200 
2201     frdp->dialog = gtk_dialog_new();
2202 #if GTK_CHECK_VERSION(3,0,0)
2203     /* Nothing equivalent to gtk_dialog_set_has_separator() in GTK+ 3. */
2204 #else
2205     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
2206 #endif
2207     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
2208     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
2209 
2210     if (do_replace)
2211     {
2212 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2213 			     CONV(_("VIM - Search and Replace...")));
2214     }
2215     else
2216     {
2217 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2218 			     CONV(_("VIM - Search...")));
2219     }
2220 
2221 #if GTK_CHECK_VERSION(3,2,0)
2222     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2223     gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
2224 #else
2225     hbox = gtk_hbox_new(FALSE, 0);
2226 #endif
2227     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
2228 #if GTK_CHECK_VERSION(3,0,0)
2229     {
2230 	GtkWidget * const dialog_vbox
2231 	    = gtk_dialog_get_content_area(GTK_DIALOG(frdp->dialog));
2232 	gtk_container_add(GTK_CONTAINER(dialog_vbox), hbox);
2233     }
2234 #else
2235     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
2236 #endif
2237 
2238     if (do_replace)
2239 #if GTK_CHECK_VERSION(3,4,0)
2240 	table = gtk_grid_new();
2241 #else
2242 	table = gtk_table_new(1024, 4, FALSE);
2243 #endif
2244     else
2245 #if GTK_CHECK_VERSION(3,4,0)
2246 	table = gtk_grid_new();
2247 #else
2248 	table = gtk_table_new(1024, 3, FALSE);
2249 #endif
2250     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
2251 #if GTK_CHECK_VERSION(3,0,0)
2252     gtk_container_set_border_width(GTK_CONTAINER(table), 4);
2253 #else
2254     gtk_container_border_width(GTK_CONTAINER(table), 4);
2255 #endif
2256 
2257     tmp = gtk_label_new(CONV(_("Find what:")));
2258 #if GTK_CHECK_VERSION(3,16,0)
2259     gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
2260     gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
2261 #elif GTK_CHECK_VERSION(3,14,0)
2262     {
2263 	GValue align_val = G_VALUE_INIT;
2264 
2265 	g_value_init(&align_val, G_TYPE_FLOAT);
2266 
2267 	g_value_set_float(&align_val, 0.0);
2268 	g_object_set_property(G_OBJECT(tmp), "xalign", &align_val);
2269 
2270 	g_value_set_float(&align_val, 0.5);
2271 	g_object_set_property(G_OBJECT(tmp), "yalign", &align_val);
2272 
2273 	g_value_unset(&align_val);
2274     }
2275 #else
2276     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2277 #endif
2278 #if GTK_CHECK_VERSION(3,4,0)
2279     gtk_grid_attach(GTK_GRID(table), tmp, 0, 0, 2, 1);
2280 #else
2281     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
2282 		     GTK_FILL, GTK_EXPAND, 2, 2);
2283 #endif
2284     frdp->what = gtk_entry_new();
2285     sensitive = (entry_text != NULL && entry_text[0] != NUL);
2286     if (entry_text != NULL)
2287 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2288 #if GTK_CHECK_VERSION(3,0,0)
2289     g_signal_connect(G_OBJECT(frdp->what), "changed",
2290 		     G_CALLBACK(entry_changed_cb), frdp->dialog);
2291     g_signal_connect_after(G_OBJECT(frdp->what), "key-press-event",
2292 			   G_CALLBACK(find_key_press_event),
2293 			   (gpointer) frdp);
2294 #else
2295     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
2296 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
2297     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
2298 				 GTK_SIGNAL_FUNC(find_key_press_event),
2299 				 (gpointer) frdp);
2300 #endif
2301 #if GTK_CHECK_VERSION(3,4,0)
2302     gtk_grid_attach(GTK_GRID(table), frdp->what, 2, 0, 5, 1);
2303 #else
2304     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
2305 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2306 #endif
2307 
2308     if (do_replace)
2309     {
2310 	tmp = gtk_label_new(CONV(_("Replace with:")));
2311 #if GTK_CHECK_VERSION(3,16,0)
2312 	gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
2313 	gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
2314 #elif GTK_CHECK_VERSION(3,14,0)
2315 	{
2316 	    GValue align_val = G_VALUE_INIT;
2317 
2318 	    g_value_init(&align_val, G_TYPE_FLOAT);
2319 
2320 	    g_value_set_float(&align_val, 0.0);
2321 	    g_object_set_property(G_OBJECT(tmp), "xalign", &align_val);
2322 
2323 	    g_value_set_float(&align_val, 0.5);
2324 	    g_object_set_property(G_OBJECT(tmp), "yalign", &align_val);
2325 
2326 	    g_value_unset(&align_val);
2327 	}
2328 #else
2329 	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2330 #endif
2331 #if GTK_CHECK_VERSION(3,4,0)
2332 	gtk_grid_attach(GTK_GRID(table), tmp, 0, 1, 2, 1);
2333 #else
2334 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
2335 			 GTK_FILL, GTK_EXPAND, 2, 2);
2336 #endif
2337 	frdp->with = gtk_entry_new();
2338 #if GTK_CHECK_VERSION(3,0,0)
2339 	g_signal_connect(G_OBJECT(frdp->with), "activate",
2340 			 G_CALLBACK(find_replace_cb),
2341 			 GINT_TO_POINTER(FRD_R_FINDNEXT));
2342 	g_signal_connect_after(G_OBJECT(frdp->with), "key-press-event",
2343 			       G_CALLBACK(find_key_press_event),
2344 			       (gpointer) frdp);
2345 #else
2346 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
2347 			   GTK_SIGNAL_FUNC(find_replace_cb),
2348 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
2349 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
2350 				 GTK_SIGNAL_FUNC(find_key_press_event),
2351 				 (gpointer) frdp);
2352 #endif
2353 #if GTK_CHECK_VERSION(3,4,0)
2354 	gtk_grid_attach(GTK_GRID(table), frdp->with, 2, 1, 5, 1);
2355 #else
2356 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
2357 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2358 #endif
2359 
2360 	/*
2361 	 * Make the entry activation only change the input focus onto the
2362 	 * with item.
2363 	 */
2364 #if GTK_CHECK_VERSION(3,0,0)
2365 	g_signal_connect(G_OBJECT(frdp->what), "activate",
2366 			 G_CALLBACK(entry_activate_cb), frdp->with);
2367 #else
2368 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2369 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
2370 #endif
2371     }
2372     else
2373     {
2374 	/*
2375 	 * Make the entry activation do the search.
2376 	 */
2377 #if GTK_CHECK_VERSION(3,0,0)
2378 	g_signal_connect(G_OBJECT(frdp->what), "activate",
2379 			 G_CALLBACK(find_replace_cb),
2380 			 GINT_TO_POINTER(FRD_FINDNEXT));
2381 #else
2382 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2383 			   GTK_SIGNAL_FUNC(find_replace_cb),
2384 			   GINT_TO_POINTER(FRD_FINDNEXT));
2385 #endif
2386     }
2387 
2388     /* whole word only button */
2389     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
2390 #if GTK_CHECK_VERSION(3,0,0)
2391     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
2392 							(gboolean)wword);
2393 #else
2394     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2395 							(gboolean)wword);
2396 #endif
2397     if (do_replace)
2398 #if GTK_CHECK_VERSION(3,4,0)
2399 	gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 2, 5, 1);
2400 #else
2401 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
2402 			 GTK_FILL, GTK_EXPAND, 2, 2);
2403 #endif
2404     else
2405 #if GTK_CHECK_VERSION(3,4,0)
2406 	gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 3, 5, 1);
2407 #else
2408 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
2409 			 GTK_FILL, GTK_EXPAND, 2, 2);
2410 #endif
2411 
2412     /* match case button */
2413     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
2414 #if GTK_CHECK_VERSION(3,0,0)
2415     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
2416 							     (gboolean)mcase);
2417 #else
2418     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2419 							     (gboolean)mcase);
2420 #endif
2421     if (do_replace)
2422 #if GTK_CHECK_VERSION(3,4,0)
2423 	gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 3, 5, 1);
2424 #else
2425 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
2426 			 GTK_FILL, GTK_EXPAND, 2, 2);
2427 #endif
2428     else
2429 #if GTK_CHECK_VERSION(3,4,0)
2430 	gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 4, 5, 1);
2431 #else
2432 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
2433 			 GTK_FILL, GTK_EXPAND, 2, 2);
2434 #endif
2435 
2436     tmp = gtk_frame_new(CONV(_("Direction")));
2437     if (do_replace)
2438 #if GTK_CHECK_VERSION(3,4,0)
2439 	gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 2, 4);
2440 #else
2441 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
2442 			 GTK_FILL, GTK_FILL, 2, 2);
2443 #endif
2444     else
2445 #if GTK_CHECK_VERSION(3,4,0)
2446 	gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 1, 3);
2447 #else
2448 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
2449 			 GTK_FILL, GTK_FILL, 2, 2);
2450 #endif
2451 #if GTK_CHECK_VERSION(3,2,0)
2452     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2453     gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
2454 #else
2455     vbox = gtk_vbox_new(FALSE, 0);
2456 #endif
2457 #if GTK_CHECK_VERSION(3,0,0)
2458     gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
2459 #else
2460     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
2461 #endif
2462     gtk_container_add(GTK_CONTAINER(tmp), vbox);
2463 
2464     /* 'Up' and 'Down' buttons */
2465     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
2466     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
2467 #if GTK_CHECK_VERSION(3,0,0)
2468     frdp->down = gtk_radio_button_new_with_label(
2469 			gtk_radio_button_get_group(GTK_RADIO_BUTTON(frdp->up)),
2470 			CONV(_("Down")));
2471 #else
2472     frdp->down = gtk_radio_button_new_with_label(
2473 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
2474 			CONV(_("Down")));
2475 #endif
2476 #if GTK_CHECK_VERSION(3,0,0)
2477     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
2478 #else
2479     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
2480 #endif
2481     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
2482     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
2483 
2484     /* vbox to hold the action buttons */
2485 #if GTK_CHECK_VERSION(3,2,0)
2486     actionarea = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
2487 #else
2488     actionarea = gtk_vbutton_box_new();
2489 #endif
2490 #if GTK_CHECK_VERSION(3,0,0)
2491     gtk_container_set_border_width(GTK_CONTAINER(actionarea), 2);
2492 #else
2493     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
2494 #endif
2495     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
2496 
2497     /* 'Find Next' button */
2498 #if GTK_CHECK_VERSION(3,10,0)
2499     frdp->find = create_image_button(NULL, _("Find Next"));
2500 #else
2501     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
2502 #endif
2503     gtk_widget_set_sensitive(frdp->find, sensitive);
2504 
2505 #if GTK_CHECK_VERSION(3,0,0)
2506     g_signal_connect(G_OBJECT(frdp->find), "clicked",
2507 		     G_CALLBACK(find_replace_cb),
2508 		     (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
2509 				  : GINT_TO_POINTER(FRD_FINDNEXT));
2510 #else
2511     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
2512 		       GTK_SIGNAL_FUNC(find_replace_cb),
2513 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
2514 				    : GINT_TO_POINTER(FRD_FINDNEXT));
2515 #endif
2516 
2517 #if GTK_CHECK_VERSION(3,0,0)
2518     gtk_widget_set_can_default(frdp->find, TRUE);
2519 #else
2520     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
2521 #endif
2522     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
2523     gtk_widget_grab_default(frdp->find);
2524 
2525     if (do_replace)
2526     {
2527 	/* 'Replace' button */
2528 #if GTK_CHECK_VERSION(3,10,0)
2529 	frdp->replace = create_image_button(NULL, _("Replace"));
2530 #else
2531 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
2532 #endif
2533 	gtk_widget_set_sensitive(frdp->replace, sensitive);
2534 #if GTK_CHECK_VERSION(3,0,0)
2535 	gtk_widget_set_can_default(frdp->find, TRUE);
2536 #else
2537 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
2538 #endif
2539 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
2540 #if GTK_CHECK_VERSION(3,0,0)
2541 	g_signal_connect(G_OBJECT(frdp->replace), "clicked",
2542 			 G_CALLBACK(find_replace_cb),
2543 			 GINT_TO_POINTER(FRD_REPLACE));
2544 #else
2545 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
2546 			   GTK_SIGNAL_FUNC(find_replace_cb),
2547 			   GINT_TO_POINTER(FRD_REPLACE));
2548 #endif
2549 
2550 	/* 'Replace All' button */
2551 #if GTK_CHECK_VERSION(3,10,0)
2552 	frdp->all = create_image_button(NULL, _("Replace All"));
2553 #else
2554 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
2555 #endif
2556 	gtk_widget_set_sensitive(frdp->all, sensitive);
2557 #if GTK_CHECK_VERSION(3,0,0)
2558 	gtk_widget_set_can_default(frdp->all, TRUE);
2559 #else
2560 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
2561 #endif
2562 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
2563 #if GTK_CHECK_VERSION(3,0,0)
2564 	g_signal_connect(G_OBJECT(frdp->all), "clicked",
2565 			 G_CALLBACK(find_replace_cb),
2566 			 GINT_TO_POINTER(FRD_REPLACEALL));
2567 #else
2568 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
2569 			   GTK_SIGNAL_FUNC(find_replace_cb),
2570 			   GINT_TO_POINTER(FRD_REPLACEALL));
2571 #endif
2572     }
2573 
2574     /* 'Cancel' button */
2575 #if GTK_CHECK_VERSION(3,10,0)
2576     tmp = gtk_button_new_with_mnemonic(_("_Close"));
2577 #else
2578     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2579 #endif
2580 #if GTK_CHECK_VERSION(3,0,0)
2581     gtk_widget_set_can_default(tmp, TRUE);
2582 #else
2583     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
2584 #endif
2585     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
2586 #if GTK_CHECK_VERSION(3,0,0)
2587     g_signal_connect_swapped(G_OBJECT(tmp),
2588 			     "clicked", G_CALLBACK(gtk_widget_hide),
2589 			     G_OBJECT(frdp->dialog));
2590     g_signal_connect_swapped(G_OBJECT(frdp->dialog),
2591 			     "delete-event", G_CALLBACK(gtk_widget_hide_on_delete),
2592 			     G_OBJECT(frdp->dialog));
2593 #else
2594     gtk_signal_connect_object(GTK_OBJECT(tmp),
2595 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
2596 			      GTK_OBJECT(frdp->dialog));
2597     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
2598 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
2599 			      GTK_OBJECT(frdp->dialog));
2600 #endif
2601 
2602 #if GTK_CHECK_VERSION(3,2,0)
2603     tmp = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
2604 #else
2605     tmp = gtk_vseparator_new();
2606 #endif
2607     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
2608 
2609     /* Suppress automatic show of the unused action area */
2610 #if GTK_CHECK_VERSION(3,0,0)
2611 # if !GTK_CHECK_VERSION(3,12,0)
2612     gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(frdp->dialog)));
2613 # endif
2614 #else
2615     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
2616 #endif
2617     gtk_widget_show_all(hbox);
2618     gtk_widget_show(frdp->dialog);
2619 
2620     vim_free(entry_text);
2621     vim_free(conv_buffer);
2622 #undef CONV
2623 }
2624 
2625     void
2626 gui_mch_find_dialog(exarg_T *eap)
2627 {
2628     if (gui.in_use)
2629 	find_replace_dialog_create(eap->arg, FALSE);
2630 }
2631 
2632     void
2633 gui_mch_replace_dialog(exarg_T *eap)
2634 {
2635     if (gui.in_use)
2636 	find_replace_dialog_create(eap->arg, TRUE);
2637 }
2638 
2639 /*
2640  * Callback for actions of the find and replace dialogs
2641  */
2642     static void
2643 find_replace_cb(GtkWidget *widget UNUSED, gpointer data)
2644 {
2645     int			flags;
2646     char_u		*find_text;
2647     char_u		*repl_text;
2648     gboolean		direction_down;
2649     SharedFindReplace	*sfr;
2650 
2651     flags = (int)(long)data;	    /* avoid a lint warning here */
2652 
2653     /* Get the search/replace strings from the dialog */
2654     if (flags == FRD_FINDNEXT)
2655     {
2656 	repl_text = NULL;
2657 	sfr = &find_widgets;
2658     }
2659     else
2660     {
2661 	repl_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(repl_widgets.with));
2662 	sfr = &repl_widgets;
2663     }
2664 
2665     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
2666 #if GTK_CHECK_VERSION(3,0,0)
2667     direction_down = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->down));
2668 #else
2669     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
2670 #endif
2671 
2672 #if GTK_CHECK_VERSION(3,0,0)
2673     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->wword)))
2674 #else
2675     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
2676 #endif
2677 	flags |= FRD_WHOLE_WORD;
2678 #if GTK_CHECK_VERSION(3,0,0)
2679     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->mcase)))
2680 #else
2681     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
2682 #endif
2683 	flags |= FRD_MATCH_CASE;
2684 
2685     repl_text = CONVERT_FROM_UTF8(repl_text);
2686     find_text = CONVERT_FROM_UTF8(find_text);
2687     gui_do_findrepl(flags, find_text, repl_text, direction_down);
2688     CONVERT_FROM_UTF8_FREE(repl_text);
2689     CONVERT_FROM_UTF8_FREE(find_text);
2690 }
2691 
2692 /* our usual callback function */
2693     static void
2694 entry_activate_cb(GtkWidget *widget UNUSED, gpointer data)
2695 {
2696     gtk_widget_grab_focus(GTK_WIDGET(data));
2697 }
2698 
2699 /*
2700  * Syncing the find/replace dialogs on the fly is utterly useless crack,
2701  * and causes nothing but problems.  Please tell me a use case for which
2702  * you'd need both a find dialog and a find/replace one at the same time,
2703  * without being able to actually use them separately since they're syncing
2704  * all the time.  I don't think it's worthwhile to fix this nonsense,
2705  * particularly evil incarnation of braindeadness, whatever; I'd much rather
2706  * see it extinguished from this planet.  Thanks for listening.  Sorry.
2707  */
2708     static void
2709 entry_changed_cb(GtkWidget * entry, GtkWidget * dialog)
2710 {
2711     const gchar	*entry_text;
2712     gboolean	nonempty;
2713 
2714     entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
2715 
2716     if (!entry_text)
2717 	return;			/* shouldn't happen */
2718 
2719     nonempty = (entry_text[0] != '\0');
2720 
2721     if (dialog == find_widgets.dialog)
2722     {
2723 	gtk_widget_set_sensitive(find_widgets.find, nonempty);
2724     }
2725 
2726     if (dialog == repl_widgets.dialog)
2727     {
2728 	gtk_widget_set_sensitive(repl_widgets.find, nonempty);
2729 	gtk_widget_set_sensitive(repl_widgets.replace, nonempty);
2730 	gtk_widget_set_sensitive(repl_widgets.all, nonempty);
2731     }
2732 }
2733 
2734 /*
2735  * ":helpfind"
2736  */
2737     void
2738 ex_helpfind(exarg_T *eap UNUSED)
2739 {
2740     /* This will fail when menus are not loaded.  Well, it's only for
2741      * backwards compatibility anyway. */
2742     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
2743 }
2744 
2745 #if defined(FEAT_BROWSE) || defined(PROTO)
2746     static void
2747 recent_func_log_func(const gchar *log_domain UNUSED,
2748 		     GLogLevelFlags log_level UNUSED,
2749 		     const gchar *message UNUSED,
2750 		     gpointer user_data UNUSED)
2751 {
2752     /* We just want to suppress the warnings. */
2753     /* http://bugzilla.gnome.org/show_bug.cgi?id=664587 */
2754 }
2755 #endif
2756