xref: /vim-8.2.3635/src/gui_gtk.c (revision 7b0294cb)
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 # if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
57 #  define ENABLE_NLS	/* so the texts in the dialog boxes are translated */
58 # endif
59 # include <gnome.h>
60 #endif
61 
62 #if defined(FEAT_GUI_DIALOG) && !defined(HAVE_GTK2)
63 # include "../pixmaps/alert.xpm"
64 # include "../pixmaps/error.xpm"
65 # include "../pixmaps/generic.xpm"
66 # include "../pixmaps/info.xpm"
67 # include "../pixmaps/quest.xpm"
68 #endif
69 
70 #if defined(FEAT_TOOLBAR) && !defined(HAVE_GTK2)
71 /*
72  * Icons used by the toolbar code.
73  */
74 #include "../pixmaps/tb_new.xpm"
75 #include "../pixmaps/tb_open.xpm"
76 #include "../pixmaps/tb_close.xpm"
77 #include "../pixmaps/tb_save.xpm"
78 #include "../pixmaps/tb_print.xpm"
79 #include "../pixmaps/tb_cut.xpm"
80 #include "../pixmaps/tb_copy.xpm"
81 #include "../pixmaps/tb_paste.xpm"
82 #include "../pixmaps/tb_find.xpm"
83 #include "../pixmaps/tb_find_next.xpm"
84 #include "../pixmaps/tb_find_prev.xpm"
85 #include "../pixmaps/tb_find_help.xpm"
86 #include "../pixmaps/tb_exit.xpm"
87 #include "../pixmaps/tb_undo.xpm"
88 #include "../pixmaps/tb_redo.xpm"
89 #include "../pixmaps/tb_help.xpm"
90 #include "../pixmaps/tb_macro.xpm"
91 #include "../pixmaps/tb_make.xpm"
92 #include "../pixmaps/tb_save_all.xpm"
93 #include "../pixmaps/tb_jump.xpm"
94 #include "../pixmaps/tb_ctags.xpm"
95 #include "../pixmaps/tb_load_session.xpm"
96 #include "../pixmaps/tb_save_session.xpm"
97 #include "../pixmaps/tb_new_session.xpm"
98 #include "../pixmaps/tb_blank.xpm"
99 #include "../pixmaps/tb_maximize.xpm"
100 #include "../pixmaps/tb_split.xpm"
101 #include "../pixmaps/tb_minimize.xpm"
102 #include "../pixmaps/tb_shell.xpm"
103 #include "../pixmaps/tb_replace.xpm"
104 #include "../pixmaps/tb_vsplit.xpm"
105 #include "../pixmaps/tb_maxwidth.xpm"
106 #include "../pixmaps/tb_minwidth.xpm"
107 #endif /* FEAT_TOOLBAR && !HAVE_GTK2 */
108 
109 #ifdef FEAT_GUI_GTK
110 # include <gdk/gdkkeysyms.h>
111 # include <gdk/gdk.h>
112 # ifdef WIN3264
113 #  include <gdk/gdkwin32.h>
114 # else
115 #  include <gdk/gdkx.h>
116 # endif
117 
118 # include <gtk/gtk.h>
119 #else
120 /* define these items to be able to generate prototypes without GTK */
121 typedef int GtkWidget;
122 # define gpointer int
123 # define guint8 int
124 # define GdkPixmap int
125 # define GdkBitmap int
126 # define GtkIconFactory int
127 # define GtkToolbar int
128 # define GtkAdjustment int
129 # define gboolean int
130 # define GdkEventKey int
131 # define CancelData int
132 #endif
133 
134 #ifdef HAVE_GTK2
135 /*
136  * Convenience macros to convert from 'encoding' to 'termencoding' and
137  * vice versa.	If no conversion is necessary the passed-in pointer is
138  * returned as is, without allocating any memory.  Thus additional _FREE()
139  * macros are provided.  The _FREE() macros also set the pointer to NULL,
140  * in order to avoid bugs due to illegal memory access only happening if
141  * 'encoding' != utf-8...
142  *
143  * Defining these macros as pure expressions looks a bit tricky but
144  * avoids depending on the context of the macro expansion.  One of the
145  * rare occasions where the comma operator comes in handy :)
146  *
147  * Note: Do NOT keep the result around when handling control back to
148  * the main Vim!  The user could change 'encoding' at any time.
149  */
150 # define CONVERT_TO_UTF8(String)				\
151     ((output_conv.vc_type == CONV_NONE || (String) == NULL)	\
152 	    ? (String)						\
153 	    : string_convert(&output_conv, (String), NULL))
154 
155 # define CONVERT_TO_UTF8_FREE(String)				\
156     ((String) = ((output_conv.vc_type == CONV_NONE)		\
157 			? (char_u *)NULL			\
158 			: (vim_free(String), (char_u *)NULL)))
159 
160 # define CONVERT_FROM_UTF8(String)				\
161     ((input_conv.vc_type == CONV_NONE || (String) == NULL)	\
162 	    ? (String)						\
163 	    : string_convert(&input_conv, (String), NULL))
164 
165 # define CONVERT_FROM_UTF8_FREE(String)				\
166     ((String) = ((input_conv.vc_type == CONV_NONE)		\
167 			? (char_u *)NULL			\
168 			: (vim_free(String), (char_u *)NULL)))
169 
170 #endif /* HAVE_GTK2 */
171 
172 static void entry_activate_cb(GtkWidget *widget, gpointer data);
173 static void entry_changed_cb(GtkWidget *entry, GtkWidget *dialog);
174 static void find_replace_cb(GtkWidget *widget, gpointer data);
175 
176 #if defined(FEAT_TOOLBAR) && defined(HAVE_GTK2)
177 /*
178  * Table from BuiltIn## icon indices to GTK+ stock IDs.  Order must exactly
179  * match toolbar_names[] in menu.c!  All stock icons including the "vim-*"
180  * ones can be overridden in your gtkrc file.
181  */
182 static const char * const menu_stock_ids[] =
183 {
184     /* 00 */ GTK_STOCK_NEW,
185     /* 01 */ GTK_STOCK_OPEN,
186     /* 02 */ GTK_STOCK_SAVE,
187     /* 03 */ GTK_STOCK_UNDO,
188     /* 04 */ GTK_STOCK_REDO,
189     /* 05 */ GTK_STOCK_CUT,
190     /* 06 */ GTK_STOCK_COPY,
191     /* 07 */ GTK_STOCK_PASTE,
192     /* 08 */ GTK_STOCK_PRINT,
193     /* 09 */ GTK_STOCK_HELP,
194     /* 10 */ GTK_STOCK_FIND,
195     /* 11 */ "vim-save-all",
196     /* 12 */ "vim-session-save",
197     /* 13 */ "vim-session-new",
198     /* 14 */ "vim-session-load",
199     /* 15 */ GTK_STOCK_EXECUTE,
200     /* 16 */ GTK_STOCK_FIND_AND_REPLACE,
201     /* 17 */ GTK_STOCK_CLOSE,		/* FIXME: fuzzy */
202     /* 18 */ "vim-window-maximize",
203     /* 19 */ "vim-window-minimize",
204     /* 20 */ "vim-window-split",
205     /* 21 */ "vim-shell",
206     /* 22 */ GTK_STOCK_GO_BACK,
207     /* 23 */ GTK_STOCK_GO_FORWARD,
208     /* 24 */ "vim-find-help",
209     /* 25 */ GTK_STOCK_CONVERT,
210     /* 26 */ GTK_STOCK_JUMP_TO,
211     /* 27 */ "vim-build-tags",
212     /* 28 */ "vim-window-split-vertical",
213     /* 29 */ "vim-window-maximize-width",
214     /* 30 */ "vim-window-minimize-width",
215     /* 31 */ GTK_STOCK_QUIT
216 };
217 
218     static void
219 add_stock_icon(GtkIconFactory	*factory,
220 	       const char	*stock_id,
221 	       const guint8	*inline_data,
222 	       int		data_length)
223 {
224     GdkPixbuf	*pixbuf;
225     GtkIconSet	*icon_set;
226 
227     pixbuf = gdk_pixbuf_new_from_inline(data_length, inline_data, FALSE, NULL);
228     icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
229 
230     gtk_icon_factory_add(factory, stock_id, icon_set);
231 
232     gtk_icon_set_unref(icon_set);
233     g_object_unref(pixbuf);
234 }
235 
236     static int
237 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
238 {
239     expand_env(iconfile, dest, MAXPATHL);
240 
241     if (mch_isFullName(dest))
242     {
243 	return vim_fexists(dest);
244     }
245     else
246     {
247 	static const char   suffixes[][4] = {"png", "xpm", "bmp"};
248 	char_u		    buf[MAXPATHL];
249 	unsigned int	    i;
250 
251 	for (i = 0; i < G_N_ELEMENTS(suffixes); ++i)
252 	    if (gui_find_bitmap(dest, buf, (char *)suffixes[i]) == OK)
253 	    {
254 		STRCPY(dest, buf);
255 		return TRUE;
256 	    }
257 
258 	return FALSE;
259     }
260 }
261 
262     static GtkWidget *
263 load_menu_iconfile(char_u *name, GtkIconSize icon_size)
264 {
265     GtkWidget	    *image = NULL;
266     GtkIconSet	    *icon_set;
267     GtkIconSource   *icon_source;
268 
269     /*
270      * Rather than loading the icon directly into a GtkImage, create
271      * a new GtkIconSet and put it in there.  This way we can easily
272      * scale the toolbar icons on the fly when needed.
273      */
274     icon_set = gtk_icon_set_new();
275     icon_source = gtk_icon_source_new();
276 
277     gtk_icon_source_set_filename(icon_source, (const char *)name);
278     gtk_icon_set_add_source(icon_set, icon_source);
279 
280     image = gtk_image_new_from_icon_set(icon_set, icon_size);
281 
282     gtk_icon_source_free(icon_source);
283     gtk_icon_set_unref(icon_set);
284 
285     return image;
286 }
287 
288     static GtkWidget *
289 create_menu_icon(vimmenu_T *menu, GtkIconSize icon_size)
290 {
291     GtkWidget	*image = NULL;
292     char_u	buf[MAXPATHL];
293 
294     /* First use a specified "icon=" argument. */
295     if (menu->iconfile != NULL && lookup_menu_iconfile(menu->iconfile, buf))
296 	image = load_menu_iconfile(buf, icon_size);
297 
298     /* If not found and not builtin specified try using the menu name. */
299     if (image == NULL && !menu->icon_builtin
300 				     && lookup_menu_iconfile(menu->name, buf))
301 	image = load_menu_iconfile(buf, icon_size);
302 
303     /* Still not found?  Then use a builtin icon, a blank one as fallback. */
304     if (image == NULL)
305     {
306 	const char  *stock_id;
307 	const int   n_ids = G_N_ELEMENTS(menu_stock_ids);
308 
309 	if (menu->iconidx >= 0 && menu->iconidx < n_ids)
310 	    stock_id = menu_stock_ids[menu->iconidx];
311 	else
312 	    stock_id = GTK_STOCK_MISSING_IMAGE;
313 
314 	image = gtk_image_new_from_stock(stock_id, icon_size);
315     }
316 
317     return image;
318 }
319 
320 #endif /* FEAT_TOOLBAR && HAVE_GTK2 */
321 
322 #if (defined(FEAT_TOOLBAR) && defined(HAVE_GTK2)) || defined(PROTO)
323 
324     void
325 gui_gtk_register_stock_icons(void)
326 {
327 #   include "../pixmaps/stock_icons.h"
328     GtkIconFactory *factory;
329 
330     factory = gtk_icon_factory_new();
331 #   define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
332 
333     ADD_ICON("vim-build-tags",		  stock_vim_build_tags);
334     ADD_ICON("vim-find-help",		  stock_vim_find_help);
335     ADD_ICON("vim-save-all",		  stock_vim_save_all);
336     ADD_ICON("vim-session-load",	  stock_vim_session_load);
337     ADD_ICON("vim-session-new",		  stock_vim_session_new);
338     ADD_ICON("vim-session-save",	  stock_vim_session_save);
339     ADD_ICON("vim-shell",		  stock_vim_shell);
340     ADD_ICON("vim-window-maximize",	  stock_vim_window_maximize);
341     ADD_ICON("vim-window-maximize-width", stock_vim_window_maximize_width);
342     ADD_ICON("vim-window-minimize",	  stock_vim_window_minimize);
343     ADD_ICON("vim-window-minimize-width", stock_vim_window_minimize_width);
344     ADD_ICON("vim-window-split",	  stock_vim_window_split);
345     ADD_ICON("vim-window-split-vertical", stock_vim_window_split_vertical);
346 
347 #   undef ADD_ICON
348     gtk_icon_factory_add_default(factory);
349     g_object_unref(factory);
350 }
351 
352 #endif /* FEAT_TOOLBAR && HAVE_GTK2 */
353 
354 
355 /*
356  * Only use accelerators when gtk_menu_ensure_uline_accel_group() is
357  * available, which is in version 1.2.1.  That was the first version where
358  * accelerators properly worked (according to the change log).
359  */
360 #ifdef GTK_CHECK_VERSION
361 # if GTK_CHECK_VERSION(1, 2, 1)
362 #  define GTK_USE_ACCEL
363 # endif
364 #endif
365 
366 #if defined(FEAT_MENU) || defined(PROTO)
367 
368 /*
369  * Translate Vim's mnemonic tagging to GTK+ style and convert to UTF-8
370  * if necessary.  The caller must vim_free() the returned string.
371  *
372  *	Input	Output
373  *	_	__
374  *	&&	&
375  *	&	_	stripped if use_mnemonic == FALSE
376  *	<Tab>		end of menu label text
377  */
378     static char_u *
379 translate_mnemonic_tag(char_u *name, int use_mnemonic)
380 {
381     char_u  *buf;
382     char_u  *psrc;
383     char_u  *pdest;
384     int	    n_underscores = 0;
385 
386 # ifdef HAVE_GTK2
387     name = CONVERT_TO_UTF8(name);
388 # endif
389     if (name == NULL)
390 	return NULL;
391 
392     for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
393 	if (*psrc == '_')
394 	    ++n_underscores;
395 
396     buf = alloc((unsigned)(psrc - name + n_underscores + 1));
397     if (buf != NULL)
398     {
399 	pdest = buf;
400 	for (psrc = name; *psrc != NUL && *psrc != TAB; ++psrc)
401 	{
402 	    if (*psrc == '_')
403 	    {
404 		*pdest++ = '_';
405 		*pdest++ = '_';
406 	    }
407 	    else if (*psrc != '&')
408 	    {
409 		*pdest++ = *psrc;
410 	    }
411 	    else if (*(psrc + 1) == '&')
412 	    {
413 		*pdest++ = *psrc++;
414 	    }
415 	    else if (use_mnemonic)
416 	    {
417 		*pdest++ = '_';
418 	    }
419 	}
420 	*pdest = NUL;
421     }
422 
423 # ifdef HAVE_GTK2
424     CONVERT_TO_UTF8_FREE(name);
425 # endif
426     return buf;
427 }
428 
429 # ifdef HAVE_GTK2
430 
431     static void
432 menu_item_new(vimmenu_T *menu, GtkWidget *parent_widget)
433 {
434     GtkWidget	*box;
435     char_u	*text;
436     int		use_mnemonic;
437 
438     /* It would be neat to have image menu items, but that would require major
439      * changes to Vim's menu system.  Not to mention that all the translations
440      * had to be updated. */
441     menu->id = gtk_menu_item_new();
442     box = gtk_hbox_new(FALSE, 20);
443 
444     use_mnemonic = (p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget));
445     text = translate_mnemonic_tag(menu->name, use_mnemonic);
446 
447     menu->label = gtk_label_new_with_mnemonic((const char *)text);
448     vim_free(text);
449 
450     gtk_box_pack_start(GTK_BOX(box), menu->label, FALSE, FALSE, 0);
451 
452     if (menu->actext != NULL && menu->actext[0] != NUL)
453     {
454 	text = CONVERT_TO_UTF8(menu->actext);
455 
456 	gtk_box_pack_end(GTK_BOX(box),
457 			 gtk_label_new((const char *)text),
458 			 FALSE, FALSE, 0);
459 
460 	CONVERT_TO_UTF8_FREE(text);
461     }
462 
463     gtk_container_add(GTK_CONTAINER(menu->id), box);
464     gtk_widget_show_all(menu->id);
465 }
466 
467 # else /* !HAVE_GTK2 */
468 
469 /*
470  * Create a highly customized menu item by hand instead of by using:
471  *
472  * gtk_menu_item_new_with_label(menu->dname);
473  *
474  * This is neccessary, since there is no other way in GTK+ 1 to get the
475  * not automatically parsed accellerator stuff right.
476  */
477     static void
478 menu_item_new(vimmenu_T *menu, GtkWidget *parent_widget)
479 {
480     GtkWidget	*widget;
481     GtkWidget	*bin;
482     GtkWidget	*label;
483     char_u	*name;
484     guint	accel_key;
485 
486     widget = gtk_widget_new(GTK_TYPE_MENU_ITEM,
487 			    "GtkWidget::visible", TRUE,
488 			    "GtkWidget::sensitive", TRUE,
489 			    /* "GtkWidget::parent", parent->submenu_id, */
490 			    NULL);
491     bin = gtk_widget_new(GTK_TYPE_HBOX,
492 			 "GtkWidget::visible", TRUE,
493 			 "GtkWidget::parent", widget,
494 			 "GtkBox::spacing", 16,
495 			 NULL);
496     label = gtk_widget_new(GTK_TYPE_ACCEL_LABEL,
497 			   "GtkWidget::visible", TRUE,
498 			   "GtkWidget::parent", bin,
499 			   "GtkAccelLabel::accel_widget", widget,
500 			   "GtkMisc::xalign", 0.0,
501 			   NULL);
502     menu->label = label;
503 
504     if (menu->actext)
505 	gtk_widget_new(GTK_TYPE_LABEL,
506 		       "GtkWidget::visible", TRUE,
507 		       "GtkWidget::parent", bin,
508 		       "GtkLabel::label", menu->actext,
509 		       "GtkMisc::xalign", 1.0,
510 			NULL);
511 
512     /*
513      * Translate VIM accelerator tagging into GTK+'s.  Note that since GTK uses
514      * underscores as the accelerator key, we need to add an additional under-
515      * score for each understore that appears in the menu name.
516      */
517 #  ifdef GTK_USE_ACCEL
518     name = translate_mnemonic_tag(menu->name,
519 		(p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget)));
520 #  else
521     name = translate_mnemonic_tag(menu->name, FALSE);
522 #  endif
523 
524     /* let GTK do its thing */
525     accel_key = gtk_label_parse_uline(GTK_LABEL(label), (const char *)name);
526     vim_free(name);
527 
528 #  ifdef GTK_USE_ACCEL
529     /* Don't add accelator if 'winaltkeys' is "no". */
530     if (accel_key != GDK_VoidSymbol)
531     {
532 	if (GTK_IS_MENU_BAR(parent_widget))
533 	{
534 	    if (*p_wak != 'n')
535 		gtk_widget_add_accelerator(widget,
536 			"activate_item",
537 			gui.accel_group,
538 			accel_key, GDK_MOD1_MASK,
539 			(GtkAccelFlags)0);
540 	}
541 	else
542 	{
543 	    gtk_widget_add_accelerator(widget,
544 		    "activate_item",
545 		    gtk_menu_ensure_uline_accel_group(GTK_MENU(parent_widget)),
546 		    accel_key, 0,
547 		    (GtkAccelFlags)0);
548 	}
549     }
550 #  endif /* GTK_USE_ACCEL */
551 
552     menu->id = widget;
553 }
554 
555 # endif /* !HAVE_GTK2 */
556 
557     void
558 gui_mch_add_menu(vimmenu_T *menu, int idx)
559 {
560     vimmenu_T	*parent;
561     GtkWidget	*parent_widget;
562 
563     if (menu->name[0] == ']' || menu_is_popup(menu->name))
564     {
565 	menu->submenu_id = gtk_menu_new();
566 	return;
567     }
568 
569     parent = menu->parent;
570 
571     if ((parent != NULL && parent->submenu_id == NULL)
572 	    || !menu_is_menubar(menu->name))
573 	return;
574 
575     parent_widget = (parent != NULL) ? parent->submenu_id : gui.menubar;
576     menu_item_new(menu, parent_widget);
577 
578     /* since the tearoff should always appear first, increment idx */
579     if (parent != NULL && !menu_is_popup(parent->name))
580 	++idx;
581 
582     gtk_menu_shell_insert(GTK_MENU_SHELL(parent_widget), menu->id, idx);
583 
584 #ifndef HAVE_GTK2
585     /*
586      * The "Help" menu is a special case, and should be placed at the far
587      * right hand side of the menu-bar.  It's detected by its high priority.
588      *
589      * Right-aligning "Help" is considered bad UI design nowadays.
590      * Thus lets disable this for GTK+ 2 to match the environment.
591      */
592     if (parent == NULL && menu->priority >= 9999)
593 	gtk_menu_item_right_justify(GTK_MENU_ITEM(menu->id));
594 #endif
595 
596     menu->submenu_id = gtk_menu_new();
597 
598     gtk_menu_set_accel_group(GTK_MENU(menu->submenu_id), gui.accel_group);
599     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->id), menu->submenu_id);
600 
601     menu->tearoff_handle = gtk_tearoff_menu_item_new();
602     if (vim_strchr(p_go, GO_TEAROFF) != NULL)
603 	gtk_widget_show(menu->tearoff_handle);
604     gtk_menu_prepend(GTK_MENU(menu->submenu_id), menu->tearoff_handle);
605 }
606 
607 /*ARGSUSED*/
608     static void
609 menu_item_activate(GtkWidget *widget, gpointer data)
610 {
611     gui_menu_cb((vimmenu_T *)data);
612 
613 # ifndef HAVE_GTK2
614     /* Work around a bug in GTK+ 1: we don't seem to get a focus-in
615      * event after clicking a menu item shown via :popup. */
616     if (!gui.in_focus)
617 	gui_focus_change(TRUE);
618 # endif
619 
620     /* make sure the menu action is taken immediately */
621     if (gtk_main_level() > 0)
622 	gtk_main_quit();
623 }
624 
625 # if defined(FEAT_TOOLBAR) && !defined(HAVE_GTK2)
626 /*
627  * These are the pixmaps used for the default buttons.
628  * Order must exactly match toolbar_names[] in menu.c!
629  */
630 static char **(built_in_pixmaps[]) =
631 {
632     tb_new_xpm,
633     tb_open_xpm,
634     tb_save_xpm,
635     tb_undo_xpm,
636     tb_redo_xpm,
637     tb_cut_xpm,
638     tb_copy_xpm,
639     tb_paste_xpm,
640     tb_print_xpm,
641     tb_help_xpm,
642     tb_find_xpm,
643     tb_save_all_xpm,
644     tb_save_session_xpm,
645     tb_new_session_xpm,
646     tb_load_session_xpm,
647     tb_macro_xpm,
648     tb_replace_xpm,
649     tb_close_xpm,
650     tb_maximize_xpm,
651     tb_minimize_xpm,
652     tb_split_xpm,
653     tb_shell_xpm,
654     tb_find_prev_xpm,
655     tb_find_next_xpm,
656     tb_find_help_xpm,
657     tb_make_xpm,
658     tb_jump_xpm,
659     tb_ctags_xpm,
660     tb_vsplit_xpm,
661     tb_maxwidth_xpm,
662     tb_minwidth_xpm,
663     tb_exit_xpm
664 };
665 
666 /*
667  * creates a blank pixmap using tb_blank
668  */
669     static void
670 pixmap_create_from_xpm(char **xpm, GdkPixmap **pixmap, GdkBitmap **mask)
671 {
672     *pixmap = gdk_pixmap_colormap_create_from_xpm_d(
673 	    NULL,
674 	    gtk_widget_get_colormap(gui.mainwin),
675 	    mask,
676 	    NULL,
677 	    xpm);
678 }
679 
680 /*
681  * creates a pixmap by using a built-in number
682  */
683     static void
684 pixmap_create_by_num(int pixmap_num, GdkPixmap **pixmap, GdkBitmap **mask)
685 {
686     if (pixmap_num >= 0 && pixmap_num < (sizeof(built_in_pixmaps)
687 					    / sizeof(built_in_pixmaps[0])))
688 	pixmap_create_from_xpm(built_in_pixmaps[pixmap_num], pixmap, mask);
689 }
690 
691 /*
692  * Creates a pixmap by using the pixmap "name" found in 'runtimepath'/bitmaps/
693  */
694     static void
695 pixmap_create_by_dir(char_u *name, GdkPixmap **pixmap, GdkBitmap **mask)
696 {
697     char_u full_pathname[MAXPATHL + 1];
698 
699     if (gui_find_bitmap(name, full_pathname, "xpm") == OK)
700 	*pixmap = gdk_pixmap_colormap_create_from_xpm(
701 		NULL,
702 		gtk_widget_get_colormap(gui.mainwin),
703 		mask,
704 		&gui.mainwin->style->bg[GTK_STATE_NORMAL],
705 		(const char *)full_pathname);
706 }
707 
708 /*
709  * Creates a pixmap by using the pixmap "fname".
710  */
711     static void
712 pixmap_create_from_file(char_u *fname, GdkPixmap **pixmap, GdkBitmap **mask)
713 {
714     *pixmap = gdk_pixmap_colormap_create_from_xpm(
715 		NULL,
716 		gtk_widget_get_colormap(gui.mainwin),
717 		mask,
718 		&gui.mainwin->style->bg[GTK_STATE_NORMAL],
719 		(const char *)fname);
720 }
721 
722 # endif /* FEAT_TOOLBAR && !HAVE_GTK2 */
723 
724     void
725 gui_mch_add_menu_item(vimmenu_T *menu, int idx)
726 {
727     vimmenu_T *parent;
728 
729     parent = menu->parent;
730 
731 # ifdef FEAT_TOOLBAR
732     if (menu_is_toolbar(parent->name))
733     {
734 	GtkToolbar *toolbar;
735 
736 	toolbar = GTK_TOOLBAR(gui.toolbar);
737 	menu->submenu_id = NULL;
738 
739 	if (menu_is_separator(menu->name))
740 	{
741 	    gtk_toolbar_insert_space(toolbar, idx);
742 	    menu->id = NULL;
743 	}
744 	else
745 	{
746 #  ifdef HAVE_GTK2
747 	    char_u *text;
748 	    char_u *tooltip;
749 
750 	    text    = CONVERT_TO_UTF8(menu->dname);
751 	    tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
752 	    if (tooltip != NULL && !utf_valid_string(tooltip, NULL))
753 		/* Invalid text, can happen when 'encoding' is changed.  Avoid
754 		 * a nasty GTK error message, skip the tooltip. */
755 		CONVERT_TO_UTF8_FREE(tooltip);
756 
757 	    menu->id = gtk_toolbar_insert_item(
758 		    toolbar,
759 		    (const char *)text,
760 		    (const char *)tooltip,
761 		    NULL,
762 		    create_menu_icon(menu, gtk_toolbar_get_icon_size(toolbar)),
763 		    G_CALLBACK(&menu_item_activate),
764 		    menu,
765 		    idx);
766 
767 	    CONVERT_TO_UTF8_FREE(text);
768 	    CONVERT_TO_UTF8_FREE(tooltip);
769 
770 #  else /* !HAVE_GTK2 */
771 
772 	    GdkPixmap *pixmap = NULL;
773 	    GdkBitmap *mask = NULL;
774 
775 	    /* First try user specified bitmap, then builtin, the a blank. */
776 	    if (menu->iconfile != NULL)
777 	    {
778 		char_u buf[MAXPATHL + 1];
779 
780 		gui_find_iconfile(menu->iconfile, buf, "xpm");
781 		pixmap_create_from_file(buf, &pixmap, &mask);
782 	    }
783 	    if (pixmap == NULL && !menu->icon_builtin)
784 		pixmap_create_by_dir(menu->name, &pixmap, &mask);
785 	    if (pixmap == NULL && menu->iconidx >= 0)
786 		pixmap_create_by_num(menu->iconidx, &pixmap, &mask);
787 	    if (pixmap == NULL)
788 		pixmap_create_from_xpm(tb_blank_xpm, &pixmap, &mask);
789 	    if (pixmap == NULL)
790 		return; /* should at least have blank pixmap, but if not... */
791 
792 	    menu->id = gtk_toolbar_insert_item(
793 				    toolbar,
794 				    (char *)(menu->dname),
795 				    (char *)(menu->strings[MENU_INDEX_TIP]),
796 				    (char *)(menu->dname),
797 				    gtk_pixmap_new(pixmap, mask),
798 				    GTK_SIGNAL_FUNC(menu_item_activate),
799 				    (gpointer)menu,
800 				    idx);
801 #  endif /* !HAVE_GTK2 */
802 	}
803     }
804     else
805 # endif /* FEAT_TOOLBAR */
806     {
807 	/* No parent, must be a non-menubar menu */
808 	if (parent->submenu_id == NULL)
809 	    return;
810 
811 	/* Make place for the possible tearoff handle item.  Not in the popup
812 	 * menu, it doesn't have a tearoff item. */
813 	if (parent != NULL && !menu_is_popup(parent->name))
814 	    ++idx;
815 
816 	if (menu_is_separator(menu->name))
817 	{
818 	    /* Separator: Just add it */
819 	    menu->id = gtk_menu_item_new();
820 	    gtk_widget_set_sensitive(menu->id, FALSE);
821 	    gtk_widget_show(menu->id);
822 	    gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
823 
824 	    return;
825 	}
826 
827 	/* Add textual menu item. */
828 	menu_item_new(menu, parent->submenu_id);
829 	gtk_widget_show(menu->id);
830 	gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
831 
832 	if (menu->id != NULL)
833 	    gtk_signal_connect(GTK_OBJECT(menu->id), "activate",
834 			       GTK_SIGNAL_FUNC(menu_item_activate), menu);
835     }
836 }
837 #endif /* FEAT_MENU */
838 
839 
840     void
841 gui_mch_set_text_area_pos(int x, int y, int w, int h)
842 {
843     gtk_form_move_resize(GTK_FORM(gui.formwin), gui.drawarea, x, y, w, h);
844 }
845 
846 
847 #if defined(FEAT_MENU) || defined(PROTO)
848 /*
849  * Enable or disable accelators for the toplevel menus.
850  */
851     void
852 gui_gtk_set_mnemonics(int enable)
853 {
854     vimmenu_T	*menu;
855     char_u	*name;
856 # if !defined(HAVE_GTK2) && defined(GTK_USE_ACCEL)
857     guint	accel_key;
858 # endif
859 
860     for (menu = root_menu; menu != NULL; menu = menu->next)
861     {
862 	if (menu->id == NULL)
863 	    continue;
864 
865 # if defined(HAVE_GTK2)
866 	name = translate_mnemonic_tag(menu->name, enable);
867 	gtk_label_set_text_with_mnemonic(GTK_LABEL(menu->label),
868 					 (const char *)name);
869 	vim_free(name);
870 # elif defined(GTK_USE_ACCEL)
871 	name = translate_mnemonic_tag(menu->name, TRUE);
872 	if (name != NULL)
873 	{
874 	    accel_key = gtk_label_parse_uline(GTK_LABEL(menu->label),
875 					      (const char *)name);
876 	    if (accel_key != GDK_VoidSymbol)
877 		gtk_widget_remove_accelerator(menu->id, gui.accel_group,
878 					      accel_key, GDK_MOD1_MASK);
879 	    if (enable && accel_key != GDK_VoidSymbol)
880 		gtk_widget_add_accelerator(menu->id, "activate_item",
881 					   gui.accel_group,
882 					   accel_key, GDK_MOD1_MASK,
883 					   (GtkAccelFlags)0);
884 	    vim_free(name);
885 	}
886 	if (!enable)
887 	{
888 	    name = translate_mnemonic_tag(menu->name, FALSE);
889 	    gtk_label_parse_uline(GTK_LABEL(menu->label), (const char *)name);
890 	    vim_free(name);
891 	}
892 # endif
893     }
894 }
895 
896     static void
897 recurse_tearoffs(vimmenu_T *menu, int val)
898 {
899     for (; menu != NULL; menu = menu->next)
900     {
901 	if (menu->submenu_id != NULL && menu->tearoff_handle != NULL
902 		&& menu->name[0] != ']' && !menu_is_popup(menu->name))
903 	{
904 	    if (val)
905 		gtk_widget_show(menu->tearoff_handle);
906 	    else
907 		gtk_widget_hide(menu->tearoff_handle);
908 	}
909 	recurse_tearoffs(menu->children, val);
910     }
911 }
912 
913     void
914 gui_mch_toggle_tearoffs(int enable)
915 {
916     recurse_tearoffs(root_menu, enable);
917 }
918 #endif /* FEAT_MENU */
919 
920 
921 #if defined(FEAT_TOOLBAR) && !defined(HAVE_GTK2)
922 /*
923  * Seems like there's a hole in the GTK Toolbar API: there's no provision for
924  * removing an item from the toolbar.  Therefore I need to resort to going
925  * really deeply into the internal widget structures.
926  *
927  * <danielk> I'm not sure the statement above is true -- at least with
928  * GTK+ 2 one can just call gtk_widget_destroy() and be done with it.
929  * It is true though that you couldn't remove space items before GTK+ 2
930  * (without digging into the internals that is).  But the code below
931  * doesn't seem to handle those either.  Well, it's obsolete anyway.
932  */
933     static void
934 toolbar_remove_item_by_text(GtkToolbar *tb, const char *text)
935 {
936     GtkContainer *container;
937     GList *childl;
938     GtkToolbarChild *gtbc;
939 
940     g_return_if_fail(tb != NULL);
941     g_return_if_fail(GTK_IS_TOOLBAR(tb));
942     container = GTK_CONTAINER(&tb->container);
943 
944     for (childl = tb->children; childl; childl = childl->next)
945     {
946 	gtbc = (GtkToolbarChild *)childl->data;
947 
948 	if (gtbc->type != GTK_TOOLBAR_CHILD_SPACE
949 		&& strcmp(GTK_LABEL(gtbc->label)->label, text) == 0)
950 	{
951 	    gboolean was_visible;
952 
953 	    was_visible = GTK_WIDGET_VISIBLE(gtbc->widget);
954 	    gtk_widget_unparent(gtbc->widget);
955 
956 	    tb->children = g_list_remove_link(tb->children, childl);
957 	    g_free(gtbc);
958 	    g_list_free(childl);
959 	    tb->num_children--;
960 
961 	    if (was_visible && GTK_WIDGET_VISIBLE(container))
962 		gtk_widget_queue_resize(GTK_WIDGET(container));
963 
964 	    break;
965 	}
966     }
967 }
968 #endif /* FEAT_TOOLBAR && !HAVE_GTK2 */
969 
970 
971 #if defined(FEAT_TOOLBAR) && defined(HAVE_GTK2)
972     static int
973 get_menu_position(vimmenu_T *menu)
974 {
975     vimmenu_T	*node;
976     int		index = 0;
977 
978     for (node = menu->parent->children; node != menu; node = node->next)
979     {
980 	g_return_val_if_fail(node != NULL, -1);
981 	++index;
982     }
983 
984     return index;
985 }
986 #endif /* FEAT_TOOLBAR && HAVE_GTK2 */
987 
988 
989 #if defined(FEAT_TOOLBAR) || defined(PROTO)
990     void
991 gui_mch_menu_set_tip(vimmenu_T *menu)
992 {
993     if (menu->id != NULL && menu->parent != NULL
994 	    && gui.toolbar != NULL && menu_is_toolbar(menu->parent->name))
995     {
996 	char_u *tooltip;
997 
998 # ifdef HAVE_GTK2
999 	tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
1000 	if (tooltip == NULL || utf_valid_string(tooltip, NULL))
1001 	    /* Only set the tooltip when it's valid utf-8. */
1002 # else
1003 	tooltip = menu->strings[MENU_INDEX_TIP];
1004 # endif
1005 	gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
1006 			     menu->id, (const char *)tooltip, NULL);
1007 # ifdef HAVE_GTK2
1008 	CONVERT_TO_UTF8_FREE(tooltip);
1009 # endif
1010     }
1011 }
1012 #endif /* FEAT_TOOLBAR */
1013 
1014 
1015 #if defined(FEAT_MENU) || defined(PROTO)
1016 /*
1017  * Destroy the machine specific menu widget.
1018  */
1019     void
1020 gui_mch_destroy_menu(vimmenu_T *menu)
1021 {
1022 # ifdef FEAT_TOOLBAR
1023     if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
1024     {
1025 #  ifdef HAVE_GTK2
1026 	if (menu_is_separator(menu->name))
1027 	    gtk_toolbar_remove_space(GTK_TOOLBAR(gui.toolbar),
1028 				     get_menu_position(menu));
1029 	else if (menu->id != NULL)
1030 	    gtk_widget_destroy(menu->id);
1031 #  else
1032 	toolbar_remove_item_by_text(GTK_TOOLBAR(gui.toolbar),
1033 				    (const char *)menu->dname);
1034 #  endif
1035     }
1036     else
1037 # endif /* FEAT_TOOLBAR */
1038     {
1039 	if (menu->submenu_id != NULL)
1040 	    gtk_widget_destroy(menu->submenu_id);
1041 
1042 	if (menu->id != NULL)
1043 	    gtk_widget_destroy(menu->id);
1044     }
1045 
1046     menu->submenu_id = NULL;
1047     menu->id = NULL;
1048 }
1049 #endif /* FEAT_MENU */
1050 
1051 
1052 /*
1053  * Scrollbar stuff.
1054  */
1055     void
1056 gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max)
1057 {
1058     if (sb->id != NULL)
1059     {
1060 	GtkAdjustment *adjustment;
1061 
1062 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
1063 
1064 	adjustment->lower = 0.0;
1065 	adjustment->value = val;
1066 	adjustment->upper = max + 1;
1067 	adjustment->page_size = size;
1068 	adjustment->page_increment = size < 3L ? 1L : size - 2L;
1069 	adjustment->step_increment = 1.0;
1070 
1071 #ifdef HAVE_GTK2
1072 	g_signal_handler_block(GTK_OBJECT(adjustment),
1073 						      (gulong)sb->handler_id);
1074 #else
1075 	gtk_signal_handler_block(GTK_OBJECT(adjustment),
1076 						       (guint)sb->handler_id);
1077 #endif
1078 	gtk_adjustment_changed(adjustment);
1079 #ifdef HAVE_GTK2
1080 	g_signal_handler_unblock(GTK_OBJECT(adjustment),
1081 						      (gulong)sb->handler_id);
1082 #else
1083 	gtk_signal_handler_unblock(GTK_OBJECT(adjustment),
1084 						       (guint)sb->handler_id);
1085 #endif
1086     }
1087 }
1088 
1089     void
1090 gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h)
1091 {
1092     if (sb->id != NULL)
1093 	gtk_form_move_resize(GTK_FORM(gui.formwin), sb->id, x, y, w, h);
1094 }
1095 
1096 /*
1097  * Take action upon scrollbar dragging.
1098  */
1099     static void
1100 adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
1101 {
1102     scrollbar_T	*sb;
1103     long	value;
1104     int		dragging = FALSE;
1105 
1106 #ifdef FEAT_XIM
1107     /* cancel any preediting */
1108     if (im_is_preediting())
1109 	xim_reset();
1110 #endif
1111 
1112     sb = gui_find_scrollbar((long)data);
1113     value = (long)adjustment->value;
1114     /*
1115      * The dragging argument must be right for the scrollbar to work with
1116      * closed folds.  This isn't documented, hopefully this will keep on
1117      * working in later GTK versions.
1118      *
1119      * FIXME: Well, it doesn't work in GTK2. :)
1120      * HACK: Get the mouse pointer position, if it appears to be on an arrow
1121      * button set "dragging" to FALSE.  This assumes square buttons!
1122      */
1123     if (sb != NULL)
1124     {
1125 #ifdef HAVE_GTK2
1126 	dragging = TRUE;
1127 
1128 	if (sb->wp != NULL)
1129 	{
1130 	    int			x;
1131 	    int			y;
1132 	    GdkModifierType	state;
1133 	    int			width;
1134 	    int			height;
1135 
1136 	    /* vertical scrollbar: need to set "dragging" properly in case
1137 	     * there are closed folds. */
1138 	    gdk_window_get_pointer(sb->id->window, &x, &y, &state);
1139 	    gdk_window_get_size(sb->id->window, &width, &height);
1140 	    if (x >= 0 && x < width && y >= 0 && y < height)
1141 	    {
1142 		if (y < width)
1143 		{
1144 		    /* up arrow: move one (closed fold) line up */
1145 		    dragging = FALSE;
1146 		    value = sb->wp->w_topline - 2;
1147 		}
1148 		else if (y > height - width)
1149 		{
1150 		    /* down arrow: move one (closed fold) line down */
1151 		    dragging = FALSE;
1152 		    value = sb->wp->w_topline;
1153 		}
1154 	    }
1155 	}
1156 #else
1157 	dragging = (GTK_RANGE(sb->id)->scroll_type == GTK_SCROLL_NONE);
1158 #endif
1159     }
1160 
1161     gui_drag_scrollbar(sb, value, dragging);
1162 
1163     if (gtk_main_level() > 0)
1164 	gtk_main_quit();
1165 }
1166 
1167 /* SBAR_VERT or SBAR_HORIZ */
1168     void
1169 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
1170 {
1171     if (orient == SBAR_HORIZ)
1172 	sb->id = gtk_hscrollbar_new(NULL);
1173     else if (orient == SBAR_VERT)
1174 	sb->id = gtk_vscrollbar_new(NULL);
1175 
1176     if (sb->id != NULL)
1177     {
1178 	GtkAdjustment *adjustment;
1179 
1180 	GTK_WIDGET_UNSET_FLAGS(sb->id, GTK_CAN_FOCUS);
1181 	gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
1182 
1183 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
1184 
1185 	sb->handler_id = gtk_signal_connect(
1186 			     GTK_OBJECT(adjustment), "value_changed",
1187 			     GTK_SIGNAL_FUNC(adjustment_value_changed),
1188 			     GINT_TO_POINTER(sb->ident));
1189 	gui_mch_update();
1190     }
1191 }
1192 
1193 #if defined(FEAT_WINDOWS) || defined(PROTO)
1194     void
1195 gui_mch_destroy_scrollbar(scrollbar_T *sb)
1196 {
1197     if (sb->id != NULL)
1198     {
1199 	gtk_widget_destroy(sb->id);
1200 	sb->id = NULL;
1201     }
1202     gui_mch_update();
1203 }
1204 #endif
1205 
1206 #if defined(FEAT_BROWSE) || defined(PROTO)
1207 /*
1208  * Implementation of the file selector related stuff
1209  */
1210 
1211 /*ARGSUSED*/
1212     static void
1213 browse_ok_cb(GtkWidget *widget, gpointer cbdata)
1214 {
1215     gui_T *vw = (gui_T *)cbdata;
1216 
1217     if (vw->browse_fname != NULL)
1218 	g_free(vw->browse_fname);
1219 
1220     vw->browse_fname = (char_u *)g_strdup(gtk_file_selection_get_filename(
1221 					GTK_FILE_SELECTION(vw->filedlg)));
1222     gtk_widget_hide(vw->filedlg);
1223     if (gtk_main_level() > 0)
1224 	gtk_main_quit();
1225 }
1226 
1227 /*ARGSUSED*/
1228     static void
1229 browse_cancel_cb(GtkWidget *widget, gpointer cbdata)
1230 {
1231     gui_T *vw = (gui_T *)cbdata;
1232 
1233     if (vw->browse_fname != NULL)
1234     {
1235 	g_free(vw->browse_fname);
1236 	vw->browse_fname = NULL;
1237     }
1238     gtk_widget_hide(vw->filedlg);
1239     if (gtk_main_level() > 0)
1240 	gtk_main_quit();
1241 }
1242 
1243 /*ARGSUSED*/
1244     static gboolean
1245 browse_destroy_cb(GtkWidget * widget)
1246 {
1247     if (gui.browse_fname != NULL)
1248     {
1249 	g_free(gui.browse_fname);
1250 	gui.browse_fname = NULL;
1251     }
1252     gui.filedlg = NULL;
1253 
1254     if (gtk_main_level() > 0)
1255 	gtk_main_quit();
1256 
1257     return FALSE;
1258 }
1259 
1260 /*
1261  * Put up a file requester.
1262  * Returns the selected name in allocated memory, or NULL for Cancel.
1263  * saving,			select file to write
1264  * title			title for the window
1265  * dflt				default name
1266  * ext				not used (extension added)
1267  * initdir			initial directory, NULL for current dir
1268  * filter			not used (file name filter)
1269  */
1270 /*ARGSUSED*/
1271     char_u *
1272 gui_mch_browse(int saving,
1273 	       char_u *title,
1274 	       char_u *dflt,
1275 	       char_u *ext,
1276 	       char_u *initdir,
1277 	       char_u *filter)
1278 {
1279     GtkFileSelection	*fs;	/* shortcut */
1280     char_u		dirbuf[MAXPATHL];
1281     char_u		*p;
1282 
1283 # ifdef HAVE_GTK2
1284     title = CONVERT_TO_UTF8(title);
1285 # endif
1286 
1287     if (!gui.filedlg)
1288     {
1289 	gui.filedlg = gtk_file_selection_new((const gchar *)title);
1290 	gtk_window_set_modal(GTK_WINDOW(gui.filedlg), TRUE);
1291 	gtk_window_set_transient_for(GTK_WINDOW(gui.filedlg),
1292 		GTK_WINDOW(gui.mainwin));
1293 	fs = GTK_FILE_SELECTION(gui.filedlg);
1294 
1295 	gtk_container_border_width(GTK_CONTAINER(fs), 4);
1296 
1297 	gtk_signal_connect(GTK_OBJECT(fs->ok_button),
1298 		"clicked", GTK_SIGNAL_FUNC(browse_ok_cb), &gui);
1299 	gtk_signal_connect(GTK_OBJECT(fs->cancel_button),
1300 		"clicked", GTK_SIGNAL_FUNC(browse_cancel_cb), &gui);
1301 	/* gtk_signal_connect() doesn't work for destroy, it causes a hang */
1302 	gtk_signal_connect_object(GTK_OBJECT(gui.filedlg),
1303 		"destroy", GTK_SIGNAL_FUNC(browse_destroy_cb),
1304 		GTK_OBJECT(gui.filedlg));
1305     }
1306     else
1307 	gtk_window_set_title(GTK_WINDOW(gui.filedlg), (const gchar *)title);
1308 
1309 # ifdef HAVE_GTK2
1310     CONVERT_TO_UTF8_FREE(title);
1311 # endif
1312 
1313     /* if our pointer is currently hidden, then we should show it. */
1314     gui_mch_mousehide(FALSE);
1315 
1316     /* Concatenate "initdir" and "dflt". */
1317     if (initdir == NULL || *initdir == NUL)
1318 	mch_dirname(dirbuf, MAXPATHL);
1319     else if (STRLEN(initdir) + 2 < MAXPATHL)
1320 	STRCPY(dirbuf, initdir);
1321     else
1322 	dirbuf[0] = NUL;
1323     /* Always need a trailing slash for a directory. */
1324     add_pathsep(dirbuf);
1325     if (dflt != NULL && *dflt != NUL
1326 			      && STRLEN(dirbuf) + 2 + STRLEN(dflt) < MAXPATHL)
1327 	STRCAT(dirbuf, dflt);
1328 
1329     gtk_file_selection_set_filename(GTK_FILE_SELECTION(gui.filedlg),
1330 						      (const gchar *)dirbuf);
1331 # ifndef HAVE_GTK2
1332     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
1333 				       GTK_WIDGET(gui.filedlg), VW_POS_MOUSE);
1334 # endif
1335 
1336     gtk_widget_show(gui.filedlg);
1337     while (gui.filedlg && GTK_WIDGET_DRAWABLE(gui.filedlg))
1338 	gtk_main_iteration_do(TRUE);
1339 
1340     if (gui.browse_fname == NULL)
1341 	return NULL;
1342 
1343     /* shorten the file name if possible */
1344     mch_dirname(dirbuf, MAXPATHL);
1345     p = shorten_fname(gui.browse_fname, dirbuf);
1346     if (p == NULL)
1347 	p = gui.browse_fname;
1348     return vim_strsave(p);
1349 }
1350 
1351 /*
1352  * Put up a directory selector
1353  * Returns the selected name in allocated memory, or NULL for Cancel.
1354  * title			title for the window
1355  * dflt				default name
1356  * initdir			initial directory, NULL for current dir
1357  */
1358 /*ARGSUSED*/
1359     char_u *
1360 gui_mch_browsedir(
1361 	       char_u *title,
1362 	       char_u *initdir)
1363 {
1364 # if defined(GTK_FILE_CHOOSER)	    /* Only in GTK 2.4 and later. */
1365     char_u		dirbuf[MAXPATHL];
1366     char_u		*p;
1367     GtkWidget		*dirdlg;	    /* file selection dialog */
1368     char_u		*dirname = NULL;
1369 
1370     title = CONVERT_TO_UTF8(title);
1371 
1372     dirdlg = gtk_file_chooser_dialog_new(
1373 	    (const gchar *)title,
1374 	    GTK_WINDOW(gui.mainwin),
1375 	    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
1376 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1377 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1378 	    NULL);
1379 
1380     CONVERT_TO_UTF8_FREE(title);
1381 
1382     /* if our pointer is currently hidden, then we should show it. */
1383     gui_mch_mousehide(FALSE);
1384 
1385     /* GTK appears to insist on an absolute path. */
1386     if (initdir == NULL || *initdir == NUL
1387 	       || vim_FullName(initdir, dirbuf, MAXPATHL - 10, FALSE) == FAIL)
1388 	mch_dirname(dirbuf, MAXPATHL - 10);
1389 
1390     /* Always need a trailing slash for a directory.
1391      * Also add a dummy file name, so that we get to the directory. */
1392     add_pathsep(dirbuf);
1393     STRCAT(dirbuf, "@zd(*&1|");
1394     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dirdlg),
1395 						      (const gchar *)dirbuf);
1396 
1397     /* Run the dialog. */
1398     if (gtk_dialog_run(GTK_DIALOG(dirdlg)) == GTK_RESPONSE_ACCEPT)
1399 	dirname = (char_u *)gtk_file_chooser_get_filename(
1400 						    GTK_FILE_CHOOSER(dirdlg));
1401     gtk_widget_destroy(dirdlg);
1402     if (dirname == NULL)
1403 	return NULL;
1404 
1405     /* shorten the file name if possible */
1406     mch_dirname(dirbuf, MAXPATHL);
1407     p = shorten_fname(dirname, dirbuf);
1408     if (p == NULL || *p == NUL)
1409 	p = dirname;
1410     p = vim_strsave(p);
1411     g_free(dirname);
1412     return p;
1413 
1414 # else
1415     /* For GTK 2.2 and earlier: fall back to ordinary file selector. */
1416     return gui_mch_browse(0, title, NULL, NULL, initdir, NULL);
1417 # endif
1418 }
1419 
1420 #endif	/* FEAT_BROWSE */
1421 
1422 #if (defined(FEAT_GUI_DIALOG) && !defined(HAVE_GTK2)) || defined(PROTO)
1423 
1424 static char_u *dialog_textfield = NULL;
1425 static GtkWidget *dialog_textentry;
1426 
1427     static void
1428 dlg_destroy(GtkWidget *dlg)
1429 {
1430     if (dialog_textfield != NULL)
1431     {
1432 	const char *text;
1433 
1434 	text = gtk_entry_get_text(GTK_ENTRY(dialog_textentry));
1435 	STRNCPY(dialog_textfield, text, IOSIZE);
1436 	dialog_textfield[IOSIZE - 1] = NUL;
1437     }
1438 
1439     /* Destroy the dialog, will break the waiting loop. */
1440     gtk_widget_destroy(dlg);
1441 }
1442 
1443 # ifdef FEAT_GUI_GNOME
1444 /* ARGSUSED */
1445     static int
1446 gui_gnome_dialog( int	type,
1447 		char_u	*title,
1448 		char_u	*message,
1449 		char_u	*buttons,
1450 		int	dfltbutton,
1451 		char_u	*textfield)
1452 {
1453     GtkWidget	*dlg;
1454     char	*gdtype;
1455     char_u	*buttons_copy, *p, *next;
1456     char	**buttons_list;
1457     int		butcount, cur;
1458 
1459     /* make a copy, so that we can insert NULs */
1460     if ((buttons_copy = vim_strsave(buttons)) == NULL)
1461 	return -1;
1462 
1463     /* determine exact number of buttons and allocate array to hold them */
1464     for (butcount = 0, p = buttons; *p; p++)
1465     {
1466 	if (*p == '\n')
1467 	    butcount++;
1468     }
1469     butcount++;
1470     buttons_list = g_new0(char *, butcount + 1);
1471 
1472     /* Add pixmap */
1473     switch (type)
1474     {
1475     case VIM_ERROR:
1476 	gdtype = GNOME_MESSAGE_BOX_ERROR;
1477 	break;
1478     case VIM_WARNING:
1479 	gdtype = GNOME_MESSAGE_BOX_WARNING;
1480 	break;
1481     case VIM_INFO:
1482 	gdtype = GNOME_MESSAGE_BOX_INFO;
1483 	break;
1484     case VIM_QUESTION:
1485 	gdtype = GNOME_MESSAGE_BOX_QUESTION;
1486 	break;
1487     default:
1488 	gdtype = GNOME_MESSAGE_BOX_GENERIC;
1489     };
1490 
1491     p = buttons_copy;
1492     for (cur = 0; cur < butcount; ++cur)
1493     {
1494 	for (next = p; *next; ++next)
1495 	{
1496 	    if (*next == DLG_HOTKEY_CHAR)
1497 		mch_memmove(next, next + 1, STRLEN(next));
1498 	    if (*next == DLG_BUTTON_SEP)
1499 	    {
1500 		*next++ = NUL;
1501 		break;
1502 	    }
1503 	}
1504 
1505 	/* this should probably go into a table, but oh well */
1506 	if (g_strcasecmp((char *)p, "Ok") == 0)
1507 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_OK);
1508 	else if (g_strcasecmp((char *)p, "Cancel") == 0)
1509 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_CANCEL);
1510 	else if (g_strcasecmp((char *)p, "Yes") == 0)
1511 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_YES);
1512 	else if (g_strcasecmp((char *)p, "No") == 0)
1513 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_NO);
1514 	else if (g_strcasecmp((char *)p, "Close") == 0)
1515 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_CLOSE);
1516 	else if (g_strcasecmp((char *)p, "Help") == 0)
1517 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_HELP);
1518 	else if (g_strcasecmp((char *)p, "Apply") == 0)
1519 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_APPLY);
1520 #if 0
1521 	/*
1522 	 * these aren't really used that often anyway, but are listed here as
1523 	 * placeholders in case we need them.
1524 	 */
1525 	else if (g_strcasecmp((char *)p, "Next") == 0)
1526 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_NEXT);
1527 	else if (g_strcasecmp((char *)p, "Prev") == 0)
1528 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_PREV);
1529 	else if (g_strcasecmp((char *)p, "Up") == 0)
1530 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_UP);
1531 	else if (g_strcasecmp((char *)p, "Down") == 0)
1532 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_DOWN);
1533 	else if (g_strcasecmp((char *)p, "Font") == 0)
1534 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_FONT);
1535 #endif
1536 	else
1537 	    buttons_list[cur] = g_strdup((char *)p);
1538 
1539 	if (*next == NUL)
1540 	    break;
1541 
1542 	p = next;
1543     }
1544     vim_free(buttons_copy);
1545 
1546     dlg = gnome_message_box_newv((const char *)message,
1547 				 (const char *)gdtype,
1548 				 (const char **)buttons_list);
1549     for (cur = 0; cur < butcount; ++cur)
1550 	g_free(buttons_list[cur]);
1551     g_free(buttons_list);
1552 
1553     dialog_textfield = textfield;
1554     if (textfield != NULL)
1555     {
1556 	/* Add text entry field */
1557 	dialog_textentry = gtk_entry_new();
1558 	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dlg)->vbox), dialog_textentry,
1559 			   TRUE, TRUE, 0);
1560 	gtk_entry_set_text(GTK_ENTRY(dialog_textentry),
1561 			   (const gchar *)textfield);
1562 	gtk_entry_select_region(GTK_ENTRY(dialog_textentry), 0,
1563 				STRLEN(textfield));
1564 	gtk_entry_set_max_length(GTK_ENTRY(dialog_textentry), IOSIZE - 1);
1565 	gtk_entry_set_position(GTK_ENTRY(dialog_textentry), STRLEN(textfield));
1566 	gtk_widget_show(dialog_textentry);
1567 	gtk_window_set_focus(GTK_WINDOW(dlg), dialog_textentry);
1568     }
1569 
1570     gtk_signal_connect_object(GTK_OBJECT(dlg), "destroy",
1571 			      GTK_SIGNAL_FUNC(dlg_destroy), GTK_OBJECT(dlg));
1572     gnome_dialog_set_default(GNOME_DIALOG(dlg), dfltbutton + 1);
1573     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
1574 			       GTK_WIDGET(dlg), VW_POS_MOUSE);
1575 
1576     return (1 + gnome_dialog_run_and_close(GNOME_DIALOG(dlg)));
1577 }
1578 
1579 # endif /* FEAT_GUI_GNOME */
1580 
1581 typedef struct _ButtonData
1582 {
1583     int		*status;
1584     int		index;
1585     GtkWidget	*dialog;
1586 } ButtonData;
1587 
1588 typedef struct _CancelData
1589 {
1590     int		*status;
1591     int		ignore_enter;
1592     GtkWidget	*dialog;
1593 } CancelData;
1594 
1595 /* ARGSUSED */
1596     static void
1597 dlg_button_clicked(GtkWidget * widget, ButtonData *data)
1598 {
1599     *(data->status) = data->index + 1;
1600     dlg_destroy(data->dialog);
1601 }
1602 
1603 /*
1604  * This makes the Escape key equivalent to the cancel button.
1605  */
1606 /*ARGSUSED*/
1607     static int
1608 dlg_key_press_event(GtkWidget * widget, GdkEventKey * event, CancelData *data)
1609 {
1610     /* Ignore hitting Enter when there is no default button. */
1611     if (data->ignore_enter && event->keyval == GDK_Return)
1612 	return TRUE;
1613 
1614     if (event->keyval != GDK_Escape && event->keyval != GDK_Return)
1615 	return FALSE;
1616 
1617     /* The result value of 0 from a dialog is signaling cancelation.
1618      * 1 means OK. */
1619     *(data->status) = (event->keyval == GDK_Return);
1620     dlg_destroy(data->dialog);
1621 
1622     return TRUE;
1623 }
1624 
1625 /*
1626  * Callback function for when the dialog was destroyed by a window manager.
1627  */
1628     static void
1629 dlg_destroy_cb(int *p)
1630 {
1631     *p = TRUE;		/* set dialog_destroyed to break out of the loop */
1632     if (gtk_main_level() > 0)
1633 	gtk_main_quit();
1634 }
1635 
1636 /* ARGSUSED */
1637     int
1638 gui_mch_dialog(	int	type,		/* type of dialog */
1639 		char_u	*title,		/* title of dialog */
1640 		char_u	*message,	/* message text */
1641 		char_u	*buttons,	/* names of buttons */
1642 		int	def_but,	/* default button */
1643 		char_u	*textfield)	/* text for textfield or NULL */
1644 {
1645     char_u	*names;
1646     char_u	*p;
1647     int		i;
1648     int		butcount;
1649     int		dialog_status = -1;
1650     int		dialog_destroyed = FALSE;
1651     int		vertical;
1652 
1653     GtkWidget		*dialog;
1654     GtkWidget		*frame;
1655     GtkWidget		*vbox;
1656     GtkWidget		*table;
1657     GtkWidget		*dialogmessage;
1658     GtkWidget		*action_area;
1659     GtkWidget		*sub_area;
1660     GtkWidget		*separator;
1661     GtkAccelGroup	*accel_group;
1662     GtkWidget		*pixmap;
1663     GdkPixmap		*icon = NULL;
1664     GdkBitmap		*mask = NULL;
1665     char		**icon_data = NULL;
1666 
1667     GtkWidget		**button;
1668     ButtonData		*data;
1669     CancelData		cancel_data;
1670 
1671     /* if our pointer is currently hidden, then we should show it. */
1672     gui_mch_mousehide(FALSE);
1673 
1674 # ifdef FEAT_GUI_GNOME
1675     /* If Gnome is available, use it for the dialog. */
1676     if (gtk_socket_id == 0)
1677 	return gui_gnome_dialog(type, title, message, buttons, def_but,
1678 								   textfield);
1679 # endif
1680 
1681     if (title == NULL)
1682 	title = (char_u *)_("Vim dialog...");
1683 
1684     if ((type < 0) || (type > VIM_LAST_TYPE))
1685 	type = VIM_GENERIC;
1686 
1687     /* Check 'v' flag in 'guioptions': vertical button placement. */
1688     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
1689 
1690     dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1691     gtk_window_set_title(GTK_WINDOW(dialog), (const gchar *)title);
1692     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
1693     gtk_widget_realize(dialog);
1694     gdk_window_set_decorations(dialog->window, GDK_DECOR_BORDER);
1695     gdk_window_set_functions(dialog->window, GDK_FUNC_MOVE);
1696 
1697     cancel_data.status = &dialog_status;
1698     cancel_data.dialog = dialog;
1699     gtk_signal_connect_after(GTK_OBJECT(dialog), "key_press_event",
1700 		    GTK_SIGNAL_FUNC(dlg_key_press_event),
1701 		    (gpointer) &cancel_data);
1702     /* Catch the destroy signal, otherwise we don't notice a window manager
1703      * destroying the dialog window. */
1704     gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
1705 		    GTK_SIGNAL_FUNC(dlg_destroy_cb),
1706 		    (gpointer)&dialog_destroyed);
1707 
1708     gtk_grab_add(dialog);
1709 
1710     /* this makes it look beter on Motif style window managers */
1711     frame = gtk_frame_new(NULL);
1712     gtk_container_add(GTK_CONTAINER(dialog), frame);
1713     gtk_widget_show(frame);
1714 
1715     vbox = gtk_vbox_new(FALSE, 0);
1716     gtk_container_add(GTK_CONTAINER(frame), vbox);
1717     gtk_widget_show(vbox);
1718 
1719     table = gtk_table_new(1, 3, FALSE);
1720     gtk_table_set_row_spacings(GTK_TABLE(table), 4);
1721     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1722     gtk_container_border_width(GTK_CONTAINER(table), 4);
1723     gtk_box_pack_start(GTK_BOX(vbox), table, 4, 4, 0);
1724     gtk_widget_show(table);
1725 
1726     /* Add pixmap */
1727     switch (type)
1728     {
1729     case VIM_GENERIC:
1730 	icon_data = generic_xpm;
1731 	break;
1732     case VIM_ERROR:
1733 	icon_data = error_xpm;
1734 	break;
1735     case VIM_WARNING:
1736 	icon_data = alert_xpm;
1737 	break;
1738     case VIM_INFO:
1739 	icon_data = info_xpm;
1740 	break;
1741     case VIM_QUESTION:
1742 	icon_data = quest_xpm;
1743 	break;
1744     default:
1745 	icon_data = generic_xpm;
1746     };
1747     icon = gdk_pixmap_colormap_create_from_xpm_d(NULL,
1748 				     gtk_widget_get_colormap(dialog),
1749 				     &mask, NULL, icon_data);
1750     if (icon)
1751     {
1752 	pixmap = gtk_pixmap_new(icon, mask);
1753 	/* gtk_misc_set_alignment(GTK_MISC(pixmap), 0.5, 0.5); */
1754 	gtk_table_attach_defaults(GTK_TABLE(table), pixmap, 0, 1, 0, 1);
1755 	gtk_widget_show(pixmap);
1756     }
1757 
1758     /* Add label */
1759     dialogmessage = gtk_label_new((const gchar *)message);
1760     gtk_table_attach_defaults(GTK_TABLE(table), dialogmessage, 1, 2, 0, 1);
1761     gtk_widget_show(dialogmessage);
1762 
1763     dialog_textfield = textfield;
1764     if (textfield != NULL)
1765     {
1766 	/* Add text entry field */
1767 	dialog_textentry = gtk_entry_new();
1768 	gtk_widget_set_usize(dialog_textentry, 400, -2);
1769 	gtk_box_pack_start(GTK_BOX(vbox), dialog_textentry, TRUE, TRUE, 0);
1770 	gtk_entry_set_text(GTK_ENTRY(dialog_textentry),
1771 						    (const gchar *)textfield);
1772 	gtk_entry_select_region(GTK_ENTRY(dialog_textentry), 0,
1773 							   STRLEN(textfield));
1774 	gtk_entry_set_max_length(GTK_ENTRY(dialog_textentry), IOSIZE - 1);
1775 	gtk_entry_set_position(GTK_ENTRY(dialog_textentry), STRLEN(textfield));
1776 	gtk_widget_show(dialog_textentry);
1777     }
1778 
1779     /* Add box for buttons */
1780     action_area = gtk_hbox_new(FALSE, 0);
1781     gtk_container_border_width(GTK_CONTAINER(action_area), 4);
1782     gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0);
1783     gtk_widget_show(action_area);
1784 
1785     /* Add a [vh]box in the hbox to center the buttons in the dialog. */
1786     if (vertical)
1787 	sub_area = gtk_vbox_new(FALSE, 0);
1788     else
1789 	sub_area = gtk_hbox_new(FALSE, 0);
1790     gtk_container_set_border_width(GTK_CONTAINER(sub_area), 0);
1791     gtk_box_pack_start(GTK_BOX(action_area), sub_area, TRUE, FALSE, 0);
1792     gtk_widget_show(sub_area);
1793 
1794     /*
1795      * Create the buttons.
1796      */
1797 
1798     /*
1799      * Translate the Vim accelerator character into an underscore for GTK+.
1800      * Double underscores to keep them in the label.
1801      */
1802     /* count the number of underscores */
1803     i = 1;
1804     for (p = buttons; *p; ++p)
1805 	if (*p == '_')
1806 	    ++i;
1807 
1808     /* make a copy of "buttons" with the translated characters */
1809     names = alloc(STRLEN(buttons) + i);
1810     if (names == NULL)
1811 	return -1;
1812 
1813     p = names;
1814     for (i = 0; buttons[i]; ++i)
1815     {
1816 	if (buttons[i] == DLG_HOTKEY_CHAR)
1817 	    *p++ = '_';
1818 	else
1819 	{
1820 	    if (buttons[i] == '_')
1821 		*p++ = '_';
1822 	    *p++ = buttons[i];
1823 	}
1824     }
1825     *p = NUL;
1826 
1827     /* Count the number of buttons and allocate button[] and data[]. */
1828     butcount = 1;
1829     for (p = names; *p; ++p)
1830 	if (*p == DLG_BUTTON_SEP)
1831 	    ++butcount;
1832     button = (GtkWidget **)alloc((unsigned)(butcount * sizeof(GtkWidget *)));
1833     data = (ButtonData *)alloc((unsigned)(butcount * sizeof(ButtonData)));
1834     if (button == NULL || data == NULL)
1835     {
1836 	vim_free(names);
1837 	vim_free(button);
1838 	vim_free(data);
1839 	return -1;
1840     }
1841 
1842     /* Attach the new accelerator group to the window. */
1843     accel_group = gtk_accel_group_new();
1844     gtk_accel_group_attach(accel_group, GTK_OBJECT(dialog));
1845 
1846     p = names;
1847     for (butcount = 0; *p; ++butcount)
1848     {
1849 	char_u		*next;
1850 	GtkWidget	*label;
1851 # ifdef GTK_USE_ACCEL
1852 	guint		accel_key;
1853 # endif
1854 
1855 	/* Chunk out this single button. */
1856 	for (next = p; *next; ++next)
1857 	{
1858 	    if (*next == DLG_BUTTON_SEP)
1859 	    {
1860 		*next++ = NUL;
1861 		break;
1862 	    }
1863 	}
1864 
1865 	button[butcount] = gtk_button_new();
1866 	GTK_WIDGET_SET_FLAGS(button[butcount], GTK_CAN_DEFAULT);
1867 
1868 	label = gtk_accel_label_new("");
1869 	gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), dialog);
1870 
1871 # ifdef GTK_USE_ACCEL
1872 	accel_key = gtk_label_parse_uline(GTK_LABEL(label), (const gchar *)p);
1873 	/* Don't add accelator if 'winaltkeys' is "no". */
1874 	if (accel_key != GDK_VoidSymbol)
1875 	{
1876 	    gtk_widget_add_accelerator(button[butcount],
1877 		    "clicked",
1878 		    accel_group,
1879 		    accel_key, 0,
1880 		    0);
1881 	}
1882 # else
1883 	(void)gtk_label_parse_uline(GTK_LABEL(label), (const gchar *)p);
1884 # endif
1885 
1886 	gtk_container_add(GTK_CONTAINER(button[butcount]), label);
1887 	gtk_widget_show_all(button[butcount]);
1888 
1889 	data[butcount].status = &dialog_status;
1890 	data[butcount].index = butcount;
1891 	data[butcount].dialog = dialog;
1892 	gtk_signal_connect(GTK_OBJECT(button[butcount]),
1893 			   (const char *)"clicked",
1894 			   GTK_SIGNAL_FUNC(dlg_button_clicked),
1895 			   (gpointer) &data[butcount]);
1896 
1897 	gtk_box_pack_start(GTK_BOX(sub_area), button[butcount],
1898 			   TRUE, FALSE, 0);
1899 	p = next;
1900     }
1901 
1902     vim_free(names);
1903 
1904     cancel_data.ignore_enter = FALSE;
1905     if (butcount > 0)
1906     {
1907 	--def_but;		/* 1 is first button */
1908 	if (def_but >= butcount)
1909 	    def_but = -1;
1910 	if (def_but >= 0)
1911 	{
1912 	    gtk_widget_grab_focus(button[def_but]);
1913 	    gtk_widget_grab_default(button[def_but]);
1914 	}
1915 	else
1916 	    /* No default, ignore hitting Enter. */
1917 	    cancel_data.ignore_enter = TRUE;
1918     }
1919 
1920     if (textfield != NULL)
1921 	gtk_window_set_focus(GTK_WINDOW(dialog), dialog_textentry);
1922 
1923     separator = gtk_hseparator_new();
1924     gtk_box_pack_end(GTK_BOX(vbox), separator, FALSE, TRUE, 0);
1925     gtk_widget_show(separator);
1926 
1927     dialog_status = -1;
1928 
1929     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
1930 					    GTK_WIDGET(dialog), VW_POS_MOUSE);
1931 
1932     gtk_widget_show(dialog);
1933 
1934     /* loop here until the dialog goes away */
1935     while (dialog_status == -1 && !dialog_destroyed
1936 					       && GTK_WIDGET_DRAWABLE(dialog))
1937 	gtk_main_iteration_do(TRUE);
1938 
1939     if (dialog_status < 0)
1940 	dialog_status = 0;
1941     if (dialog_status != 1 && textfield != NULL)
1942 	*textfield = NUL;	/* dialog was cancelled */
1943 
1944     /* let the garbage collector know that we don't need it any longer */
1945     gtk_accel_group_unref(accel_group);
1946 
1947     vim_free(button);
1948     vim_free(data);
1949 
1950     return dialog_status;
1951 }
1952 
1953 #endif /* FEAT_GUI_DIALOG && !HAVE_GTK2 */
1954 
1955 
1956 #if defined(FEAT_GUI_DIALOG) && defined(HAVE_GTK2)
1957 
1958     static GtkWidget *
1959 create_message_dialog(int type, char_u *title, char_u *message)
1960 {
1961     GtkWidget	    *dialog;
1962     GtkMessageType  message_type;
1963 
1964     switch (type)
1965     {
1966 	case VIM_ERROR:	    message_type = GTK_MESSAGE_ERROR;	 break;
1967 	case VIM_WARNING:   message_type = GTK_MESSAGE_WARNING;	 break;
1968 	case VIM_QUESTION:  message_type = GTK_MESSAGE_QUESTION; break;
1969 	default:	    message_type = GTK_MESSAGE_INFO;	 break;
1970     }
1971 
1972     message = CONVERT_TO_UTF8(message);
1973     dialog  = gtk_message_dialog_new(GTK_WINDOW(gui.mainwin),
1974 				     GTK_DIALOG_DESTROY_WITH_PARENT,
1975 				     message_type,
1976 				     GTK_BUTTONS_NONE,
1977 				     "%s", (const char *)message);
1978     CONVERT_TO_UTF8_FREE(message);
1979 
1980     if (title != NULL)
1981     {
1982 	title = CONVERT_TO_UTF8(title);
1983 	gtk_window_set_title(GTK_WINDOW(dialog), (const char *)title);
1984 	CONVERT_TO_UTF8_FREE(title);
1985     }
1986     else if (type == VIM_GENERIC)
1987     {
1988 	gtk_window_set_title(GTK_WINDOW(dialog), "VIM");
1989     }
1990 
1991     return dialog;
1992 }
1993 
1994 /*
1995  * Split up button_string into individual button labels by inserting
1996  * NUL bytes.  Also replace the Vim-style mnemonic accelerator prefix
1997  * '&' with '_'.  button_string must point to allocated memory!
1998  * Return an allocated array of pointers into button_string.
1999  */
2000     static char **
2001 split_button_string(char_u *button_string, int *n_buttons)
2002 {
2003     char	    **array;
2004     char_u	    *p;
2005     unsigned int    count = 1;
2006 
2007     for (p = button_string; *p != NUL; ++p)
2008 	if (*p == DLG_BUTTON_SEP)
2009 	    ++count;
2010 
2011     array = (char **)alloc((count + 1) * sizeof(char *));
2012     count = 0;
2013 
2014     if (array != NULL)
2015     {
2016 	array[count++] = (char *)button_string;
2017 	for (p = button_string; *p != NUL; ++p)
2018 	{
2019 	    if (*p == DLG_BUTTON_SEP)
2020 	    {
2021 		*p = NUL;
2022 		array[count++] = (char *)p + 1;
2023 	    }
2024 	    else if (*p == DLG_HOTKEY_CHAR)
2025 		*p = '_';
2026 #ifdef FEAT_MBYTE
2027 	    else if (has_mbyte)
2028 		p += (*mb_ptr2len_check)(p) - 1;
2029 #endif
2030 	}
2031 	array[count] = NULL; /* currently not relied upon, but doesn't hurt */
2032     }
2033 
2034     *n_buttons = count;
2035     return array;
2036 }
2037 
2038     static char **
2039 split_button_translation(const char *message)
2040 {
2041     char    **buttons = NULL;
2042     char_u  *str;
2043     int	    n_buttons = 0;
2044     int	    n_expected = 1;
2045 
2046     for (str = (char_u *)message; *str != NUL; ++str)
2047 	if (*str == DLG_BUTTON_SEP)
2048 	    ++n_expected;
2049 
2050     str = (char_u *)_(message);
2051     if (str != NULL)
2052     {
2053 	if (output_conv.vc_type != CONV_NONE)
2054 	    str = string_convert(&output_conv, str, NULL);
2055 	else
2056 	    str = vim_strsave(str);
2057 
2058 	if (str != NULL)
2059 	    buttons = split_button_string(str, &n_buttons);
2060     }
2061     /*
2062      * Uh-oh... this should never ever happen.	But we don't wanna crash
2063      * if the translation is broken, thus fall back to the untranslated
2064      * buttons string in case of emergency.
2065      */
2066     if (buttons == NULL || n_buttons != n_expected)
2067     {
2068 	vim_free(buttons);
2069 	vim_free(str);
2070 	buttons = NULL;
2071 	str = vim_strsave((char_u *)message);
2072 
2073 	if (str != NULL)
2074 	    buttons = split_button_string(str, &n_buttons);
2075 	if (buttons == NULL)
2076 	    vim_free(str);
2077     }
2078 
2079     return buttons;
2080 }
2081 
2082     static int
2083 button_equal(const char *a, const char *b)
2084 {
2085     while (*a != '\0' && *b != '\0')
2086     {
2087 	if (*a == '_' && *++a == '\0')
2088 	    break;
2089 	if (*b == '_' && *++b == '\0')
2090 	    break;
2091 
2092 	if (g_unichar_tolower(g_utf8_get_char(a))
2093 		!= g_unichar_tolower(g_utf8_get_char(b)))
2094 	    return FALSE;
2095 
2096 	a = g_utf8_next_char(a);
2097 	b = g_utf8_next_char(b);
2098     }
2099 
2100     return (*a == '\0' && *b == '\0');
2101 }
2102 
2103     static void
2104 dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
2105 {
2106     char    **ok;
2107     char    **ync;  /* "yes no cancel" */
2108     char    **buttons;
2109     int	    n_buttons = 0;
2110     int	    index;
2111 
2112     button_string = vim_strsave(button_string); /* must be writable */
2113     if (button_string == NULL)
2114 	return;
2115 
2116     /* Check 'v' flag in 'guioptions': vertical button placement. */
2117     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
2118     {
2119 	GtkWidget	*vbutton_box;
2120 
2121 	vbutton_box = gtk_vbutton_box_new();
2122 	gtk_widget_show(vbutton_box);
2123 	gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2124 						 vbutton_box, TRUE, FALSE, 0);
2125 	/* Overrule the "action_area" value, hopefully this works... */
2126 	GTK_DIALOG(dialog)->action_area = vbutton_box;
2127     }
2128 
2129     /*
2130      * Yes this is ugly, I don't particularly like it either.  But doing it
2131      * this way has the compelling advantage that translations need not to
2132      * be touched at all.  See below what 'ok' and 'ync' are used for.
2133      */
2134     ok	    = split_button_translation(N_("&Ok"));
2135     ync     = split_button_translation(N_("&Yes\n&No\n&Cancel"));
2136     buttons = split_button_string(button_string, &n_buttons);
2137 
2138     /*
2139      * Yes, the buttons are in reversed order to match the GNOME 2 desktop
2140      * environment.  Don't hit me -- it's all about consistency.
2141      * Well, apparently somebody changed his mind: with GTK 2.2.4 it works the
2142      * other way around...
2143      */
2144     for (index = 1; index <= n_buttons; ++index)
2145     {
2146 	char	*label;
2147 	char_u	*label8;
2148 
2149 	label = buttons[index - 1];
2150 	/*
2151 	 * Perform some guesswork to find appropriate stock items for the
2152 	 * buttons.  We have to compare with a sample of the translated
2153 	 * button string to get things right.  Yes, this is hackish :/
2154 	 *
2155 	 * But even the common button labels aren't necessarily translated,
2156 	 * since anyone can create their own dialogs using Vim functions.
2157 	 * Thus we have to check for those too.
2158 	 */
2159 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
2160 	{
2161 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
2162 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
2163 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
2164 	    else if (button_equal(label, ync[2]))   label = GTK_STOCK_CANCEL;
2165 	    else if (button_equal(label, "Ok"))     label = GTK_STOCK_OK;
2166 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
2167 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
2168 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
2169 	}
2170 	label8 = CONVERT_TO_UTF8((char_u *)label);
2171 	gtk_dialog_add_button(dialog, (const gchar *)label8, index);
2172 	CONVERT_TO_UTF8_FREE(label8);
2173     }
2174 
2175     if (ok != NULL)
2176 	vim_free(*ok);
2177     if (ync != NULL)
2178 	vim_free(*ync);
2179     vim_free(ok);
2180     vim_free(ync);
2181     vim_free(buttons);
2182     vim_free(button_string);
2183 }
2184 
2185 /*
2186  * Allow mnemonic accelerators to be activated without pressing <Alt>.
2187  * I'm not sure if it's a wise idea to do this.  However, the old GTK+ 1.2
2188  * GUI used to work this way, and I consider the impact on UI consistency
2189  * low enough to justify implementing this as a special Vim feature.
2190  */
2191 typedef struct _DialogInfo
2192 {
2193     int		ignore_enter;	    /* no default button, ignore "Enter" */
2194     int		noalt;		    /* accept accelerators without Alt */
2195     GtkDialog	*dialog;	    /* Widget of the dialog */
2196 } DialogInfo;
2197 
2198 /*ARGSUSED2*/
2199     static gboolean
2200 dialog_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2201 {
2202     DialogInfo *di = (DialogInfo *)data;
2203 
2204     /* Ignore hitting "Enter" if there is no default button. */
2205     if (di->ignore_enter && event->keyval == GDK_Return)
2206 	return TRUE;
2207 
2208     /* Close the dialog when hitting "Esc". */
2209     if (event->keyval == GDK_Escape)
2210     {
2211 	gtk_dialog_response(di->dialog, GTK_RESPONSE_REJECT);
2212 	return TRUE;
2213     }
2214 
2215     if (di->noalt
2216 	      && (event->state & gtk_accelerator_get_default_mod_mask()) == 0)
2217     {
2218 	return gtk_window_mnemonic_activate(
2219 		   GTK_WINDOW(widget), event->keyval,
2220 		   gtk_window_get_mnemonic_modifier(GTK_WINDOW(widget)));
2221     }
2222 
2223     return FALSE; /* continue emission */
2224 }
2225 
2226     int
2227 gui_mch_dialog(int	type,	    /* type of dialog */
2228 	       char_u	*title,	    /* title of dialog */
2229 	       char_u	*message,   /* message text */
2230 	       char_u	*buttons,   /* names of buttons */
2231 	       int	def_but,    /* default button */
2232 	       char_u	*textfield) /* text for textfield or NULL */
2233 {
2234     GtkWidget	*dialog;
2235     GtkWidget	*entry = NULL;
2236     char_u	*text;
2237     int		response;
2238     DialogInfo  dialoginfo;
2239 
2240     dialog = create_message_dialog(type, title, message);
2241     dialoginfo.dialog = GTK_DIALOG(dialog);
2242     dialog_add_buttons(GTK_DIALOG(dialog), buttons);
2243 
2244     if (textfield != NULL)
2245     {
2246 	GtkWidget *alignment;
2247 
2248 	entry = gtk_entry_new();
2249 	gtk_widget_show(entry);
2250 
2251 	text = CONVERT_TO_UTF8(textfield);
2252 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
2253 	CONVERT_TO_UTF8_FREE(text);
2254 
2255 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
2256 						      (float)1.0, (float)1.0);
2257 	gtk_container_add(GTK_CONTAINER(alignment), entry);
2258 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
2259 	gtk_widget_show(alignment);
2260 
2261 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2262 			   alignment, TRUE, FALSE, 0);
2263 	dialoginfo.noalt = FALSE;
2264     }
2265     else
2266 	dialoginfo.noalt = TRUE;
2267 
2268     /* Allow activation of mnemonic accelerators without pressing <Alt> when
2269      * there is no textfield.  Handle pressing Enter and Esc. */
2270     g_signal_connect(G_OBJECT(dialog), "key_press_event",
2271 			 G_CALLBACK(&dialog_key_press_event_cb), &dialoginfo);
2272 
2273     if (def_but > 0)
2274     {
2275 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), def_but);
2276 	dialoginfo.ignore_enter = FALSE;
2277     }
2278     else
2279 	/* No default button, ignore pressing Enter. */
2280 	dialoginfo.ignore_enter = TRUE;
2281 
2282     /* Show the mouse pointer if it's currently hidden. */
2283     gui_mch_mousehide(FALSE);
2284 
2285     response = gtk_dialog_run(GTK_DIALOG(dialog));
2286 
2287     /* GTK_RESPONSE_NONE means the dialog was programmatically destroyed. */
2288     if (response != GTK_RESPONSE_NONE)
2289     {
2290 	if (textfield != NULL)
2291 	{
2292 	    text = (char_u *)gtk_entry_get_text(GTK_ENTRY(entry));
2293 	    text = CONVERT_FROM_UTF8(text);
2294 
2295 	    STRNCPY(textfield, text, IOSIZE);
2296 	    textfield[IOSIZE - 1] = NUL;
2297 
2298 	    CONVERT_FROM_UTF8_FREE(text);
2299 	}
2300 	gtk_widget_destroy(dialog);
2301     }
2302 
2303     /* Terrible hack: When the text area still has focus when we remove the
2304      * dialog, somehow gvim loses window focus.  This is with "point to type"
2305      * in the KDE 3.1 window manager.  Warp the mouse pointer to outside the
2306      * window and back to avoid that. */
2307     if (!gui.in_focus)
2308     {
2309 	int x, y;
2310 
2311 	gdk_window_get_pointer(gui.drawarea->window, &x, &y, NULL);
2312 	gui_mch_setmouse(-100, -100);
2313 	gui_mch_setmouse(x, y);
2314     }
2315 
2316     return response > 0 ? response : 0;
2317 }
2318 
2319 #endif /* FEAT_GUI_DIALOG && HAVE_GTK2 */
2320 
2321 
2322 #if defined(FEAT_MENU) || defined(PROTO)
2323 
2324     void
2325 gui_mch_show_popupmenu(vimmenu_T *menu)
2326 {
2327 # if defined(FEAT_XIM) && defined(HAVE_GTK2)
2328     /*
2329      * Append a submenu for selecting an input method.	This is
2330      * currently the only way to switch input methods at runtime.
2331      */
2332     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
2333 					 "vim-has-im-menu") == NULL)
2334     {
2335 	GtkWidget   *menuitem;
2336 	GtkWidget   *submenu;
2337 	char_u	    *name;
2338 
2339 	menuitem = gtk_separator_menu_item_new();
2340 	gtk_widget_show(menuitem);
2341 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
2342 
2343 	name = (char_u *)_("Input _Methods");
2344 	name = CONVERT_TO_UTF8(name);
2345 	menuitem = gtk_menu_item_new_with_mnemonic((const char *)name);
2346 	CONVERT_TO_UTF8_FREE(name);
2347 	gtk_widget_show(menuitem);
2348 
2349 	submenu = gtk_menu_new();
2350 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
2351 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
2352 
2353 	gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(xic),
2354 					     GTK_MENU_SHELL(submenu));
2355 	g_object_set_data(G_OBJECT(menu->submenu_id),
2356 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
2357     }
2358 # endif /* FEAT_XIM && HAVE_GTK2 */
2359 
2360     gtk_menu_popup(GTK_MENU(menu->submenu_id),
2361 		   NULL, NULL,
2362 		   (GtkMenuPositionFunc)NULL, NULL,
2363 		   3U, (guint32)GDK_CURRENT_TIME);
2364 }
2365 
2366 /*
2367  * Menu position callback; used by gui_make_popup() to place the menu
2368  * at the current text cursor position.
2369  *
2370  * Note: The push_in output argument seems to affect scrolling of huge
2371  * menus that don't fit on the screen.	Leave it at the default for now.
2372  */
2373 /*ARGSUSED0*/
2374     static void
2375 popup_menu_position_func(GtkMenu *menu,
2376 			 gint *x, gint *y,
2377 # ifdef HAVE_GTK2
2378 			 gboolean *push_in,
2379 # endif
2380 			 gpointer user_data)
2381 {
2382     if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
2383     {
2384 	gdk_window_get_origin(gui.drawarea->window, x, y);
2385 
2386 	/* Find the cursor position in the current window */
2387 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
2388 	*y += FILL_Y(W_WINROW(curwin) + curwin->w_wrow + 1) + 1;
2389     }
2390 }
2391 
2392     void
2393 gui_make_popup(char_u *path_name)
2394 {
2395     vimmenu_T *menu;
2396 
2397     menu = gui_find_menu(path_name);
2398 
2399     if (menu != NULL && menu->submenu_id != NULL)
2400     {
2401 	gtk_menu_popup(GTK_MENU(menu->submenu_id),
2402 		       NULL, NULL,
2403 		       &popup_menu_position_func, NULL,
2404 		       0U, (guint32)GDK_CURRENT_TIME);
2405     }
2406 }
2407 
2408 #endif /* FEAT_MENU */
2409 
2410 
2411 /*
2412  * We don't create it twice.
2413  */
2414 
2415 typedef struct _SharedFindReplace
2416 {
2417     GtkWidget *dialog;	/* the main dialog widget */
2418     GtkWidget *wword;	/* 'Whole word only' check button */
2419     GtkWidget *mcase;	/* 'Match case' check button */
2420     GtkWidget *up;	/* search direction 'Up' radio button */
2421     GtkWidget *down;	/* search direction 'Down' radio button */
2422     GtkWidget *what;	/* 'Find what' entry text widget */
2423     GtkWidget *with;	/* 'Replace with' entry text widget */
2424     GtkWidget *find;	/* 'Find Next' action button */
2425     GtkWidget *replace;	/* 'Replace With' action button */
2426     GtkWidget *all;	/* 'Replace All' action button */
2427 } SharedFindReplace;
2428 
2429 static SharedFindReplace find_widgets = { NULL, };
2430 static SharedFindReplace repl_widgets = { NULL, };
2431 
2432 /* ARGSUSED */
2433     static int
2434 find_key_press_event(
2435 		GtkWidget	*widget,
2436 		GdkEventKey	*event,
2437 		SharedFindReplace *frdp)
2438 {
2439     /* If the user is holding one of the key modifiers we will just bail out,
2440      * thus preserving the possibility of normal focus traversal.
2441      */
2442     if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
2443 	return FALSE;
2444 
2445     /* the Escape key synthesizes a cancellation action */
2446     if (event->keyval == GDK_Escape)
2447     {
2448 	gtk_widget_hide(frdp->dialog);
2449 
2450 	return TRUE;
2451     }
2452     /*
2453      * What the **** is this for?  Disabled for GTK+ 2 because due to
2454      * gtk_signal_connect_after() it doesn't have any effect anyway.
2455      * (Fortunately.)
2456      */
2457 #ifndef HAVE_GTK2
2458     /* block traversal resulting from those keys */
2459     if (event->keyval == GDK_Left
2460 	    || event->keyval == GDK_Right
2461 	    || event->keyval == GDK_space)
2462 	return TRUE;
2463 #endif
2464 
2465     /* It would be delightfull if it where possible to do search history
2466      * operations on the K_UP and K_DOWN keys here.
2467      */
2468 
2469     return FALSE;
2470 }
2471 
2472 #ifdef HAVE_GTK2
2473     static GtkWidget *
2474 create_image_button(const char *stock_id, const char *label)
2475 {
2476     char_u	*text;
2477     GtkWidget	*box;
2478     GtkWidget	*alignment;
2479     GtkWidget	*button;
2480 
2481     text = CONVERT_TO_UTF8((char_u *)label);
2482 
2483     box = gtk_hbox_new(FALSE, 3);
2484     gtk_box_pack_start(GTK_BOX(box),
2485 		       gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
2486 		       FALSE, FALSE, 0);
2487     gtk_box_pack_start(GTK_BOX(box),
2488 		       gtk_label_new((const char *)text),
2489 		       FALSE, FALSE, 0);
2490 
2491     CONVERT_TO_UTF8_FREE(text);
2492 
2493     alignment = gtk_alignment_new((float)0.5, (float)0.5,
2494 						      (float)0.0, (float)0.0);
2495     gtk_container_add(GTK_CONTAINER(alignment), box);
2496     gtk_widget_show_all(alignment);
2497 
2498     button = gtk_button_new();
2499     gtk_container_add(GTK_CONTAINER(button), alignment);
2500 
2501     return button;
2502 }
2503 
2504 /*
2505  * This is currently only used by find_replace_dialog_create(), and
2506  * I'd really like to keep it at that.	In other words: don't spread
2507  * this nasty hack all over the code.  Think twice.
2508  */
2509     static const char *
2510 convert_localized_message(char_u **buffer, const char *message)
2511 {
2512     if (output_conv.vc_type == CONV_NONE)
2513 	return message;
2514 
2515     vim_free(*buffer);
2516     *buffer = string_convert(&output_conv, (char_u *)message, NULL);
2517 
2518     return (const char *)*buffer;
2519 }
2520 #endif /* HAVE_GTK2 */
2521 
2522     static void
2523 find_replace_dialog_create(char_u *arg, int do_replace)
2524 {
2525 #ifndef HAVE_GTK2
2526     GtkWidget	*frame;
2527 #endif
2528     GtkWidget	*hbox;		/* main top down box */
2529     GtkWidget	*actionarea;
2530     GtkWidget	*table;
2531     GtkWidget	*tmp;
2532     GtkWidget	*vbox;
2533     gboolean	sensitive;
2534     SharedFindReplace *frdp;
2535     char_u	*entry_text;
2536     int		wword = FALSE;
2537     int		mcase = !p_ic;
2538 #ifdef HAVE_GTK2
2539     char_u	*conv_buffer = NULL;
2540 #   define CONV(message) convert_localized_message(&conv_buffer, (message))
2541 #else
2542 #   define CONV(message) (message)
2543 #endif
2544 
2545     frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
2546 
2547     /* Get the search string to use. */
2548     entry_text = get_find_dialog_text(arg, &wword, &mcase);
2549 
2550 #ifdef HAVE_GTK2
2551     if (entry_text != NULL && output_conv.vc_type != CONV_NONE)
2552     {
2553 	char_u *old_text = entry_text;
2554 	entry_text = string_convert(&output_conv, entry_text, NULL);
2555 	vim_free(old_text);
2556     }
2557 #endif
2558 
2559     /*
2560      * If the dialog already exists, just raise it.
2561      */
2562     if (frdp->dialog)
2563     {
2564 #ifndef HAVE_GTK2
2565 	/* always make the dialog appear where you want it even if the mainwin
2566 	 * has moved -- dbv */
2567 	gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
2568 				      GTK_WIDGET(frdp->dialog), VW_POS_MOUSE);
2569 	gui_gtk_synch_fonts();
2570 
2571 	if (!GTK_WIDGET_VISIBLE(frdp->dialog))
2572 	{
2573 	    gtk_widget_grab_focus(frdp->what);
2574 	    gtk_widget_show(frdp->dialog);
2575 	}
2576 #endif
2577 	if (entry_text != NULL)
2578 	{
2579 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2580 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2581 							     (gboolean)wword);
2582 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2583 							     (gboolean)mcase);
2584 	}
2585 #ifdef HAVE_GTK2
2586 	gtk_window_present(GTK_WINDOW(frdp->dialog));
2587 #else
2588 	gdk_window_raise(frdp->dialog->window);
2589 #endif
2590 	vim_free(entry_text);
2591 	return;
2592     }
2593 
2594 #ifdef HAVE_GTK2
2595     frdp->dialog = gtk_dialog_new();
2596     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
2597     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
2598     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
2599 #else
2600     frdp->dialog = gtk_window_new(GTK_WINDOW_DIALOG);
2601 #endif
2602 
2603     if (do_replace)
2604     {
2605 #ifndef HAVE_GTK2
2606 	gtk_window_set_wmclass(GTK_WINDOW(frdp->dialog), "searchrepl", "gvim");
2607 #endif
2608 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2609 			     CONV(_("VIM - Search and Replace...")));
2610     }
2611     else
2612     {
2613 #ifndef HAVE_GTK2
2614 	gtk_window_set_wmclass(GTK_WINDOW(frdp->dialog), "search", "gvim");
2615 #endif
2616 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2617 			     CONV(_("VIM - Search...")));
2618     }
2619 
2620 #ifndef HAVE_GTK2 /* Utter crack.  Shudder. */
2621     gtk_widget_realize(frdp->dialog);
2622     gdk_window_set_decorations(frdp->dialog->window,
2623 	    GDK_DECOR_TITLE | GDK_DECOR_BORDER | GDK_DECOR_RESIZEH);
2624     gdk_window_set_functions(frdp->dialog->window,
2625 	    GDK_FUNC_RESIZE | GDK_FUNC_MOVE);
2626 #endif
2627 
2628 #ifndef HAVE_GTK2
2629     /* this makes it look better on Motif style window managers */
2630     frame = gtk_frame_new(NULL);
2631     gtk_container_add(GTK_CONTAINER(frdp->dialog), frame);
2632 #endif
2633 
2634     hbox = gtk_hbox_new(FALSE, 0);
2635 #ifdef HAVE_GTK2
2636     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
2637     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
2638 #else
2639     gtk_container_add(GTK_CONTAINER(frame), hbox);
2640 #endif
2641 
2642     if (do_replace)
2643 	table = gtk_table_new(1024, 4, FALSE);
2644     else
2645 	table = gtk_table_new(1024, 3, FALSE);
2646     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
2647     gtk_container_border_width(GTK_CONTAINER(table), 4);
2648 
2649     tmp = gtk_label_new(CONV(_("Find what:")));
2650     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2651     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
2652 		     GTK_FILL, GTK_EXPAND, 2, 2);
2653     frdp->what = gtk_entry_new();
2654     sensitive = (entry_text != NULL && entry_text[0] != NUL);
2655     if (entry_text != NULL)
2656 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2657     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
2658 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
2659     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
2660 				 GTK_SIGNAL_FUNC(find_key_press_event),
2661 				 (gpointer) frdp);
2662     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
2663 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2664 
2665     if (do_replace)
2666     {
2667 	tmp = gtk_label_new(CONV(_("Replace with:")));
2668 	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2669 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
2670 			 GTK_FILL, GTK_EXPAND, 2, 2);
2671 	frdp->with = gtk_entry_new();
2672 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
2673 			   GTK_SIGNAL_FUNC(find_replace_cb),
2674 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
2675 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
2676 				 GTK_SIGNAL_FUNC(find_key_press_event),
2677 				 (gpointer) frdp);
2678 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
2679 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2680 
2681 	/*
2682 	 * Make the entry activation only change the input focus onto the
2683 	 * with item.
2684 	 */
2685 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2686 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
2687     }
2688     else
2689     {
2690 	/*
2691 	 * Make the entry activation do the search.
2692 	 */
2693 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2694 			   GTK_SIGNAL_FUNC(find_replace_cb),
2695 			   GINT_TO_POINTER(FRD_FINDNEXT));
2696     }
2697 
2698     /* whole word only button */
2699     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
2700     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2701 							(gboolean)wword);
2702     if (do_replace)
2703 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
2704 			 GTK_FILL, GTK_EXPAND, 2, 2);
2705     else
2706 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
2707 			 GTK_FILL, GTK_EXPAND, 2, 2);
2708 
2709     /* match case button */
2710     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
2711     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2712 							     (gboolean)mcase);
2713     if (do_replace)
2714 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
2715 			 GTK_FILL, GTK_EXPAND, 2, 2);
2716     else
2717 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
2718 			 GTK_FILL, GTK_EXPAND, 2, 2);
2719 
2720     tmp = gtk_frame_new(CONV(_("Direction")));
2721     if (do_replace)
2722 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
2723 			 GTK_FILL, GTK_FILL, 2, 2);
2724     else
2725 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
2726 			 GTK_FILL, GTK_FILL, 2, 2);
2727     vbox = gtk_vbox_new(FALSE, 0);
2728     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
2729     gtk_container_add(GTK_CONTAINER(tmp), vbox);
2730 
2731     /* 'Up' and 'Down' buttons */
2732     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
2733     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
2734     frdp->down = gtk_radio_button_new_with_label(
2735 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
2736 			CONV(_("Down")));
2737     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
2738 #ifdef HAVE_GTK2
2739     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
2740 #endif
2741     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
2742 
2743     /* vbox to hold the action buttons */
2744     actionarea = gtk_vbutton_box_new();
2745     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
2746 #ifndef HAVE_GTK2
2747     if (do_replace)
2748     {
2749 	gtk_button_box_set_layout(GTK_BUTTON_BOX(actionarea),
2750 				  GTK_BUTTONBOX_END);
2751 	gtk_button_box_set_spacing(GTK_BUTTON_BOX(actionarea), 0);
2752     }
2753 #endif
2754     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
2755 
2756     /* 'Find Next' button */
2757 #ifdef HAVE_GTK2
2758     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
2759 #else
2760     frdp->find = gtk_button_new_with_label(_("Find Next"));
2761 #endif
2762     gtk_widget_set_sensitive(frdp->find, sensitive);
2763 
2764     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
2765 		       GTK_SIGNAL_FUNC(find_replace_cb),
2766 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
2767 				    : GINT_TO_POINTER(FRD_FINDNEXT));
2768 
2769     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
2770     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
2771     gtk_widget_grab_default(frdp->find);
2772 
2773     if (do_replace)
2774     {
2775 	/* 'Replace' button */
2776 #ifdef HAVE_GTK2
2777 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
2778 #else
2779 	frdp->replace = gtk_button_new_with_label(_("Replace"));
2780 #endif
2781 	gtk_widget_set_sensitive(frdp->replace, sensitive);
2782 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
2783 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
2784 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
2785 			   GTK_SIGNAL_FUNC(find_replace_cb),
2786 			   GINT_TO_POINTER(FRD_REPLACE));
2787 
2788 	/* 'Replace All' button */
2789 #ifdef HAVE_GTK2
2790 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
2791 #else
2792 	frdp->all = gtk_button_new_with_label(_("Replace All"));
2793 #endif
2794 	gtk_widget_set_sensitive(frdp->all, sensitive);
2795 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
2796 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
2797 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
2798 			   GTK_SIGNAL_FUNC(find_replace_cb),
2799 			   GINT_TO_POINTER(FRD_REPLACEALL));
2800     }
2801 
2802     /* 'Cancel' button */
2803 #ifdef HAVE_GTK2
2804     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2805 #else
2806     tmp = gtk_button_new_with_label(_("Cancel"));
2807 #endif
2808     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
2809     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
2810     gtk_signal_connect_object(GTK_OBJECT(tmp),
2811 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
2812 			      GTK_OBJECT(frdp->dialog));
2813     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
2814 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
2815 			      GTK_OBJECT(frdp->dialog));
2816 
2817     tmp = gtk_vseparator_new();
2818 #ifdef HAVE_GTK2
2819     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
2820 #else
2821     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, TRUE, 0);
2822 #endif
2823 
2824 #ifndef HAVE_GTK2
2825     gtk_widget_grab_focus(frdp->what);
2826 
2827     /* show the frame and realize the frdp->dialog this gives us a window size
2828      * request that we'll use to position the window within the boundary of
2829      * the mainwin --dbv */
2830     gtk_widget_show_all(frame);
2831     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
2832 				      GTK_WIDGET(frdp->dialog), VW_POS_MOUSE);
2833     gui_gtk_synch_fonts();
2834     gtk_widget_show_all(frdp->dialog);
2835 #endif
2836 
2837 #ifdef HAVE_GTK2
2838     /* Suppress automatic show of the unused action area */
2839     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
2840     gtk_widget_show_all(hbox);
2841     gtk_widget_show(frdp->dialog);
2842 #endif
2843 
2844     vim_free(entry_text);
2845 #ifdef HAVE_GTK2
2846     vim_free(conv_buffer);
2847 #endif
2848 #undef CONV
2849 }
2850 
2851     void
2852 gui_mch_find_dialog(exarg_T *eap)
2853 {
2854     if (gui.in_use)
2855 	find_replace_dialog_create(eap->arg, FALSE);
2856 }
2857 
2858     void
2859 gui_mch_replace_dialog(exarg_T *eap)
2860 {
2861     if (gui.in_use)
2862 	find_replace_dialog_create(eap->arg, TRUE);
2863 }
2864 
2865 
2866 #if !defined(HAVE_GTK2) || defined(PROTO)
2867 /*
2868  * Synchronize all gui elements, which are dependant upon the
2869  * main text font used. Those are in esp. the find/replace dialogs.
2870  * If you don't understand why this should be needed, please try to
2871  * search for "pięść" in iso8859-2.
2872  *
2873  * (<danielk> I converted the comment above to UTF-8 to put
2874  *  a stopper to the encoding mess.  Forgive me :)
2875  *
2876  * Obsolete with GTK2.
2877  */
2878     void
2879 gui_gtk_synch_fonts(void)
2880 {
2881     SharedFindReplace *frdp;
2882     int do_replace;
2883 
2884     /* OK this loop is a bit tricky... */
2885     for (do_replace = 0; do_replace <= 1; ++do_replace)
2886     {
2887 	frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
2888 	if (frdp->dialog)
2889 	{
2890 	    GtkStyle *style;
2891 
2892 	    /* synch the font with whats used by the text itself */
2893 	    style = gtk_style_copy(gtk_widget_get_style(frdp->what));
2894 	    gdk_font_unref(style->font);
2895 # ifdef FEAT_XFONTSET
2896 	    if (gui.fontset != NOFONTSET)
2897 		style->font = gui.fontset;
2898 	    else
2899 # endif
2900 		style->font = gui.norm_font;
2901 	    gdk_font_ref(style->font);
2902 	    gtk_widget_set_style(frdp->what, style);
2903 	    gtk_style_unref(style);
2904 	    if (do_replace)
2905 	    {
2906 		style = gtk_style_copy(gtk_widget_get_style(frdp->with));
2907 		gdk_font_unref(style->font);
2908 # ifdef FEAT_XFONTSET
2909 		if (gui.fontset != NOFONTSET)
2910 		    style->font = gui.fontset;
2911 		else
2912 # endif
2913 		    style->font = gui.norm_font;
2914 		gdk_font_ref(style->font);
2915 		gtk_widget_set_style(frdp->with, style);
2916 		gtk_style_unref(style);
2917 	    }
2918 	}
2919     }
2920 }
2921 #endif /* !HAVE_GTK2 */
2922 
2923 
2924 /*
2925  * Callback for actions of the find and replace dialogs
2926  */
2927 /*ARGSUSED*/
2928     static void
2929 find_replace_cb(GtkWidget *widget, gpointer data)
2930 {
2931     int			flags;
2932     char_u		*find_text;
2933     char_u		*repl_text;
2934     gboolean		direction_down;
2935     SharedFindReplace	*sfr;
2936     int			rc;
2937 
2938     flags = (int)(long)data;	    /* avoid a lint warning here */
2939 
2940     /* Get the search/replace strings from the dialog */
2941     if (flags == FRD_FINDNEXT)
2942     {
2943 	repl_text = NULL;
2944 	sfr = &find_widgets;
2945     }
2946     else
2947     {
2948 	repl_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(repl_widgets.with));
2949 	sfr = &repl_widgets;
2950     }
2951 
2952     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
2953     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
2954 
2955     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
2956 	flags |= FRD_WHOLE_WORD;
2957     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
2958 	flags |= FRD_MATCH_CASE;
2959 
2960 #ifdef HAVE_GTK2
2961     repl_text = CONVERT_FROM_UTF8(repl_text);
2962     find_text = CONVERT_FROM_UTF8(find_text);
2963 #endif
2964     rc = gui_do_findrepl(flags, find_text, repl_text, direction_down);
2965 #ifdef HAVE_GTK2
2966     CONVERT_FROM_UTF8_FREE(repl_text);
2967     CONVERT_FROM_UTF8_FREE(find_text);
2968 #endif
2969 
2970     if (rc && gtk_main_level() > 0)
2971 	gtk_main_quit(); /* make sure cmd will be handled immediately */
2972 }
2973 
2974 /* our usual callback function */
2975 /*ARGSUSED*/
2976     static void
2977 entry_activate_cb(GtkWidget *widget, gpointer data)
2978 {
2979     gtk_widget_grab_focus(GTK_WIDGET(data));
2980 }
2981 
2982 /*
2983  * Syncing the find/replace dialogs on the fly is utterly useless crack,
2984  * and causes nothing but problems.  Please tell me a use case for which
2985  * you'd need both a find dialog and a find/replace one at the same time,
2986  * without being able to actually use them separately since they're syncing
2987  * all the time.  I don't think it's worthwhile to fix this nonsense,
2988  * particularly evil incarnation of braindeadness, whatever; I'd much rather
2989  * see it extinguished from this planet.  Thanks for listening.  Sorry.
2990  */
2991     static void
2992 entry_changed_cb(GtkWidget * entry, GtkWidget * dialog)
2993 {
2994     const gchar	*entry_text;
2995     gboolean	nonempty;
2996 
2997     entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
2998 
2999     if (!entry_text)
3000 	return;			/* shouldn't happen */
3001 
3002     nonempty = (entry_text[0] != '\0');
3003 
3004     if (dialog == find_widgets.dialog)
3005     {
3006 	gtk_widget_set_sensitive(find_widgets.find, nonempty);
3007     }
3008 
3009     if (dialog == repl_widgets.dialog)
3010     {
3011 	gtk_widget_set_sensitive(repl_widgets.find, nonempty);
3012 	gtk_widget_set_sensitive(repl_widgets.replace, nonempty);
3013 	gtk_widget_set_sensitive(repl_widgets.all, nonempty);
3014     }
3015 }
3016 
3017 /*
3018  * ":helpfind"
3019  */
3020 /*ARGSUSED*/
3021     void
3022 ex_helpfind(eap)
3023     exarg_T	*eap;
3024 {
3025     /* This will fail when menus are not loaded.  Well, it's only for
3026      * backwards compatibility anyway. */
3027     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
3028 }
3029 
3030 #if !defined(HAVE_GTK2) || defined(PROTO) /* Crack crack crack.  Brrrr. */
3031 
3032 /*  gui_gtk_position_in_parent
3033  *
3034  *  this function causes a child window to be placed within the boundary of
3035  *  the parent (mainwin) window.
3036  *
3037  *  you can specify where the window will be positioned by the third argument
3038  *  (defined in gui.h):
3039  *	VW_POS_CENTER		at center of parent window
3040  *	VW_POS_MOUSE		center of child at mouse position
3041  *	VW_POS_TOP_CENTER	top of child at top of parent centered
3042  *				horizontally about the mouse.
3043  *
3044  *  NOTE: for this function to act as desired the child window must have a
3045  *	  window size requested.  this can be accomplished by packing/placing
3046  *	  child widgets onto a gtk_frame widget rather than the gtk_window
3047  *	  widget...
3048  *
3049  *  brent -- dbv
3050  */
3051     void
3052 gui_gtk_position_in_parent(
3053 	GtkWidget	*parent,
3054 	GtkWidget	*child,
3055 	gui_win_pos_T	where)
3056 {
3057     GtkRequisition	c_size;
3058     gint		xPm, yPm;
3059     gint		xP, yP, wP, hP, pos_x, pos_y;
3060 
3061     /* make sure the child widget is set up then get its size. */
3062     gtk_widget_size_request(child, &c_size);
3063 
3064     /* get origin and size of parent window */
3065     gdk_window_get_origin((GdkWindow *)(parent->window), &xP, &yP);
3066     gdk_window_get_size((GdkWindow *)(parent->window), &wP, &hP);
3067 
3068     if (c_size.width > wP || c_size.height > hP)
3069     {
3070 	/* doh! maybe the user should consider giving gVim a little more
3071 	 * screen real estate */
3072 	gtk_widget_set_uposition(child , xP + 2 , yP + 2);
3073 	return;
3074     }
3075 
3076     if (where == VW_POS_MOUSE)
3077     {
3078 	/* position window at mouse pointer */
3079 	gtk_widget_get_pointer(parent, &xPm, &yPm);
3080 	pos_x = xP + xPm - (c_size.width) / 2;
3081 	pos_y = yP + yPm - (c_size.height) / 2;
3082     }
3083     else
3084     {
3085 	/* set child x origin so it is in center of Vim window */
3086 	pos_x =  xP + (wP - c_size.width) / 2;
3087 
3088 	if (where == VW_POS_TOP_CENTER)
3089 	    pos_y = yP + 2;
3090 	else
3091 	    /* where == VW_POS_CENTER */
3092 	    pos_y = yP + (hP - c_size.height) / 2;
3093     }
3094 
3095     /* now, make sure the window will be inside the Vim window... */
3096     if (pos_x < xP)
3097 	pos_x = xP + 2;
3098     if (pos_y < yP)
3099 	pos_y = yP + 2;
3100     if ((pos_x + c_size.width) > (wP + xP))
3101 	pos_x = xP + wP - c_size.width - 2;
3102     /* Assume 'guiheadroom' indicates the title bar height... */
3103     if ((pos_y + c_size.height + p_ghr / 2) > (hP + yP))
3104 	pos_y = yP + hP - c_size.height - 2 - p_ghr / 2;
3105 
3106     gtk_widget_set_uposition(child, pos_x, pos_y);
3107 }
3108 
3109 #endif /* !HAVE_GTK2 */
3110