xref: /vim-8.2.3635/src/gui_gtk.c (revision 2bf24176)
1 /* vi:set ts=8 sts=4 sw=4:
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 
27 #ifdef FEAT_GUI_GTK
28 # include "gui_gtk_f.h"
29 #endif
30 
31 /* GTK defines MAX and MIN, but some system header files as well.  Undefine
32  * them and don't use them. */
33 #ifdef MIN
34 # undef MIN
35 #endif
36 #ifdef MAX
37 # undef MAX
38 #endif
39 
40 #include "vim.h"
41 
42 #ifdef FEAT_GUI_GNOME
43 /* Gnome redefines _() and N_().  Grrr... */
44 # ifdef _
45 #  undef _
46 # endif
47 # ifdef N_
48 #  undef N_
49 # endif
50 # ifdef textdomain
51 #  undef textdomain
52 # endif
53 # ifdef bindtextdomain
54 #  undef bindtextdomain
55 # endif
56 # ifdef bind_textdomain_codeset
57 #  undef bind_textdomain_codeset
58 # endif
59 # if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
60 #  define ENABLE_NLS	/* so the texts in the dialog boxes are translated */
61 # endif
62 # include <gnome.h>
63 #endif
64 
65 #ifdef FEAT_GUI_GTK
66 # include <gdk/gdkkeysyms.h>
67 # include <gdk/gdk.h>
68 # ifdef WIN3264
69 #  include <gdk/gdkwin32.h>
70 # else
71 #  include <gdk/gdkx.h>
72 # endif
73 
74 # include <gtk/gtk.h>
75 #else
76 /* define these items to be able to generate prototypes without GTK */
77 typedef int GtkWidget;
78 # define gpointer int
79 # define guint8 int
80 # define GdkPixmap int
81 # define GdkBitmap int
82 # define GtkIconFactory int
83 # define GtkToolbar int
84 # define GtkAdjustment int
85 # define gboolean int
86 # define GdkEventKey int
87 # define CancelData int
88 #endif
89 
90 static void entry_activate_cb(GtkWidget *widget, gpointer data);
91 static void entry_changed_cb(GtkWidget *entry, GtkWidget *dialog);
92 static void find_replace_cb(GtkWidget *widget, gpointer data);
93 #if defined(FEAT_BROWSE) || defined(PROTO)
94 static void recent_func_log_func(
95 	const gchar *log_domain,
96 	GLogLevelFlags log_level,
97 	const gchar *message,
98 	gpointer user_data);
99 #endif
100 
101 #if defined(FEAT_TOOLBAR)
102 /*
103  * Table from BuiltIn## icon indices to GTK+ stock IDs.  Order must exactly
104  * match toolbar_names[] in menu.c!  All stock icons including the "vim-*"
105  * ones can be overridden in your gtkrc file.
106  */
107 static const char * const menu_stock_ids[] =
108 {
109     /* 00 */ GTK_STOCK_NEW,
110     /* 01 */ GTK_STOCK_OPEN,
111     /* 02 */ GTK_STOCK_SAVE,
112     /* 03 */ GTK_STOCK_UNDO,
113     /* 04 */ GTK_STOCK_REDO,
114     /* 05 */ GTK_STOCK_CUT,
115     /* 06 */ GTK_STOCK_COPY,
116     /* 07 */ GTK_STOCK_PASTE,
117     /* 08 */ GTK_STOCK_PRINT,
118     /* 09 */ GTK_STOCK_HELP,
119     /* 10 */ GTK_STOCK_FIND,
120     /* 11 */ "vim-save-all",
121     /* 12 */ "vim-session-save",
122     /* 13 */ "vim-session-new",
123     /* 14 */ "vim-session-load",
124     /* 15 */ GTK_STOCK_EXECUTE,
125     /* 16 */ GTK_STOCK_FIND_AND_REPLACE,
126     /* 17 */ GTK_STOCK_CLOSE,		/* FIXME: fuzzy */
127     /* 18 */ "vim-window-maximize",
128     /* 19 */ "vim-window-minimize",
129     /* 20 */ "vim-window-split",
130     /* 21 */ "vim-shell",
131     /* 22 */ GTK_STOCK_GO_BACK,
132     /* 23 */ GTK_STOCK_GO_FORWARD,
133     /* 24 */ "vim-find-help",
134     /* 25 */ GTK_STOCK_CONVERT,
135     /* 26 */ GTK_STOCK_JUMP_TO,
136     /* 27 */ "vim-build-tags",
137     /* 28 */ "vim-window-split-vertical",
138     /* 29 */ "vim-window-maximize-width",
139     /* 30 */ "vim-window-minimize-width",
140     /* 31 */ GTK_STOCK_QUIT
141 };
142 
143 #ifdef USE_GRESOURCE
144 typedef struct IconNames {
145     const char *icon_name;
146     const char *file_name;
147 } IconNames;
148 
149 static IconNames stock_vim_icons[] = {
150     { "vim-build-tags", "stock_vim_build_tags.png" },
151     { "vim-find-help", "stock_vim_find_help.png" },
152     { "vim-save-all", "stock_vim_save_all.png" },
153     { "vim-session-load", "stock_vim_session_load.png" },
154     { "vim-session-new", "stock_vim_session_new.png" },
155     { "vim-session-save", "stock_vim_session_save.png" },
156     { "vim-shell", "stock_vim_shell.png" },
157     { "vim-window-maximize", "stock_vim_window_maximize.png" },
158     { "vim-window-maximize-width", "stock_vim_window_maximize_width.png" },
159     { "vim-window-minimize", "stock_vim_window_minimize.png" },
160     { "vim-window-minimize-width", "stock_vim_window_minimize_width.png" },
161     { "vim-window-split", "stock_vim_window_split.png" },
162     { "vim-window-split-vertical", "stock_vim_window_split_vertical.png" },
163     { NULL, NULL }
164 };
165 #endif
166 
167 #ifndef USE_GRESOURCE
168     static void
169 add_stock_icon(GtkIconFactory	*factory,
170 	       const char	*stock_id,
171 	       const guint8	*inline_data,
172 	       int		data_length)
173 {
174     GdkPixbuf	*pixbuf;
175     GtkIconSet	*icon_set;
176 
177     pixbuf = gdk_pixbuf_new_from_inline(data_length, inline_data, FALSE, NULL);
178     icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
179 
180     gtk_icon_factory_add(factory, stock_id, icon_set);
181 
182     gtk_icon_set_unref(icon_set);
183     g_object_unref(pixbuf);
184 }
185 #endif
186 
187     static int
188 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
189 {
190     expand_env(iconfile, dest, MAXPATHL);
191 
192     if (mch_isFullName(dest))
193     {
194 	return vim_fexists(dest);
195     }
196     else
197     {
198 	static const char   suffixes[][4] = {"png", "xpm", "bmp"};
199 	char_u		    buf[MAXPATHL];
200 	unsigned int	    i;
201 
202 	for (i = 0; i < G_N_ELEMENTS(suffixes); ++i)
203 	    if (gui_find_bitmap(dest, buf, (char *)suffixes[i]) == OK)
204 	    {
205 		STRCPY(dest, buf);
206 		return TRUE;
207 	    }
208 
209 	return FALSE;
210     }
211 }
212 
213     static GtkWidget *
214 load_menu_iconfile(char_u *name, GtkIconSize icon_size)
215 {
216     GtkWidget	    *image = NULL;
217     GtkIconSet	    *icon_set;
218     GtkIconSource   *icon_source;
219 
220     /*
221      * Rather than loading the icon directly into a GtkImage, create
222      * a new GtkIconSet and put it in there.  This way we can easily
223      * scale the toolbar icons on the fly when needed.
224      */
225     icon_set = gtk_icon_set_new();
226     icon_source = gtk_icon_source_new();
227 
228     gtk_icon_source_set_filename(icon_source, (const char *)name);
229     gtk_icon_set_add_source(icon_set, icon_source);
230 
231     image = gtk_image_new_from_icon_set(icon_set, icon_size);
232 
233     gtk_icon_source_free(icon_source);
234     gtk_icon_set_unref(icon_set);
235 
236     return image;
237 }
238 
239     static GtkWidget *
240 create_menu_icon(vimmenu_T *menu, GtkIconSize icon_size)
241 {
242     GtkWidget	*image = NULL;
243     char_u	buf[MAXPATHL];
244 
245     /* First use a specified "icon=" argument. */
246     if (menu->iconfile != NULL && lookup_menu_iconfile(menu->iconfile, buf))
247 	image = load_menu_iconfile(buf, icon_size);
248 
249     /* If not found and not builtin specified try using the menu name. */
250     if (image == NULL && !menu->icon_builtin
251 				     && lookup_menu_iconfile(menu->name, buf))
252 	image = load_menu_iconfile(buf, icon_size);
253 
254     /* Still not found?  Then use a builtin icon, a blank one as fallback. */
255     if (image == NULL)
256     {
257 	const char  *stock_id;
258 	const int   n_ids = G_N_ELEMENTS(menu_stock_ids);
259 
260 	if (menu->iconidx >= 0 && menu->iconidx < n_ids)
261 	    stock_id = menu_stock_ids[menu->iconidx];
262 	else
263 	    stock_id = GTK_STOCK_MISSING_IMAGE;
264 
265 	image = gtk_image_new_from_stock(stock_id, icon_size);
266     }
267 
268     return image;
269 }
270 
271     static gint
272 toolbar_button_focus_in_event(GtkWidget *widget UNUSED,
273 			      GdkEventFocus *event UNUSED,
274 			      gpointer data UNUSED)
275 {
276     /* When we're in a GtkPlug, we don't have window focus events, only widget
277      * focus.  To emulate stand-alone gvim, if a button gets focus (e.g.,
278      * <Tab> into GtkPlug) immediately pass it to mainwin. */
279     if (gtk_socket_id != 0)
280 	gtk_widget_grab_focus(gui.drawarea);
281 
282     return TRUE;
283 }
284 #endif /* FEAT_TOOLBAR */
285 
286 #if defined(FEAT_TOOLBAR) || defined(PROTO)
287 
288     void
289 gui_gtk_register_stock_icons(void)
290 {
291 #ifndef USE_GRESOURCE
292 # include "../pixmaps/stock_icons.h"
293     GtkIconFactory *factory;
294 
295     factory = gtk_icon_factory_new();
296 # define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
297 
298     ADD_ICON("vim-build-tags",		  stock_vim_build_tags);
299     ADD_ICON("vim-find-help",		  stock_vim_find_help);
300     ADD_ICON("vim-save-all",		  stock_vim_save_all);
301     ADD_ICON("vim-session-load",	  stock_vim_session_load);
302     ADD_ICON("vim-session-new",		  stock_vim_session_new);
303     ADD_ICON("vim-session-save",	  stock_vim_session_save);
304     ADD_ICON("vim-shell",		  stock_vim_shell);
305     ADD_ICON("vim-window-maximize",	  stock_vim_window_maximize);
306     ADD_ICON("vim-window-maximize-width", stock_vim_window_maximize_width);
307     ADD_ICON("vim-window-minimize",	  stock_vim_window_minimize);
308     ADD_ICON("vim-window-minimize-width", stock_vim_window_minimize_width);
309     ADD_ICON("vim-window-split",	  stock_vim_window_split);
310     ADD_ICON("vim-window-split-vertical", stock_vim_window_split_vertical);
311 
312 # undef ADD_ICON
313 #else
314     GtkIconFactory * const factory = gtk_icon_factory_new();
315     const char * const path_prefix = "/org/vim/gui/icon";
316     IconNames *names;
317 
318     for (names = stock_vim_icons; names->icon_name != NULL; names++)
319     {
320         char path[MAXPATHL];
321         GdkPixbuf *pixbuf;
322 
323         vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
324         pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
325         if (pixbuf != NULL)
326         {
327             GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
328             gtk_icon_factory_add(factory, names->icon_name, icon_set);
329             gtk_icon_set_unref(icon_set);
330             g_object_unref(pixbuf);
331         }
332     }
333 #endif
334     gtk_icon_factory_add_default(factory);
335     g_object_unref(factory);
336 }
337 
338 #endif /* FEAT_TOOLBAR */
339 
340 
341 #if defined(FEAT_MENU) || defined(PROTO)
342 
343 /*
344  * Translate Vim's mnemonic tagging to GTK+ style and convert to UTF-8
345  * if necessary.  The caller must vim_free() the returned string.
346  *
347  *	Input	Output
348  *	_	__
349  *	&&	&
350  *	&	_	stripped if use_mnemonic == FALSE
351  *	<Tab>		end of menu label text
352  */
353     static char_u *
354 translate_mnemonic_tag(char_u *name, int use_mnemonic)
355 {
356     char_u  *buf;
357     char_u  *psrc;
358     char_u  *pdest;
359     int	    n_underscores = 0;
360 
361     name = CONVERT_TO_UTF8(name);
362     if (name == NULL)
363 	return NULL;
364 
365     for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
366 	if (*psrc == '_')
367 	    ++n_underscores;
368 
369     buf = alloc((unsigned)(psrc - name + n_underscores + 1));
370     if (buf != NULL)
371     {
372 	pdest = buf;
373 	for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
374 	{
375 	    if (*psrc == '_')
376 	    {
377 		*pdest++ = '_';
378 		*pdest++ = '_';
379 	    }
380 	    else if (*psrc != '&')
381 	    {
382 		*pdest++ = *psrc;
383 	    }
384 	    else if (*(psrc + 1) == '&')
385 	    {
386 		*pdest++ = *psrc++;
387 	    }
388 	    else if (use_mnemonic)
389 	    {
390 		*pdest++ = '_';
391 	    }
392 	}
393 	*pdest = NUL;
394     }
395 
396     CONVERT_TO_UTF8_FREE(name);
397     return buf;
398 }
399 
400     static void
401 menu_item_new(vimmenu_T *menu, GtkWidget *parent_widget)
402 {
403     GtkWidget	*box;
404     char_u	*text;
405     int		use_mnemonic;
406 
407     /* It would be neat to have image menu items, but that would require major
408      * changes to Vim's menu system.  Not to mention that all the translations
409      * had to be updated. */
410     menu->id = gtk_menu_item_new();
411     box = gtk_hbox_new(FALSE, 20);
412 
413     use_mnemonic = (p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget));
414     text = translate_mnemonic_tag(menu->name, use_mnemonic);
415 
416     menu->label = gtk_label_new_with_mnemonic((const char *)text);
417     vim_free(text);
418 
419     gtk_box_pack_start(GTK_BOX(box), menu->label, FALSE, FALSE, 0);
420 
421     if (menu->actext != NULL && menu->actext[0] != NUL)
422     {
423 	text = CONVERT_TO_UTF8(menu->actext);
424 
425 	gtk_box_pack_end(GTK_BOX(box),
426 			 gtk_label_new((const char *)text),
427 			 FALSE, FALSE, 0);
428 
429 	CONVERT_TO_UTF8_FREE(text);
430     }
431 
432     gtk_container_add(GTK_CONTAINER(menu->id), box);
433     gtk_widget_show_all(menu->id);
434 }
435 
436     void
437 gui_mch_add_menu(vimmenu_T *menu, int idx)
438 {
439     vimmenu_T	*parent;
440     GtkWidget	*parent_widget;
441 
442     if (menu->name[0] == ']' || menu_is_popup(menu->name))
443     {
444 	menu->submenu_id = gtk_menu_new();
445 	return;
446     }
447 
448     parent = menu->parent;
449 
450     if ((parent != NULL && parent->submenu_id == NULL)
451 	    || !menu_is_menubar(menu->name))
452 	return;
453 
454     parent_widget = (parent != NULL) ? parent->submenu_id : gui.menubar;
455     menu_item_new(menu, parent_widget);
456 
457     /* since the tearoff should always appear first, increment idx */
458     if (parent != NULL && !menu_is_popup(parent->name))
459 	++idx;
460 
461     gtk_menu_shell_insert(GTK_MENU_SHELL(parent_widget), menu->id, idx);
462 
463     menu->submenu_id = gtk_menu_new();
464 
465     gtk_menu_set_accel_group(GTK_MENU(menu->submenu_id), gui.accel_group);
466     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->id), menu->submenu_id);
467 
468     menu->tearoff_handle = gtk_tearoff_menu_item_new();
469     if (vim_strchr(p_go, GO_TEAROFF) != NULL)
470 	gtk_widget_show(menu->tearoff_handle);
471     gtk_menu_prepend(GTK_MENU(menu->submenu_id), menu->tearoff_handle);
472 }
473 
474     static void
475 menu_item_activate(GtkWidget *widget UNUSED, gpointer data)
476 {
477     gui_menu_cb((vimmenu_T *)data);
478 }
479 
480     void
481 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
482 {
483     vimmenu_T *parent;
484 
485     parent = menu->parent;
486 
487 # ifdef FEAT_TOOLBAR
488     if (menu_is_toolbar(parent->name))
489     {
490 	GtkToolbar *toolbar;
491 
492 	toolbar = GTK_TOOLBAR(gui.toolbar);
493 	menu->submenu_id = NULL;
494 
495 	if (menu_is_separator(menu->name))
496 	{
497 	    gtk_toolbar_insert_space(toolbar, idx);
498 	    menu->id = NULL;
499 	}
500 	else
501 	{
502 	    char_u *text;
503 	    char_u *tooltip;
504 
505 	    text    = CONVERT_TO_UTF8(menu->dname);
506 	    tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
507 	    if (tooltip != NULL && !utf_valid_string(tooltip, NULL))
508 		/* Invalid text, can happen when 'encoding' is changed.  Avoid
509 		 * a nasty GTK error message, skip the tooltip. */
510 		CONVERT_TO_UTF8_FREE(tooltip);
511 
512 	    menu->id = gtk_toolbar_insert_item(
513 		    toolbar,
514 		    (const char *)text,
515 		    (const char *)tooltip,
516 		    NULL,
517 		    create_menu_icon(menu, gtk_toolbar_get_icon_size(toolbar)),
518 		    G_CALLBACK(&menu_item_activate),
519 		    menu,
520 		    idx);
521 
522 	    if (gtk_socket_id != 0)
523 		gtk_signal_connect(GTK_OBJECT(menu->id), "focus_in_event",
524 			GTK_SIGNAL_FUNC(toolbar_button_focus_in_event), NULL);
525 
526 	    CONVERT_TO_UTF8_FREE(text);
527 	    CONVERT_TO_UTF8_FREE(tooltip);
528 	}
529     }
530     else
531 # endif /* FEAT_TOOLBAR */
532     {
533 	/* No parent, must be a non-menubar menu */
534 	if (parent == NULL || parent->submenu_id == NULL)
535 	    return;
536 
537 	/* Make place for the possible tearoff handle item.  Not in the popup
538 	 * menu, it doesn't have a tearoff item. */
539 	if (!menu_is_popup(parent->name))
540 	    ++idx;
541 
542 	if (menu_is_separator(menu->name))
543 	{
544 	    /* Separator: Just add it */
545 	    menu->id = gtk_menu_item_new();
546 	    gtk_widget_set_sensitive(menu->id, FALSE);
547 	    gtk_widget_show(menu->id);
548 	    gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
549 
550 	    return;
551 	}
552 
553 	/* Add textual menu item. */
554 	menu_item_new(menu, parent->submenu_id);
555 	gtk_widget_show(menu->id);
556 	gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
557 
558 	if (menu->id != NULL)
559 	    gtk_signal_connect(GTK_OBJECT(menu->id), "activate",
560 			       GTK_SIGNAL_FUNC(menu_item_activate), menu);
561     }
562 }
563 #endif /* FEAT_MENU */
564 
565 
566     void
567 gui_mch_set_text_area_pos(int x, int y, int w, int h)
568 {
569     gtk_form_move_resize(GTK_FORM(gui.formwin), gui.drawarea, x, y, w, h);
570 }
571 
572 
573 #if defined(FEAT_MENU) || defined(PROTO)
574 /*
575  * Enable or disable accelerators for the toplevel menus.
576  */
577     void
578 gui_gtk_set_mnemonics(int enable)
579 {
580     vimmenu_T	*menu;
581     char_u	*name;
582 
583     for (menu = root_menu; menu != NULL; menu = menu->next)
584     {
585 	if (menu->id == NULL)
586 	    continue;
587 
588 	name = translate_mnemonic_tag(menu->name, enable);
589 	gtk_label_set_text_with_mnemonic(GTK_LABEL(menu->label),
590 					 (const char *)name);
591 	vim_free(name);
592     }
593 }
594 
595     static void
596 recurse_tearoffs(vimmenu_T *menu, int val)
597 {
598     for (; menu != NULL; menu = menu->next)
599     {
600 	if (menu->submenu_id != NULL && menu->tearoff_handle != NULL
601 		&& menu->name[0] != ']' && !menu_is_popup(menu->name))
602 	{
603 	    if (val)
604 		gtk_widget_show(menu->tearoff_handle);
605 	    else
606 		gtk_widget_hide(menu->tearoff_handle);
607 	}
608 	recurse_tearoffs(menu->children, val);
609     }
610 }
611 
612     void
613 gui_mch_toggle_tearoffs(int enable)
614 {
615     recurse_tearoffs(root_menu, enable);
616 }
617 #endif /* FEAT_MENU */
618 
619 #if defined(FEAT_TOOLBAR)
620     static int
621 get_menu_position(vimmenu_T *menu)
622 {
623     vimmenu_T	*node;
624     int		idx = 0;
625 
626     for (node = menu->parent->children; node != menu; node = node->next)
627     {
628 	g_return_val_if_fail(node != NULL, -1);
629 	++idx;
630     }
631 
632     return idx;
633 }
634 #endif /* FEAT_TOOLBAR */
635 
636 
637 #if defined(FEAT_TOOLBAR) || defined(PROTO)
638     void
639 gui_mch_menu_set_tip(vimmenu_T *menu)
640 {
641     if (menu->id != NULL && menu->parent != NULL
642 	    && gui.toolbar != NULL && menu_is_toolbar(menu->parent->name))
643     {
644 	char_u *tooltip;
645 
646 	tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
647 	if (tooltip == NULL || utf_valid_string(tooltip, NULL))
648 	    /* Only set the tooltip when it's valid utf-8. */
649 	gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
650 			     menu->id, (const char *)tooltip, NULL);
651 	CONVERT_TO_UTF8_FREE(tooltip);
652     }
653 }
654 #endif /* FEAT_TOOLBAR */
655 
656 
657 #if defined(FEAT_MENU) || defined(PROTO)
658 /*
659  * Destroy the machine specific menu widget.
660  */
661     void
662 gui_mch_destroy_menu(vimmenu_T *menu)
663 {
664     /* Don't let gtk_container_remove automatically destroy menu->id. */
665     if (menu->id != NULL)
666 	g_object_ref(menu->id);
667 
668     /* Workaround for a spurious gtk warning in Ubuntu: "Trying to remove
669      * a child that doesn't believe we're it's parent."
670      * Remove widget from gui.menubar before destroying it. */
671     if (menu->id != NULL && gui.menubar != NULL
672 			    && gtk_widget_get_parent(menu->id) == gui.menubar)
673 	gtk_container_remove(GTK_CONTAINER(gui.menubar), menu->id);
674 
675 # ifdef FEAT_TOOLBAR
676     if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
677     {
678 	if (menu_is_separator(menu->name))
679 	    gtk_toolbar_remove_space(GTK_TOOLBAR(gui.toolbar),
680 				     get_menu_position(menu));
681 	else if (menu->id != NULL)
682 	    gtk_widget_destroy(menu->id);
683     }
684     else
685 # endif /* FEAT_TOOLBAR */
686     {
687 	if (menu->submenu_id != NULL)
688 	    gtk_widget_destroy(menu->submenu_id);
689 
690 	if (menu->id != NULL)
691 	    gtk_widget_destroy(menu->id);
692     }
693 
694     if (menu->id != NULL)
695 	g_object_unref(menu->id);
696     menu->submenu_id = NULL;
697     menu->id = NULL;
698 }
699 #endif /* FEAT_MENU */
700 
701 
702 /*
703  * Scrollbar stuff.
704  */
705     void
706 gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max)
707 {
708     if (sb->id != NULL)
709     {
710 	GtkAdjustment *adjustment;
711 
712 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
713 
714 	adjustment->lower = 0.0;
715 	adjustment->value = val;
716 	adjustment->upper = max + 1;
717 	adjustment->page_size = size;
718 	adjustment->page_increment = size < 3L ? 1L : size - 2L;
719 	adjustment->step_increment = 1.0;
720 
721 	g_signal_handler_block(GTK_OBJECT(adjustment),
722 						      (gulong)sb->handler_id);
723 	gtk_adjustment_changed(adjustment);
724 	g_signal_handler_unblock(GTK_OBJECT(adjustment),
725 						      (gulong)sb->handler_id);
726     }
727 }
728 
729     void
730 gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h)
731 {
732     if (sb->id != NULL)
733 	gtk_form_move_resize(GTK_FORM(gui.formwin), sb->id, x, y, w, h);
734 }
735 
736 /*
737  * Take action upon scrollbar dragging.
738  */
739     static void
740 adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
741 {
742     scrollbar_T	*sb;
743     long	value;
744     int		dragging = FALSE;
745 
746 #ifdef FEAT_XIM
747     /* cancel any preediting */
748     if (im_is_preediting())
749 	xim_reset();
750 #endif
751 
752     sb = gui_find_scrollbar((long)data);
753     value = (long)adjustment->value;
754     /*
755      * The dragging argument must be right for the scrollbar to work with
756      * closed folds.  This isn't documented, hopefully this will keep on
757      * working in later GTK versions.
758      *
759      * FIXME: Well, it doesn't work in GTK2. :)
760      * HACK: Get the mouse pointer position, if it appears to be on an arrow
761      * button set "dragging" to FALSE.  This assumes square buttons!
762      */
763     if (sb != NULL)
764     {
765 	dragging = TRUE;
766 
767 	if (sb->wp != NULL)
768 	{
769 	    int			x;
770 	    int			y;
771 	    GdkModifierType	state;
772 	    int			width;
773 	    int			height;
774 
775 	    /* vertical scrollbar: need to set "dragging" properly in case
776 	     * there are closed folds. */
777 	    gdk_window_get_pointer(sb->id->window, &x, &y, &state);
778 	    gdk_window_get_size(sb->id->window, &width, &height);
779 	    if (x >= 0 && x < width && y >= 0 && y < height)
780 	    {
781 		if (y < width)
782 		{
783 		    /* up arrow: move one (closed fold) line up */
784 		    dragging = FALSE;
785 		    value = sb->wp->w_topline - 2;
786 		}
787 		else if (y > height - width)
788 		{
789 		    /* down arrow: move one (closed fold) line down */
790 		    dragging = FALSE;
791 		    value = sb->wp->w_topline;
792 		}
793 	    }
794 	}
795     }
796 
797     gui_drag_scrollbar(sb, value, dragging);
798 }
799 
800 /* SBAR_VERT or SBAR_HORIZ */
801     void
802 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
803 {
804     if (orient == SBAR_HORIZ)
805 	sb->id = gtk_hscrollbar_new(NULL);
806     else if (orient == SBAR_VERT)
807 	sb->id = gtk_vscrollbar_new(NULL);
808 
809     if (sb->id != NULL)
810     {
811 	GtkAdjustment *adjustment;
812 
813 	GTK_WIDGET_UNSET_FLAGS(sb->id, GTK_CAN_FOCUS);
814 	gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
815 
816 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
817 
818 	sb->handler_id = gtk_signal_connect(
819 			     GTK_OBJECT(adjustment), "value_changed",
820 			     GTK_SIGNAL_FUNC(adjustment_value_changed),
821 			     GINT_TO_POINTER(sb->ident));
822 	gui_mch_update();
823     }
824 }
825 
826 #if defined(FEAT_WINDOWS) || defined(PROTO)
827     void
828 gui_mch_destroy_scrollbar(scrollbar_T *sb)
829 {
830     if (sb->id != NULL)
831     {
832 	gtk_widget_destroy(sb->id);
833 	sb->id = NULL;
834     }
835     gui_mch_update();
836 }
837 #endif
838 
839 #if defined(FEAT_BROWSE) || defined(PROTO)
840 /*
841  * Implementation of the file selector related stuff
842  */
843 
844 #ifndef USE_FILE_CHOOSER
845     static void
846 browse_ok_cb(GtkWidget *widget UNUSED, gpointer cbdata)
847 {
848     gui_T *vw = (gui_T *)cbdata;
849 
850     if (vw->browse_fname != NULL)
851 	g_free(vw->browse_fname);
852 
853     vw->browse_fname = (char_u *)g_strdup(gtk_file_selection_get_filename(
854 					GTK_FILE_SELECTION(vw->filedlg)));
855     gtk_widget_hide(vw->filedlg);
856 }
857 
858     static void
859 browse_cancel_cb(GtkWidget *widget UNUSED, gpointer cbdata)
860 {
861     gui_T *vw = (gui_T *)cbdata;
862 
863     if (vw->browse_fname != NULL)
864     {
865 	g_free(vw->browse_fname);
866 	vw->browse_fname = NULL;
867     }
868     gtk_widget_hide(vw->filedlg);
869 }
870 
871     static gboolean
872 browse_destroy_cb(GtkWidget *widget UNUSED)
873 {
874     if (gui.browse_fname != NULL)
875     {
876 	g_free(gui.browse_fname);
877 	gui.browse_fname = NULL;
878     }
879     gui.filedlg = NULL;
880     gtk_main_quit();
881     return FALSE;
882 }
883 #endif
884 
885 /*
886  * Put up a file requester.
887  * Returns the selected name in allocated memory, or NULL for Cancel.
888  * saving,			select file to write
889  * title			title for the window
890  * dflt				default name
891  * ext				not used (extension added)
892  * initdir			initial directory, NULL for current dir
893  * filter			not used (file name filter)
894  */
895     char_u *
896 gui_mch_browse(int saving UNUSED,
897 	       char_u *title,
898 	       char_u *dflt,
899 	       char_u *ext UNUSED,
900 	       char_u *initdir,
901 	       char_u *filter)
902 {
903 #ifdef USE_FILE_CHOOSER
904     GtkWidget		*fc;
905 #endif
906     char_u		dirbuf[MAXPATHL];
907     guint		log_handler;
908     const gchar		*domain = "Gtk";
909 
910     title = CONVERT_TO_UTF8(title);
911 
912     /* GTK has a bug, it only works with an absolute path. */
913     if (initdir == NULL || *initdir == NUL)
914 	mch_dirname(dirbuf, MAXPATHL);
915     else if (vim_FullName(initdir, dirbuf, MAXPATHL - 2, FALSE) == FAIL)
916 	dirbuf[0] = NUL;
917     /* Always need a trailing slash for a directory. */
918     add_pathsep(dirbuf);
919 
920     /* If our pointer is currently hidden, then we should show it. */
921     gui_mch_mousehide(FALSE);
922 
923     /* Hack: The GTK file dialog warns when it can't access a new file, this
924      * makes it shut up. http://bugzilla.gnome.org/show_bug.cgi?id=664587 */
925     log_handler = g_log_set_handler(domain, G_LOG_LEVEL_WARNING,
926 						  recent_func_log_func, NULL);
927 
928 #ifdef USE_FILE_CHOOSER
929     /* We create the dialog each time, so that the button text can be "Open"
930      * or "Save" according to the action. */
931     fc = gtk_file_chooser_dialog_new((const gchar *)title,
932 	    GTK_WINDOW(gui.mainwin),
933 	    saving ? GTK_FILE_CHOOSER_ACTION_SAVE
934 					   : GTK_FILE_CHOOSER_ACTION_OPEN,
935 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
936 	    saving ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
937 	    NULL);
938     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),
939 						       (const gchar *)dirbuf);
940 
941     if (filter != NULL && *filter != NUL)
942     {
943 	int     i = 0;
944 	char_u  *patt;
945 	char_u  *p = filter;
946 	GtkFileFilter	*gfilter;
947 
948 	gfilter = gtk_file_filter_new();
949 	patt = alloc(STRLEN(filter));
950 	while (p != NULL && *p != NUL)
951 	{
952 	    if (*p == '\n' || *p == ';' || *p == '\t')
953 	    {
954 		STRNCPY(patt, filter, i);
955 		patt[i] = '\0';
956 		if (*p == '\t')
957 		    gtk_file_filter_set_name(gfilter, (gchar *)patt);
958 		else
959 		{
960 		    gtk_file_filter_add_pattern(gfilter, (gchar *)patt);
961 		    if (*p == '\n')
962 		    {
963 			gtk_file_chooser_add_filter((GtkFileChooser *)fc,
964 								     gfilter);
965 			if (*(p + 1) != NUL)
966 			    gfilter = gtk_file_filter_new();
967 		    }
968 		}
969 		filter = ++p;
970 		i = 0;
971 	    }
972 	    else
973 	    {
974 		p++;
975 		i++;
976 	    }
977 	}
978 	vim_free(patt);
979     }
980     if (saving && dflt != NULL && *dflt != NUL)
981 	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fc), (char *)dflt);
982 
983     gui.browse_fname = NULL;
984     if (gtk_dialog_run(GTK_DIALOG(fc)) == GTK_RESPONSE_ACCEPT)
985     {
986 	char *filename;
987 
988 	filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
989 	gui.browse_fname = (char_u *)g_strdup(filename);
990 	g_free(filename);
991     }
992     gtk_widget_destroy(GTK_WIDGET(fc));
993 
994 #else
995 
996     if (gui.filedlg == NULL)
997     {
998 	GtkFileSelection	*fs;	/* shortcut */
999 
1000 	gui.filedlg = gtk_file_selection_new((const gchar *)title);
1001 	gtk_window_set_modal(GTK_WINDOW(gui.filedlg), TRUE);
1002 	gtk_window_set_transient_for(GTK_WINDOW(gui.filedlg),
1003 						     GTK_WINDOW(gui.mainwin));
1004 	fs = GTK_FILE_SELECTION(gui.filedlg);
1005 
1006 	gtk_container_border_width(GTK_CONTAINER(fs), 4);
1007 
1008 	gtk_signal_connect(GTK_OBJECT(fs->ok_button),
1009 		"clicked", GTK_SIGNAL_FUNC(browse_ok_cb), &gui);
1010 	gtk_signal_connect(GTK_OBJECT(fs->cancel_button),
1011 		"clicked", GTK_SIGNAL_FUNC(browse_cancel_cb), &gui);
1012 	/* gtk_signal_connect() doesn't work for destroy, it causes a hang */
1013 	gtk_signal_connect_object(GTK_OBJECT(gui.filedlg),
1014 		"destroy", GTK_SIGNAL_FUNC(browse_destroy_cb),
1015 		GTK_OBJECT(gui.filedlg));
1016     }
1017     else
1018 	gtk_window_set_title(GTK_WINDOW(gui.filedlg), (const gchar *)title);
1019 
1020     /* Concatenate "initdir" and "dflt". */
1021     if (dflt != NULL && *dflt != NUL
1022 			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
1023 	STRCAT(dirbuf, dflt);
1024 
1025     gtk_file_selection_set_filename(GTK_FILE_SELECTION(gui.filedlg),
1026 						      (const gchar *)dirbuf);
1027 
1028     gtk_widget_show(gui.filedlg);
1029     gtk_main();
1030 #endif
1031     g_log_remove_handler(domain, log_handler);
1032 
1033     CONVERT_TO_UTF8_FREE(title);
1034     if (gui.browse_fname == NULL)
1035 	return NULL;
1036 
1037     /* shorten the file name if possible */
1038     return vim_strsave(shorten_fname1(gui.browse_fname));
1039 }
1040 
1041 /*
1042  * Put up a directory selector
1043  * Returns the selected name in allocated memory, or NULL for Cancel.
1044  * title			title for the window
1045  * dflt				default name
1046  * initdir			initial directory, NULL for current dir
1047  */
1048     char_u *
1049 gui_mch_browsedir(
1050 	       char_u *title,
1051 	       char_u *initdir)
1052 {
1053 # if defined(GTK_FILE_CHOOSER)	    /* Only in GTK 2.4 and later. */
1054     char_u		dirbuf[MAXPATHL];
1055     char_u		*p;
1056     GtkWidget		*dirdlg;	    /* file selection dialog */
1057     char_u		*dirname = NULL;
1058 
1059     title = CONVERT_TO_UTF8(title);
1060 
1061     dirdlg = gtk_file_chooser_dialog_new(
1062 	    (const gchar *)title,
1063 	    GTK_WINDOW(gui.mainwin),
1064 	    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1065 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1066 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1067 	    NULL);
1068 
1069     CONVERT_TO_UTF8_FREE(title);
1070 
1071     /* if our pointer is currently hidden, then we should show it. */
1072     gui_mch_mousehide(FALSE);
1073 
1074     /* GTK appears to insist on an absolute path. */
1075     if (initdir == NULL || *initdir == NUL
1076 	       || vim_FullName(initdir, dirbuf, MAXPATHL - 10, FALSE) == FAIL)
1077 	mch_dirname(dirbuf, MAXPATHL - 10);
1078 
1079     /* Always need a trailing slash for a directory.
1080      * Also add a dummy file name, so that we get to the directory. */
1081     add_pathsep(dirbuf);
1082     STRCAT(dirbuf, "@zd(*&1|");
1083     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dirdlg),
1084 						      (const gchar *)dirbuf);
1085 
1086     /* Run the dialog. */
1087     if (gtk_dialog_run(GTK_DIALOG(dirdlg)) == GTK_RESPONSE_ACCEPT)
1088 	dirname = (char_u *)gtk_file_chooser_get_filename(
1089 						    GTK_FILE_CHOOSER(dirdlg));
1090     gtk_widget_destroy(dirdlg);
1091     if (dirname == NULL)
1092 	return NULL;
1093 
1094     /* shorten the file name if possible */
1095     p = vim_strsave(shorten_fname1(dirname));
1096     g_free(dirname);
1097     return p;
1098 
1099 # else
1100     /* For GTK 2.2 and earlier: fall back to ordinary file selector. */
1101     return gui_mch_browse(0, title, NULL, NULL, initdir, NULL);
1102 # endif
1103 }
1104 
1105 
1106 #endif	/* FEAT_BROWSE */
1107 
1108 #if defined(FEAT_GUI_DIALOG) || defined(PROTO)
1109 
1110     static GtkWidget *
1111 create_message_dialog(int type, char_u *title, char_u *message)
1112 {
1113     GtkWidget	    *dialog;
1114     GtkMessageType  message_type;
1115 
1116     switch (type)
1117     {
1118 	case VIM_ERROR:	    message_type = GTK_MESSAGE_ERROR;	 break;
1119 	case VIM_WARNING:   message_type = GTK_MESSAGE_WARNING;	 break;
1120 	case VIM_QUESTION:  message_type = GTK_MESSAGE_QUESTION; break;
1121 	default:	    message_type = GTK_MESSAGE_INFO;	 break;
1122     }
1123 
1124     message = CONVERT_TO_UTF8(message);
1125     dialog  = gtk_message_dialog_new(GTK_WINDOW(gui.mainwin),
1126 				     GTK_DIALOG_DESTROY_WITH_PARENT,
1127 				     message_type,
1128 				     GTK_BUTTONS_NONE,
1129 				     "%s", (const char *)message);
1130     CONVERT_TO_UTF8_FREE(message);
1131 
1132     if (title != NULL)
1133     {
1134 	title = CONVERT_TO_UTF8(title);
1135 	gtk_window_set_title(GTK_WINDOW(dialog), (const char *)title);
1136 	CONVERT_TO_UTF8_FREE(title);
1137     }
1138     else if (type == VIM_GENERIC)
1139     {
1140 	gtk_window_set_title(GTK_WINDOW(dialog), "VIM");
1141     }
1142 
1143     return dialog;
1144 }
1145 
1146 /*
1147  * Split up button_string into individual button labels by inserting
1148  * NUL bytes.  Also replace the Vim-style mnemonic accelerator prefix
1149  * '&' with '_'.  button_string must point to allocated memory!
1150  * Return an allocated array of pointers into button_string.
1151  */
1152     static char **
1153 split_button_string(char_u *button_string, int *n_buttons)
1154 {
1155     char	    **array;
1156     char_u	    *p;
1157     unsigned int    count = 1;
1158 
1159     for (p = button_string; *p != NUL; ++p)
1160 	if (*p == DLG_BUTTON_SEP)
1161 	    ++count;
1162 
1163     array = (char **)alloc((count + 1) * sizeof(char *));
1164     count = 0;
1165 
1166     if (array != NULL)
1167     {
1168 	array[count++] = (char *)button_string;
1169 	for (p = button_string; *p != NUL; )
1170 	{
1171 	    if (*p == DLG_BUTTON_SEP)
1172 	    {
1173 		*p++ = NUL;
1174 		array[count++] = (char *)p;
1175 	    }
1176 	    else if (*p == DLG_HOTKEY_CHAR)
1177 		*p++ = '_';
1178 	    else
1179 		mb_ptr_adv(p);
1180 	}
1181 	array[count] = NULL; /* currently not relied upon, but doesn't hurt */
1182     }
1183 
1184     *n_buttons = count;
1185     return array;
1186 }
1187 
1188     static char **
1189 split_button_translation(const char *message)
1190 {
1191     char    **buttons = NULL;
1192     char_u  *str;
1193     int	    n_buttons = 0;
1194     int	    n_expected = 1;
1195 
1196     for (str = (char_u *)message; *str != NUL; ++str)
1197 	if (*str == DLG_BUTTON_SEP)
1198 	    ++n_expected;
1199 
1200     str = (char_u *)_(message);
1201     if (str != NULL)
1202     {
1203 	if (output_conv.vc_type != CONV_NONE)
1204 	    str = string_convert(&output_conv, str, NULL);
1205 	else
1206 	    str = vim_strsave(str);
1207 
1208 	if (str != NULL)
1209 	    buttons = split_button_string(str, &n_buttons);
1210     }
1211     /*
1212      * Uh-oh... this should never ever happen.	But we don't wanna crash
1213      * if the translation is broken, thus fall back to the untranslated
1214      * buttons string in case of emergency.
1215      */
1216     if (buttons == NULL || n_buttons != n_expected)
1217     {
1218 	vim_free(buttons);
1219 	vim_free(str);
1220 	buttons = NULL;
1221 	str = vim_strsave((char_u *)message);
1222 
1223 	if (str != NULL)
1224 	    buttons = split_button_string(str, &n_buttons);
1225 	if (buttons == NULL)
1226 	    vim_free(str);
1227     }
1228 
1229     return buttons;
1230 }
1231 
1232     static int
1233 button_equal(const char *a, const char *b)
1234 {
1235     while (*a != '\0' && *b != '\0')
1236     {
1237 	if (*a == '_' && *++a == '\0')
1238 	    break;
1239 	if (*b == '_' && *++b == '\0')
1240 	    break;
1241 
1242 	if (g_unichar_tolower(g_utf8_get_char(a))
1243 		!= g_unichar_tolower(g_utf8_get_char(b)))
1244 	    return FALSE;
1245 
1246 	a = g_utf8_next_char(a);
1247 	b = g_utf8_next_char(b);
1248     }
1249 
1250     return (*a == '\0' && *b == '\0');
1251 }
1252 
1253     static void
1254 dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
1255 {
1256     char    **ok;
1257     char    **ync;  /* "yes no cancel" */
1258     char    **buttons;
1259     int	    n_buttons = 0;
1260     int	    idx;
1261 
1262     button_string = vim_strsave(button_string); /* must be writable */
1263     if (button_string == NULL)
1264 	return;
1265 
1266     /* Check 'v' flag in 'guioptions': vertical button placement. */
1267     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
1268     {
1269 	GtkWidget	*vbutton_box;
1270 
1271 	vbutton_box = gtk_vbutton_box_new();
1272 	gtk_widget_show(vbutton_box);
1273 	gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1274 						 vbutton_box, TRUE, FALSE, 0);
1275 	/* Overrule the "action_area" value, hopefully this works... */
1276 	GTK_DIALOG(dialog)->action_area = vbutton_box;
1277     }
1278 
1279     /*
1280      * Yes this is ugly, I don't particularly like it either.  But doing it
1281      * this way has the compelling advantage that translations need not to
1282      * be touched at all.  See below what 'ok' and 'ync' are used for.
1283      */
1284     ok	    = split_button_translation(N_("&Ok"));
1285     ync     = split_button_translation(N_("&Yes\n&No\n&Cancel"));
1286     buttons = split_button_string(button_string, &n_buttons);
1287 
1288     /*
1289      * Yes, the buttons are in reversed order to match the GNOME 2 desktop
1290      * environment.  Don't hit me -- it's all about consistency.
1291      * Well, apparently somebody changed his mind: with GTK 2.2.4 it works the
1292      * other way around...
1293      */
1294     for (idx = 1; idx <= n_buttons; ++idx)
1295     {
1296 	char	*label;
1297 	char_u	*label8;
1298 
1299 	label = buttons[idx - 1];
1300 	/*
1301 	 * Perform some guesswork to find appropriate stock items for the
1302 	 * buttons.  We have to compare with a sample of the translated
1303 	 * button string to get things right.  Yes, this is hackish :/
1304 	 *
1305 	 * But even the common button labels aren't necessarily translated,
1306 	 * since anyone can create their own dialogs using Vim functions.
1307 	 * Thus we have to check for those too.
1308 	 */
1309 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
1310 	{
1311 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
1312 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
1313 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
1314 	    else if (button_equal(label, ync[2]))   label = GTK_STOCK_CANCEL;
1315 	    else if (button_equal(label, "Ok"))     label = GTK_STOCK_OK;
1316 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
1317 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
1318 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
1319 	}
1320 	label8 = CONVERT_TO_UTF8((char_u *)label);
1321 	gtk_dialog_add_button(dialog, (const gchar *)label8, idx);
1322 	CONVERT_TO_UTF8_FREE(label8);
1323     }
1324 
1325     if (ok != NULL)
1326 	vim_free(*ok);
1327     if (ync != NULL)
1328 	vim_free(*ync);
1329     vim_free(ok);
1330     vim_free(ync);
1331     vim_free(buttons);
1332     vim_free(button_string);
1333 }
1334 
1335 /*
1336  * Allow mnemonic accelerators to be activated without pressing <Alt>.
1337  * I'm not sure if it's a wise idea to do this.  However, the old GTK+ 1.2
1338  * GUI used to work this way, and I consider the impact on UI consistency
1339  * low enough to justify implementing this as a special Vim feature.
1340  */
1341 typedef struct _DialogInfo
1342 {
1343     int		ignore_enter;	    /* no default button, ignore "Enter" */
1344     int		noalt;		    /* accept accelerators without Alt */
1345     GtkDialog	*dialog;	    /* Widget of the dialog */
1346 } DialogInfo;
1347 
1348     static gboolean
1349 dialog_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1350 {
1351     DialogInfo *di = (DialogInfo *)data;
1352 
1353     /* Ignore hitting Enter (or Space) when there is no default button. */
1354     if (di->ignore_enter && (event->keyval == GDK_Return
1355 						     || event->keyval == ' '))
1356 	return TRUE;
1357     else    /* A different key was pressed, return to normal behavior */
1358 	di->ignore_enter = FALSE;
1359 
1360     /* Close the dialog when hitting "Esc". */
1361     if (event->keyval == GDK_Escape)
1362     {
1363 	gtk_dialog_response(di->dialog, GTK_RESPONSE_REJECT);
1364 	return TRUE;
1365     }
1366 
1367     if (di->noalt
1368 	      && (event->state & gtk_accelerator_get_default_mod_mask()) == 0)
1369     {
1370 	return gtk_window_mnemonic_activate(
1371 		   GTK_WINDOW(widget), event->keyval,
1372 		   gtk_window_get_mnemonic_modifier(GTK_WINDOW(widget)));
1373     }
1374 
1375     return FALSE; /* continue emission */
1376 }
1377 
1378     int
1379 gui_mch_dialog(int	type,	    /* type of dialog */
1380 	       char_u	*title,	    /* title of dialog */
1381 	       char_u	*message,   /* message text */
1382 	       char_u	*buttons,   /* names of buttons */
1383 	       int	def_but,    /* default button */
1384 	       char_u	*textfield, /* text for textfield or NULL */
1385 	       int	ex_cmd UNUSED)
1386 {
1387     GtkWidget	*dialog;
1388     GtkWidget	*entry = NULL;
1389     char_u	*text;
1390     int		response;
1391     DialogInfo  dialoginfo;
1392 
1393     dialog = create_message_dialog(type, title, message);
1394     dialoginfo.dialog = GTK_DIALOG(dialog);
1395     dialog_add_buttons(GTK_DIALOG(dialog), buttons);
1396 
1397     if (textfield != NULL)
1398     {
1399 	GtkWidget *alignment;
1400 
1401 	entry = gtk_entry_new();
1402 	gtk_widget_show(entry);
1403 
1404 	/* Make Enter work like pressing OK. */
1405 	gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
1406 
1407 	text = CONVERT_TO_UTF8(textfield);
1408 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
1409 	CONVERT_TO_UTF8_FREE(text);
1410 
1411 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
1412 						      (float)1.0, (float)1.0);
1413 	gtk_container_add(GTK_CONTAINER(alignment), entry);
1414 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
1415 	gtk_widget_show(alignment);
1416 
1417 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1418 			   alignment, TRUE, FALSE, 0);
1419 	dialoginfo.noalt = FALSE;
1420     }
1421     else
1422 	dialoginfo.noalt = TRUE;
1423 
1424     /* Allow activation of mnemonic accelerators without pressing <Alt> when
1425      * there is no textfield.  Handle pressing Esc. */
1426     g_signal_connect(G_OBJECT(dialog), "key_press_event",
1427 			 G_CALLBACK(&dialog_key_press_event_cb), &dialoginfo);
1428 
1429     if (def_but > 0)
1430     {
1431 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), def_but);
1432 	dialoginfo.ignore_enter = FALSE;
1433     }
1434     else
1435 	/* No default button, ignore pressing Enter. */
1436 	dialoginfo.ignore_enter = TRUE;
1437 
1438     /* Show the mouse pointer if it's currently hidden. */
1439     gui_mch_mousehide(FALSE);
1440 
1441     response = gtk_dialog_run(GTK_DIALOG(dialog));
1442 
1443     /* GTK_RESPONSE_NONE means the dialog was programmatically destroyed. */
1444     if (response != GTK_RESPONSE_NONE)
1445     {
1446 	if (response == GTK_RESPONSE_ACCEPT)	    /* Enter pressed */
1447 	    response = def_but;
1448 	if (textfield != NULL)
1449 	{
1450 	    text = (char_u *)gtk_entry_get_text(GTK_ENTRY(entry));
1451 	    text = CONVERT_FROM_UTF8(text);
1452 
1453 	    vim_strncpy(textfield, text, IOSIZE - 1);
1454 
1455 	    CONVERT_FROM_UTF8_FREE(text);
1456 	}
1457 	gtk_widget_destroy(dialog);
1458     }
1459 
1460     return response > 0 ? response : 0;
1461 }
1462 
1463 #endif /* FEAT_GUI_DIALOG */
1464 
1465 
1466 #if defined(FEAT_MENU) || defined(PROTO)
1467 
1468     void
1469 gui_mch_show_popupmenu(vimmenu_T *menu)
1470 {
1471 # if defined(FEAT_XIM)
1472     /*
1473      * Append a submenu for selecting an input method.	This is
1474      * currently the only way to switch input methods at runtime.
1475      */
1476     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
1477 					 "vim-has-im-menu") == NULL)
1478     {
1479 	GtkWidget   *menuitem;
1480 	GtkWidget   *submenu;
1481 	char_u	    *name;
1482 
1483 	menuitem = gtk_separator_menu_item_new();
1484 	gtk_widget_show(menuitem);
1485 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
1486 
1487 	name = (char_u *)_("Input _Methods");
1488 	name = CONVERT_TO_UTF8(name);
1489 	menuitem = gtk_menu_item_new_with_mnemonic((const char *)name);
1490 	CONVERT_TO_UTF8_FREE(name);
1491 	gtk_widget_show(menuitem);
1492 
1493 	submenu = gtk_menu_new();
1494 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
1495 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
1496 
1497 	gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(xic),
1498 					     GTK_MENU_SHELL(submenu));
1499 	g_object_set_data(G_OBJECT(menu->submenu_id),
1500 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
1501     }
1502 # endif /* FEAT_XIM */
1503 
1504     gtk_menu_popup(GTK_MENU(menu->submenu_id),
1505 		   NULL, NULL,
1506 		   (GtkMenuPositionFunc)NULL, NULL,
1507 		   3U, gui.event_time);
1508 }
1509 
1510 /* Ugly global variable to pass "mouse_pos" flag from gui_make_popup() to
1511  * popup_menu_position_func(). */
1512 static int popup_mouse_pos;
1513 
1514 /*
1515  * Menu position callback; used by gui_make_popup() to place the menu
1516  * at the current text cursor position.
1517  *
1518  * Note: The push_in output argument seems to affect scrolling of huge
1519  * menus that don't fit on the screen.	Leave it at the default for now.
1520  */
1521     static void
1522 popup_menu_position_func(GtkMenu *menu UNUSED,
1523 			 gint *x, gint *y,
1524 			 gboolean *push_in UNUSED,
1525 			 gpointer user_data UNUSED)
1526 {
1527     gdk_window_get_origin(gui.drawarea->window, x, y);
1528 
1529     if (popup_mouse_pos)
1530     {
1531 	int	mx, my;
1532 
1533 	gui_mch_getmouse(&mx, &my);
1534 	*x += mx;
1535 	*y += my;
1536     }
1537     else if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
1538     {
1539 	/* Find the cursor position in the current window */
1540 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
1541 	*y += FILL_Y(W_WINROW(curwin) + curwin->w_wrow + 1) + 1;
1542     }
1543 }
1544 
1545     void
1546 gui_make_popup(char_u *path_name, int mouse_pos)
1547 {
1548     vimmenu_T *menu;
1549 
1550     popup_mouse_pos = mouse_pos;
1551 
1552     menu = gui_find_menu(path_name);
1553 
1554     if (menu != NULL && menu->submenu_id != NULL)
1555     {
1556 	gtk_menu_popup(GTK_MENU(menu->submenu_id),
1557 		       NULL, NULL,
1558 		       &popup_menu_position_func, NULL,
1559 		       0U, (guint32)GDK_CURRENT_TIME);
1560     }
1561 }
1562 
1563 #endif /* FEAT_MENU */
1564 
1565 
1566 /*
1567  * We don't create it twice.
1568  */
1569 
1570 typedef struct _SharedFindReplace
1571 {
1572     GtkWidget *dialog;	/* the main dialog widget */
1573     GtkWidget *wword;	/* 'Whole word only' check button */
1574     GtkWidget *mcase;	/* 'Match case' check button */
1575     GtkWidget *up;	/* search direction 'Up' radio button */
1576     GtkWidget *down;	/* search direction 'Down' radio button */
1577     GtkWidget *what;	/* 'Find what' entry text widget */
1578     GtkWidget *with;	/* 'Replace with' entry text widget */
1579     GtkWidget *find;	/* 'Find Next' action button */
1580     GtkWidget *replace;	/* 'Replace With' action button */
1581     GtkWidget *all;	/* 'Replace All' action button */
1582 } SharedFindReplace;
1583 
1584 static SharedFindReplace find_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1585 static SharedFindReplace repl_widgets = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1586 
1587     static int
1588 find_key_press_event(
1589 		GtkWidget	*widget UNUSED,
1590 		GdkEventKey	*event,
1591 		SharedFindReplace *frdp)
1592 {
1593     /* If the user is holding one of the key modifiers we will just bail out,
1594      * thus preserving the possibility of normal focus traversal.
1595      */
1596     if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
1597 	return FALSE;
1598 
1599     /* the Escape key synthesizes a cancellation action */
1600     if (event->keyval == GDK_Escape)
1601     {
1602 	gtk_widget_hide(frdp->dialog);
1603 
1604 	return TRUE;
1605     }
1606 
1607     /* It would be delightful if it where possible to do search history
1608      * operations on the K_UP and K_DOWN keys here.
1609      */
1610 
1611     return FALSE;
1612 }
1613 
1614     static GtkWidget *
1615 create_image_button(const char *stock_id, const char *label)
1616 {
1617     char_u	*text;
1618     GtkWidget	*box;
1619     GtkWidget	*alignment;
1620     GtkWidget	*button;
1621 
1622     text = CONVERT_TO_UTF8((char_u *)label);
1623 
1624     box = gtk_hbox_new(FALSE, 3);
1625     gtk_box_pack_start(GTK_BOX(box),
1626 		       gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
1627 		       FALSE, FALSE, 0);
1628     gtk_box_pack_start(GTK_BOX(box),
1629 		       gtk_label_new((const char *)text),
1630 		       FALSE, FALSE, 0);
1631 
1632     CONVERT_TO_UTF8_FREE(text);
1633 
1634     alignment = gtk_alignment_new((float)0.5, (float)0.5,
1635 						      (float)0.0, (float)0.0);
1636     gtk_container_add(GTK_CONTAINER(alignment), box);
1637     gtk_widget_show_all(alignment);
1638 
1639     button = gtk_button_new();
1640     gtk_container_add(GTK_CONTAINER(button), alignment);
1641 
1642     return button;
1643 }
1644 
1645 /*
1646  * This is currently only used by find_replace_dialog_create(), and
1647  * I'd really like to keep it at that.	In other words: don't spread
1648  * this nasty hack all over the code.  Think twice.
1649  */
1650     static const char *
1651 convert_localized_message(char_u **buffer, const char *message)
1652 {
1653     if (output_conv.vc_type == CONV_NONE)
1654 	return message;
1655 
1656     vim_free(*buffer);
1657     *buffer = string_convert(&output_conv, (char_u *)message, NULL);
1658 
1659     return (const char *)*buffer;
1660 }
1661 
1662     static void
1663 find_replace_dialog_create(char_u *arg, int do_replace)
1664 {
1665     GtkWidget	*hbox;		/* main top down box */
1666     GtkWidget	*actionarea;
1667     GtkWidget	*table;
1668     GtkWidget	*tmp;
1669     GtkWidget	*vbox;
1670     gboolean	sensitive;
1671     SharedFindReplace *frdp;
1672     char_u	*entry_text;
1673     int		wword = FALSE;
1674     int		mcase = !p_ic;
1675     char_u	*conv_buffer = NULL;
1676 #   define CONV(message) convert_localized_message(&conv_buffer, (message))
1677 
1678     frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
1679 
1680     /* Get the search string to use. */
1681     entry_text = get_find_dialog_text(arg, &wword, &mcase);
1682 
1683     if (entry_text != NULL && output_conv.vc_type != CONV_NONE)
1684     {
1685 	char_u *old_text = entry_text;
1686 	entry_text = string_convert(&output_conv, entry_text, NULL);
1687 	vim_free(old_text);
1688     }
1689 
1690     /*
1691      * If the dialog already exists, just raise it.
1692      */
1693     if (frdp->dialog)
1694     {
1695 	if (entry_text != NULL)
1696 	{
1697 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
1698 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
1699 							     (gboolean)wword);
1700 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
1701 							     (gboolean)mcase);
1702 	}
1703 	gtk_window_present(GTK_WINDOW(frdp->dialog));
1704 	vim_free(entry_text);
1705 	return;
1706     }
1707 
1708     frdp->dialog = gtk_dialog_new();
1709     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
1710     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
1711     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
1712 
1713     if (do_replace)
1714     {
1715 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
1716 			     CONV(_("VIM - Search and Replace...")));
1717     }
1718     else
1719     {
1720 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
1721 			     CONV(_("VIM - Search...")));
1722     }
1723 
1724     hbox = gtk_hbox_new(FALSE, 0);
1725     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
1726     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
1727 
1728     if (do_replace)
1729 	table = gtk_table_new(1024, 4, FALSE);
1730     else
1731 	table = gtk_table_new(1024, 3, FALSE);
1732     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
1733     gtk_container_border_width(GTK_CONTAINER(table), 4);
1734 
1735     tmp = gtk_label_new(CONV(_("Find what:")));
1736     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
1737     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
1738 		     GTK_FILL, GTK_EXPAND, 2, 2);
1739     frdp->what = gtk_entry_new();
1740     sensitive = (entry_text != NULL && entry_text[0] != NUL);
1741     if (entry_text != NULL)
1742 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
1743     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
1744 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
1745     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
1746 				 GTK_SIGNAL_FUNC(find_key_press_event),
1747 				 (gpointer) frdp);
1748     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
1749 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
1750 
1751     if (do_replace)
1752     {
1753 	tmp = gtk_label_new(CONV(_("Replace with:")));
1754 	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
1755 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
1756 			 GTK_FILL, GTK_EXPAND, 2, 2);
1757 	frdp->with = gtk_entry_new();
1758 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
1759 			   GTK_SIGNAL_FUNC(find_replace_cb),
1760 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
1761 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
1762 				 GTK_SIGNAL_FUNC(find_key_press_event),
1763 				 (gpointer) frdp);
1764 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
1765 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
1766 
1767 	/*
1768 	 * Make the entry activation only change the input focus onto the
1769 	 * with item.
1770 	 */
1771 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
1772 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
1773     }
1774     else
1775     {
1776 	/*
1777 	 * Make the entry activation do the search.
1778 	 */
1779 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
1780 			   GTK_SIGNAL_FUNC(find_replace_cb),
1781 			   GINT_TO_POINTER(FRD_FINDNEXT));
1782     }
1783 
1784     /* whole word only button */
1785     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
1786     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
1787 							(gboolean)wword);
1788     if (do_replace)
1789 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
1790 			 GTK_FILL, GTK_EXPAND, 2, 2);
1791     else
1792 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
1793 			 GTK_FILL, GTK_EXPAND, 2, 2);
1794 
1795     /* match case button */
1796     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
1797     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
1798 							     (gboolean)mcase);
1799     if (do_replace)
1800 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
1801 			 GTK_FILL, GTK_EXPAND, 2, 2);
1802     else
1803 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
1804 			 GTK_FILL, GTK_EXPAND, 2, 2);
1805 
1806     tmp = gtk_frame_new(CONV(_("Direction")));
1807     if (do_replace)
1808 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
1809 			 GTK_FILL, GTK_FILL, 2, 2);
1810     else
1811 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
1812 			 GTK_FILL, GTK_FILL, 2, 2);
1813     vbox = gtk_vbox_new(FALSE, 0);
1814     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
1815     gtk_container_add(GTK_CONTAINER(tmp), vbox);
1816 
1817     /* 'Up' and 'Down' buttons */
1818     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
1819     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
1820     frdp->down = gtk_radio_button_new_with_label(
1821 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
1822 			CONV(_("Down")));
1823     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
1824     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
1825     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
1826 
1827     /* vbox to hold the action buttons */
1828     actionarea = gtk_vbutton_box_new();
1829     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
1830     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
1831 
1832     /* 'Find Next' button */
1833     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
1834     gtk_widget_set_sensitive(frdp->find, sensitive);
1835 
1836     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
1837 		       GTK_SIGNAL_FUNC(find_replace_cb),
1838 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
1839 				    : GINT_TO_POINTER(FRD_FINDNEXT));
1840 
1841     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
1842     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
1843     gtk_widget_grab_default(frdp->find);
1844 
1845     if (do_replace)
1846     {
1847 	/* 'Replace' button */
1848 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
1849 	gtk_widget_set_sensitive(frdp->replace, sensitive);
1850 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
1851 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
1852 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
1853 			   GTK_SIGNAL_FUNC(find_replace_cb),
1854 			   GINT_TO_POINTER(FRD_REPLACE));
1855 
1856 	/* 'Replace All' button */
1857 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
1858 	gtk_widget_set_sensitive(frdp->all, sensitive);
1859 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
1860 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
1861 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
1862 			   GTK_SIGNAL_FUNC(find_replace_cb),
1863 			   GINT_TO_POINTER(FRD_REPLACEALL));
1864     }
1865 
1866     /* 'Cancel' button */
1867     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1868     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
1869     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
1870     gtk_signal_connect_object(GTK_OBJECT(tmp),
1871 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
1872 			      GTK_OBJECT(frdp->dialog));
1873     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
1874 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
1875 			      GTK_OBJECT(frdp->dialog));
1876 
1877     tmp = gtk_vseparator_new();
1878     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
1879 
1880     /* Suppress automatic show of the unused action area */
1881     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
1882     gtk_widget_show_all(hbox);
1883     gtk_widget_show(frdp->dialog);
1884 
1885     vim_free(entry_text);
1886     vim_free(conv_buffer);
1887 #undef CONV
1888 }
1889 
1890     void
1891 gui_mch_find_dialog(exarg_T *eap)
1892 {
1893     if (gui.in_use)
1894 	find_replace_dialog_create(eap->arg, FALSE);
1895 }
1896 
1897     void
1898 gui_mch_replace_dialog(exarg_T *eap)
1899 {
1900     if (gui.in_use)
1901 	find_replace_dialog_create(eap->arg, TRUE);
1902 }
1903 
1904 /*
1905  * Callback for actions of the find and replace dialogs
1906  */
1907     static void
1908 find_replace_cb(GtkWidget *widget UNUSED, gpointer data)
1909 {
1910     int			flags;
1911     char_u		*find_text;
1912     char_u		*repl_text;
1913     gboolean		direction_down;
1914     SharedFindReplace	*sfr;
1915 
1916     flags = (int)(long)data;	    /* avoid a lint warning here */
1917 
1918     /* Get the search/replace strings from the dialog */
1919     if (flags == FRD_FINDNEXT)
1920     {
1921 	repl_text = NULL;
1922 	sfr = &find_widgets;
1923     }
1924     else
1925     {
1926 	repl_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(repl_widgets.with));
1927 	sfr = &repl_widgets;
1928     }
1929 
1930     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
1931     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
1932 
1933     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
1934 	flags |= FRD_WHOLE_WORD;
1935     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
1936 	flags |= FRD_MATCH_CASE;
1937 
1938     repl_text = CONVERT_FROM_UTF8(repl_text);
1939     find_text = CONVERT_FROM_UTF8(find_text);
1940     gui_do_findrepl(flags, find_text, repl_text, direction_down);
1941     CONVERT_FROM_UTF8_FREE(repl_text);
1942     CONVERT_FROM_UTF8_FREE(find_text);
1943 }
1944 
1945 /* our usual callback function */
1946     static void
1947 entry_activate_cb(GtkWidget *widget UNUSED, gpointer data)
1948 {
1949     gtk_widget_grab_focus(GTK_WIDGET(data));
1950 }
1951 
1952 /*
1953  * Syncing the find/replace dialogs on the fly is utterly useless crack,
1954  * and causes nothing but problems.  Please tell me a use case for which
1955  * you'd need both a find dialog and a find/replace one at the same time,
1956  * without being able to actually use them separately since they're syncing
1957  * all the time.  I don't think it's worthwhile to fix this nonsense,
1958  * particularly evil incarnation of braindeadness, whatever; I'd much rather
1959  * see it extinguished from this planet.  Thanks for listening.  Sorry.
1960  */
1961     static void
1962 entry_changed_cb(GtkWidget * entry, GtkWidget * dialog)
1963 {
1964     const gchar	*entry_text;
1965     gboolean	nonempty;
1966 
1967     entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
1968 
1969     if (!entry_text)
1970 	return;			/* shouldn't happen */
1971 
1972     nonempty = (entry_text[0] != '\0');
1973 
1974     if (dialog == find_widgets.dialog)
1975     {
1976 	gtk_widget_set_sensitive(find_widgets.find, nonempty);
1977     }
1978 
1979     if (dialog == repl_widgets.dialog)
1980     {
1981 	gtk_widget_set_sensitive(repl_widgets.find, nonempty);
1982 	gtk_widget_set_sensitive(repl_widgets.replace, nonempty);
1983 	gtk_widget_set_sensitive(repl_widgets.all, nonempty);
1984     }
1985 }
1986 
1987 /*
1988  * ":helpfind"
1989  */
1990     void
1991 ex_helpfind(eap)
1992     exarg_T	*eap UNUSED;
1993 {
1994     /* This will fail when menus are not loaded.  Well, it's only for
1995      * backwards compatibility anyway. */
1996     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
1997 }
1998 
1999 #if defined(FEAT_BROWSE) || defined(PROTO)
2000     static void
2001 recent_func_log_func(const gchar *log_domain UNUSED,
2002 		     GLogLevelFlags log_level UNUSED,
2003 		     const gchar *message UNUSED,
2004 		     gpointer user_data UNUSED)
2005 {
2006     /* We just want to suppress the warnings. */
2007     /* http://bugzilla.gnome.org/show_bug.cgi?id=664587 */
2008 }
2009 #endif
2010