xref: /vim-8.2.3635/src/gui_gtk.c (revision 3fdfa4a9)
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 #endif	/* FEAT_BROWSE */
1352 
1353 #if (defined(FEAT_GUI_DIALOG) && !defined(HAVE_GTK2)) || defined(PROTO)
1354 
1355 static char_u *dialog_textfield = NULL;
1356 static GtkWidget *dialog_textentry;
1357 
1358     static void
1359 dlg_destroy(GtkWidget *dlg)
1360 {
1361     if (dialog_textfield != NULL)
1362     {
1363 	const char *text;
1364 
1365 	text = gtk_entry_get_text(GTK_ENTRY(dialog_textentry));
1366 	STRNCPY(dialog_textfield, text, IOSIZE);
1367 	dialog_textfield[IOSIZE - 1] = NUL;
1368     }
1369 
1370     /* Destroy the dialog, will break the waiting loop. */
1371     gtk_widget_destroy(dlg);
1372 }
1373 
1374 # ifdef FEAT_GUI_GNOME
1375 /* ARGSUSED */
1376     static int
1377 gui_gnome_dialog( int	type,
1378 		char_u	*title,
1379 		char_u	*message,
1380 		char_u	*buttons,
1381 		int	dfltbutton,
1382 		char_u	*textfield)
1383 {
1384     GtkWidget	*dlg;
1385     char	*gdtype;
1386     char_u	*buttons_copy, *p, *next;
1387     char	**buttons_list;
1388     int		butcount, cur;
1389 
1390     /* make a copy, so that we can insert NULs */
1391     if ((buttons_copy = vim_strsave(buttons)) == NULL)
1392 	return -1;
1393 
1394     /* determine exact number of buttons and allocate array to hold them */
1395     for (butcount = 0, p = buttons; *p; p++)
1396     {
1397 	if (*p == '\n')
1398 	    butcount++;
1399     }
1400     butcount++;
1401     buttons_list = g_new0(char *, butcount + 1);
1402 
1403     /* Add pixmap */
1404     switch (type)
1405     {
1406     case VIM_ERROR:
1407 	gdtype = GNOME_MESSAGE_BOX_ERROR;
1408 	break;
1409     case VIM_WARNING:
1410 	gdtype = GNOME_MESSAGE_BOX_WARNING;
1411 	break;
1412     case VIM_INFO:
1413 	gdtype = GNOME_MESSAGE_BOX_INFO;
1414 	break;
1415     case VIM_QUESTION:
1416 	gdtype = GNOME_MESSAGE_BOX_QUESTION;
1417 	break;
1418     default:
1419 	gdtype = GNOME_MESSAGE_BOX_GENERIC;
1420     };
1421 
1422     p = buttons_copy;
1423     for (cur = 0; cur < butcount; ++cur)
1424     {
1425 	for (next = p; *next; ++next)
1426 	{
1427 	    if (*next == DLG_HOTKEY_CHAR)
1428 		mch_memmove(next, next + 1, STRLEN(next));
1429 	    if (*next == DLG_BUTTON_SEP)
1430 	    {
1431 		*next++ = NUL;
1432 		break;
1433 	    }
1434 	}
1435 
1436 	/* this should probably go into a table, but oh well */
1437 	if (g_strcasecmp((char *)p, "Ok") == 0)
1438 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_OK);
1439 	else if (g_strcasecmp((char *)p, "Cancel") == 0)
1440 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_CANCEL);
1441 	else if (g_strcasecmp((char *)p, "Yes") == 0)
1442 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_YES);
1443 	else if (g_strcasecmp((char *)p, "No") == 0)
1444 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_NO);
1445 	else if (g_strcasecmp((char *)p, "Close") == 0)
1446 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_CLOSE);
1447 	else if (g_strcasecmp((char *)p, "Help") == 0)
1448 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_HELP);
1449 	else if (g_strcasecmp((char *)p, "Apply") == 0)
1450 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_APPLY);
1451 #if 0
1452 	/*
1453 	 * these aren't really used that often anyway, but are listed here as
1454 	 * placeholders in case we need them.
1455 	 */
1456 	else if (g_strcasecmp((char *)p, "Next") == 0)
1457 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_NEXT);
1458 	else if (g_strcasecmp((char *)p, "Prev") == 0)
1459 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_PREV);
1460 	else if (g_strcasecmp((char *)p, "Up") == 0)
1461 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_UP);
1462 	else if (g_strcasecmp((char *)p, "Down") == 0)
1463 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_DOWN);
1464 	else if (g_strcasecmp((char *)p, "Font") == 0)
1465 	    buttons_list[cur] = g_strdup(GNOME_STOCK_BUTTON_FONT);
1466 #endif
1467 	else
1468 	    buttons_list[cur] = g_strdup((char *)p);
1469 
1470 	if (*next == NUL)
1471 	    break;
1472 
1473 	p = next;
1474     }
1475     vim_free(buttons_copy);
1476 
1477     dlg = gnome_message_box_newv((const char *)message,
1478 				 (const char *)gdtype,
1479 				 (const char **)buttons_list);
1480     for (cur = 0; cur < butcount; ++cur)
1481 	g_free(buttons_list[cur]);
1482     g_free(buttons_list);
1483 
1484     dialog_textfield = textfield;
1485     if (textfield != NULL)
1486     {
1487 	/* Add text entry field */
1488 	dialog_textentry = gtk_entry_new();
1489 	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dlg)->vbox), dialog_textentry,
1490 			   TRUE, TRUE, 0);
1491 	gtk_entry_set_text(GTK_ENTRY(dialog_textentry),
1492 			   (const gchar *)textfield);
1493 	gtk_entry_select_region(GTK_ENTRY(dialog_textentry), 0,
1494 				STRLEN(textfield));
1495 	gtk_entry_set_max_length(GTK_ENTRY(dialog_textentry), IOSIZE - 1);
1496 	gtk_entry_set_position(GTK_ENTRY(dialog_textentry), STRLEN(textfield));
1497 	gtk_widget_show(dialog_textentry);
1498 	gtk_window_set_focus(GTK_WINDOW(dlg), dialog_textentry);
1499     }
1500 
1501     gtk_signal_connect_object(GTK_OBJECT(dlg), "destroy",
1502 			      GTK_SIGNAL_FUNC(dlg_destroy), GTK_OBJECT(dlg));
1503     gnome_dialog_set_default(GNOME_DIALOG(dlg), dfltbutton + 1);
1504     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
1505 			       GTK_WIDGET(dlg), VW_POS_MOUSE);
1506 
1507     return (1 + gnome_dialog_run_and_close(GNOME_DIALOG(dlg)));
1508 }
1509 
1510 # endif /* FEAT_GUI_GNOME */
1511 
1512 typedef struct _ButtonData
1513 {
1514     int		*status;
1515     int		index;
1516     GtkWidget	*dialog;
1517 } ButtonData;
1518 
1519 typedef struct _CancelData
1520 {
1521     int		*status;
1522     int		ignore_enter;
1523     GtkWidget	*dialog;
1524 } CancelData;
1525 
1526 /* ARGSUSED */
1527     static void
1528 dlg_button_clicked(GtkWidget * widget, ButtonData *data)
1529 {
1530     *(data->status) = data->index + 1;
1531     dlg_destroy(data->dialog);
1532 }
1533 
1534 /*
1535  * This makes the Escape key equivalent to the cancel button.
1536  */
1537 /*ARGSUSED*/
1538     static int
1539 dlg_key_press_event(GtkWidget * widget, GdkEventKey * event, CancelData *data)
1540 {
1541     /* Ignore hitting Enter when there is no default button. */
1542     if (data->ignore_enter && event->keyval == GDK_Return)
1543 	return TRUE;
1544 
1545     if (event->keyval != GDK_Escape && event->keyval != GDK_Return)
1546 	return FALSE;
1547 
1548     /* The result value of 0 from a dialog is signaling cancelation.
1549      * 1 means OK. */
1550     *(data->status) = (event->keyval == GDK_Return);
1551     dlg_destroy(data->dialog);
1552 
1553     return TRUE;
1554 }
1555 
1556 /*
1557  * Callback function for when the dialog was destroyed by a window manager.
1558  */
1559     static void
1560 dlg_destroy_cb(int *p)
1561 {
1562     *p = TRUE;		/* set dialog_destroyed to break out of the loop */
1563     if (gtk_main_level() > 0)
1564 	gtk_main_quit();
1565 }
1566 
1567 /* ARGSUSED */
1568     int
1569 gui_mch_dialog(	int	type,		/* type of dialog */
1570 		char_u	*title,		/* title of dialog */
1571 		char_u	*message,	/* message text */
1572 		char_u	*buttons,	/* names of buttons */
1573 		int	def_but,	/* default button */
1574 		char_u	*textfield)	/* text for textfield or NULL */
1575 {
1576     char_u	*names;
1577     char_u	*p;
1578     int		i;
1579     int		butcount;
1580     int		dialog_status = -1;
1581     int		dialog_destroyed = FALSE;
1582     int		vertical;
1583 
1584     GtkWidget		*dialog;
1585     GtkWidget		*frame;
1586     GtkWidget		*vbox;
1587     GtkWidget		*table;
1588     GtkWidget		*dialogmessage;
1589     GtkWidget		*action_area;
1590     GtkWidget		*sub_area;
1591     GtkWidget		*separator;
1592     GtkAccelGroup	*accel_group;
1593     GtkWidget		*pixmap;
1594     GdkPixmap		*icon = NULL;
1595     GdkBitmap		*mask = NULL;
1596     char		**icon_data = NULL;
1597 
1598     GtkWidget		**button;
1599     ButtonData		*data;
1600     CancelData		cancel_data;
1601 
1602     /* if our pointer is currently hidden, then we should show it. */
1603     gui_mch_mousehide(FALSE);
1604 
1605 # ifdef FEAT_GUI_GNOME
1606     /* If Gnome is available, use it for the dialog. */
1607     if (gtk_socket_id == 0)
1608 	return gui_gnome_dialog(type, title, message, buttons, def_but,
1609 								   textfield);
1610 # endif
1611 
1612     if (title == NULL)
1613 	title = (char_u *)_("Vim dialog...");
1614 
1615     if ((type < 0) || (type > VIM_LAST_TYPE))
1616 	type = VIM_GENERIC;
1617 
1618     /* Check 'v' flag in 'guioptions': vertical button placement. */
1619     vertical = (vim_strchr(p_go, GO_VERTICAL) != NULL);
1620 
1621     dialog = gtk_window_new(GTK_WINDOW_DIALOG);
1622     gtk_window_set_title(GTK_WINDOW(dialog), (const gchar *)title);
1623     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
1624     gtk_widget_realize(dialog);
1625     gdk_window_set_decorations(dialog->window, GDK_DECOR_BORDER);
1626     gdk_window_set_functions(dialog->window, GDK_FUNC_MOVE);
1627 
1628     cancel_data.status = &dialog_status;
1629     cancel_data.dialog = dialog;
1630     gtk_signal_connect_after(GTK_OBJECT(dialog), "key_press_event",
1631 		    GTK_SIGNAL_FUNC(dlg_key_press_event),
1632 		    (gpointer) &cancel_data);
1633     /* Catch the destroy signal, otherwise we don't notice a window manager
1634      * destroying the dialog window. */
1635     gtk_signal_connect_object(GTK_OBJECT(dialog), "destroy",
1636 		    GTK_SIGNAL_FUNC(dlg_destroy_cb),
1637 		    (gpointer)&dialog_destroyed);
1638 
1639     gtk_grab_add(dialog);
1640 
1641     /* this makes it look beter on Motif style window managers */
1642     frame = gtk_frame_new(NULL);
1643     gtk_container_add(GTK_CONTAINER(dialog), frame);
1644     gtk_widget_show(frame);
1645 
1646     vbox = gtk_vbox_new(FALSE, 0);
1647     gtk_container_add(GTK_CONTAINER(frame), vbox);
1648     gtk_widget_show(vbox);
1649 
1650     table = gtk_table_new(1, 3, FALSE);
1651     gtk_table_set_row_spacings(GTK_TABLE(table), 4);
1652     gtk_table_set_col_spacings(GTK_TABLE(table), 8);
1653     gtk_container_border_width(GTK_CONTAINER(table), 4);
1654     gtk_box_pack_start(GTK_BOX(vbox), table, 4, 4, 0);
1655     gtk_widget_show(table);
1656 
1657     /* Add pixmap */
1658     switch (type)
1659     {
1660     case VIM_GENERIC:
1661 	icon_data = generic_xpm;
1662 	break;
1663     case VIM_ERROR:
1664 	icon_data = error_xpm;
1665 	break;
1666     case VIM_WARNING:
1667 	icon_data = alert_xpm;
1668 	break;
1669     case VIM_INFO:
1670 	icon_data = info_xpm;
1671 	break;
1672     case VIM_QUESTION:
1673 	icon_data = quest_xpm;
1674 	break;
1675     default:
1676 	icon_data = generic_xpm;
1677     };
1678     icon = gdk_pixmap_colormap_create_from_xpm_d(NULL,
1679 				     gtk_widget_get_colormap(dialog),
1680 				     &mask, NULL, icon_data);
1681     if (icon)
1682     {
1683 	pixmap = gtk_pixmap_new(icon, mask);
1684 	/* gtk_misc_set_alignment(GTK_MISC(pixmap), 0.5, 0.5); */
1685 	gtk_table_attach_defaults(GTK_TABLE(table), pixmap, 0, 1, 0, 1);
1686 	gtk_widget_show(pixmap);
1687     }
1688 
1689     /* Add label */
1690     dialogmessage = gtk_label_new((const gchar *)message);
1691     gtk_table_attach_defaults(GTK_TABLE(table), dialogmessage, 1, 2, 0, 1);
1692     gtk_widget_show(dialogmessage);
1693 
1694     dialog_textfield = textfield;
1695     if (textfield != NULL)
1696     {
1697 	/* Add text entry field */
1698 	dialog_textentry = gtk_entry_new();
1699 	gtk_widget_set_usize(dialog_textentry, 400, -2);
1700 	gtk_box_pack_start(GTK_BOX(vbox), dialog_textentry, TRUE, TRUE, 0);
1701 	gtk_entry_set_text(GTK_ENTRY(dialog_textentry),
1702 						    (const gchar *)textfield);
1703 	gtk_entry_select_region(GTK_ENTRY(dialog_textentry), 0,
1704 							   STRLEN(textfield));
1705 	gtk_entry_set_max_length(GTK_ENTRY(dialog_textentry), IOSIZE - 1);
1706 	gtk_entry_set_position(GTK_ENTRY(dialog_textentry), STRLEN(textfield));
1707 	gtk_widget_show(dialog_textentry);
1708     }
1709 
1710     /* Add box for buttons */
1711     action_area = gtk_hbox_new(FALSE, 0);
1712     gtk_container_border_width(GTK_CONTAINER(action_area), 4);
1713     gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0);
1714     gtk_widget_show(action_area);
1715 
1716     /* Add a [vh]box in the hbox to center the buttons in the dialog. */
1717     if (vertical)
1718 	sub_area = gtk_vbox_new(FALSE, 0);
1719     else
1720 	sub_area = gtk_hbox_new(FALSE, 0);
1721     gtk_container_set_border_width(GTK_CONTAINER(sub_area), 0);
1722     gtk_box_pack_start(GTK_BOX(action_area), sub_area, TRUE, FALSE, 0);
1723     gtk_widget_show(sub_area);
1724 
1725     /*
1726      * Create the buttons.
1727      */
1728 
1729     /*
1730      * Translate the Vim accelerator character into an underscore for GTK+.
1731      * Double underscores to keep them in the label.
1732      */
1733     /* count the number of underscores */
1734     i = 1;
1735     for (p = buttons; *p; ++p)
1736 	if (*p == '_')
1737 	    ++i;
1738 
1739     /* make a copy of "buttons" with the translated characters */
1740     names = alloc(STRLEN(buttons) + i);
1741     if (names == NULL)
1742 	return -1;
1743 
1744     p = names;
1745     for (i = 0; buttons[i]; ++i)
1746     {
1747 	if (buttons[i] == DLG_HOTKEY_CHAR)
1748 	    *p++ = '_';
1749 	else
1750 	{
1751 	    if (buttons[i] == '_')
1752 		*p++ = '_';
1753 	    *p++ = buttons[i];
1754 	}
1755     }
1756     *p = NUL;
1757 
1758     /* Count the number of buttons and allocate button[] and data[]. */
1759     butcount = 1;
1760     for (p = names; *p; ++p)
1761 	if (*p == DLG_BUTTON_SEP)
1762 	    ++butcount;
1763     button = (GtkWidget **)alloc((unsigned)(butcount * sizeof(GtkWidget *)));
1764     data = (ButtonData *)alloc((unsigned)(butcount * sizeof(ButtonData)));
1765     if (button == NULL || data == NULL)
1766     {
1767 	vim_free(names);
1768 	vim_free(button);
1769 	vim_free(data);
1770 	return -1;
1771     }
1772 
1773     /* Attach the new accelerator group to the window. */
1774     accel_group = gtk_accel_group_new();
1775     gtk_accel_group_attach(accel_group, GTK_OBJECT(dialog));
1776 
1777     p = names;
1778     for (butcount = 0; *p; ++butcount)
1779     {
1780 	char_u		*next;
1781 	GtkWidget	*label;
1782 # ifdef GTK_USE_ACCEL
1783 	guint		accel_key;
1784 # endif
1785 
1786 	/* Chunk out this single button. */
1787 	for (next = p; *next; ++next)
1788 	{
1789 	    if (*next == DLG_BUTTON_SEP)
1790 	    {
1791 		*next++ = NUL;
1792 		break;
1793 	    }
1794 	}
1795 
1796 	button[butcount] = gtk_button_new();
1797 	GTK_WIDGET_SET_FLAGS(button[butcount], GTK_CAN_DEFAULT);
1798 
1799 	label = gtk_accel_label_new("");
1800 	gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), dialog);
1801 
1802 # ifdef GTK_USE_ACCEL
1803 	accel_key = gtk_label_parse_uline(GTK_LABEL(label), (const gchar *)p);
1804 	/* Don't add accelator if 'winaltkeys' is "no". */
1805 	if (accel_key != GDK_VoidSymbol)
1806 	{
1807 	    gtk_widget_add_accelerator(button[butcount],
1808 		    "clicked",
1809 		    accel_group,
1810 		    accel_key, 0,
1811 		    0);
1812 	}
1813 # else
1814 	(void)gtk_label_parse_uline(GTK_LABEL(label), (const gchar *)p);
1815 # endif
1816 
1817 	gtk_container_add(GTK_CONTAINER(button[butcount]), label);
1818 	gtk_widget_show_all(button[butcount]);
1819 
1820 	data[butcount].status = &dialog_status;
1821 	data[butcount].index = butcount;
1822 	data[butcount].dialog = dialog;
1823 	gtk_signal_connect(GTK_OBJECT(button[butcount]),
1824 			   (const char *)"clicked",
1825 			   GTK_SIGNAL_FUNC(dlg_button_clicked),
1826 			   (gpointer) &data[butcount]);
1827 
1828 	gtk_box_pack_start(GTK_BOX(sub_area), button[butcount],
1829 			   TRUE, FALSE, 0);
1830 	p = next;
1831     }
1832 
1833     vim_free(names);
1834 
1835     cancel_data.ignore_enter = FALSE;
1836     if (butcount > 0)
1837     {
1838 	--def_but;		/* 1 is first button */
1839 	if (def_but >= butcount)
1840 	    def_but = -1;
1841 	if (def_but >= 0)
1842 	{
1843 	    gtk_widget_grab_focus(button[def_but]);
1844 	    gtk_widget_grab_default(button[def_but]);
1845 	}
1846 	else
1847 	    /* No default, ignore hitting Enter. */
1848 	    cancel_data.ignore_enter = TRUE;
1849     }
1850 
1851     if (textfield != NULL)
1852 	gtk_window_set_focus(GTK_WINDOW(dialog), dialog_textentry);
1853 
1854     separator = gtk_hseparator_new();
1855     gtk_box_pack_end(GTK_BOX(vbox), separator, FALSE, TRUE, 0);
1856     gtk_widget_show(separator);
1857 
1858     dialog_status = -1;
1859 
1860     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
1861 					    GTK_WIDGET(dialog), VW_POS_MOUSE);
1862 
1863     gtk_widget_show(dialog);
1864 
1865     /* loop here until the dialog goes away */
1866     while (dialog_status == -1 && !dialog_destroyed
1867 					       && GTK_WIDGET_DRAWABLE(dialog))
1868 	gtk_main_iteration_do(TRUE);
1869 
1870     if (dialog_status < 0)
1871 	dialog_status = 0;
1872     if (dialog_status != 1 && textfield != NULL)
1873 	*textfield = NUL;	/* dialog was cancelled */
1874 
1875     /* let the garbage collector know that we don't need it any longer */
1876     gtk_accel_group_unref(accel_group);
1877 
1878     vim_free(button);
1879     vim_free(data);
1880 
1881     return dialog_status;
1882 }
1883 
1884 #endif /* FEAT_GUI_DIALOG && !HAVE_GTK2 */
1885 
1886 
1887 #if defined(FEAT_GUI_DIALOG) && defined(HAVE_GTK2)
1888 
1889     static GtkWidget *
1890 create_message_dialog(int type, char_u *title, char_u *message)
1891 {
1892     GtkWidget	    *dialog;
1893     GtkMessageType  message_type;
1894 
1895     switch (type)
1896     {
1897 	case VIM_ERROR:	    message_type = GTK_MESSAGE_ERROR;	 break;
1898 	case VIM_WARNING:   message_type = GTK_MESSAGE_WARNING;	 break;
1899 	case VIM_QUESTION:  message_type = GTK_MESSAGE_QUESTION; break;
1900 	default:	    message_type = GTK_MESSAGE_INFO;	 break;
1901     }
1902 
1903     message = CONVERT_TO_UTF8(message);
1904     dialog  = gtk_message_dialog_new(GTK_WINDOW(gui.mainwin),
1905 				     GTK_DIALOG_DESTROY_WITH_PARENT,
1906 				     message_type,
1907 				     GTK_BUTTONS_NONE,
1908 				     "%s", (const char *)message);
1909     CONVERT_TO_UTF8_FREE(message);
1910 
1911     if (title != NULL)
1912     {
1913 	title = CONVERT_TO_UTF8(title);
1914 	gtk_window_set_title(GTK_WINDOW(dialog), (const char *)title);
1915 	CONVERT_TO_UTF8_FREE(title);
1916     }
1917     else if (type == VIM_GENERIC)
1918     {
1919 	gtk_window_set_title(GTK_WINDOW(dialog), "VIM");
1920     }
1921 
1922     return dialog;
1923 }
1924 
1925 /*
1926  * Split up button_string into individual button labels by inserting
1927  * NUL bytes.  Also replace the Vim-style mnemonic accelerator prefix
1928  * '&' with '_'.  button_string must point to allocated memory!
1929  * Return an allocated array of pointers into button_string.
1930  */
1931     static char **
1932 split_button_string(char_u *button_string, int *n_buttons)
1933 {
1934     char	    **array;
1935     char_u	    *p;
1936     unsigned int    count = 1;
1937 
1938     for (p = button_string; *p != NUL; ++p)
1939 	if (*p == DLG_BUTTON_SEP)
1940 	    ++count;
1941 
1942     array = (char **)alloc((count + 1) * sizeof(char *));
1943     count = 0;
1944 
1945     if (array != NULL)
1946     {
1947 	array[count++] = (char *)button_string;
1948 	for (p = button_string; *p != NUL; ++p)
1949 	{
1950 	    if (*p == DLG_BUTTON_SEP)
1951 	    {
1952 		*p = NUL;
1953 		array[count++] = (char *)p + 1;
1954 	    }
1955 	    else if (*p == DLG_HOTKEY_CHAR)
1956 		*p = '_';
1957 #ifdef FEAT_MBYTE
1958 	    else if (has_mbyte)
1959 		p += (*mb_ptr2len_check)(p) - 1;
1960 #endif
1961 	}
1962 	array[count] = NULL; /* currently not relied upon, but doesn't hurt */
1963     }
1964 
1965     *n_buttons = count;
1966     return array;
1967 }
1968 
1969     static char **
1970 split_button_translation(const char *message)
1971 {
1972     char    **buttons = NULL;
1973     char_u  *str;
1974     int	    n_buttons = 0;
1975     int	    n_expected = 1;
1976 
1977     for (str = (char_u *)message; *str != NUL; ++str)
1978 	if (*str == DLG_BUTTON_SEP)
1979 	    ++n_expected;
1980 
1981     str = (char_u *)_(message);
1982     if (str != NULL)
1983     {
1984 	if (output_conv.vc_type != CONV_NONE)
1985 	    str = string_convert(&output_conv, str, NULL);
1986 	else
1987 	    str = vim_strsave(str);
1988 
1989 	if (str != NULL)
1990 	    buttons = split_button_string(str, &n_buttons);
1991     }
1992     /*
1993      * Uh-oh... this should never ever happen.	But we don't wanna crash
1994      * if the translation is broken, thus fall back to the untranslated
1995      * buttons string in case of emergency.
1996      */
1997     if (buttons == NULL || n_buttons != n_expected)
1998     {
1999 	vim_free(buttons);
2000 	vim_free(str);
2001 	buttons = NULL;
2002 	str = vim_strsave((char_u *)message);
2003 
2004 	if (str != NULL)
2005 	    buttons = split_button_string(str, &n_buttons);
2006 	if (buttons == NULL)
2007 	    vim_free(str);
2008     }
2009 
2010     return buttons;
2011 }
2012 
2013     static int
2014 button_equal(const char *a, const char *b)
2015 {
2016     while (*a != '\0' && *b != '\0')
2017     {
2018 	if (*a == '_' && *++a == '\0')
2019 	    break;
2020 	if (*b == '_' && *++b == '\0')
2021 	    break;
2022 
2023 	if (g_unichar_tolower(g_utf8_get_char(a))
2024 		!= g_unichar_tolower(g_utf8_get_char(b)))
2025 	    return FALSE;
2026 
2027 	a = g_utf8_next_char(a);
2028 	b = g_utf8_next_char(b);
2029     }
2030 
2031     return (*a == '\0' && *b == '\0');
2032 }
2033 
2034     static void
2035 dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
2036 {
2037     char    **ok;
2038     char    **ync;  /* "yes no cancel" */
2039     char    **buttons;
2040     int	    n_buttons = 0;
2041     int	    index;
2042 
2043     button_string = vim_strsave(button_string); /* must be writable */
2044     if (button_string == NULL)
2045 	return;
2046 
2047     /* Check 'v' flag in 'guioptions': vertical button placement. */
2048     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
2049     {
2050 	GtkWidget	*vbutton_box;
2051 
2052 	vbutton_box = gtk_vbutton_box_new();
2053 	gtk_widget_show(vbutton_box);
2054 	gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2055 						 vbutton_box, TRUE, FALSE, 0);
2056 	/* Overrule the "action_area" value, hopefully this works... */
2057 	GTK_DIALOG(dialog)->action_area = vbutton_box;
2058     }
2059 
2060     /*
2061      * Yes this is ugly, I don't particularly like it either.  But doing it
2062      * this way has the compelling advantage that translations need not to
2063      * be touched at all.  See below what 'ok' and 'ync' are used for.
2064      */
2065     ok	    = split_button_translation(N_("&Ok"));
2066     ync     = split_button_translation(N_("&Yes\n&No\n&Cancel"));
2067     buttons = split_button_string(button_string, &n_buttons);
2068 
2069     /*
2070      * Yes, the buttons are in reversed order to match the GNOME 2 desktop
2071      * environment.  Don't hit me -- it's all about consistency.
2072      * Well, apparently somebody changed his mind: with GTK 2.2.4 it works the
2073      * other way around...
2074      */
2075     for (index = 1; index <= n_buttons; ++index)
2076     {
2077 	char	*label;
2078 	char_u	*label8;
2079 
2080 	label = buttons[index - 1];
2081 	/*
2082 	 * Perform some guesswork to find appropriate stock items for the
2083 	 * buttons.  We have to compare with a sample of the translated
2084 	 * button string to get things right.  Yes, this is hackish :/
2085 	 *
2086 	 * But even the common button labels aren't necessarily translated,
2087 	 * since anyone can create their own dialogs using Vim functions.
2088 	 * Thus we have to check for those too.
2089 	 */
2090 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
2091 	{
2092 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
2093 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
2094 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
2095 	    else if (button_equal(label, ync[2]))   label = GTK_STOCK_CANCEL;
2096 	    else if (button_equal(label, "Ok"))     label = GTK_STOCK_OK;
2097 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
2098 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
2099 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
2100 	}
2101 	label8 = CONVERT_TO_UTF8((char_u *)label);
2102 	gtk_dialog_add_button(dialog, (const gchar *)label8, index);
2103 	CONVERT_TO_UTF8_FREE(label8);
2104     }
2105 
2106     if (ok != NULL)
2107 	vim_free(*ok);
2108     if (ync != NULL)
2109 	vim_free(*ync);
2110     vim_free(ok);
2111     vim_free(ync);
2112     vim_free(buttons);
2113     vim_free(button_string);
2114 }
2115 
2116 /*
2117  * Allow mnemonic accelerators to be activated without pressing <Alt>.
2118  * I'm not sure if it's a wise idea to do this.  However, the old GTK+ 1.2
2119  * GUI used to work this way, and I consider the impact on UI consistency
2120  * low enough to justify implementing this as a special Vim feature.
2121  */
2122 typedef struct _DialogInfo
2123 {
2124     int		ignore_enter;	    /* no default button, ignore "Enter" */
2125     int		noalt;		    /* accept accelerators without Alt */
2126     GtkDialog	*dialog;	    /* Widget of the dialog */
2127 } DialogInfo;
2128 
2129 /*ARGSUSED2*/
2130     static gboolean
2131 dialog_key_press_event_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
2132 {
2133     DialogInfo *di = (DialogInfo *)data;
2134 
2135     /* Ignore hitting "Enter" if there is no default button. */
2136     if (di->ignore_enter && event->keyval == GDK_Return)
2137 	return TRUE;
2138 
2139     /* Close the dialog when hitting "Esc". */
2140     if (event->keyval == GDK_Escape)
2141     {
2142 	gtk_dialog_response(di->dialog, GTK_RESPONSE_REJECT);
2143 	return TRUE;
2144     }
2145 
2146     if (di->noalt
2147 	      && (event->state & gtk_accelerator_get_default_mod_mask()) == 0)
2148     {
2149 	return gtk_window_mnemonic_activate(
2150 		   GTK_WINDOW(widget), event->keyval,
2151 		   gtk_window_get_mnemonic_modifier(GTK_WINDOW(widget)));
2152     }
2153 
2154     return FALSE; /* continue emission */
2155 }
2156 
2157     int
2158 gui_mch_dialog(int	type,	    /* type of dialog */
2159 	       char_u	*title,	    /* title of dialog */
2160 	       char_u	*message,   /* message text */
2161 	       char_u	*buttons,   /* names of buttons */
2162 	       int	def_but,    /* default button */
2163 	       char_u	*textfield) /* text for textfield or NULL */
2164 {
2165     GtkWidget	*dialog;
2166     GtkWidget	*entry = NULL;
2167     char_u	*text;
2168     int		response;
2169     DialogInfo  dialoginfo;
2170 
2171     dialog = create_message_dialog(type, title, message);
2172     dialoginfo.dialog = GTK_DIALOG(dialog);
2173     dialog_add_buttons(GTK_DIALOG(dialog), buttons);
2174 
2175     if (textfield != NULL)
2176     {
2177 	GtkWidget *alignment;
2178 
2179 	entry = gtk_entry_new();
2180 	gtk_widget_show(entry);
2181 
2182 	text = CONVERT_TO_UTF8(textfield);
2183 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
2184 	CONVERT_TO_UTF8_FREE(text);
2185 
2186 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
2187 						      (float)1.0, (float)1.0);
2188 	gtk_container_add(GTK_CONTAINER(alignment), entry);
2189 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
2190 	gtk_widget_show(alignment);
2191 
2192 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2193 			   alignment, TRUE, FALSE, 0);
2194 	dialoginfo.noalt = FALSE;
2195     }
2196     else
2197 	dialoginfo.noalt = TRUE;
2198 
2199     /* Allow activation of mnemonic accelerators without pressing <Alt> when
2200      * there is no textfield.  Handle pressing Enter and Esc. */
2201     g_signal_connect(G_OBJECT(dialog), "key_press_event",
2202 			 G_CALLBACK(&dialog_key_press_event_cb), &dialoginfo);
2203 
2204     if (def_but > 0)
2205     {
2206 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), def_but);
2207 	dialoginfo.ignore_enter = FALSE;
2208     }
2209     else
2210 	/* No default button, ignore pressing Enter. */
2211 	dialoginfo.ignore_enter = TRUE;
2212 
2213     /* Show the mouse pointer if it's currently hidden. */
2214     gui_mch_mousehide(FALSE);
2215 
2216     response = gtk_dialog_run(GTK_DIALOG(dialog));
2217 
2218     /* GTK_RESPONSE_NONE means the dialog was programmatically destroyed. */
2219     if (response != GTK_RESPONSE_NONE)
2220     {
2221 	if (textfield != NULL)
2222 	{
2223 	    text = (char_u *)gtk_entry_get_text(GTK_ENTRY(entry));
2224 	    text = CONVERT_FROM_UTF8(text);
2225 
2226 	    STRNCPY(textfield, text, IOSIZE);
2227 	    textfield[IOSIZE - 1] = NUL;
2228 
2229 	    CONVERT_FROM_UTF8_FREE(text);
2230 	}
2231 	gtk_widget_destroy(dialog);
2232     }
2233 
2234     /* Terrible hack: When the text area still has focus when we remove the
2235      * dialog, somehow gvim loses window focus.  This is with "point to type"
2236      * in the KDE 3.1 window manager.  Warp the mouse pointer to outside the
2237      * window and back to avoid that. */
2238     if (!gui.in_focus)
2239     {
2240 	int x, y;
2241 
2242 	gdk_window_get_pointer(gui.drawarea->window, &x, &y, NULL);
2243 	gui_mch_setmouse(-100, -100);
2244 	gui_mch_setmouse(x, y);
2245     }
2246 
2247     return response > 0 ? response : 0;
2248 }
2249 
2250 #endif /* FEAT_GUI_DIALOG && HAVE_GTK2 */
2251 
2252 
2253 #if defined(FEAT_MENU) || defined(PROTO)
2254 
2255     void
2256 gui_mch_show_popupmenu(vimmenu_T *menu)
2257 {
2258 # if defined(FEAT_XIM) && defined(HAVE_GTK2)
2259     /*
2260      * Append a submenu for selecting an input method.	This is
2261      * currently the only way to switch input methods at runtime.
2262      */
2263     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
2264 					 "vim-has-im-menu") == NULL)
2265     {
2266 	GtkWidget   *menuitem;
2267 	GtkWidget   *submenu;
2268 	char_u	    *name;
2269 
2270 	menuitem = gtk_separator_menu_item_new();
2271 	gtk_widget_show(menuitem);
2272 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
2273 
2274 	name = (char_u *)_("Input _Methods");
2275 	name = CONVERT_TO_UTF8(name);
2276 	menuitem = gtk_menu_item_new_with_mnemonic((const char *)name);
2277 	CONVERT_TO_UTF8_FREE(name);
2278 	gtk_widget_show(menuitem);
2279 
2280 	submenu = gtk_menu_new();
2281 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
2282 	gtk_menu_shell_append(GTK_MENU_SHELL(menu->submenu_id), menuitem);
2283 
2284 	gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(xic),
2285 					     GTK_MENU_SHELL(submenu));
2286 	g_object_set_data(G_OBJECT(menu->submenu_id),
2287 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
2288     }
2289 # endif /* FEAT_XIM && HAVE_GTK2 */
2290 
2291     gtk_menu_popup(GTK_MENU(menu->submenu_id),
2292 		   NULL, NULL,
2293 		   (GtkMenuPositionFunc)NULL, NULL,
2294 		   3U, (guint32)GDK_CURRENT_TIME);
2295 }
2296 
2297 /*
2298  * Menu position callback; used by gui_make_popup() to place the menu
2299  * at the current text cursor position.
2300  *
2301  * Note: The push_in output argument seems to affect scrolling of huge
2302  * menus that don't fit on the screen.	Leave it at the default for now.
2303  */
2304 /*ARGSUSED0*/
2305     static void
2306 popup_menu_position_func(GtkMenu *menu,
2307 			 gint *x, gint *y,
2308 # ifdef HAVE_GTK2
2309 			 gboolean *push_in,
2310 # endif
2311 			 gpointer user_data)
2312 {
2313     if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
2314     {
2315 	gdk_window_get_origin(gui.drawarea->window, x, y);
2316 
2317 	/* Find the cursor position in the current window */
2318 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
2319 	*y += FILL_Y(W_WINROW(curwin) + curwin->w_wrow + 1) + 1;
2320     }
2321 }
2322 
2323     void
2324 gui_make_popup(char_u *path_name)
2325 {
2326     vimmenu_T *menu;
2327 
2328     menu = gui_find_menu(path_name);
2329 
2330     if (menu != NULL && menu->submenu_id != NULL)
2331     {
2332 	gtk_menu_popup(GTK_MENU(menu->submenu_id),
2333 		       NULL, NULL,
2334 		       &popup_menu_position_func, NULL,
2335 		       0U, (guint32)GDK_CURRENT_TIME);
2336     }
2337 }
2338 
2339 #endif /* FEAT_MENU */
2340 
2341 
2342 /*
2343  * We don't create it twice.
2344  */
2345 
2346 typedef struct _SharedFindReplace
2347 {
2348     GtkWidget *dialog;	/* the main dialog widget */
2349     GtkWidget *wword;	/* 'Whole word only' check button */
2350     GtkWidget *mcase;	/* 'Match case' check button */
2351     GtkWidget *up;	/* search direction 'Up' radio button */
2352     GtkWidget *down;	/* search direction 'Down' radio button */
2353     GtkWidget *what;	/* 'Find what' entry text widget */
2354     GtkWidget *with;	/* 'Replace with' entry text widget */
2355     GtkWidget *find;	/* 'Find Next' action button */
2356     GtkWidget *replace;	/* 'Replace With' action button */
2357     GtkWidget *all;	/* 'Replace All' action button */
2358 } SharedFindReplace;
2359 
2360 static SharedFindReplace find_widgets = { NULL, };
2361 static SharedFindReplace repl_widgets = { NULL, };
2362 
2363 /* ARGSUSED */
2364     static int
2365 find_key_press_event(
2366 		GtkWidget	*widget,
2367 		GdkEventKey	*event,
2368 		SharedFindReplace *frdp)
2369 {
2370     /* If the user is holding one of the key modifiers we will just bail out,
2371      * thus preserving the possibility of normal focus traversal.
2372      */
2373     if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
2374 	return FALSE;
2375 
2376     /* the Escape key synthesizes a cancellation action */
2377     if (event->keyval == GDK_Escape)
2378     {
2379 	gtk_widget_hide(frdp->dialog);
2380 
2381 	return TRUE;
2382     }
2383     /*
2384      * What the **** is this for?  Disabled for GTK+ 2 because due to
2385      * gtk_signal_connect_after() it doesn't have any effect anyway.
2386      * (Fortunately.)
2387      */
2388 #ifndef HAVE_GTK2
2389     /* block traversal resulting from those keys */
2390     if (event->keyval == GDK_Left
2391 	    || event->keyval == GDK_Right
2392 	    || event->keyval == GDK_space)
2393 	return TRUE;
2394 #endif
2395 
2396     /* It would be delightfull if it where possible to do search history
2397      * operations on the K_UP and K_DOWN keys here.
2398      */
2399 
2400     return FALSE;
2401 }
2402 
2403 #ifdef HAVE_GTK2
2404     static GtkWidget *
2405 create_image_button(const char *stock_id, const char *label)
2406 {
2407     char_u	*text;
2408     GtkWidget	*box;
2409     GtkWidget	*alignment;
2410     GtkWidget	*button;
2411 
2412     text = CONVERT_TO_UTF8((char_u *)label);
2413 
2414     box = gtk_hbox_new(FALSE, 3);
2415     gtk_box_pack_start(GTK_BOX(box),
2416 		       gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
2417 		       FALSE, FALSE, 0);
2418     gtk_box_pack_start(GTK_BOX(box),
2419 		       gtk_label_new((const char *)text),
2420 		       FALSE, FALSE, 0);
2421 
2422     CONVERT_TO_UTF8_FREE(text);
2423 
2424     alignment = gtk_alignment_new((float)0.5, (float)0.5,
2425 						      (float)0.0, (float)0.0);
2426     gtk_container_add(GTK_CONTAINER(alignment), box);
2427     gtk_widget_show_all(alignment);
2428 
2429     button = gtk_button_new();
2430     gtk_container_add(GTK_CONTAINER(button), alignment);
2431 
2432     return button;
2433 }
2434 
2435 /*
2436  * This is currently only used by find_replace_dialog_create(), and
2437  * I'd really like to keep it at that.	In other words: don't spread
2438  * this nasty hack all over the code.  Think twice.
2439  */
2440     static const char *
2441 convert_localized_message(char_u **buffer, const char *message)
2442 {
2443     if (output_conv.vc_type == CONV_NONE)
2444 	return message;
2445 
2446     vim_free(*buffer);
2447     *buffer = string_convert(&output_conv, (char_u *)message, NULL);
2448 
2449     return (const char *)*buffer;
2450 }
2451 #endif /* HAVE_GTK2 */
2452 
2453     static void
2454 find_replace_dialog_create(char_u *arg, int do_replace)
2455 {
2456 #ifndef HAVE_GTK2
2457     GtkWidget	*frame;
2458 #endif
2459     GtkWidget	*hbox;		/* main top down box */
2460     GtkWidget	*actionarea;
2461     GtkWidget	*table;
2462     GtkWidget	*tmp;
2463     GtkWidget	*vbox;
2464     gboolean	sensitive;
2465     SharedFindReplace *frdp;
2466     char_u	*entry_text;
2467     int		wword = FALSE;
2468     int		mcase = !p_ic;
2469 #ifdef HAVE_GTK2
2470     char_u	*conv_buffer = NULL;
2471 #   define CONV(message) convert_localized_message(&conv_buffer, (message))
2472 #else
2473 #   define CONV(message) (message)
2474 #endif
2475 
2476     frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
2477 
2478     /* Get the search string to use. */
2479     entry_text = get_find_dialog_text(arg, &wword, &mcase);
2480 
2481 #ifdef HAVE_GTK2
2482     if (entry_text != NULL && output_conv.vc_type != CONV_NONE)
2483     {
2484 	char_u *old_text = entry_text;
2485 	entry_text = string_convert(&output_conv, entry_text, NULL);
2486 	vim_free(old_text);
2487     }
2488 #endif
2489 
2490     /*
2491      * If the dialog already exists, just raise it.
2492      */
2493     if (frdp->dialog)
2494     {
2495 #ifndef HAVE_GTK2
2496 	/* always make the dialog appear where you want it even if the mainwin
2497 	 * has moved -- dbv */
2498 	gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
2499 				      GTK_WIDGET(frdp->dialog), VW_POS_MOUSE);
2500 	gui_gtk_synch_fonts();
2501 
2502 	if (!GTK_WIDGET_VISIBLE(frdp->dialog))
2503 	{
2504 	    gtk_widget_grab_focus(frdp->what);
2505 	    gtk_widget_show(frdp->dialog);
2506 	}
2507 #endif
2508 	if (entry_text != NULL)
2509 	{
2510 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2511 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2512 							     (gboolean)wword);
2513 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2514 							     (gboolean)mcase);
2515 	}
2516 #ifdef HAVE_GTK2
2517 	gtk_window_present(GTK_WINDOW(frdp->dialog));
2518 #else
2519 	gdk_window_raise(frdp->dialog->window);
2520 #endif
2521 	vim_free(entry_text);
2522 	return;
2523     }
2524 
2525 #ifdef HAVE_GTK2
2526     frdp->dialog = gtk_dialog_new();
2527     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
2528     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
2529     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
2530 #else
2531     frdp->dialog = gtk_window_new(GTK_WINDOW_DIALOG);
2532 #endif
2533 
2534     if (do_replace)
2535     {
2536 #ifndef HAVE_GTK2
2537 	gtk_window_set_wmclass(GTK_WINDOW(frdp->dialog), "searchrepl", "gvim");
2538 #endif
2539 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2540 			     CONV(_("VIM - Search and Replace...")));
2541     }
2542     else
2543     {
2544 #ifndef HAVE_GTK2
2545 	gtk_window_set_wmclass(GTK_WINDOW(frdp->dialog), "search", "gvim");
2546 #endif
2547 	gtk_window_set_title(GTK_WINDOW(frdp->dialog),
2548 			     CONV(_("VIM - Search...")));
2549     }
2550 
2551 #ifndef HAVE_GTK2 /* Utter crack.  Shudder. */
2552     gtk_widget_realize(frdp->dialog);
2553     gdk_window_set_decorations(frdp->dialog->window,
2554 	    GDK_DECOR_TITLE | GDK_DECOR_BORDER | GDK_DECOR_RESIZEH);
2555     gdk_window_set_functions(frdp->dialog->window,
2556 	    GDK_FUNC_RESIZE | GDK_FUNC_MOVE);
2557 #endif
2558 
2559 #ifndef HAVE_GTK2
2560     /* this makes it look better on Motif style window managers */
2561     frame = gtk_frame_new(NULL);
2562     gtk_container_add(GTK_CONTAINER(frdp->dialog), frame);
2563 #endif
2564 
2565     hbox = gtk_hbox_new(FALSE, 0);
2566 #ifdef HAVE_GTK2
2567     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
2568     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
2569 #else
2570     gtk_container_add(GTK_CONTAINER(frame), hbox);
2571 #endif
2572 
2573     if (do_replace)
2574 	table = gtk_table_new(1024, 4, FALSE);
2575     else
2576 	table = gtk_table_new(1024, 3, FALSE);
2577     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
2578     gtk_container_border_width(GTK_CONTAINER(table), 4);
2579 
2580     tmp = gtk_label_new(CONV(_("Find what:")));
2581     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2582     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
2583 		     GTK_FILL, GTK_EXPAND, 2, 2);
2584     frdp->what = gtk_entry_new();
2585     sensitive = (entry_text != NULL && entry_text[0] != NUL);
2586     if (entry_text != NULL)
2587 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
2588     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
2589 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
2590     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
2591 				 GTK_SIGNAL_FUNC(find_key_press_event),
2592 				 (gpointer) frdp);
2593     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
2594 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2595 
2596     if (do_replace)
2597     {
2598 	tmp = gtk_label_new(CONV(_("Replace with:")));
2599 	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
2600 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
2601 			 GTK_FILL, GTK_EXPAND, 2, 2);
2602 	frdp->with = gtk_entry_new();
2603 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
2604 			   GTK_SIGNAL_FUNC(find_replace_cb),
2605 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
2606 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
2607 				 GTK_SIGNAL_FUNC(find_key_press_event),
2608 				 (gpointer) frdp);
2609 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
2610 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
2611 
2612 	/*
2613 	 * Make the entry activation only change the input focus onto the
2614 	 * with item.
2615 	 */
2616 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2617 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
2618     }
2619     else
2620     {
2621 	/*
2622 	 * Make the entry activation do the search.
2623 	 */
2624 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
2625 			   GTK_SIGNAL_FUNC(find_replace_cb),
2626 			   GINT_TO_POINTER(FRD_FINDNEXT));
2627     }
2628 
2629     /* whole word only button */
2630     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
2631     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
2632 							(gboolean)wword);
2633     if (do_replace)
2634 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
2635 			 GTK_FILL, GTK_EXPAND, 2, 2);
2636     else
2637 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
2638 			 GTK_FILL, GTK_EXPAND, 2, 2);
2639 
2640     /* match case button */
2641     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
2642     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
2643 							     (gboolean)mcase);
2644     if (do_replace)
2645 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
2646 			 GTK_FILL, GTK_EXPAND, 2, 2);
2647     else
2648 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
2649 			 GTK_FILL, GTK_EXPAND, 2, 2);
2650 
2651     tmp = gtk_frame_new(CONV(_("Direction")));
2652     if (do_replace)
2653 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
2654 			 GTK_FILL, GTK_FILL, 2, 2);
2655     else
2656 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
2657 			 GTK_FILL, GTK_FILL, 2, 2);
2658     vbox = gtk_vbox_new(FALSE, 0);
2659     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
2660     gtk_container_add(GTK_CONTAINER(tmp), vbox);
2661 
2662     /* 'Up' and 'Down' buttons */
2663     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
2664     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
2665     frdp->down = gtk_radio_button_new_with_label(
2666 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
2667 			CONV(_("Down")));
2668     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
2669 #ifdef HAVE_GTK2
2670     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
2671 #endif
2672     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
2673 
2674     /* vbox to hold the action buttons */
2675     actionarea = gtk_vbutton_box_new();
2676     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
2677 #ifndef HAVE_GTK2
2678     if (do_replace)
2679     {
2680 	gtk_button_box_set_layout(GTK_BUTTON_BOX(actionarea),
2681 				  GTK_BUTTONBOX_END);
2682 	gtk_button_box_set_spacing(GTK_BUTTON_BOX(actionarea), 0);
2683     }
2684 #endif
2685     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
2686 
2687     /* 'Find Next' button */
2688 #ifdef HAVE_GTK2
2689     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
2690 #else
2691     frdp->find = gtk_button_new_with_label(_("Find Next"));
2692 #endif
2693     gtk_widget_set_sensitive(frdp->find, sensitive);
2694 
2695     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
2696 		       GTK_SIGNAL_FUNC(find_replace_cb),
2697 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
2698 				    : GINT_TO_POINTER(FRD_FINDNEXT));
2699 
2700     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
2701     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
2702     gtk_widget_grab_default(frdp->find);
2703 
2704     if (do_replace)
2705     {
2706 	/* 'Replace' button */
2707 #ifdef HAVE_GTK2
2708 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
2709 #else
2710 	frdp->replace = gtk_button_new_with_label(_("Replace"));
2711 #endif
2712 	gtk_widget_set_sensitive(frdp->replace, sensitive);
2713 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
2714 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
2715 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
2716 			   GTK_SIGNAL_FUNC(find_replace_cb),
2717 			   GINT_TO_POINTER(FRD_REPLACE));
2718 
2719 	/* 'Replace All' button */
2720 #ifdef HAVE_GTK2
2721 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
2722 #else
2723 	frdp->all = gtk_button_new_with_label(_("Replace All"));
2724 #endif
2725 	gtk_widget_set_sensitive(frdp->all, sensitive);
2726 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
2727 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
2728 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
2729 			   GTK_SIGNAL_FUNC(find_replace_cb),
2730 			   GINT_TO_POINTER(FRD_REPLACEALL));
2731     }
2732 
2733     /* 'Cancel' button */
2734 #ifdef HAVE_GTK2
2735     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
2736 #else
2737     tmp = gtk_button_new_with_label(_("Cancel"));
2738 #endif
2739     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
2740     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
2741     gtk_signal_connect_object(GTK_OBJECT(tmp),
2742 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
2743 			      GTK_OBJECT(frdp->dialog));
2744     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
2745 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
2746 			      GTK_OBJECT(frdp->dialog));
2747 
2748     tmp = gtk_vseparator_new();
2749 #ifdef HAVE_GTK2
2750     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
2751 #else
2752     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, TRUE, 0);
2753 #endif
2754 
2755 #ifndef HAVE_GTK2
2756     gtk_widget_grab_focus(frdp->what);
2757 
2758     /* show the frame and realize the frdp->dialog this gives us a window size
2759      * request that we'll use to position the window within the boundary of
2760      * the mainwin --dbv */
2761     gtk_widget_show_all(frame);
2762     gui_gtk_position_in_parent(GTK_WIDGET(gui.mainwin),
2763 				      GTK_WIDGET(frdp->dialog), VW_POS_MOUSE);
2764     gui_gtk_synch_fonts();
2765     gtk_widget_show_all(frdp->dialog);
2766 #endif
2767 
2768 #ifdef HAVE_GTK2
2769     /* Suppress automatic show of the unused action area */
2770     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
2771     gtk_widget_show_all(hbox);
2772     gtk_widget_show(frdp->dialog);
2773 #endif
2774 
2775     vim_free(entry_text);
2776 #ifdef HAVE_GTK2
2777     vim_free(conv_buffer);
2778 #endif
2779 #undef CONV
2780 }
2781 
2782     void
2783 gui_mch_find_dialog(exarg_T *eap)
2784 {
2785     if (gui.in_use)
2786 	find_replace_dialog_create(eap->arg, FALSE);
2787 }
2788 
2789     void
2790 gui_mch_replace_dialog(exarg_T *eap)
2791 {
2792     if (gui.in_use)
2793 	find_replace_dialog_create(eap->arg, TRUE);
2794 }
2795 
2796 
2797 #if !defined(HAVE_GTK2) || defined(PROTO)
2798 /*
2799  * Synchronize all gui elements, which are dependant upon the
2800  * main text font used. Those are in esp. the find/replace dialogs.
2801  * If you don't understand why this should be needed, please try to
2802  * search for "pięść" in iso8859-2.
2803  *
2804  * (<danielk> I converted the comment above to UTF-8 to put
2805  *  a stopper to the encoding mess.  Forgive me :)
2806  *
2807  * Obsolete with GTK2.
2808  */
2809     void
2810 gui_gtk_synch_fonts(void)
2811 {
2812     SharedFindReplace *frdp;
2813     int do_replace;
2814 
2815     /* OK this loop is a bit tricky... */
2816     for (do_replace = 0; do_replace <= 1; ++do_replace)
2817     {
2818 	frdp = (do_replace) ? (&repl_widgets) : (&find_widgets);
2819 	if (frdp->dialog)
2820 	{
2821 	    GtkStyle *style;
2822 
2823 	    /* synch the font with whats used by the text itself */
2824 	    style = gtk_style_copy(gtk_widget_get_style(frdp->what));
2825 	    gdk_font_unref(style->font);
2826 # ifdef FEAT_XFONTSET
2827 	    if (gui.fontset != NOFONTSET)
2828 		style->font = gui.fontset;
2829 	    else
2830 # endif
2831 		style->font = gui.norm_font;
2832 	    gdk_font_ref(style->font);
2833 	    gtk_widget_set_style(frdp->what, style);
2834 	    gtk_style_unref(style);
2835 	    if (do_replace)
2836 	    {
2837 		style = gtk_style_copy(gtk_widget_get_style(frdp->with));
2838 		gdk_font_unref(style->font);
2839 # ifdef FEAT_XFONTSET
2840 		if (gui.fontset != NOFONTSET)
2841 		    style->font = gui.fontset;
2842 		else
2843 # endif
2844 		    style->font = gui.norm_font;
2845 		gdk_font_ref(style->font);
2846 		gtk_widget_set_style(frdp->with, style);
2847 		gtk_style_unref(style);
2848 	    }
2849 	}
2850     }
2851 }
2852 #endif /* !HAVE_GTK2 */
2853 
2854 
2855 /*
2856  * Callback for actions of the find and replace dialogs
2857  */
2858 /*ARGSUSED*/
2859     static void
2860 find_replace_cb(GtkWidget *widget, gpointer data)
2861 {
2862     int			flags;
2863     char_u		*find_text;
2864     char_u		*repl_text;
2865     gboolean		direction_down;
2866     SharedFindReplace	*sfr;
2867     int			rc;
2868 
2869     flags = (int)(long)data;	    /* avoid a lint warning here */
2870 
2871     /* Get the search/replace strings from the dialog */
2872     if (flags == FRD_FINDNEXT)
2873     {
2874 	repl_text = NULL;
2875 	sfr = &find_widgets;
2876     }
2877     else
2878     {
2879 	repl_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(repl_widgets.with));
2880 	sfr = &repl_widgets;
2881     }
2882 
2883     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
2884     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
2885 
2886     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
2887 	flags |= FRD_WHOLE_WORD;
2888     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
2889 	flags |= FRD_MATCH_CASE;
2890 
2891 #ifdef HAVE_GTK2
2892     repl_text = CONVERT_FROM_UTF8(repl_text);
2893     find_text = CONVERT_FROM_UTF8(find_text);
2894 #endif
2895     rc = gui_do_findrepl(flags, find_text, repl_text, direction_down);
2896 #ifdef HAVE_GTK2
2897     CONVERT_FROM_UTF8_FREE(repl_text);
2898     CONVERT_FROM_UTF8_FREE(find_text);
2899 #endif
2900 
2901     if (rc && gtk_main_level() > 0)
2902 	gtk_main_quit(); /* make sure cmd will be handled immediately */
2903 }
2904 
2905 /* our usual callback function */
2906 /*ARGSUSED*/
2907     static void
2908 entry_activate_cb(GtkWidget *widget, gpointer data)
2909 {
2910     gtk_widget_grab_focus(GTK_WIDGET(data));
2911 }
2912 
2913 /*
2914  * Syncing the find/replace dialogs on the fly is utterly useless crack,
2915  * and causes nothing but problems.  Please tell me a use case for which
2916  * you'd need both a find dialog and a find/replace one at the same time,
2917  * without being able to actually use them separately since they're syncing
2918  * all the time.  I don't think it's worthwhile to fix this nonsense,
2919  * particularly evil incarnation of braindeadness, whatever; I'd much rather
2920  * see it extinguished from this planet.  Thanks for listening.  Sorry.
2921  */
2922     static void
2923 entry_changed_cb(GtkWidget * entry, GtkWidget * dialog)
2924 {
2925     const gchar	*entry_text;
2926     gboolean	nonempty;
2927 
2928     entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
2929 
2930     if (!entry_text)
2931 	return;			/* shouldn't happen */
2932 
2933     nonempty = (entry_text[0] != '\0');
2934 
2935     if (dialog == find_widgets.dialog)
2936     {
2937 	gtk_widget_set_sensitive(find_widgets.find, nonempty);
2938     }
2939 
2940     if (dialog == repl_widgets.dialog)
2941     {
2942 	gtk_widget_set_sensitive(repl_widgets.find, nonempty);
2943 	gtk_widget_set_sensitive(repl_widgets.replace, nonempty);
2944 	gtk_widget_set_sensitive(repl_widgets.all, nonempty);
2945     }
2946 }
2947 
2948 /*
2949  * ":helpfind"
2950  */
2951 /*ARGSUSED*/
2952     void
2953 ex_helpfind(eap)
2954     exarg_T	*eap;
2955 {
2956     /* This will fail when menus are not loaded.  Well, it's only for
2957      * backwards compatibility anyway. */
2958     do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp");
2959 }
2960 
2961 #if !defined(HAVE_GTK2) || defined(PROTO) /* Crack crack crack.  Brrrr. */
2962 
2963 /*  gui_gtk_position_in_parent
2964  *
2965  *  this function causes a child window to be placed within the boundary of
2966  *  the parent (mainwin) window.
2967  *
2968  *  you can specify where the window will be positioned by the third argument
2969  *  (defined in gui.h):
2970  *	VW_POS_CENTER		at center of parent window
2971  *	VW_POS_MOUSE		center of child at mouse position
2972  *	VW_POS_TOP_CENTER	top of child at top of parent centered
2973  *				horizontally about the mouse.
2974  *
2975  *  NOTE: for this function to act as desired the child window must have a
2976  *	  window size requested.  this can be accomplished by packing/placing
2977  *	  child widgets onto a gtk_frame widget rather than the gtk_window
2978  *	  widget...
2979  *
2980  *  brent -- dbv
2981  */
2982     void
2983 gui_gtk_position_in_parent(
2984 	GtkWidget	*parent,
2985 	GtkWidget	*child,
2986 	gui_win_pos_T	where)
2987 {
2988     GtkRequisition	c_size;
2989     gint		xPm, yPm;
2990     gint		xP, yP, wP, hP, pos_x, pos_y;
2991 
2992     /* make sure the child widget is set up then get its size. */
2993     gtk_widget_size_request(child, &c_size);
2994 
2995     /* get origin and size of parent window */
2996     gdk_window_get_origin((GdkWindow *)(parent->window), &xP, &yP);
2997     gdk_window_get_size((GdkWindow *)(parent->window), &wP, &hP);
2998 
2999     if (c_size.width > wP || c_size.height > hP)
3000     {
3001 	/* doh! maybe the user should consider giving gVim a little more
3002 	 * screen real estate */
3003 	gtk_widget_set_uposition(child , xP + 2 , yP + 2);
3004 	return;
3005     }
3006 
3007     if (where == VW_POS_MOUSE)
3008     {
3009 	/* position window at mouse pointer */
3010 	gtk_widget_get_pointer(parent, &xPm, &yPm);
3011 	pos_x = xP + xPm - (c_size.width) / 2;
3012 	pos_y = yP + yPm - (c_size.height) / 2;
3013     }
3014     else
3015     {
3016 	/* set child x origin so it is in center of Vim window */
3017 	pos_x =  xP + (wP - c_size.width) / 2;
3018 
3019 	if (where == VW_POS_TOP_CENTER)
3020 	    pos_y = yP + 2;
3021 	else
3022 	    /* where == VW_POS_CENTER */
3023 	    pos_y = yP + (hP - c_size.height) / 2;
3024     }
3025 
3026     /* now, make sure the window will be inside the Vim window... */
3027     if (pos_x < xP)
3028 	pos_x = xP + 2;
3029     if (pos_y < yP)
3030 	pos_y = yP + 2;
3031     if ((pos_x + c_size.width) > (wP + xP))
3032 	pos_x = xP + wP - c_size.width - 2;
3033     /* Assume 'guiheadroom' indicates the title bar height... */
3034     if ((pos_y + c_size.height + p_ghr / 2) > (hP + yP))
3035 	pos_y = yP + hP - c_size.height - 2 - p_ghr / 2;
3036 
3037     gtk_widget_set_uposition(child, pos_x, pos_y);
3038 }
3039 
3040 #endif /* !HAVE_GTK2 */
3041 
3042