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 <martin@dalecki.de>
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  <jason@peaceworks.ca>
21 *		  Daniel Elstner  <daniel.elstner@gmx.net>
22 */
23
24#include "vim.h"
25
26#ifdef FEAT_GUI_GNOME
27/* Gnome redefines _() and N_().  Grrr... */
28# ifdef _
29#  undef _
30# endif
31# ifdef N_
32#  undef N_
33# endif
34# ifdef textdomain
35#  undef textdomain
36# endif
37# ifdef bindtextdomain
38#  undef bindtextdomain
39# endif
40# ifdef bind_textdomain_codeset
41#  undef bind_textdomain_codeset
42# endif
43# if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
44#  define ENABLE_NLS	/* so the texts in the dialog boxes are translated */
45# endif
46# include <gnome.h>
47# include "version.h"
48/* missing prototype in bonobo-dock-item.h */
49extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockItemBehavior beh);
50#endif
51
52#if !defined(FEAT_GUI_GTK) && defined(PROTO)
53/* When generating prototypes we don't want syntax errors. */
54# define GdkAtom int
55# define GdkEventExpose int
56# define GdkEventFocus int
57# define GdkEventVisibility int
58# define GdkEventProperty int
59# define GtkContainer int
60# define GtkTargetEntry int
61# define GtkType int
62# define GtkWidget int
63# define gint int
64# define gpointer int
65# define guint int
66# define GdkEventKey int
67# define GdkEventSelection int
68# define GtkSelectionData int
69# define GdkEventMotion int
70# define GdkEventButton int
71# define GdkDragContext int
72# define GdkEventConfigure int
73# define GdkEventClient int
74#else
75# include <gdk/gdkkeysyms.h>
76# include <gdk/gdk.h>
77# ifdef WIN3264
78#  include <gdk/gdkwin32.h>
79# else
80#  include <gdk/gdkx.h>
81# endif
82
83# include <gtk/gtk.h>
84# include "gui_gtk_f.h"
85#endif
86
87#ifdef HAVE_X11_SUNKEYSYM_H
88# include <X11/Sunkeysym.h>
89static guint32 clipboard_event_time = CurrentTime;
90#endif
91
92/*
93 * Easy-to-use macro for multihead support.
94 */
95#ifdef HAVE_GTK_MULTIHEAD
96# define GET_X_ATOM(atom)	gdk_x11_atom_to_xatom_for_display( \
97				    gtk_widget_get_display(gui.mainwin), atom)
98#else
99# define GET_X_ATOM(atom)	((Atom)(atom))
100#endif
101
102/* Selection type distinguishers */
103enum
104{
105    TARGET_TYPE_NONE,
106    TARGET_UTF8_STRING,
107    TARGET_STRING,
108    TARGET_COMPOUND_TEXT,
109    TARGET_HTML,
110    TARGET_TEXT,
111    TARGET_TEXT_URI_LIST,
112    TARGET_TEXT_PLAIN,
113    TARGET_VIM,
114    TARGET_VIMENC
115};
116
117/*
118 * Table of selection targets supported by Vim.
119 * Note: Order matters, preferred types should come first.
120 */
121static const GtkTargetEntry selection_targets[] =
122{
123    {VIMENC_ATOM_NAME,	0, TARGET_VIMENC},
124    {VIM_ATOM_NAME,	0, TARGET_VIM},
125    {"text/html",	0, TARGET_HTML},
126    {"UTF8_STRING",	0, TARGET_UTF8_STRING},
127    {"COMPOUND_TEXT",	0, TARGET_COMPOUND_TEXT},
128    {"TEXT",		0, TARGET_TEXT},
129    {"STRING",		0, TARGET_STRING}
130};
131#define N_SELECTION_TARGETS (sizeof(selection_targets) / sizeof(selection_targets[0]))
132
133#ifdef FEAT_DND
134/*
135 * Table of DnD targets supported by Vim.
136 * Note: Order matters, preferred types should come first.
137 */
138static const GtkTargetEntry dnd_targets[] =
139{
140    {"text/uri-list",	0, TARGET_TEXT_URI_LIST},
141    {"text/html",	0, TARGET_HTML},
142    {"UTF8_STRING",	0, TARGET_UTF8_STRING},
143    {"STRING",		0, TARGET_STRING},
144    {"text/plain",	0, TARGET_TEXT_PLAIN}
145};
146# define N_DND_TARGETS (sizeof(dnd_targets) / sizeof(dnd_targets[0]))
147#endif
148
149
150/*
151 * "Monospace" is a standard font alias that should be present
152 * on all proper Pango/fontconfig installations.
153 */
154# define DEFAULT_FONT	"Monospace 10"
155
156#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
157/*
158 * Atoms used to communicate save-yourself from the X11 session manager. There
159 * is no need to move them into the GUI struct, since they should be constant.
160 */
161static GdkAtom wm_protocols_atom = GDK_NONE;
162static GdkAtom save_yourself_atom = GDK_NONE;
163#endif
164
165/*
166 * Atoms used to control/reference X11 selections.
167 */
168static GdkAtom html_atom = GDK_NONE;
169static GdkAtom utf8_string_atom = GDK_NONE;
170static GdkAtom vim_atom = GDK_NONE;	/* Vim's own special selection format */
171static GdkAtom vimenc_atom = GDK_NONE;	/* Vim's extended selection format */
172
173/*
174 * Keycodes recognized by vim.
175 * NOTE: when changing this, the table in gui_x11.c probably needs the same
176 * change!
177 */
178static struct special_key
179{
180    guint key_sym;
181    char_u code0;
182    char_u code1;
183}
184const special_keys[] =
185{
186    {GDK_Up,		'k', 'u'},
187    {GDK_Down,		'k', 'd'},
188    {GDK_Left,		'k', 'l'},
189    {GDK_Right,		'k', 'r'},
190    {GDK_F1,		'k', '1'},
191    {GDK_F2,		'k', '2'},
192    {GDK_F3,		'k', '3'},
193    {GDK_F4,		'k', '4'},
194    {GDK_F5,		'k', '5'},
195    {GDK_F6,		'k', '6'},
196    {GDK_F7,		'k', '7'},
197    {GDK_F8,		'k', '8'},
198    {GDK_F9,		'k', '9'},
199    {GDK_F10,		'k', ';'},
200    {GDK_F11,		'F', '1'},
201    {GDK_F12,		'F', '2'},
202    {GDK_F13,		'F', '3'},
203    {GDK_F14,		'F', '4'},
204    {GDK_F15,		'F', '5'},
205    {GDK_F16,		'F', '6'},
206    {GDK_F17,		'F', '7'},
207    {GDK_F18,		'F', '8'},
208    {GDK_F19,		'F', '9'},
209    {GDK_F20,		'F', 'A'},
210    {GDK_F21,		'F', 'B'},
211    {GDK_Pause,		'F', 'B'}, /* Pause == F21 according to netbeans.txt */
212    {GDK_F22,		'F', 'C'},
213    {GDK_F23,		'F', 'D'},
214    {GDK_F24,		'F', 'E'},
215    {GDK_F25,		'F', 'F'},
216    {GDK_F26,		'F', 'G'},
217    {GDK_F27,		'F', 'H'},
218    {GDK_F28,		'F', 'I'},
219    {GDK_F29,		'F', 'J'},
220    {GDK_F30,		'F', 'K'},
221    {GDK_F31,		'F', 'L'},
222    {GDK_F32,		'F', 'M'},
223    {GDK_F33,		'F', 'N'},
224    {GDK_F34,		'F', 'O'},
225    {GDK_F35,		'F', 'P'},
226#ifdef SunXK_F36
227    {SunXK_F36,		'F', 'Q'},
228    {SunXK_F37,		'F', 'R'},
229#endif
230    {GDK_Help,		'%', '1'},
231    {GDK_Undo,		'&', '8'},
232    {GDK_BackSpace,	'k', 'b'},
233    {GDK_Insert,	'k', 'I'},
234    {GDK_Delete,	'k', 'D'},
235    {GDK_3270_BackTab,	'k', 'B'},
236    {GDK_Clear,		'k', 'C'},
237    {GDK_Home,		'k', 'h'},
238    {GDK_End,		'@', '7'},
239    {GDK_Prior,		'k', 'P'},
240    {GDK_Next,		'k', 'N'},
241    {GDK_Print,		'%', '9'},
242    /* Keypad keys: */
243    {GDK_KP_Left,	'k', 'l'},
244    {GDK_KP_Right,	'k', 'r'},
245    {GDK_KP_Up,		'k', 'u'},
246    {GDK_KP_Down,	'k', 'd'},
247    {GDK_KP_Insert,	KS_EXTRA, (char_u)KE_KINS},
248    {GDK_KP_Delete,	KS_EXTRA, (char_u)KE_KDEL},
249    {GDK_KP_Home,	'K', '1'},
250    {GDK_KP_End,	'K', '4'},
251    {GDK_KP_Prior,	'K', '3'},  /* page up */
252    {GDK_KP_Next,	'K', '5'},  /* page down */
253
254    {GDK_KP_Add,	'K', '6'},
255    {GDK_KP_Subtract,	'K', '7'},
256    {GDK_KP_Divide,	'K', '8'},
257    {GDK_KP_Multiply,	'K', '9'},
258    {GDK_KP_Enter,	'K', 'A'},
259    {GDK_KP_Decimal,	'K', 'B'},
260
261    {GDK_KP_0,		'K', 'C'},
262    {GDK_KP_1,		'K', 'D'},
263    {GDK_KP_2,		'K', 'E'},
264    {GDK_KP_3,		'K', 'F'},
265    {GDK_KP_4,		'K', 'G'},
266    {GDK_KP_5,		'K', 'H'},
267    {GDK_KP_6,		'K', 'I'},
268    {GDK_KP_7,		'K', 'J'},
269    {GDK_KP_8,		'K', 'K'},
270    {GDK_KP_9,		'K', 'L'},
271
272    /* End of list marker: */
273    {0, 0, 0}
274};
275
276/*
277 * Flags for command line options table below.
278 */
279#define ARG_FONT	1
280#define ARG_GEOMETRY	2
281#define ARG_REVERSE	3
282#define ARG_NOREVERSE	4
283#define ARG_BACKGROUND	5
284#define ARG_FOREGROUND	6
285#define ARG_ICONIC	7
286#define ARG_ROLE	8
287#define ARG_NETBEANS	9
288#define ARG_XRM		10	/* ignored */
289#define ARG_MENUFONT	11	/* ignored */
290#define ARG_INDEX_MASK	0x00ff
291#define ARG_HAS_VALUE	0x0100	/* a value is expected after the argument */
292#define ARG_NEEDS_GUI	0x0200	/* need to initialize the GUI for this	  */
293#define ARG_FOR_GTK	0x0400	/* argument is handled by GTK+ or GNOME   */
294#define ARG_COMPAT_LONG	0x0800	/* accept -foo but substitute with --foo  */
295#define ARG_KEEP	0x1000	/* don't remove argument from argv[] */
296
297/*
298 * This table holds all the X GUI command line options allowed.  This includes
299 * the standard ones so that we can skip them when Vim is started without the
300 * GUI (but the GUI might start up later).
301 *
302 * When changing this, also update doc/gui_x11.txt and the usage message!!!
303 */
304typedef struct
305{
306    const char	    *name;
307    unsigned int    flags;
308}
309cmdline_option_T;
310
311static const cmdline_option_T cmdline_options[] =
312{
313    /* We handle these options ourselves */
314    {"-fn",		ARG_FONT|ARG_HAS_VALUE},
315    {"-font",		ARG_FONT|ARG_HAS_VALUE},
316    {"-geom",		ARG_GEOMETRY|ARG_HAS_VALUE},
317    {"-geometry",	ARG_GEOMETRY|ARG_HAS_VALUE},
318    {"-rv",		ARG_REVERSE},
319    {"-reverse",	ARG_REVERSE},
320    {"+rv",		ARG_NOREVERSE},
321    {"+reverse",	ARG_NOREVERSE},
322    {"-bg",		ARG_BACKGROUND|ARG_HAS_VALUE},
323    {"-background",	ARG_BACKGROUND|ARG_HAS_VALUE},
324    {"-fg",		ARG_FOREGROUND|ARG_HAS_VALUE},
325    {"-foreground",	ARG_FOREGROUND|ARG_HAS_VALUE},
326    {"-iconic",		ARG_ICONIC},
327    {"--role",		ARG_ROLE|ARG_HAS_VALUE},
328#ifdef FEAT_NETBEANS_INTG
329    {"-nb",		ARG_NETBEANS},	      /* non-standard value format */
330    {"-xrm",		ARG_XRM|ARG_HAS_VALUE},		/* not implemented */
331    {"-mf",		ARG_MENUFONT|ARG_HAS_VALUE},	/* not implemented */
332    {"-menufont",	ARG_MENUFONT|ARG_HAS_VALUE},	/* not implemented */
333#endif
334    /* Arguments handled by GTK (and GNOME) internally. */
335    {"--g-fatal-warnings",	ARG_FOR_GTK},
336    {"--gdk-debug",		ARG_FOR_GTK|ARG_HAS_VALUE},
337    {"--gdk-no-debug",		ARG_FOR_GTK|ARG_HAS_VALUE},
338    {"--gtk-debug",		ARG_FOR_GTK|ARG_HAS_VALUE},
339    {"--gtk-no-debug",		ARG_FOR_GTK|ARG_HAS_VALUE},
340    {"--gtk-module",		ARG_FOR_GTK|ARG_HAS_VALUE},
341    {"--sync",			ARG_FOR_GTK},
342    {"--display",		ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
343    {"--name",			ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
344    {"--class",			ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
345    {"--screen",		ARG_FOR_GTK|ARG_HAS_VALUE},
346    {"--gxid-host",		ARG_FOR_GTK|ARG_HAS_VALUE},
347    {"--gxid-port",		ARG_FOR_GTK|ARG_HAS_VALUE},
348#ifdef FEAT_GUI_GNOME
349    {"--load-modules",		ARG_FOR_GTK|ARG_HAS_VALUE},
350    {"--sm-client-id",		ARG_FOR_GTK|ARG_HAS_VALUE},
351    {"--sm-config-prefix",	ARG_FOR_GTK|ARG_HAS_VALUE},
352    {"--sm-disable",		ARG_FOR_GTK},
353    {"--oaf-ior-fd",		ARG_FOR_GTK|ARG_HAS_VALUE},
354    {"--oaf-activate-iid",	ARG_FOR_GTK|ARG_HAS_VALUE},
355    {"--oaf-private",		ARG_FOR_GTK},
356    {"--enable-sound",		ARG_FOR_GTK},
357    {"--disable-sound",		ARG_FOR_GTK},
358    {"--espeaker",		ARG_FOR_GTK|ARG_HAS_VALUE},
359    {"-?",			ARG_FOR_GTK|ARG_NEEDS_GUI},
360    {"--help",			ARG_FOR_GTK|ARG_NEEDS_GUI|ARG_KEEP},
361    {"--usage",			ARG_FOR_GTK|ARG_NEEDS_GUI},
362# if 0 /* conflicts with Vim's own --version argument */
363    {"--version",		ARG_FOR_GTK|ARG_NEEDS_GUI},
364# endif
365    {"--disable-crash-dialog",	ARG_FOR_GTK},
366#endif
367    {NULL, 0}
368};
369
370static int    gui_argc = 0;
371static char **gui_argv = NULL;
372
373static const char *role_argument = NULL;
374#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
375static const char *restart_command = NULL;
376static       char *abs_restart_command = NULL;
377#endif
378static int found_iconic_arg = FALSE;
379
380#ifdef FEAT_GUI_GNOME
381/*
382 * Can't use Gnome if --socketid given
383 */
384static int using_gnome = 0;
385#else
386# define using_gnome 0
387#endif
388
389/*
390 * Parse the GUI related command-line arguments.  Any arguments used are
391 * deleted from argv, and *argc is decremented accordingly.  This is called
392 * when vim is started, whether or not the GUI has been started.
393 */
394    void
395gui_mch_prepare(int *argc, char **argv)
396{
397    const cmdline_option_T  *option;
398    int			    i	= 0;
399    int			    len = 0;
400
401#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
402    /*
403     * Determine the command used to invoke Vim, to be passed as restart
404     * command to the session manager.	If argv[0] contains any directory
405     * components try building an absolute path, otherwise leave it as is.
406     */
407    restart_command = argv[0];
408
409    if (strchr(argv[0], G_DIR_SEPARATOR) != NULL)
410    {
411	char_u buf[MAXPATHL];
412
413	if (mch_FullName((char_u *)argv[0], buf, (int)sizeof(buf), TRUE) == OK)
414	{
415	    abs_restart_command = (char *)vim_strsave(buf);
416	    restart_command = abs_restart_command;
417	}
418    }
419#endif
420
421    /*
422     * Move all the entries in argv which are relevant to GTK+ and GNOME
423     * into gui_argv.  Freed later in gui_mch_init().
424     */
425    gui_argc = 0;
426    gui_argv = (char **)alloc((unsigned)((*argc + 1) * sizeof(char *)));
427
428    g_return_if_fail(gui_argv != NULL);
429
430    gui_argv[gui_argc++] = argv[i++];
431
432    while (i < *argc)
433    {
434	/* Don't waste CPU cycles on non-option arguments. */
435	if (argv[i][0] != '-' && argv[i][0] != '+')
436	{
437	    ++i;
438	    continue;
439	}
440
441	/* Look for argv[i] in cmdline_options[] table. */
442	for (option = &cmdline_options[0]; option->name != NULL; ++option)
443	{
444	    len = strlen(option->name);
445
446	    if (strncmp(argv[i], option->name, len) == 0)
447	    {
448		if (argv[i][len] == '\0')
449		    break;
450		/* allow --foo=bar style */
451		if (argv[i][len] == '=' && (option->flags & ARG_HAS_VALUE))
452		    break;
453#ifdef FEAT_NETBEANS_INTG
454		/* darn, -nb has non-standard syntax */
455		if (vim_strchr((char_u *)":=", argv[i][len]) != NULL
456			&& (option->flags & ARG_INDEX_MASK) == ARG_NETBEANS)
457		    break;
458#endif
459	    }
460	    else if ((option->flags & ARG_COMPAT_LONG)
461			&& strcmp(argv[i], option->name + 1) == 0)
462	    {
463		/* Replace the standard X arguments "-name" and "-display"
464		 * with their GNU-style long option counterparts. */
465		argv[i] = (char *)option->name;
466		break;
467	    }
468	}
469	if (option->name == NULL) /* no match */
470	{
471	    ++i;
472	    continue;
473	}
474
475	if (option->flags & ARG_FOR_GTK)
476	{
477	    /* Move the argument into gui_argv, which
478	     * will later be passed to gtk_init_check() */
479	    gui_argv[gui_argc++] = argv[i];
480	}
481	else
482	{
483	    char *value = NULL;
484
485	    /* Extract the option's value if there is one.
486	     * Accept both "--foo bar" and "--foo=bar" style. */
487	    if (option->flags & ARG_HAS_VALUE)
488	    {
489		if (argv[i][len] == '=')
490		    value = &argv[i][len + 1];
491		else if (i + 1 < *argc && strcmp(argv[i + 1], "--") != 0)
492		    value = argv[i + 1];
493	    }
494
495	    /* Check for options handled by Vim itself */
496	    switch (option->flags & ARG_INDEX_MASK)
497	    {
498		case ARG_REVERSE:
499		    found_reverse_arg = TRUE;
500		    break;
501		case ARG_NOREVERSE:
502		    found_reverse_arg = FALSE;
503		    break;
504		case ARG_FONT:
505		    font_argument = value;
506		    break;
507		case ARG_GEOMETRY:
508		    if (value != NULL)
509			gui.geom = vim_strsave((char_u *)value);
510		    break;
511		case ARG_BACKGROUND:
512		    background_argument = value;
513		    break;
514		case ARG_FOREGROUND:
515		    foreground_argument = value;
516		    break;
517		case ARG_ICONIC:
518		    found_iconic_arg = TRUE;
519		    break;
520		case ARG_ROLE:
521		    role_argument = value; /* used later in gui_mch_open() */
522		    break;
523#ifdef FEAT_NETBEANS_INTG
524		case ARG_NETBEANS:
525		    gui.dofork = FALSE; /* don't fork() when starting GUI */
526		    netbeansArg = argv[i];
527		    break;
528#endif
529		default:
530		    break;
531	    }
532	}
533
534	/* These arguments make gnome_program_init() print a message and exit.
535	 * Must start the GUI for this, otherwise ":gui" will exit later! */
536	if (option->flags & ARG_NEEDS_GUI)
537	    gui.starting = TRUE;
538
539	if (option->flags & ARG_KEEP)
540	    ++i;
541	else
542	{
543	    /* Remove the flag from the argument vector. */
544	    if (--*argc > i)
545	    {
546		int n_strip = 1;
547
548		/* Move the argument's value as well, if there is one. */
549		if ((option->flags & ARG_HAS_VALUE)
550			&& argv[i][len] != '='
551			&& strcmp(argv[i + 1], "--") != 0)
552		{
553		    ++n_strip;
554		    --*argc;
555		    if (option->flags & ARG_FOR_GTK)
556			gui_argv[gui_argc++] = argv[i + 1];
557		}
558
559		if (*argc > i)
560		    mch_memmove(&argv[i], &argv[i + n_strip],
561						(*argc - i) * sizeof(char *));
562	    }
563	    argv[*argc] = NULL;
564	}
565    }
566
567    gui_argv[gui_argc] = NULL;
568}
569
570#if defined(EXITFREE) || defined(PROTO)
571    void
572gui_mch_free_all()
573{
574    vim_free(gui_argv);
575#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
576    vim_free(abs_restart_command);
577#endif
578}
579#endif
580
581/*
582 * This should be maybe completely removed.
583 * Doesn't seem possible, since check_copy_area() relies on
584 * this information.  --danielk
585 */
586    static gint
587visibility_event(GtkWidget *widget UNUSED,
588		 GdkEventVisibility *event,
589		 gpointer data UNUSED)
590{
591    gui.visibility = event->state;
592    /*
593     * When we do an gdk_window_copy_area(), and the window is partially
594     * obscured, we want to receive an event to tell us whether it worked
595     * or not.
596     */
597    if (gui.text_gc != NULL)
598	gdk_gc_set_exposures(gui.text_gc,
599			     gui.visibility != GDK_VISIBILITY_UNOBSCURED);
600    return FALSE;
601}
602
603/*
604 * Redraw the corresponding portions of the screen.
605 */
606    static gint
607expose_event(GtkWidget *widget UNUSED,
608	     GdkEventExpose *event,
609	     gpointer data UNUSED)
610{
611    /* Skip this when the GUI isn't set up yet, will redraw later. */
612    if (gui.starting)
613	return FALSE;
614
615    out_flush();		/* make sure all output has been processed */
616    gui_redraw(event->area.x, event->area.y,
617	       event->area.width, event->area.height);
618
619    /* Clear the border areas if needed */
620    if (event->area.x < FILL_X(0))
621	gdk_window_clear_area(gui.drawarea->window, 0, 0, FILL_X(0), 0);
622    if (event->area.y < FILL_Y(0))
623	gdk_window_clear_area(gui.drawarea->window, 0, 0, 0, FILL_Y(0));
624    if (event->area.x > FILL_X(Columns))
625	gdk_window_clear_area(gui.drawarea->window,
626			      FILL_X((int)Columns), 0, 0, 0);
627    if (event->area.y > FILL_Y(Rows))
628	gdk_window_clear_area(gui.drawarea->window, 0, FILL_Y((int)Rows), 0, 0);
629
630    return FALSE;
631}
632
633#ifdef FEAT_CLIENTSERVER
634/*
635 * Handle changes to the "Comm" property
636 */
637    static gint
638property_event(GtkWidget *widget,
639	       GdkEventProperty *event,
640	       gpointer data UNUSED)
641{
642    if (event->type == GDK_PROPERTY_NOTIFY
643	    && event->state == (int)GDK_PROPERTY_NEW_VALUE
644	    && GDK_WINDOW_XWINDOW(event->window) == commWindow
645	    && GET_X_ATOM(event->atom) == commProperty)
646    {
647	XEvent xev;
648
649	/* Translate to XLib */
650	xev.xproperty.type = PropertyNotify;
651	xev.xproperty.atom = commProperty;
652	xev.xproperty.window = commWindow;
653	xev.xproperty.state = PropertyNewValue;
654	serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev);
655    }
656    return FALSE;
657}
658#endif
659
660
661/****************************************************************************
662 * Focus handlers:
663 */
664
665
666/*
667 * This is a simple state machine:
668 * BLINK_NONE	not blinking at all
669 * BLINK_OFF	blinking, cursor is not shown
670 * BLINK_ON	blinking, cursor is shown
671 */
672
673#define BLINK_NONE  0
674#define BLINK_OFF   1
675#define BLINK_ON    2
676
677static int blink_state = BLINK_NONE;
678static long_u blink_waittime = 700;
679static long_u blink_ontime = 400;
680static long_u blink_offtime = 250;
681static guint blink_timer = 0;
682
683    void
684gui_mch_set_blinking(long waittime, long on, long off)
685{
686    blink_waittime = waittime;
687    blink_ontime = on;
688    blink_offtime = off;
689}
690
691/*
692 * Stop the cursor blinking.  Show the cursor if it wasn't shown.
693 */
694    void
695gui_mch_stop_blink(void)
696{
697    if (blink_timer)
698    {
699	gtk_timeout_remove(blink_timer);
700	blink_timer = 0;
701    }
702    if (blink_state == BLINK_OFF)
703	gui_update_cursor(TRUE, FALSE);
704    blink_state = BLINK_NONE;
705}
706
707    static gint
708blink_cb(gpointer data UNUSED)
709{
710    if (blink_state == BLINK_ON)
711    {
712	gui_undraw_cursor();
713	blink_state = BLINK_OFF;
714	blink_timer = gtk_timeout_add((guint32)blink_offtime,
715				   (GtkFunction) blink_cb, NULL);
716    }
717    else
718    {
719	gui_update_cursor(TRUE, FALSE);
720	blink_state = BLINK_ON;
721	blink_timer = gtk_timeout_add((guint32)blink_ontime,
722				   (GtkFunction) blink_cb, NULL);
723    }
724
725    return FALSE;		/* don't happen again */
726}
727
728/*
729 * Start the cursor blinking.  If it was already blinking, this restarts the
730 * waiting time and shows the cursor.
731 */
732    void
733gui_mch_start_blink(void)
734{
735    if (blink_timer)
736	gtk_timeout_remove(blink_timer);
737    /* Only switch blinking on if none of the times is zero */
738    if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
739    {
740	blink_timer = gtk_timeout_add((guint32)blink_waittime,
741				   (GtkFunction) blink_cb, NULL);
742	blink_state = BLINK_ON;
743	gui_update_cursor(TRUE, FALSE);
744    }
745}
746
747    static gint
748enter_notify_event(GtkWidget *widget UNUSED,
749		   GdkEventCrossing *event UNUSED,
750		   gpointer data UNUSED)
751{
752    if (blink_state == BLINK_NONE)
753	gui_mch_start_blink();
754
755    /* make sure keyboard input goes there */
756    if (gtk_socket_id == 0 || !GTK_WIDGET_HAS_FOCUS(gui.drawarea))
757	gtk_widget_grab_focus(gui.drawarea);
758
759    return FALSE;
760}
761
762    static gint
763leave_notify_event(GtkWidget *widget UNUSED,
764		   GdkEventCrossing *event UNUSED,
765		   gpointer data UNUSED)
766{
767    if (blink_state != BLINK_NONE)
768	gui_mch_stop_blink();
769
770    return FALSE;
771}
772
773    static gint
774focus_in_event(GtkWidget *widget,
775	       GdkEventFocus *event UNUSED,
776	       gpointer data UNUSED)
777{
778    gui_focus_change(TRUE);
779
780    if (blink_state == BLINK_NONE)
781	gui_mch_start_blink();
782
783    /* make sure keyboard input goes to the draw area (if this is focus for a
784     * window) */
785    if (widget != gui.drawarea)
786	gtk_widget_grab_focus(gui.drawarea);
787
788    return TRUE;
789}
790
791    static gint
792focus_out_event(GtkWidget *widget UNUSED,
793		GdkEventFocus *event UNUSED,
794		gpointer data UNUSED)
795{
796    gui_focus_change(FALSE);
797
798    if (blink_state != BLINK_NONE)
799	gui_mch_stop_blink();
800
801    return TRUE;
802}
803
804
805/*
806 * Translate a GDK key value to UTF-8 independently of the current locale.
807 * The output is written to string, which must have room for at least 6 bytes
808 * plus the NUL terminator.  Returns the length in bytes.
809 *
810 * This function is used in the GTK+ 2 GUI only.  The GTK+ 1 code makes use
811 * of GdkEventKey::string instead.  But event->string is evil; see here why:
812 * http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html#GdkEventKey
813 */
814    static int
815keyval_to_string(unsigned int keyval, unsigned int state, char_u *string)
816{
817    int	    len;
818    guint32 uc;
819
820    uc = gdk_keyval_to_unicode(keyval);
821    if (uc != 0)
822    {
823	/* Check for CTRL-foo */
824	if ((state & GDK_CONTROL_MASK) && uc >= 0x20 && uc < 0x80)
825	{
826	    /* These mappings look arbitrary at the first glance, but in fact
827	     * resemble quite exactly the behaviour of the GTK+ 1.2 GUI on my
828	     * machine.  The only difference is BS vs. DEL for CTRL-8 (makes
829	     * more sense and is consistent with usual terminal behaviour). */
830	    if (uc >= '@')
831		string[0] = uc & 0x1F;
832	    else if (uc == '2')
833		string[0] = NUL;
834	    else if (uc >= '3' && uc <= '7')
835		string[0] = uc ^ 0x28;
836	    else if (uc == '8')
837		string[0] = BS;
838	    else if (uc == '?')
839		string[0] = DEL;
840	    else
841		string[0] = uc;
842	    len = 1;
843	}
844	else
845	{
846	    /* Translate a normal key to UTF-8.  This doesn't work for dead
847	     * keys of course, you _have_ to use an input method for that. */
848	    len = utf_char2bytes((int)uc, string);
849	}
850    }
851    else
852    {
853	/* Translate keys which are represented by ASCII control codes in Vim.
854	 * There are only a few of those; most control keys are translated to
855	 * special terminal-like control sequences. */
856	len = 1;
857	switch (keyval)
858	{
859	    case GDK_Tab: case GDK_KP_Tab: case GDK_ISO_Left_Tab:
860		string[0] = TAB;
861		break;
862	    case GDK_Linefeed:
863		string[0] = NL;
864		break;
865	    case GDK_Return: case GDK_ISO_Enter: case GDK_3270_Enter:
866		string[0] = CAR;
867		break;
868	    case GDK_Escape:
869		string[0] = ESC;
870		break;
871	    default:
872		len = 0;
873		break;
874	}
875    }
876    string[len] = NUL;
877
878    return len;
879}
880
881    static int
882modifiers_gdk2vim(guint state)
883{
884    int modifiers = 0;
885
886    if (state & GDK_SHIFT_MASK)
887	modifiers |= MOD_MASK_SHIFT;
888    if (state & GDK_CONTROL_MASK)
889	modifiers |= MOD_MASK_CTRL;
890    if (state & GDK_MOD1_MASK)
891	modifiers |= MOD_MASK_ALT;
892#if GTK_CHECK_VERSION(2,10,0)
893    if (state & GDK_SUPER_MASK)
894	modifiers |= MOD_MASK_META;
895#endif
896    if (state & GDK_MOD4_MASK)
897	modifiers |= MOD_MASK_META;
898
899    return modifiers;
900}
901
902    static int
903modifiers_gdk2mouse(guint state)
904{
905    int modifiers = 0;
906
907    if (state & GDK_SHIFT_MASK)
908	modifiers |= MOUSE_SHIFT;
909    if (state & GDK_CONTROL_MASK)
910	modifiers |= MOUSE_CTRL;
911    if (state & GDK_MOD1_MASK)
912	modifiers |= MOUSE_ALT;
913
914    return modifiers;
915}
916
917/*
918 * Main keyboard handler:
919 */
920    static gint
921key_press_event(GtkWidget *widget UNUSED,
922		GdkEventKey *event,
923		gpointer data UNUSED)
924{
925    /* For GTK+ 2 we know for sure how large the string might get.
926     * (That is, up to 6 bytes + NUL + CSI escapes + safety measure.) */
927    char_u	string[32], string2[32];
928    guint	key_sym;
929    int		len;
930    int		i;
931    int		modifiers;
932    int		key;
933    guint	state;
934    char_u	*s, *d;
935
936    clipboard_event_time = event->time;
937    key_sym = event->keyval;
938    state = event->state;
939
940#ifdef FEAT_XIM
941    if (xim_queue_key_press_event(event, TRUE))
942	return TRUE;
943#endif
944
945#ifdef FEAT_HANGULIN
946    if (key_sym == GDK_space && (state & GDK_SHIFT_MASK))
947    {
948	hangul_input_state_toggle();
949	return TRUE;
950    }
951#endif
952
953#ifdef SunXK_F36
954    /*
955     * These keys have bogus lookup strings, and trapping them here is
956     * easier than trying to XRebindKeysym() on them with every possible
957     * combination of modifiers.
958     */
959    if (key_sym == SunXK_F36 || key_sym == SunXK_F37)
960	len = 0;
961    else
962#endif
963    {
964	len = keyval_to_string(key_sym, state, string2);
965
966	/* Careful: convert_input() doesn't handle the NUL character.
967	 * No need to convert pure ASCII anyway, thus the len > 1 check. */
968	if (len > 1 && input_conv.vc_type != CONV_NONE)
969	    len = convert_input(string2, len, sizeof(string2));
970
971	s = string2;
972	d = string;
973	for (i = 0; i < len; ++i)
974	{
975	    *d++ = s[i];
976	    if (d[-1] == CSI && d + 2 < string + sizeof(string))
977	    {
978		/* Turn CSI into K_CSI. */
979		*d++ = KS_EXTRA;
980		*d++ = (int)KE_CSI;
981	    }
982	}
983	len = d - string;
984    }
985
986    /* Shift-Tab results in Left_Tab, but we want <S-Tab> */
987    if (key_sym == GDK_ISO_Left_Tab)
988    {
989	key_sym = GDK_Tab;
990	state |= GDK_SHIFT_MASK;
991    }
992
993#ifdef FEAT_MENU
994    /* If there is a menu and 'wak' is "yes", or 'wak' is "menu" and the key
995     * is a menu shortcut, we ignore everything with the ALT modifier. */
996    if ((state & GDK_MOD1_MASK)
997	    && gui.menu_is_active
998	    && (*p_wak == 'y'
999		|| (*p_wak == 'm'
1000		    && len == 1
1001		    && gui_is_menu_shortcut(string[0]))))
1002	/* For GTK2 we return false to signify that we haven't handled the
1003	 * keypress, so that gtk will handle the mnemonic or accelerator. */
1004	return FALSE;
1005#endif
1006
1007    /* Check for Alt/Meta key (Mod1Mask), but not for a BS, DEL or character
1008     * that already has the 8th bit set.
1009     * Don't do this for <S-M-Tab>, that should become K_S_TAB with ALT.
1010     * Don't do this for double-byte encodings, it turns the char into a lead
1011     * byte. */
1012    if (len == 1
1013	    && ((state & GDK_MOD1_MASK)
1014#if GTK_CHECK_VERSION(2,10,0)
1015		|| (state & GDK_SUPER_MASK)
1016#endif
1017		)
1018	    && !(key_sym == GDK_BackSpace || key_sym == GDK_Delete)
1019	    && (string[0] & 0x80) == 0
1020	    && !(key_sym == GDK_Tab && (state & GDK_SHIFT_MASK))
1021	    && !enc_dbcs
1022	    )
1023    {
1024	string[0] |= 0x80;
1025	state &= ~GDK_MOD1_MASK;	/* don't use it again */
1026	if (enc_utf8) /* convert to utf-8 */
1027	{
1028	    string[1] = string[0] & 0xbf;
1029	    string[0] = ((unsigned)string[0] >> 6) + 0xc0;
1030	    if (string[1] == CSI)
1031	    {
1032		string[2] = KS_EXTRA;
1033		string[3] = (int)KE_CSI;
1034		len = 4;
1035	    }
1036	    else
1037		len = 2;
1038	}
1039    }
1040
1041    /* Check for special keys.	Also do this when len == 1 (key has an ASCII
1042     * value) to detect backspace, delete and keypad keys. */
1043    if (len == 0 || len == 1)
1044    {
1045	for (i = 0; special_keys[i].key_sym != 0; i++)
1046	{
1047	    if (special_keys[i].key_sym == key_sym)
1048	    {
1049		string[0] = CSI;
1050		string[1] = special_keys[i].code0;
1051		string[2] = special_keys[i].code1;
1052		len = -3;
1053		break;
1054	    }
1055	}
1056    }
1057
1058    if (len == 0)   /* Unrecognized key */
1059	return TRUE;
1060
1061    /* Special keys (and a few others) may have modifiers. Also when using a
1062     * double-byte encoding (can't set the 8th bit). */
1063    if (len == -3 || key_sym == GDK_space || key_sym == GDK_Tab
1064	    || key_sym == GDK_Return || key_sym == GDK_Linefeed
1065	    || key_sym == GDK_Escape || key_sym == GDK_KP_Tab
1066	    || key_sym == GDK_ISO_Enter || key_sym == GDK_3270_Enter
1067	    || (enc_dbcs && len == 1 && ((state & GDK_MOD1_MASK)
1068#if GTK_CHECK_VERSION(2,10,0)
1069		    || (state & GDK_SUPER_MASK)
1070#endif
1071		    )))
1072    {
1073	modifiers = modifiers_gdk2vim(state);
1074
1075	/*
1076	 * For some keys a shift modifier is translated into another key
1077	 * code.
1078	 */
1079	if (len == -3)
1080	    key = TO_SPECIAL(string[1], string[2]);
1081	else
1082	    key = string[0];
1083
1084	key = simplify_key(key, &modifiers);
1085	if (key == CSI)
1086	    key = K_CSI;
1087	if (IS_SPECIAL(key))
1088	{
1089	    string[0] = CSI;
1090	    string[1] = K_SECOND(key);
1091	    string[2] = K_THIRD(key);
1092	    len = 3;
1093	}
1094	else
1095	{
1096	    string[0] = key;
1097	    len = 1;
1098	}
1099
1100	if (modifiers != 0)
1101	{
1102	    string2[0] = CSI;
1103	    string2[1] = KS_MODIFIER;
1104	    string2[2] = modifiers;
1105	    add_to_input_buf(string2, 3);
1106	}
1107    }
1108
1109    if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
1110		   || (string[0] == intr_char && intr_char != Ctrl_C)))
1111    {
1112	trash_input_buf();
1113	got_int = TRUE;
1114    }
1115
1116    add_to_input_buf(string, len);
1117
1118    /* blank out the pointer if necessary */
1119    if (p_mh)
1120	gui_mch_mousehide(TRUE);
1121
1122    return TRUE;
1123}
1124
1125#if defined(FEAT_XIM)
1126    static gboolean
1127key_release_event(GtkWidget *widget UNUSED,
1128		  GdkEventKey *event,
1129		  gpointer data UNUSED)
1130{
1131    clipboard_event_time = event->time;
1132    /*
1133     * GTK+ 2 input methods may do fancy stuff on key release events too.
1134     * With the default IM for instance, you can enter any UCS code point
1135     * by holding down CTRL-SHIFT and typing hexadecimal digits.
1136     */
1137    return xim_queue_key_press_event(event, FALSE);
1138}
1139#endif
1140
1141
1142/****************************************************************************
1143 * Selection handlers:
1144 */
1145
1146    static gint
1147selection_clear_event(GtkWidget		*widget UNUSED,
1148		      GdkEventSelection	*event,
1149		      gpointer		user_data UNUSED)
1150{
1151    if (event->selection == clip_plus.gtk_sel_atom)
1152	clip_lose_selection(&clip_plus);
1153    else
1154	clip_lose_selection(&clip_star);
1155
1156    return TRUE;
1157}
1158
1159#define RS_NONE	0	/* selection_received_cb() not called yet */
1160#define RS_OK	1	/* selection_received_cb() called and OK */
1161#define RS_FAIL	2	/* selection_received_cb() called and failed */
1162static int received_selection = RS_NONE;
1163
1164    static void
1165selection_received_cb(GtkWidget		*widget UNUSED,
1166		      GtkSelectionData	*data,
1167		      guint		time_ UNUSED,
1168		      gpointer		user_data UNUSED)
1169{
1170    VimClipboard    *cbd;
1171    char_u	    *text;
1172    char_u	    *tmpbuf = NULL;
1173    guchar	    *tmpbuf_utf8 = NULL;
1174    int		    len;
1175    int		    motion_type;
1176
1177    if (data->selection == clip_plus.gtk_sel_atom)
1178	cbd = &clip_plus;
1179    else
1180	cbd = &clip_star;
1181
1182    text = (char_u *)data->data;
1183    len  = data->length;
1184    motion_type = MCHAR;
1185
1186    if (text == NULL || len <= 0)
1187    {
1188	received_selection = RS_FAIL;
1189	/* clip_free_selection(cbd); ??? */
1190
1191	return;
1192    }
1193
1194    if (data->type == vim_atom)
1195    {
1196	motion_type = *text++;
1197	--len;
1198    }
1199
1200    else if (data->type == vimenc_atom)
1201    {
1202	char_u		*enc;
1203	vimconv_T	conv;
1204
1205	motion_type = *text++;
1206	--len;
1207
1208	enc = text;
1209	text += STRLEN(text) + 1;
1210	len -= text - enc;
1211
1212	/* If the encoding of the text is different from 'encoding', attempt
1213	 * converting it. */
1214	conv.vc_type = CONV_NONE;
1215	convert_setup(&conv, enc, p_enc);
1216	if (conv.vc_type != CONV_NONE)
1217	{
1218	    tmpbuf = string_convert(&conv, text, &len);
1219	    if (tmpbuf != NULL)
1220		text = tmpbuf;
1221	    convert_setup(&conv, NULL, NULL);
1222	}
1223    }
1224
1225    /* gtk_selection_data_get_text() handles all the nasty details
1226     * and targets and encodings etc.  This rocks so hard. */
1227    else
1228    {
1229	tmpbuf_utf8 = gtk_selection_data_get_text(data);
1230	if (tmpbuf_utf8 != NULL)
1231	{
1232	    len = STRLEN(tmpbuf_utf8);
1233	    if (input_conv.vc_type != CONV_NONE)
1234	    {
1235		tmpbuf = string_convert(&input_conv, tmpbuf_utf8, &len);
1236		if (tmpbuf != NULL)
1237		    text = tmpbuf;
1238	    }
1239	    else
1240		text = tmpbuf_utf8;
1241	}
1242	else if (len >= 2 && text[0] == 0xff && text[1] == 0xfe)
1243	{
1244	    vimconv_T conv;
1245
1246	    /* UTF-16, we get this for HTML */
1247	    conv.vc_type = CONV_NONE;
1248	    convert_setup_ext(&conv, (char_u *)"utf-16le", FALSE, p_enc, TRUE);
1249
1250	    if (conv.vc_type != CONV_NONE)
1251	    {
1252		text += 2;
1253		len -= 2;
1254		tmpbuf = string_convert(&conv, text, &len);
1255		convert_setup(&conv, NULL, NULL);
1256	    }
1257	    if (tmpbuf != NULL)
1258		text = tmpbuf;
1259	}
1260    }
1261
1262    /* Chop off any traiing NUL bytes.  OpenOffice sends these. */
1263    while (len > 0 && text[len - 1] == NUL)
1264	--len;
1265
1266    clip_yank_selection(motion_type, text, (long)len, cbd);
1267    received_selection = RS_OK;
1268    vim_free(tmpbuf);
1269    g_free(tmpbuf_utf8);
1270}
1271
1272/*
1273 * Prepare our selection data for passing it to the external selection
1274 * client.
1275 */
1276    static void
1277selection_get_cb(GtkWidget	    *widget UNUSED,
1278		 GtkSelectionData   *selection_data,
1279		 guint		    info,
1280		 guint		    time_ UNUSED,
1281		 gpointer	    user_data UNUSED)
1282{
1283    char_u	    *string;
1284    char_u	    *tmpbuf;
1285    long_u	    tmplen;
1286    int		    length;
1287    int		    motion_type;
1288    GdkAtom	    type;
1289    VimClipboard    *cbd;
1290
1291    if (selection_data->selection == clip_plus.gtk_sel_atom)
1292	cbd = &clip_plus;
1293    else
1294	cbd = &clip_star;
1295
1296    if (!cbd->owned)
1297	return;			/* Shouldn't ever happen */
1298
1299    if (info != (guint)TARGET_STRING
1300	    && (!clip_html || info != (guint)TARGET_HTML)
1301	    && info != (guint)TARGET_UTF8_STRING
1302	    && info != (guint)TARGET_VIMENC
1303	    && info != (guint)TARGET_VIM
1304	    && info != (guint)TARGET_COMPOUND_TEXT
1305	    && info != (guint)TARGET_TEXT)
1306	return;
1307
1308    /* get the selection from the '*'/'+' register */
1309    clip_get_selection(cbd);
1310
1311    motion_type = clip_convert_selection(&string, &tmplen, cbd);
1312    if (motion_type < 0 || string == NULL)
1313	return;
1314    /* Due to int arguments we can't handle more than G_MAXINT.  Also
1315     * reserve one extra byte for NUL or the motion type; just in case.
1316     * (Not that pasting 2G of text is ever going to work, but... ;-) */
1317    length = MIN(tmplen, (long_u)(G_MAXINT - 1));
1318
1319    if (info == (guint)TARGET_VIM)
1320    {
1321	tmpbuf = alloc((unsigned)length + 1);
1322	if (tmpbuf != NULL)
1323	{
1324	    tmpbuf[0] = motion_type;
1325	    mch_memmove(tmpbuf + 1, string, (size_t)length);
1326	}
1327	/* For our own format, the first byte contains the motion type */
1328	++length;
1329	vim_free(string);
1330	string = tmpbuf;
1331	type = vim_atom;
1332    }
1333
1334    else if (info == (guint)TARGET_HTML)
1335    {
1336	vimconv_T conv;
1337
1338	/* Since we get utf-16, we probably should set it as well. */
1339	conv.vc_type = CONV_NONE;
1340	convert_setup_ext(&conv, p_enc, TRUE, (char_u *)"utf-16le", FALSE);
1341	if (conv.vc_type != CONV_NONE)
1342	{
1343	    tmpbuf = string_convert(&conv, string, &length);
1344	    convert_setup(&conv, NULL, NULL);
1345	    vim_free(string);
1346	    string = tmpbuf;
1347	}
1348
1349	/* Prepend the BOM: "fffe" */
1350	if (string != NULL)
1351	{
1352	    tmpbuf = alloc(length + 2);
1353	    tmpbuf[0] = 0xff;
1354	    tmpbuf[1] = 0xfe;
1355	    mch_memmove(tmpbuf + 2, string, (size_t)length);
1356	    vim_free(string);
1357	    string = tmpbuf;
1358	    length += 2;
1359
1360	    selection_data->type = selection_data->target;
1361	    selection_data->format = 16;	/* 16 bits per char */
1362	    gtk_selection_data_set(selection_data, html_atom, 16,
1363							      string, length);
1364	    vim_free(string);
1365	}
1366	return;
1367    }
1368    else if (info == (guint)TARGET_VIMENC)
1369    {
1370	int l = STRLEN(p_enc);
1371
1372	/* contents: motion_type 'encoding' NUL text */
1373	tmpbuf = alloc((unsigned)length + l + 2);
1374	if (tmpbuf != NULL)
1375	{
1376	    tmpbuf[0] = motion_type;
1377	    STRCPY(tmpbuf + 1, p_enc);
1378	    mch_memmove(tmpbuf + l + 2, string, (size_t)length);
1379	}
1380	length += l + 2;
1381	vim_free(string);
1382	string = tmpbuf;
1383	type = vimenc_atom;
1384    }
1385
1386    /* gtk_selection_data_set_text() handles everything for us.  This is
1387     * so easy and simple and cool, it'd be insane not to use it. */
1388    else
1389    {
1390	if (output_conv.vc_type != CONV_NONE)
1391	{
1392	    tmpbuf = string_convert(&output_conv, string, &length);
1393	    vim_free(string);
1394	    if (tmpbuf == NULL)
1395		return;
1396	    string = tmpbuf;
1397	}
1398	/* Validate the string to avoid runtime warnings */
1399	if (g_utf8_validate((const char *)string, (gssize)length, NULL))
1400	{
1401	    gtk_selection_data_set_text(selection_data,
1402					(const char *)string, length);
1403	}
1404	vim_free(string);
1405	return;
1406    }
1407
1408    if (string != NULL)
1409    {
1410	selection_data->type = selection_data->target;
1411	selection_data->format = 8;	/* 8 bits per char */
1412
1413	gtk_selection_data_set(selection_data, type, 8, string, length);
1414	vim_free(string);
1415    }
1416}
1417
1418/*
1419 * Check if the GUI can be started.  Called before gvimrc is sourced.
1420 * Return OK or FAIL.
1421 */
1422    int
1423gui_mch_init_check(void)
1424{
1425#ifdef FEAT_GUI_GNOME
1426    if (gtk_socket_id == 0)
1427	using_gnome = 1;
1428#endif
1429
1430    /* Don't use gtk_init() or gnome_init(), it exits on failure. */
1431    if (!gtk_init_check(&gui_argc, &gui_argv))
1432    {
1433	gui.dying = TRUE;
1434	EMSG(_((char *)e_opendisp));
1435	return FAIL;
1436    }
1437
1438    return OK;
1439}
1440
1441
1442/****************************************************************************
1443 * Mouse handling callbacks
1444 */
1445
1446
1447static guint mouse_click_timer = 0;
1448static int mouse_timed_out = TRUE;
1449
1450/*
1451 * Timer used to recognize multiple clicks of the mouse button
1452 */
1453    static gint
1454mouse_click_timer_cb(gpointer data)
1455{
1456    /* we don't use this information currently */
1457    int *timed_out = (int *) data;
1458
1459    *timed_out = TRUE;
1460    return FALSE;		/* don't happen again */
1461}
1462
1463static guint motion_repeat_timer  = 0;
1464static int   motion_repeat_offset = FALSE;
1465static gint  motion_repeat_timer_cb(gpointer);
1466
1467    static void
1468process_motion_notify(int x, int y, GdkModifierType state)
1469{
1470    int	    button;
1471    int_u   vim_modifiers;
1472
1473    button = (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1474		       GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
1475		       GDK_BUTTON5_MASK))
1476	      ? MOUSE_DRAG : ' ';
1477
1478    /* If our pointer is currently hidden, then we should show it. */
1479    gui_mch_mousehide(FALSE);
1480
1481    /* Just moving the rodent above the drawing area without any button
1482     * being pressed. */
1483    if (button != MOUSE_DRAG)
1484    {
1485	gui_mouse_moved(x, y);
1486	return;
1487    }
1488
1489    /* translate modifier coding between the main engine and GTK */
1490    vim_modifiers = modifiers_gdk2mouse(state);
1491
1492    /* inform the editor engine about the occurrence of this event */
1493    gui_send_mouse_event(button, x, y, FALSE, vim_modifiers);
1494
1495    /*
1496     * Auto repeat timer handling.
1497     */
1498    if (x < 0 || y < 0
1499	    || x >= gui.drawarea->allocation.width
1500	    || y >= gui.drawarea->allocation.height)
1501    {
1502
1503	int dx;
1504	int dy;
1505	int offshoot;
1506	int delay = 10;
1507
1508	/* Calculate the maximal distance of the cursor from the drawing area.
1509	 * (offshoot can't become negative here!).
1510	 */
1511	dx = x < 0 ? -x : x - gui.drawarea->allocation.width;
1512	dy = y < 0 ? -y : y - gui.drawarea->allocation.height;
1513
1514	offshoot = dx > dy ? dx : dy;
1515
1516	/* Make a linearly decaying timer delay with a threshold of 5 at a
1517	 * distance of 127 pixels from the main window.
1518	 *
1519	 * One could think endlessly about the most ergonomic variant here.
1520	 * For example it could make sense to calculate the distance from the
1521	 * drags start instead...
1522	 *
1523	 * Maybe a parabolic interpolation would suite us better here too...
1524	 */
1525	if (offshoot > 127)
1526	{
1527	    /* 5 appears to be somehow near to my perceptual limits :-). */
1528	    delay = 5;
1529	}
1530	else
1531	{
1532	    delay = (130 * (127 - offshoot)) / 127 + 5;
1533	}
1534
1535	/* shoot again */
1536	if (!motion_repeat_timer)
1537	    motion_repeat_timer = gtk_timeout_add((guint32)delay,
1538						motion_repeat_timer_cb, NULL);
1539    }
1540}
1541
1542/*
1543 * Timer used to recognize multiple clicks of the mouse button.
1544 */
1545    static gint
1546motion_repeat_timer_cb(gpointer data UNUSED)
1547{
1548    int		    x;
1549    int		    y;
1550    GdkModifierType state;
1551
1552    gdk_window_get_pointer(gui.drawarea->window, &x, &y, &state);
1553
1554    if (!(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1555		   GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
1556		   GDK_BUTTON5_MASK)))
1557    {
1558	motion_repeat_timer = 0;
1559	return FALSE;
1560    }
1561
1562    /* If there already is a mouse click in the input buffer, wait another
1563     * time (otherwise we would create a backlog of clicks) */
1564    if (vim_used_in_input_buf() > 10)
1565	return TRUE;
1566
1567    motion_repeat_timer = 0;
1568
1569    /*
1570     * Fake a motion event.
1571     * Trick: Pretend the mouse moved to the next character on every other
1572     * event, otherwise drag events will be discarded, because they are still
1573     * in the same character.
1574     */
1575    if (motion_repeat_offset)
1576	x += gui.char_width;
1577
1578    motion_repeat_offset = !motion_repeat_offset;
1579    process_motion_notify(x, y, state);
1580
1581    /* Don't happen again.  We will get reinstalled in the synthetic event
1582     * if needed -- thus repeating should still work. */
1583    return FALSE;
1584}
1585
1586    static gint
1587motion_notify_event(GtkWidget *widget,
1588		    GdkEventMotion *event,
1589		    gpointer data UNUSED)
1590{
1591    if (event->is_hint)
1592    {
1593	int		x;
1594	int		y;
1595	GdkModifierType	state;
1596
1597	gdk_window_get_pointer(widget->window, &x, &y, &state);
1598	process_motion_notify(x, y, state);
1599    }
1600    else
1601    {
1602	process_motion_notify((int)event->x, (int)event->y,
1603			      (GdkModifierType)event->state);
1604    }
1605
1606    return TRUE; /* handled */
1607}
1608
1609
1610/*
1611 * Mouse button handling.  Note please that we are capturing multiple click's
1612 * by our own timeout mechanism instead of the one provided by GTK+ itself.
1613 * This is due to the way the generic VIM code is recognizing multiple clicks.
1614 */
1615    static gint
1616button_press_event(GtkWidget *widget,
1617		   GdkEventButton *event,
1618		   gpointer data UNUSED)
1619{
1620    int button;
1621    int repeated_click = FALSE;
1622    int x, y;
1623    int_u vim_modifiers;
1624
1625    clipboard_event_time = event->time;
1626
1627    /* Make sure we have focus now we've been selected */
1628    if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
1629	gtk_widget_grab_focus(widget);
1630
1631    /*
1632     * Don't let additional events about multiple clicks send by GTK to us
1633     * after the initial button press event confuse us.
1634     */
1635    if (event->type != GDK_BUTTON_PRESS)
1636	return FALSE;
1637
1638    x = event->x;
1639    y = event->y;
1640
1641    /* Handle multiple clicks */
1642    if (!mouse_timed_out && mouse_click_timer)
1643    {
1644	gtk_timeout_remove(mouse_click_timer);
1645	mouse_click_timer = 0;
1646	repeated_click = TRUE;
1647    }
1648
1649    mouse_timed_out = FALSE;
1650    mouse_click_timer = gtk_timeout_add((guint32)p_mouset,
1651				  mouse_click_timer_cb, &mouse_timed_out);
1652
1653    switch (event->button)
1654    {
1655    case 1:
1656	button = MOUSE_LEFT;
1657	break;
1658    case 2:
1659	button = MOUSE_MIDDLE;
1660	break;
1661    case 3:
1662	button = MOUSE_RIGHT;
1663	break;
1664    default:
1665	return FALSE;		/* Unknown button */
1666    }
1667
1668#ifdef FEAT_XIM
1669    /* cancel any preediting */
1670    if (im_is_preediting())
1671	xim_reset();
1672#endif
1673
1674    vim_modifiers = modifiers_gdk2mouse(event->state);
1675
1676    gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
1677
1678    return TRUE;
1679}
1680
1681/*
1682 * GTK+ 2 abstracts scrolling via the GdkEventScroll.
1683 */
1684    static gboolean
1685scroll_event(GtkWidget *widget,
1686	     GdkEventScroll *event,
1687	     gpointer data UNUSED)
1688{
1689    int	    button;
1690    int_u   vim_modifiers;
1691
1692    if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
1693	gtk_widget_grab_focus(widget);
1694
1695    switch (event->direction)
1696    {
1697	case GDK_SCROLL_UP:
1698	    button = MOUSE_4;
1699	    break;
1700	case GDK_SCROLL_DOWN:
1701	    button = MOUSE_5;
1702	    break;
1703	case GDK_SCROLL_LEFT:
1704	    button = MOUSE_7;
1705	    break;
1706	case GDK_SCROLL_RIGHT:
1707	    button = MOUSE_6;
1708	    break;
1709	default: /* This shouldn't happen */
1710	    return FALSE;
1711    }
1712
1713# ifdef FEAT_XIM
1714    /* cancel any preediting */
1715    if (im_is_preediting())
1716	xim_reset();
1717# endif
1718
1719    vim_modifiers = modifiers_gdk2mouse(event->state);
1720
1721    gui_send_mouse_event(button, (int)event->x, (int)event->y,
1722							FALSE, vim_modifiers);
1723
1724    return TRUE;
1725}
1726
1727
1728    static gint
1729button_release_event(GtkWidget *widget UNUSED,
1730		     GdkEventButton *event,
1731		     gpointer data UNUSED)
1732{
1733    int x, y;
1734    int_u vim_modifiers;
1735
1736    clipboard_event_time = event->time;
1737
1738    /* Remove any motion "machine gun" timers used for automatic further
1739       extension of allocation areas if outside of the applications window
1740       area .*/
1741    if (motion_repeat_timer)
1742    {
1743	gtk_timeout_remove(motion_repeat_timer);
1744	motion_repeat_timer = 0;
1745    }
1746
1747    x = event->x;
1748    y = event->y;
1749
1750    vim_modifiers = modifiers_gdk2mouse(event->state);
1751
1752    gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, vim_modifiers);
1753
1754    return TRUE;
1755}
1756
1757
1758#ifdef FEAT_DND
1759/****************************************************************************
1760 * Drag aNd Drop support handlers.
1761 */
1762
1763/*
1764 * Count how many items there may be and separate them with a NUL.
1765 * Apparently the items are separated with \r\n.  This is not documented,
1766 * thus be careful not to go past the end.	Also allow separation with
1767 * NUL characters.
1768 */
1769    static int
1770count_and_decode_uri_list(char_u *out, char_u *raw, int len)
1771{
1772    int		i;
1773    char_u	*p = out;
1774    int		count = 0;
1775
1776    for (i = 0; i < len; ++i)
1777    {
1778	if (raw[i] == NUL || raw[i] == '\n' || raw[i] == '\r')
1779	{
1780	    if (p > out && p[-1] != NUL)
1781	    {
1782		++count;
1783		*p++ = NUL;
1784	    }
1785	}
1786	else if (raw[i] == '%' && i + 2 < len && hexhex2nr(raw + i + 1) > 0)
1787	{
1788	    *p++ = hexhex2nr(raw + i + 1);
1789	    i += 2;
1790	}
1791	else
1792	    *p++ = raw[i];
1793    }
1794    if (p > out && p[-1] != NUL)
1795    {
1796	*p = NUL;	/* last item didn't have \r or \n */
1797	++count;
1798    }
1799    return count;
1800}
1801
1802/*
1803 * Parse NUL separated "src" strings.  Make it an array "outlist" form.  On
1804 * this process, URI which protocol is not "file:" are removed.  Return
1805 * length of array (less than "max").
1806 */
1807    static int
1808filter_uri_list(char_u **outlist, int max, char_u *src)
1809{
1810    int	i, j;
1811
1812    for (i = j = 0; i < max; ++i)
1813    {
1814	outlist[i] = NULL;
1815	if (STRNCMP(src, "file:", 5) == 0)
1816	{
1817	    src += 5;
1818	    if (STRNCMP(src, "//localhost", 11) == 0)
1819		src += 11;
1820	    while (src[0] == '/' && src[1] == '/')
1821		++src;
1822	    outlist[j++] = vim_strsave(src);
1823	}
1824	src += STRLEN(src) + 1;
1825    }
1826    return j;
1827}
1828
1829    static char_u **
1830parse_uri_list(int *count, char_u *data, int len)
1831{
1832    int	    n	    = 0;
1833    char_u  *tmp    = NULL;
1834    char_u  **array = NULL;;
1835
1836    if (data != NULL && len > 0 && (tmp = (char_u *)alloc(len + 1)) != NULL)
1837    {
1838	n = count_and_decode_uri_list(tmp, data, len);
1839	if (n > 0 && (array = (char_u **)alloc(n * sizeof(char_u *))) != NULL)
1840	    n = filter_uri_list(array, n, tmp);
1841    }
1842    vim_free(tmp);
1843    *count = n;
1844    return array;
1845}
1846
1847    static void
1848drag_handle_uri_list(GdkDragContext	*context,
1849		     GtkSelectionData	*data,
1850		     guint		time_,
1851		     GdkModifierType	state,
1852		     gint		x,
1853		     gint		y)
1854{
1855    char_u  **fnames;
1856    int	    nfiles = 0;
1857
1858    fnames = parse_uri_list(&nfiles, data->data, data->length);
1859
1860    if (fnames != NULL && nfiles > 0)
1861    {
1862	int_u   modifiers;
1863
1864	gtk_drag_finish(context, TRUE, FALSE, time_); /* accept */
1865
1866	modifiers = modifiers_gdk2mouse(state);
1867
1868	gui_handle_drop(x, y, modifiers, fnames, nfiles);
1869    }
1870    else
1871	vim_free(fnames);
1872}
1873
1874    static void
1875drag_handle_text(GdkDragContext	    *context,
1876		 GtkSelectionData   *data,
1877		 guint		    time_,
1878		 GdkModifierType    state)
1879{
1880    char_u  dropkey[6] = {CSI, KS_MODIFIER, 0, CSI, KS_EXTRA, (char_u)KE_DROP};
1881    char_u  *text;
1882    int	    len;
1883    char_u  *tmpbuf = NULL;
1884
1885    text = data->data;
1886    len  = data->length;
1887
1888    if (data->type == utf8_string_atom)
1889    {
1890	if (input_conv.vc_type != CONV_NONE)
1891	    tmpbuf = string_convert(&input_conv, text, &len);
1892	if (tmpbuf != NULL)
1893	    text = tmpbuf;
1894    }
1895
1896    dnd_yank_drag_data(text, (long)len);
1897    gtk_drag_finish(context, TRUE, FALSE, time_); /* accept */
1898    vim_free(tmpbuf);
1899
1900    dropkey[2] = modifiers_gdk2vim(state);
1901
1902    if (dropkey[2] != 0)
1903	add_to_input_buf(dropkey, (int)sizeof(dropkey));
1904    else
1905	add_to_input_buf(dropkey + 3, (int)(sizeof(dropkey) - 3));
1906}
1907
1908/*
1909 * DND receiver.
1910 */
1911    static void
1912drag_data_received_cb(GtkWidget		*widget,
1913		      GdkDragContext	*context,
1914		      gint		x,
1915		      gint		y,
1916		      GtkSelectionData	*data,
1917		      guint		info,
1918		      guint		time_,
1919		      gpointer		user_data UNUSED)
1920{
1921    GdkModifierType state;
1922
1923    /* Guard against trash */
1924    if (data->data == NULL
1925	    || data->length <= 0
1926	    || data->format != 8
1927	    || data->data[data->length] != '\0')
1928    {
1929	gtk_drag_finish(context, FALSE, FALSE, time_);
1930	return;
1931    }
1932
1933    /* Get the current modifier state for proper distinguishment between
1934     * different operations later. */
1935    gdk_window_get_pointer(widget->window, NULL, NULL, &state);
1936
1937    /* Not sure about the role of "text/plain" here... */
1938    if (info == (guint)TARGET_TEXT_URI_LIST)
1939	drag_handle_uri_list(context, data, time_, state, x, y);
1940    else
1941	drag_handle_text(context, data, time_, state);
1942
1943}
1944#endif /* FEAT_DND */
1945
1946
1947#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
1948/*
1949 * GnomeClient interact callback.  Check for unsaved buffers that cannot
1950 * be abandoned and pop up a dialog asking the user for confirmation if
1951 * necessary.
1952 */
1953    static void
1954sm_client_check_changed_any(GnomeClient	    *client UNUSED,
1955			    gint	    key,
1956			    GnomeDialogType type UNUSED,
1957			    gpointer	    data UNUSED)
1958{
1959    cmdmod_T	save_cmdmod;
1960    gboolean	shutdown_cancelled;
1961
1962    save_cmdmod = cmdmod;
1963
1964# ifdef FEAT_BROWSE
1965    cmdmod.browse = TRUE;
1966# endif
1967# if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
1968    cmdmod.confirm = TRUE;
1969# endif
1970    /*
1971     * If there are changed buffers, present the user with
1972     * a dialog if possible, otherwise give an error message.
1973     */
1974    shutdown_cancelled = check_changed_any(FALSE);
1975
1976    exiting = FALSE;
1977    cmdmod = save_cmdmod;
1978    setcursor(); /* position the cursor */
1979    out_flush();
1980    /*
1981     * If the user hit the [Cancel] button the whole shutdown
1982     * will be cancelled.  Wow, quite powerful feature (:
1983     */
1984    gnome_interaction_key_return(key, shutdown_cancelled);
1985}
1986
1987/*
1988 * Generate a script that can be used to restore the current editing session.
1989 * Save the value of v:this_session before running :mksession in order to make
1990 * automagic session save fully transparent.  Return TRUE on success.
1991 */
1992    static int
1993write_session_file(char_u *filename)
1994{
1995    char_u	    *escaped_filename;
1996    char	    *mksession_cmdline;
1997    unsigned int    save_ssop_flags;
1998    int		    failed;
1999
2000    /*
2001     * Build an ex command line to create a script that restores the current
2002     * session if executed.  Escape the filename to avoid nasty surprises.
2003     */
2004    escaped_filename = vim_strsave_escaped(filename, escape_chars);
2005    if (escaped_filename == NULL)
2006	return FALSE;
2007    mksession_cmdline = g_strconcat("mksession ", (char *)escaped_filename,
2008									NULL);
2009    vim_free(escaped_filename);
2010
2011    /*
2012     * Use a reasonable hardcoded set of 'sessionoptions' flags to avoid
2013     * unpredictable effects when the session is saved automatically.  Also,
2014     * we definitely need SSOP_GLOBALS to be able to restore v:this_session.
2015     * Don't use SSOP_BUFFERS to prevent the buffer list from becoming
2016     * enormously large if the GNOME session feature is used regularly.
2017     */
2018    save_ssop_flags = ssop_flags;
2019    ssop_flags = (SSOP_BLANK|SSOP_CURDIR|SSOP_FOLDS|SSOP_GLOBALS
2020		  |SSOP_HELP|SSOP_OPTIONS|SSOP_WINSIZE|SSOP_TABPAGES);
2021
2022    do_cmdline_cmd((char_u *)"let Save_VV_this_session = v:this_session");
2023    failed = (do_cmdline_cmd((char_u *)mksession_cmdline) == FAIL);
2024    do_cmdline_cmd((char_u *)"let v:this_session = Save_VV_this_session");
2025    do_unlet((char_u *)"Save_VV_this_session", TRUE);
2026
2027    ssop_flags = save_ssop_flags;
2028    g_free(mksession_cmdline);
2029    /*
2030     * Reopen the file and append a command to restore v:this_session,
2031     * as if this save never happened.	This is to avoid conflicts with
2032     * the user's own sessions.  FIXME: It's probably less hackish to add
2033     * a "stealth" flag to 'sessionoptions' -- gotta ask Bram.
2034     */
2035    if (!failed)
2036    {
2037	FILE *fd;
2038
2039	fd = open_exfile(filename, TRUE, APPENDBIN);
2040
2041	failed = (fd == NULL
2042	       || put_line(fd, "let v:this_session = Save_VV_this_session") == FAIL
2043	       || put_line(fd, "unlet Save_VV_this_session") == FAIL);
2044
2045	if (fd != NULL && fclose(fd) != 0)
2046	    failed = TRUE;
2047
2048	if (failed)
2049	    mch_remove(filename);
2050    }
2051
2052    return !failed;
2053}
2054
2055/*
2056 * "save_yourself" signal handler.  Initiate an interaction to ask the user
2057 * for confirmation if necessary.  Save the current editing session and tell
2058 * the session manager how to restart Vim.
2059 */
2060    static gboolean
2061sm_client_save_yourself(GnomeClient	    *client,
2062			gint		    phase UNUSED,
2063			GnomeSaveStyle	    save_style UNUSED,
2064			gboolean	    shutdown UNUSED,
2065			GnomeInteractStyle  interact_style,
2066			gboolean	    fast UNUSED,
2067			gpointer	    data UNUSED)
2068{
2069    static const char	suffix[] = "-session.vim";
2070    char		*session_file;
2071    unsigned int	len;
2072    gboolean		success;
2073
2074    /* Always request an interaction if possible.  check_changed_any()
2075     * won't actually show a dialog unless any buffers have been modified.
2076     * There doesn't seem to be an obvious way to check that without
2077     * automatically firing the dialog.  Anyway, it works just fine. */
2078    if (interact_style == GNOME_INTERACT_ANY)
2079	gnome_client_request_interaction(client, GNOME_DIALOG_NORMAL,
2080					 &sm_client_check_changed_any,
2081					 NULL);
2082    out_flush();
2083    ml_sync_all(FALSE, FALSE); /* preserve all swap files */
2084
2085    /* The path is unique for each session save.  We do neither know nor care
2086     * which session script will actually be used later.  This decision is in
2087     * the domain of the session manager. */
2088    session_file = gnome_config_get_real_path(
2089			gnome_client_get_config_prefix(client));
2090    len = strlen(session_file);
2091
2092    if (len > 0 && session_file[len-1] == G_DIR_SEPARATOR)
2093	--len; /* get rid of the superfluous trailing '/' */
2094
2095    session_file = g_renew(char, session_file, len + sizeof(suffix));
2096    memcpy(session_file + len, suffix, sizeof(suffix));
2097
2098    success = write_session_file((char_u *)session_file);
2099
2100    if (success)
2101    {
2102	const char  *argv[8];
2103	int	    i;
2104
2105	/* Tell the session manager how to wipe out the stored session data.
2106	 * This isn't as dangerous as it looks, don't worry :)	session_file
2107	 * is a unique absolute filename.  Usually it'll be something like
2108	 * `/home/user/.gnome2/vim-XXXXXX-session.vim'. */
2109	i = 0;
2110	argv[i++] = "rm";
2111	argv[i++] = session_file;
2112	argv[i] = NULL;
2113
2114	gnome_client_set_discard_command(client, i, (char **)argv);
2115
2116	/* Tell the session manager how to restore the just saved session.
2117	 * This is easily done thanks to Vim's -S option.  Pass the -f flag
2118	 * since there's no need to fork -- it might even cause confusion.
2119	 * Also pass the window role to give the WM something to match on.
2120	 * The role is set in gui_mch_open(), thus should _never_ be NULL. */
2121	i = 0;
2122	argv[i++] = restart_command;
2123	argv[i++] = "-f";
2124	argv[i++] = "-g";
2125	argv[i++] = "--role";
2126	argv[i++] = gtk_window_get_role(GTK_WINDOW(gui.mainwin));
2127	argv[i++] = "-S";
2128	argv[i++] = session_file;
2129	argv[i] = NULL;
2130
2131	gnome_client_set_restart_command(client, i, (char **)argv);
2132	gnome_client_set_clone_command(client, 0, NULL);
2133    }
2134
2135    g_free(session_file);
2136
2137    return success;
2138}
2139
2140/*
2141 * Called when the session manager wants us to die.  There isn't much to save
2142 * here since "save_yourself" has been emitted before (unless serious trouble
2143 * is happening).
2144 */
2145    static void
2146sm_client_die(GnomeClient *client UNUSED, gpointer data UNUSED)
2147{
2148    /* Don't write messages to the GUI anymore */
2149    full_screen = FALSE;
2150
2151    vim_strncpy(IObuff, (char_u *)
2152		    _("Vim: Received \"die\" request from session manager\n"),
2153		    IOSIZE - 1);
2154    preserve_exit();
2155}
2156
2157/*
2158 * Connect our signal handlers to be notified on session save and shutdown.
2159 */
2160    static void
2161setup_save_yourself(void)
2162{
2163    GnomeClient *client;
2164
2165    client = gnome_master_client();
2166
2167    if (client != NULL)
2168    {
2169	/* Must use the deprecated gtk_signal_connect() for compatibility
2170	 * with GNOME 1.  Arrgh, zombies! */
2171	gtk_signal_connect(GTK_OBJECT(client), "save_yourself",
2172			   GTK_SIGNAL_FUNC(&sm_client_save_yourself), NULL);
2173	gtk_signal_connect(GTK_OBJECT(client), "die",
2174			   GTK_SIGNAL_FUNC(&sm_client_die), NULL);
2175    }
2176}
2177
2178#else /* !(FEAT_GUI_GNOME && FEAT_SESSION) */
2179
2180# ifdef USE_XSMP
2181/*
2182 * GTK tells us that XSMP needs attention
2183 */
2184    static gboolean
2185local_xsmp_handle_requests(source, condition, data)
2186    GIOChannel		*source UNUSED;
2187    GIOCondition	condition;
2188    gpointer		data;
2189{
2190    if (condition == G_IO_IN)
2191    {
2192	/* Do stuff; maybe close connection */
2193	if (xsmp_handle_requests() == FAIL)
2194	    g_io_channel_unref((GIOChannel *)data);
2195	return TRUE;
2196    }
2197    /* Error */
2198    g_io_channel_unref((GIOChannel *)data);
2199    xsmp_close();
2200    return TRUE;
2201}
2202# endif /* USE_XSMP */
2203
2204/*
2205 * Setup the WM_PROTOCOLS to indicate we want the WM_SAVE_YOURSELF event.
2206 * This is an ugly use of X functions.	GTK doesn't offer an alternative.
2207 */
2208    static void
2209setup_save_yourself(void)
2210{
2211    Atom    *existing_atoms = NULL;
2212    int	    count = 0;
2213
2214#ifdef USE_XSMP
2215    if (xsmp_icefd != -1)
2216    {
2217	/*
2218	 * Use XSMP is preference to legacy WM_SAVE_YOURSELF;
2219	 * set up GTK IO monitor
2220	 */
2221	GIOChannel *g_io = g_io_channel_unix_new(xsmp_icefd);
2222
2223	g_io_add_watch(g_io, G_IO_IN | G_IO_ERR | G_IO_HUP,
2224				  local_xsmp_handle_requests, (gpointer)g_io);
2225    }
2226    else
2227#endif
2228    {
2229	/* Fall back to old method */
2230
2231	/* first get the existing value */
2232	if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
2233		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
2234		    &existing_atoms, &count))
2235	{
2236	    Atom	*new_atoms;
2237	    Atom	save_yourself_xatom;
2238	    int	i;
2239
2240	    save_yourself_xatom = GET_X_ATOM(save_yourself_atom);
2241
2242	    /* check if WM_SAVE_YOURSELF isn't there yet */
2243	    for (i = 0; i < count; ++i)
2244		if (existing_atoms[i] == save_yourself_xatom)
2245		    break;
2246
2247	    if (i == count)
2248	    {
2249		/* allocate an Atoms array which is one item longer */
2250		new_atoms = (Atom *)alloc((unsigned)((count + 1)
2251							     * sizeof(Atom)));
2252		if (new_atoms != NULL)
2253		{
2254		    memcpy(new_atoms, existing_atoms, count * sizeof(Atom));
2255		    new_atoms[count] = save_yourself_xatom;
2256		    XSetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
2257			    GDK_WINDOW_XWINDOW(gui.mainwin->window),
2258			    new_atoms, count + 1);
2259		    vim_free(new_atoms);
2260		}
2261	    }
2262	    XFree(existing_atoms);
2263	}
2264    }
2265}
2266
2267/*
2268 * Installing a global event filter seems to be the only way to catch
2269 * client messages of type WM_PROTOCOLS without overriding GDK's own
2270 * client message event filter.  Well, that's still better than trying
2271 * to guess what the GDK filter had done if it had been invoked instead
2272 *
2273 * GTK2_FIXME:	This doesn't seem to work.  For some reason we never
2274 * receive WM_SAVE_YOURSELF even though everything is set up correctly.
2275 * I have the nasty feeling modern session managers just don't send this
2276 * deprecated message anymore.	Addition: confirmed by several people.
2277 *
2278 * The GNOME session support is much cooler anyway.  Unlike this ugly
2279 * WM_SAVE_YOURSELF hack it actually stores the session...  And yes,
2280 * it should work with KDE as well.
2281 */
2282    static GdkFilterReturn
2283global_event_filter(GdkXEvent *xev,
2284		    GdkEvent *event UNUSED,
2285		    gpointer data UNUSED)
2286{
2287    XEvent *xevent = (XEvent *)xev;
2288
2289    if (xevent != NULL
2290	    && xevent->type == ClientMessage
2291	    && xevent->xclient.message_type == GET_X_ATOM(wm_protocols_atom)
2292	    && (long_u)xevent->xclient.data.l[0]
2293					    == GET_X_ATOM(save_yourself_atom))
2294    {
2295	out_flush();
2296	ml_sync_all(FALSE, FALSE); /* preserve all swap files */
2297	/*
2298	 * Set the window's WM_COMMAND property, to let the window manager
2299	 * know we are done saving ourselves.  We don't want to be
2300	 * restarted, thus set argv to NULL.
2301	 */
2302	XSetCommand(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
2303		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
2304		    NULL, 0);
2305	return GDK_FILTER_REMOVE;
2306    }
2307
2308    return GDK_FILTER_CONTINUE;
2309}
2310#endif /* !(FEAT_GUI_GNOME && FEAT_SESSION) */
2311
2312
2313/*
2314 * Setup the window icon & xcmdsrv comm after the main window has been realized.
2315 */
2316    static void
2317mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
2318{
2319/* If you get an error message here, you still need to unpack the runtime
2320 * archive! */
2321#ifdef magick
2322# undef magick
2323#endif
2324  /* A bit hackish, but avoids casting later and allows optimization */
2325# define static static const
2326#define magick vim32x32
2327#include "../runtime/vim32x32.xpm"
2328#undef magick
2329#define magick vim16x16
2330#include "../runtime/vim16x16.xpm"
2331#undef magick
2332#define magick vim48x48
2333#include "../runtime/vim48x48.xpm"
2334#undef magick
2335# undef static
2336
2337    /* When started with "--echo-wid" argument, write window ID on stdout. */
2338    if (echo_wid_arg)
2339    {
2340	printf("WID: %ld\n", (long)GDK_WINDOW_XWINDOW(gui.mainwin->window));
2341	fflush(stdout);
2342    }
2343
2344    if (vim_strchr(p_go, GO_ICON) != NULL)
2345    {
2346	/*
2347	 * Add an icon to the main window. For fun and convenience of the user.
2348	 */
2349	GList *icons = NULL;
2350
2351	icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim16x16));
2352	icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim32x32));
2353	icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim48x48));
2354
2355	gtk_window_set_icon_list(GTK_WINDOW(gui.mainwin), icons);
2356
2357	g_list_foreach(icons, (GFunc)&g_object_unref, NULL);
2358	g_list_free(icons);
2359    }
2360
2361#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
2362    /* Register a handler for WM_SAVE_YOURSELF with GDK's low-level X I/F */
2363    gdk_window_add_filter(NULL, &global_event_filter, NULL);
2364#endif
2365    /* Setup to indicate to the window manager that we want to catch the
2366     * WM_SAVE_YOURSELF event.	For GNOME, this connects to the session
2367     * manager instead. */
2368#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
2369    if (using_gnome)
2370#endif
2371	setup_save_yourself();
2372
2373#ifdef FEAT_CLIENTSERVER
2374    if (serverName == NULL && serverDelayedStartName != NULL)
2375    {
2376	/* This is a :gui command in a plain vim with no previous server */
2377	commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window);
2378
2379	(void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
2380				 serverDelayedStartName);
2381    }
2382    else
2383    {
2384	/*
2385	 * Cannot handle "XLib-only" windows with gtk event routines, we'll
2386	 * have to change the "server" registration to that of the main window
2387	 * If we have not registered a name yet, remember the window
2388	 */
2389	serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
2390				     GDK_WINDOW_XWINDOW(gui.mainwin->window));
2391    }
2392    gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
2393    gtk_signal_connect(GTK_OBJECT(gui.mainwin), "property_notify_event",
2394		       GTK_SIGNAL_FUNC(property_event), NULL);
2395#endif
2396}
2397
2398    static GdkCursor *
2399create_blank_pointer(void)
2400{
2401    GdkWindow	*root_window = NULL;
2402    GdkPixmap	*blank_mask;
2403    GdkCursor	*cursor;
2404    GdkColor	color = { 0, 0, 0, 0 };
2405    char	blank_data[] = { 0x0 };
2406
2407#ifdef HAVE_GTK_MULTIHEAD
2408    root_window = gtk_widget_get_root_window(gui.mainwin);
2409#endif
2410
2411    /* Create a pseudo blank pointer, which is in fact one pixel by one pixel
2412     * in size. */
2413    blank_mask = gdk_bitmap_create_from_data(root_window, blank_data, 1, 1);
2414    cursor = gdk_cursor_new_from_pixmap(blank_mask, blank_mask,
2415					&color, &color, 0, 0);
2416    gdk_bitmap_unref(blank_mask);
2417
2418    return cursor;
2419}
2420
2421#ifdef HAVE_GTK_MULTIHEAD
2422    static void
2423mainwin_screen_changed_cb(GtkWidget  *widget,
2424			  GdkScreen  *previous_screen UNUSED,
2425			  gpointer   data UNUSED)
2426{
2427    if (!gtk_widget_has_screen(widget))
2428	return;
2429
2430    /*
2431     * Recreate the invisible mouse cursor.
2432     */
2433    if (gui.blank_pointer != NULL)
2434	gdk_cursor_unref(gui.blank_pointer);
2435
2436    gui.blank_pointer = create_blank_pointer();
2437
2438    if (gui.pointer_hidden && gui.drawarea->window != NULL)
2439	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
2440
2441    /*
2442     * Create a new PangoContext for this screen, and initialize it
2443     * with the current font if necessary.
2444     */
2445    if (gui.text_context != NULL)
2446	g_object_unref(gui.text_context);
2447
2448    gui.text_context = gtk_widget_create_pango_context(widget);
2449    pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
2450
2451    if (gui.norm_font != NULL)
2452    {
2453	gui_mch_init_font(p_guifont, FALSE);
2454	gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
2455    }
2456}
2457#endif /* HAVE_GTK_MULTIHEAD */
2458
2459/*
2460 * After the drawing area comes up, we calculate all colors and create the
2461 * dummy blank cursor.
2462 *
2463 * Don't try to set any VIM scrollbar sizes anywhere here. I'm relying on the
2464 * fact that the main VIM engine doesn't take them into account anywhere.
2465 */
2466    static void
2467drawarea_realize_cb(GtkWidget *widget, gpointer data UNUSED)
2468{
2469    GtkWidget *sbar;
2470
2471#ifdef FEAT_XIM
2472    xim_init();
2473#endif
2474    gui_mch_new_colors();
2475    gui.text_gc = gdk_gc_new(gui.drawarea->window);
2476
2477    gui.blank_pointer = create_blank_pointer();
2478    if (gui.pointer_hidden)
2479	gdk_window_set_cursor(widget->window, gui.blank_pointer);
2480
2481    /* get the actual size of the scrollbars, if they are realized */
2482    sbar = firstwin->w_scrollbars[SBAR_LEFT].id;
2483    if (!sbar || (!gui.which_scrollbars[SBAR_LEFT]
2484				    && firstwin->w_scrollbars[SBAR_RIGHT].id))
2485	sbar = firstwin->w_scrollbars[SBAR_RIGHT].id;
2486    if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.width)
2487	gui.scrollbar_width = sbar->allocation.width;
2488
2489    sbar = gui.bottom_sbar.id;
2490    if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.height)
2491	gui.scrollbar_height = sbar->allocation.height;
2492}
2493
2494/*
2495 * Properly clean up on shutdown.
2496 */
2497    static void
2498drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
2499{
2500    /* Don't write messages to the GUI anymore */
2501    full_screen = FALSE;
2502
2503#ifdef FEAT_XIM
2504    im_shutdown();
2505#endif
2506    if (gui.ascii_glyphs != NULL)
2507    {
2508	pango_glyph_string_free(gui.ascii_glyphs);
2509	gui.ascii_glyphs = NULL;
2510    }
2511    if (gui.ascii_font != NULL)
2512    {
2513	g_object_unref(gui.ascii_font);
2514	gui.ascii_font = NULL;
2515    }
2516    g_object_unref(gui.text_context);
2517    gui.text_context = NULL;
2518
2519    g_object_unref(gui.text_gc);
2520    gui.text_gc = NULL;
2521
2522    gdk_cursor_unref(gui.blank_pointer);
2523    gui.blank_pointer = NULL;
2524}
2525
2526    static void
2527drawarea_style_set_cb(GtkWidget	*widget UNUSED,
2528		      GtkStyle	*previous_style UNUSED,
2529		      gpointer	data UNUSED)
2530{
2531    gui_mch_new_colors();
2532}
2533
2534/*
2535 * Callback routine for the "delete_event" signal on the toplevel window.
2536 * Tries to vim gracefully, or refuses to exit with changed buffers.
2537 */
2538    static gint
2539delete_event_cb(GtkWidget *widget UNUSED,
2540		GdkEventAny *event UNUSED,
2541		gpointer data UNUSED)
2542{
2543    gui_shell_closed();
2544    return TRUE;
2545}
2546
2547#if defined(FEAT_MENU) || defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
2548    static int
2549get_item_dimensions(GtkWidget *widget, GtkOrientation orientation)
2550{
2551    GtkOrientation item_orientation = GTK_ORIENTATION_HORIZONTAL;
2552
2553#ifdef FEAT_GUI_GNOME
2554    if (using_gnome && widget != NULL)
2555    {
2556	GtkWidget *parent;
2557	BonoboDockItem *dockitem;
2558
2559	parent	 = gtk_widget_get_parent(widget);
2560	if (G_TYPE_FROM_INSTANCE(parent) == BONOBO_TYPE_DOCK_ITEM)
2561	{
2562	    /* Only menu & toolbar are dock items.  Could tabline be?
2563	     * Seem to be only the 2 defined in GNOME */
2564	    widget = parent;
2565	    dockitem = BONOBO_DOCK_ITEM(widget);
2566
2567	    if (dockitem == NULL || dockitem->is_floating)
2568		return 0;
2569	    item_orientation = bonobo_dock_item_get_orientation(dockitem);
2570	}
2571    }
2572#endif
2573    if (widget != NULL
2574	    && item_orientation == orientation
2575	    && GTK_WIDGET_REALIZED(widget)
2576	    && GTK_WIDGET_VISIBLE(widget))
2577    {
2578	if (orientation == GTK_ORIENTATION_HORIZONTAL)
2579	    return widget->allocation.height;
2580	else
2581	    return widget->allocation.width;
2582    }
2583    return 0;
2584}
2585#endif
2586
2587    static int
2588get_menu_tool_width(void)
2589{
2590    int width = 0;
2591
2592#ifdef FEAT_GUI_GNOME /* these are never vertical without GNOME */
2593# ifdef FEAT_MENU
2594    width += get_item_dimensions(gui.menubar, GTK_ORIENTATION_VERTICAL);
2595# endif
2596# ifdef FEAT_TOOLBAR
2597    width += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_VERTICAL);
2598# endif
2599# ifdef FEAT_GUI_TABLINE
2600    if (gui.tabline != NULL)
2601	width += get_item_dimensions(gui.tabline, GTK_ORIENTATION_VERTICAL);
2602# endif
2603#endif
2604
2605    return width;
2606}
2607
2608    static int
2609get_menu_tool_height(void)
2610{
2611    int height = 0;
2612
2613#ifdef FEAT_MENU
2614    height += get_item_dimensions(gui.menubar, GTK_ORIENTATION_HORIZONTAL);
2615#endif
2616#ifdef FEAT_TOOLBAR
2617    height += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_HORIZONTAL);
2618#endif
2619#ifdef FEAT_GUI_TABLINE
2620    if (gui.tabline != NULL)
2621	height += get_item_dimensions(gui.tabline, GTK_ORIENTATION_HORIZONTAL);
2622#endif
2623
2624    return height;
2625}
2626
2627/* This controls whether we can set the real window hints at
2628 * start-up when in a GtkPlug.
2629 * 0 = normal processing (default)
2630 * 1 = init. hints set, no-one's tried to reset since last check
2631 * 2 = init. hints set, attempt made to change hints
2632 */
2633static int init_window_hints_state = 0;
2634
2635    static void
2636update_window_manager_hints(int force_width, int force_height)
2637{
2638    static int old_width  = 0;
2639    static int old_height = 0;
2640    static int old_min_width  = 0;
2641    static int old_min_height = 0;
2642    static int old_char_width  = 0;
2643    static int old_char_height = 0;
2644
2645    int width;
2646    int height;
2647    int min_width;
2648    int min_height;
2649
2650    /* At start-up, don't try to set the hints until the initial
2651     * values have been used (those that dictate our initial size)
2652     * Let forced (i.e., correct) values through always.
2653     */
2654    if (!(force_width && force_height)  &&  init_window_hints_state > 0)
2655    {
2656	/* Don't do it! */
2657	init_window_hints_state = 2;
2658	return;
2659    }
2660
2661    /* This also needs to be done when the main window isn't there yet,
2662     * otherwise the hints don't work. */
2663    width  = gui_get_base_width();
2664    height = gui_get_base_height();
2665# ifdef FEAT_MENU
2666    height += tabline_height() * gui.char_height;
2667# endif
2668    width  += get_menu_tool_width();
2669    height += get_menu_tool_height();
2670
2671    /* GtkSockets use GtkPlug's [gui,mainwin] min-size hints to determine
2672     * their actual widget size.  When we set our size ourselves (e.g.,
2673     * 'set columns=' or init. -geom) we briefly set the min. to the size
2674     * we wish to be instead of the legitimate minimum so that we actually
2675     * resize correctly.
2676     */
2677    if (force_width && force_height)
2678    {
2679	min_width  = force_width;
2680	min_height = force_height;
2681    }
2682    else
2683    {
2684	min_width  = width  + MIN_COLUMNS * gui.char_width;
2685	min_height = height + MIN_LINES   * gui.char_height;
2686    }
2687
2688    /* Avoid an expose event when the size didn't change. */
2689    if (width != old_width
2690	    || height != old_height
2691	    || min_width != old_min_width
2692	    || min_height != old_min_height
2693	    || gui.char_width != old_char_width
2694	    || gui.char_height != old_char_height)
2695    {
2696	GdkGeometry	geometry;
2697	GdkWindowHints	geometry_mask;
2698
2699	geometry.width_inc   = gui.char_width;
2700	geometry.height_inc  = gui.char_height;
2701	geometry.base_width  = width;
2702	geometry.base_height = height;
2703	geometry.min_width   = min_width;
2704	geometry.min_height  = min_height;
2705	geometry_mask	     = GDK_HINT_BASE_SIZE|GDK_HINT_RESIZE_INC
2706			       |GDK_HINT_MIN_SIZE;
2707	/* Using gui.formwin as geometry widget doesn't work as expected
2708	 * with GTK+ 2 -- dunno why.  Presumably all the resizing hacks
2709	 * in Vim confuse GTK+. */
2710	gtk_window_set_geometry_hints(GTK_WINDOW(gui.mainwin), gui.mainwin,
2711				      &geometry, geometry_mask);
2712	old_width       = width;
2713	old_height      = height;
2714	old_min_width   = min_width;
2715	old_min_height  = min_height;
2716	old_char_width  = gui.char_width;
2717	old_char_height = gui.char_height;
2718    }
2719}
2720
2721#ifdef FEAT_TOOLBAR
2722
2723/*
2724 * This extra effort wouldn't be necessary if we only used stock icons in the
2725 * toolbar, as we do for all builtin icons.  But user-defined toolbar icons
2726 * shouldn't be treated differently, thus we do need this.
2727 */
2728    static void
2729icon_size_changed_foreach(GtkWidget *widget, gpointer user_data)
2730{
2731    if (GTK_IS_IMAGE(widget))
2732    {
2733	GtkImage *image = (GtkImage *)widget;
2734
2735	/* User-defined icons are stored in a GtkIconSet */
2736	if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_SET)
2737	{
2738	    GtkIconSet	*icon_set;
2739	    GtkIconSize	icon_size;
2740
2741	    gtk_image_get_icon_set(image, &icon_set, &icon_size);
2742	    icon_size = (GtkIconSize)(long)user_data;
2743
2744	    gtk_icon_set_ref(icon_set);
2745	    gtk_image_set_from_icon_set(image, icon_set, icon_size);
2746	    gtk_icon_set_unref(icon_set);
2747	}
2748    }
2749    else if (GTK_IS_CONTAINER(widget))
2750    {
2751	gtk_container_foreach((GtkContainer *)widget,
2752			      &icon_size_changed_foreach,
2753			      user_data);
2754    }
2755}
2756
2757    static void
2758set_toolbar_style(GtkToolbar *toolbar)
2759{
2760    GtkToolbarStyle style;
2761    GtkIconSize	    size;
2762    GtkIconSize	    oldsize;
2763
2764    if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ))
2765		      == (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ))
2766	style = GTK_TOOLBAR_BOTH_HORIZ;
2767    else if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS))
2768		      == (TOOLBAR_TEXT | TOOLBAR_ICONS))
2769	style = GTK_TOOLBAR_BOTH;
2770    else if (toolbar_flags & TOOLBAR_TEXT)
2771	style = GTK_TOOLBAR_TEXT;
2772    else
2773	style = GTK_TOOLBAR_ICONS;
2774
2775    gtk_toolbar_set_style(toolbar, style);
2776    gtk_toolbar_set_tooltips(toolbar, (toolbar_flags & TOOLBAR_TOOLTIPS) != 0);
2777
2778    switch (tbis_flags)
2779    {
2780	case TBIS_TINY:	    size = GTK_ICON_SIZE_MENU;		break;
2781	case TBIS_SMALL:    size = GTK_ICON_SIZE_SMALL_TOOLBAR;	break;
2782	case TBIS_MEDIUM:   size = GTK_ICON_SIZE_BUTTON;	break;
2783	case TBIS_LARGE:    size = GTK_ICON_SIZE_LARGE_TOOLBAR;	break;
2784	default:	    size = GTK_ICON_SIZE_INVALID;	break;
2785    }
2786    oldsize = gtk_toolbar_get_icon_size(toolbar);
2787
2788    if (size == GTK_ICON_SIZE_INVALID)
2789    {
2790	/* Let global user preferences decide the icon size. */
2791	gtk_toolbar_unset_icon_size(toolbar);
2792	size = gtk_toolbar_get_icon_size(toolbar);
2793    }
2794    if (size != oldsize)
2795    {
2796	gtk_container_foreach(GTK_CONTAINER(toolbar),
2797			      &icon_size_changed_foreach,
2798			      GINT_TO_POINTER((int)size));
2799    }
2800    gtk_toolbar_set_icon_size(toolbar, size);
2801}
2802
2803#endif /* FEAT_TOOLBAR */
2804
2805#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
2806static int ignore_tabline_evt = FALSE;
2807static GtkWidget *tabline_menu;
2808static GtkTooltips *tabline_tooltip;
2809static int clicked_page;	    /* page clicked in tab line */
2810
2811/*
2812 * Handle selecting an item in the tab line popup menu.
2813 */
2814    static void
2815tabline_menu_handler(GtkMenuItem *item UNUSED, gpointer user_data)
2816{
2817    /* Add the string cmd into input buffer */
2818    send_tabline_menu_event(clicked_page, (int)(long)user_data);
2819}
2820
2821    static void
2822add_tabline_menu_item(GtkWidget *menu, char_u *text, int resp)
2823{
2824    GtkWidget	*item;
2825    char_u	*utf_text;
2826
2827    utf_text = CONVERT_TO_UTF8(text);
2828    item = gtk_menu_item_new_with_label((const char *)utf_text);
2829    gtk_widget_show(item);
2830    CONVERT_TO_UTF8_FREE(utf_text);
2831
2832    gtk_container_add(GTK_CONTAINER(menu), item);
2833    gtk_signal_connect(GTK_OBJECT(item), "activate",
2834	    GTK_SIGNAL_FUNC(tabline_menu_handler),
2835	    (gpointer)(long)resp);
2836}
2837
2838/*
2839 * Create a menu for the tab line.
2840 */
2841    static GtkWidget *
2842create_tabline_menu(void)
2843{
2844    GtkWidget *menu;
2845
2846    menu = gtk_menu_new();
2847    add_tabline_menu_item(menu, (char_u *)_("Close"), TABLINE_MENU_CLOSE);
2848    add_tabline_menu_item(menu, (char_u *)_("New tab"), TABLINE_MENU_NEW);
2849    add_tabline_menu_item(menu, (char_u *)_("Open Tab..."), TABLINE_MENU_OPEN);
2850
2851    return menu;
2852}
2853
2854    static gboolean
2855on_tabline_menu(GtkWidget *widget, GdkEvent *event)
2856{
2857    /* Was this button press event ? */
2858    if (event->type == GDK_BUTTON_PRESS)
2859    {
2860	GdkEventButton *bevent = (GdkEventButton *)event;
2861	int		x = bevent->x;
2862	int		y = bevent->y;
2863	GtkWidget	*tabwidget;
2864	GdkWindow	*tabwin;
2865
2866	/* When ignoring events return TRUE so that the selected page doesn't
2867	 * change. */
2868	if (hold_gui_events
2869# ifdef FEAT_CMDWIN
2870		|| cmdwin_type != 0
2871# endif
2872	   )
2873	    return TRUE;
2874
2875	tabwin = gdk_window_at_pointer(&x, &y);
2876	gdk_window_get_user_data(tabwin, (gpointer)&tabwidget);
2877	clicked_page = (int)(long)gtk_object_get_user_data(
2878						       GTK_OBJECT(tabwidget));
2879
2880	/* If the event was generated for 3rd button popup the menu. */
2881	if (bevent->button == 3)
2882	{
2883	    gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL,
2884						bevent->button, bevent->time);
2885	    /* We handled the event. */
2886	    return TRUE;
2887	}
2888	else if (bevent->button == 1)
2889	{
2890	    if (clicked_page == 0)
2891	    {
2892		/* Click after all tabs moves to next tab page.  When "x" is
2893		 * small guess it's the left button. */
2894		send_tabline_event(x < 50 ? -1 : 0);
2895	    }
2896	}
2897    }
2898
2899    /* We didn't handle the event. */
2900    return FALSE;
2901}
2902
2903/*
2904 * Handle selecting one of the tabs.
2905 */
2906    static void
2907on_select_tab(
2908	GtkNotebook	*notebook UNUSED,
2909	GtkNotebookPage *page UNUSED,
2910	gint		idx,
2911	gpointer	data UNUSED)
2912{
2913    if (!ignore_tabline_evt)
2914    {
2915	send_tabline_event(idx + 1);
2916    }
2917}
2918
2919/*
2920 * Show or hide the tabline.
2921 */
2922    void
2923gui_mch_show_tabline(int showit)
2924{
2925    if (gui.tabline == NULL)
2926	return;
2927
2928    if (!showit != !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline)))
2929    {
2930	/* Note: this may cause a resize event */
2931	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit);
2932	update_window_manager_hints(0, 0);
2933	if (showit)
2934	    GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(gui.tabline), GTK_CAN_FOCUS);
2935    }
2936
2937    gui_mch_update();
2938}
2939
2940/*
2941 * Return TRUE when tabline is displayed.
2942 */
2943    int
2944gui_mch_showing_tabline(void)
2945{
2946    return gui.tabline != NULL
2947		     && gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline));
2948}
2949
2950/*
2951 * Update the labels of the tabline.
2952 */
2953    void
2954gui_mch_update_tabline(void)
2955{
2956    GtkWidget	    *page;
2957    GtkWidget	    *event_box;
2958    GtkWidget	    *label;
2959    tabpage_T	    *tp;
2960    int		    nr = 0;
2961    int		    tab_num;
2962    int		    curtabidx = 0;
2963    char_u	    *labeltext;
2964
2965    if (gui.tabline == NULL)
2966	return;
2967
2968    ignore_tabline_evt = TRUE;
2969
2970    /* Add a label for each tab page.  They all contain the same text area. */
2971    for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
2972    {
2973	if (tp == curtab)
2974	    curtabidx = nr;
2975
2976	tab_num = nr + 1;
2977
2978	page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr);
2979	if (page == NULL)
2980	{
2981	    /* Add notebook page */
2982	    page = gtk_vbox_new(FALSE, 0);
2983	    gtk_widget_show(page);
2984	    event_box = gtk_event_box_new();
2985	    gtk_widget_show(event_box);
2986	    label = gtk_label_new("-Empty-");
2987	    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
2988	    gtk_container_add(GTK_CONTAINER(event_box), label);
2989	    gtk_widget_show(label);
2990	    gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline),
2991		    page,
2992		    event_box,
2993		    nr++);
2994	}
2995
2996	event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page);
2997	gtk_object_set_user_data(GTK_OBJECT(event_box),
2998						     (gpointer)(long)tab_num);
2999	label = GTK_BIN(event_box)->child;
3000	get_tabline_label(tp, FALSE);
3001	labeltext = CONVERT_TO_UTF8(NameBuff);
3002	gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext);
3003	CONVERT_TO_UTF8_FREE(labeltext);
3004
3005	get_tabline_label(tp, TRUE);
3006	labeltext = CONVERT_TO_UTF8(NameBuff);
3007	gtk_tooltips_set_tip(GTK_TOOLTIPS(tabline_tooltip), event_box,
3008			     (const char *)labeltext, NULL);
3009	CONVERT_TO_UTF8_FREE(labeltext);
3010    }
3011
3012    /* Remove any old labels. */
3013    while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL)
3014	gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr);
3015
3016    if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
3017	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
3018
3019    /* Make sure everything is in place before drawing text. */
3020    gui_mch_update();
3021
3022    ignore_tabline_evt = FALSE;
3023}
3024
3025/*
3026 * Set the current tab to "nr".  First tab is 1.
3027 */
3028    void
3029gui_mch_set_curtab(nr)
3030    int		nr;
3031{
3032    if (gui.tabline == NULL)
3033	return;
3034
3035    ignore_tabline_evt = TRUE;
3036    if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
3037	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
3038    ignore_tabline_evt = FALSE;
3039}
3040
3041#endif /* FEAT_GUI_TABLINE */
3042
3043/*
3044 * Add selection targets for PRIMARY and CLIPBOARD selections.
3045 */
3046    void
3047gui_gtk_set_selection_targets(void)
3048{
3049    int		    i, j = 0;
3050    int		    n_targets = N_SELECTION_TARGETS;
3051    GtkTargetEntry  targets[N_SELECTION_TARGETS];
3052
3053    for (i = 0; i < (int)N_SELECTION_TARGETS; ++i)
3054    {
3055	/* OpenOffice tries to use TARGET_HTML and fails when it doesn't
3056	 * return something, instead of trying another target. Therefore only
3057	 * offer TARGET_HTML when it works. */
3058	if (!clip_html && selection_targets[i].info == TARGET_HTML)
3059	    n_targets--;
3060	else
3061	    targets[j++] = selection_targets[i];
3062    }
3063
3064    gtk_selection_clear_targets(gui.drawarea, (GdkAtom)GDK_SELECTION_PRIMARY);
3065    gtk_selection_clear_targets(gui.drawarea, (GdkAtom)clip_plus.gtk_sel_atom);
3066    gtk_selection_add_targets(gui.drawarea,
3067			      (GdkAtom)GDK_SELECTION_PRIMARY,
3068			      targets, n_targets);
3069    gtk_selection_add_targets(gui.drawarea,
3070			      (GdkAtom)clip_plus.gtk_sel_atom,
3071			      targets, n_targets);
3072}
3073
3074/*
3075 * Set up for receiving DND items.
3076 */
3077    void
3078gui_gtk_set_dnd_targets(void)
3079{
3080    int		    i, j = 0;
3081    int		    n_targets = N_DND_TARGETS;
3082    GtkTargetEntry  targets[N_DND_TARGETS];
3083
3084    for (i = 0; i < (int)N_DND_TARGETS; ++i)
3085    {
3086	if (!clip_html && selection_targets[i].info == TARGET_HTML)
3087	    n_targets--;
3088	else
3089	    targets[j++] = dnd_targets[i];
3090    }
3091
3092    gtk_drag_dest_unset(gui.drawarea);
3093    gtk_drag_dest_set(gui.drawarea,
3094		      GTK_DEST_DEFAULT_ALL,
3095		      targets, n_targets,
3096		      GDK_ACTION_COPY);
3097}
3098
3099/*
3100 * Initialize the GUI.	Create all the windows, set up all the callbacks etc.
3101 * Returns OK for success, FAIL when the GUI can't be started.
3102 */
3103    int
3104gui_mch_init(void)
3105{
3106    GtkWidget *vbox;
3107
3108#ifdef FEAT_GUI_GNOME
3109    /* Initialize the GNOME libraries.	gnome_program_init()/gnome_init()
3110     * exits on failure, but that's a non-issue because we already called
3111     * gtk_init_check() in gui_mch_init_check(). */
3112    if (using_gnome)
3113	gnome_program_init(VIMPACKAGE, VIM_VERSION_SHORT,
3114			   LIBGNOMEUI_MODULE, gui_argc, gui_argv, NULL);
3115#endif
3116    vim_free(gui_argv);
3117    gui_argv = NULL;
3118
3119#if GLIB_CHECK_VERSION(2,1,3)
3120    /* Set the human-readable application name */
3121    g_set_application_name("Vim");
3122#endif
3123    /*
3124     * Force UTF-8 output no matter what the value of 'encoding' is.
3125     * did_set_string_option() in option.c prohibits changing 'termencoding'
3126     * to something else than UTF-8 if the GUI is in use.
3127     */
3128    set_option_value((char_u *)"termencoding", 0L, (char_u *)"utf-8", 0);
3129
3130#ifdef FEAT_TOOLBAR
3131    gui_gtk_register_stock_icons();
3132#endif
3133    /* FIXME: Need to install the classic icons and a gtkrc.classic file.
3134     * The hard part is deciding install locations and the Makefile magic. */
3135#if 0
3136    gtk_rc_parse("gtkrc");
3137#endif
3138
3139    /* Initialize values */
3140    gui.border_width = 2;
3141    gui.scrollbar_width = SB_DEFAULT_WIDTH;
3142    gui.scrollbar_height = SB_DEFAULT_WIDTH;
3143    /* LINTED: avoid warning: conversion to 'unsigned long' */
3144    gui.fgcolor = g_new0(GdkColor, 1);
3145    /* LINTED: avoid warning: conversion to 'unsigned long' */
3146    gui.bgcolor = g_new0(GdkColor, 1);
3147    /* LINTED: avoid warning: conversion to 'unsigned long' */
3148    gui.spcolor = g_new0(GdkColor, 1);
3149
3150    /* Initialise atoms */
3151    html_atom = gdk_atom_intern("text/html", FALSE);
3152    utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
3153
3154    /* Set default foreground and background colors. */
3155    gui.norm_pixel = gui.def_norm_pixel;
3156    gui.back_pixel = gui.def_back_pixel;
3157
3158    if (gtk_socket_id != 0)
3159    {
3160	GtkWidget *plug;
3161
3162	/* Use GtkSocket from another app. */
3163#ifdef HAVE_GTK_MULTIHEAD
3164	plug = gtk_plug_new_for_display(gdk_display_get_default(),
3165					gtk_socket_id);
3166#else
3167	plug = gtk_plug_new(gtk_socket_id);
3168#endif
3169	if (plug != NULL && GTK_PLUG(plug)->socket_window != NULL)
3170	{
3171	    gui.mainwin = plug;
3172	}
3173	else
3174	{
3175	    g_warning("Connection to GTK+ socket (ID %u) failed",
3176		      (unsigned int)gtk_socket_id);
3177	    /* Pretend we never wanted it if it failed (get own window) */
3178	    gtk_socket_id = 0;
3179	}
3180    }
3181
3182    if (gtk_socket_id == 0)
3183    {
3184#ifdef FEAT_GUI_GNOME
3185	if (using_gnome)
3186	{
3187	    gui.mainwin = gnome_app_new("Vim", NULL);
3188# ifdef USE_XSMP
3189	    /* Use the GNOME save-yourself functionality now. */
3190	    xsmp_close();
3191# endif
3192	}
3193	else
3194#endif
3195	    gui.mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3196    }
3197
3198    gtk_widget_set_name(gui.mainwin, "vim-main-window");
3199
3200    /* Create the PangoContext used for drawing all text. */
3201    gui.text_context = gtk_widget_create_pango_context(gui.mainwin);
3202    pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
3203
3204    gtk_container_border_width(GTK_CONTAINER(gui.mainwin), 0);
3205    gtk_widget_add_events(gui.mainwin, GDK_VISIBILITY_NOTIFY_MASK);
3206
3207    gtk_signal_connect(GTK_OBJECT(gui.mainwin), "delete_event",
3208		       GTK_SIGNAL_FUNC(&delete_event_cb), NULL);
3209
3210    gtk_signal_connect(GTK_OBJECT(gui.mainwin), "realize",
3211		       GTK_SIGNAL_FUNC(&mainwin_realize), NULL);
3212#ifdef HAVE_GTK_MULTIHEAD
3213    g_signal_connect(G_OBJECT(gui.mainwin), "screen_changed",
3214		     G_CALLBACK(&mainwin_screen_changed_cb), NULL);
3215#endif
3216    gui.accel_group = gtk_accel_group_new();
3217    gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group);
3218
3219    /* A vertical box holds the menubar, toolbar and main text window. */
3220    vbox = gtk_vbox_new(FALSE, 0);
3221
3222#ifdef FEAT_GUI_GNOME
3223    if (using_gnome)
3224    {
3225# if defined(FEAT_MENU)
3226	/* automagically restore menubar/toolbar placement */
3227	gnome_app_enable_layout_config(GNOME_APP(gui.mainwin), TRUE);
3228# endif
3229	gnome_app_set_contents(GNOME_APP(gui.mainwin), vbox);
3230    }
3231    else
3232#endif
3233    {
3234	gtk_container_add(GTK_CONTAINER(gui.mainwin), vbox);
3235	gtk_widget_show(vbox);
3236    }
3237
3238#ifdef FEAT_MENU
3239    /*
3240     * Create the menubar and handle
3241     */
3242    gui.menubar = gtk_menu_bar_new();
3243    gtk_widget_set_name(gui.menubar, "vim-menubar");
3244
3245    /* Avoid that GTK takes <F10> away from us. */
3246    {
3247	GtkSettings *gtk_settings;
3248
3249	gtk_settings = gtk_settings_get_for_screen(gdk_screen_get_default());
3250	g_object_set(gtk_settings, "gtk-menu-bar-accel", NULL, NULL);
3251    }
3252
3253
3254# ifdef FEAT_GUI_GNOME
3255    if (using_gnome)
3256    {
3257	BonoboDockItem *dockitem;
3258
3259	gnome_app_set_menus(GNOME_APP(gui.mainwin), GTK_MENU_BAR(gui.menubar));
3260	dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin),
3261						   GNOME_APP_MENUBAR_NAME);
3262	/* We don't want the menu to float. */
3263	bonobo_dock_item_set_behavior(dockitem,
3264		bonobo_dock_item_get_behavior(dockitem)
3265				       | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING);
3266	gui.menubar_h = GTK_WIDGET(dockitem);
3267    }
3268    else
3269# endif	/* FEAT_GUI_GNOME */
3270    {
3271	/* Always show the menubar, otherwise <F10> doesn't work.  It may be
3272	 * disabled in gui_init() later. */
3273	gtk_widget_show(gui.menubar);
3274	gtk_box_pack_start(GTK_BOX(vbox), gui.menubar, FALSE, FALSE, 0);
3275    }
3276#endif	/* FEAT_MENU */
3277
3278#ifdef FEAT_TOOLBAR
3279    /*
3280     * Create the toolbar and handle
3281     */
3282    /* some aesthetics on the toolbar */
3283    gtk_rc_parse_string(
3284	    "style \"vim-toolbar-style\" {\n"
3285	    "  GtkToolbar::button_relief = GTK_RELIEF_NONE\n"
3286	    "}\n"
3287	    "widget \"*.vim-toolbar\" style \"vim-toolbar-style\"\n");
3288    gui.toolbar = gtk_toolbar_new();
3289    gtk_widget_set_name(gui.toolbar, "vim-toolbar");
3290    set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
3291
3292# ifdef FEAT_GUI_GNOME
3293    if (using_gnome)
3294    {
3295	BonoboDockItem *dockitem;
3296
3297	gnome_app_set_toolbar(GNOME_APP(gui.mainwin), GTK_TOOLBAR(gui.toolbar));
3298	dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin),
3299						   GNOME_APP_TOOLBAR_NAME);
3300	gui.toolbar_h = GTK_WIDGET(dockitem);
3301	/* When the toolbar is floating it gets stuck.  So long as that isn't
3302	 * fixed let's disallow floating. */
3303	bonobo_dock_item_set_behavior(dockitem,
3304		bonobo_dock_item_get_behavior(dockitem)
3305				       | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING);
3306	gtk_container_set_border_width(GTK_CONTAINER(gui.toolbar), 0);
3307    }
3308    else
3309# endif	/* FEAT_GUI_GNOME */
3310    {
3311	if (vim_strchr(p_go, GO_TOOLBAR) != NULL
3312		&& (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS)))
3313	    gtk_widget_show(gui.toolbar);
3314	gtk_box_pack_start(GTK_BOX(vbox), gui.toolbar, FALSE, FALSE, 0);
3315    }
3316#endif /* FEAT_TOOLBAR */
3317
3318#ifdef FEAT_GUI_TABLINE
3319    /*
3320     * Use a Notebook for the tab pages labels.  The labels are hidden by
3321     * default.
3322     */
3323    gui.tabline = gtk_notebook_new();
3324    gtk_widget_show(gui.tabline);
3325    gtk_box_pack_start(GTK_BOX(vbox), gui.tabline, FALSE, FALSE, 0);
3326    gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE);
3327    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE);
3328    gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE);
3329    gtk_notebook_set_tab_border(GTK_NOTEBOOK(gui.tabline), FALSE);
3330
3331    tabline_tooltip = gtk_tooltips_new();
3332    gtk_tooltips_enable(GTK_TOOLTIPS(tabline_tooltip));
3333
3334    {
3335	GtkWidget *page, *label, *event_box;
3336
3337	/* Add the first tab. */
3338	page = gtk_vbox_new(FALSE, 0);
3339	gtk_widget_show(page);
3340	gtk_container_add(GTK_CONTAINER(gui.tabline), page);
3341	label = gtk_label_new("-Empty-");
3342	gtk_widget_show(label);
3343	event_box = gtk_event_box_new();
3344	gtk_widget_show(event_box);
3345	gtk_object_set_user_data(GTK_OBJECT(event_box), (gpointer)1L);
3346	gtk_misc_set_padding(GTK_MISC(label), 2, 2);
3347	gtk_container_add(GTK_CONTAINER(event_box), label);
3348	gtk_notebook_set_tab_label(GTK_NOTEBOOK(gui.tabline), page, event_box);
3349    }
3350
3351    gtk_signal_connect(GTK_OBJECT(gui.tabline), "switch_page",
3352		       GTK_SIGNAL_FUNC(on_select_tab), NULL);
3353
3354    /* Create a popup menu for the tab line and connect it. */
3355    tabline_menu = create_tabline_menu();
3356    gtk_signal_connect_object(GTK_OBJECT(gui.tabline), "button_press_event",
3357	    GTK_SIGNAL_FUNC(on_tabline_menu), GTK_OBJECT(tabline_menu));
3358#endif
3359
3360    gui.formwin = gtk_form_new();
3361    gtk_container_border_width(GTK_CONTAINER(gui.formwin), 0);
3362    gtk_widget_set_events(gui.formwin, GDK_EXPOSURE_MASK);
3363
3364    gui.drawarea = gtk_drawing_area_new();
3365
3366    /* Determine which events we will filter. */
3367    gtk_widget_set_events(gui.drawarea,
3368			  GDK_EXPOSURE_MASK |
3369			  GDK_ENTER_NOTIFY_MASK |
3370			  GDK_LEAVE_NOTIFY_MASK |
3371			  GDK_BUTTON_PRESS_MASK |
3372			  GDK_BUTTON_RELEASE_MASK |
3373			  GDK_SCROLL_MASK |
3374			  GDK_KEY_PRESS_MASK |
3375			  GDK_KEY_RELEASE_MASK |
3376			  GDK_POINTER_MOTION_MASK |
3377			  GDK_POINTER_MOTION_HINT_MASK);
3378
3379    gtk_widget_show(gui.drawarea);
3380    gtk_form_put(GTK_FORM(gui.formwin), gui.drawarea, 0, 0);
3381    gtk_widget_show(gui.formwin);
3382    gtk_box_pack_start(GTK_BOX(vbox), gui.formwin, TRUE, TRUE, 0);
3383
3384    /* For GtkSockets, key-presses must go to the focus widget (drawarea)
3385     * and not the window. */
3386    gtk_signal_connect((gtk_socket_id == 0) ? GTK_OBJECT(gui.mainwin)
3387					    : GTK_OBJECT(gui.drawarea),
3388		       "key_press_event",
3389		       GTK_SIGNAL_FUNC(key_press_event), NULL);
3390#if defined(FEAT_XIM)
3391    /* Also forward key release events for the benefit of GTK+ 2 input
3392     * modules.  Try CTRL-SHIFT-xdigits to enter a Unicode code point. */
3393    g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
3394					  : G_OBJECT(gui.drawarea),
3395		     "key_release_event",
3396		     G_CALLBACK(&key_release_event), NULL);
3397#endif
3398    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "realize",
3399		       GTK_SIGNAL_FUNC(drawarea_realize_cb), NULL);
3400    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "unrealize",
3401		       GTK_SIGNAL_FUNC(drawarea_unrealize_cb), NULL);
3402
3403    gtk_signal_connect_after(GTK_OBJECT(gui.drawarea), "style_set",
3404			     GTK_SIGNAL_FUNC(&drawarea_style_set_cb), NULL);
3405
3406    gui.visibility = GDK_VISIBILITY_UNOBSCURED;
3407
3408#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
3409    wm_protocols_atom = gdk_atom_intern("WM_PROTOCOLS", FALSE);
3410    save_yourself_atom = gdk_atom_intern("WM_SAVE_YOURSELF", FALSE);
3411#endif
3412
3413    if (gtk_socket_id != 0)
3414	/* make sure keyboard input can go to the drawarea */
3415	GTK_WIDGET_SET_FLAGS(gui.drawarea, GTK_CAN_FOCUS);
3416
3417    /*
3418     * Set clipboard specific atoms
3419     */
3420    vim_atom = gdk_atom_intern(VIM_ATOM_NAME, FALSE);
3421    vimenc_atom = gdk_atom_intern(VIMENC_ATOM_NAME, FALSE);
3422    clip_star.gtk_sel_atom = GDK_SELECTION_PRIMARY;
3423    clip_plus.gtk_sel_atom = gdk_atom_intern("CLIPBOARD", FALSE);
3424
3425    /*
3426     * Start out by adding the configured border width into the border offset.
3427     */
3428    gui.border_offset = gui.border_width;
3429
3430    gtk_signal_connect(GTK_OBJECT(gui.mainwin), "visibility_notify_event",
3431		       GTK_SIGNAL_FUNC(visibility_event), NULL);
3432    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "expose_event",
3433		       GTK_SIGNAL_FUNC(expose_event), NULL);
3434
3435    /*
3436     * Only install these enter/leave callbacks when 'p' in 'guioptions'.
3437     * Only needed for some window managers.
3438     */
3439    if (vim_strchr(p_go, GO_POINTER) != NULL)
3440    {
3441	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "leave_notify_event",
3442			   GTK_SIGNAL_FUNC(leave_notify_event), NULL);
3443	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "enter_notify_event",
3444			   GTK_SIGNAL_FUNC(enter_notify_event), NULL);
3445    }
3446
3447    /* Real windows can get focus ... GtkPlug, being a mere container can't,
3448     * only its widgets.  Arguably, this could be common code and we not use
3449     * the window focus at all, but let's be safe.
3450     */
3451    if (gtk_socket_id == 0)
3452    {
3453	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_out_event",
3454			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
3455	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_in_event",
3456			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
3457    }
3458    else
3459    {
3460	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_out_event",
3461			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
3462	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_in_event",
3463			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
3464#ifdef FEAT_GUI_TABLINE
3465	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_out_event",
3466			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
3467	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_in_event",
3468			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
3469#endif /* FEAT_GUI_TABLINE */
3470    }
3471
3472    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "motion_notify_event",
3473		       GTK_SIGNAL_FUNC(motion_notify_event), NULL);
3474    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_press_event",
3475		       GTK_SIGNAL_FUNC(button_press_event), NULL);
3476    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_release_event",
3477		       GTK_SIGNAL_FUNC(button_release_event), NULL);
3478    g_signal_connect(G_OBJECT(gui.drawarea), "scroll_event",
3479		     G_CALLBACK(&scroll_event), NULL);
3480
3481    /*
3482     * Add selection handler functions.
3483     */
3484    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_clear_event",
3485		       GTK_SIGNAL_FUNC(selection_clear_event), NULL);
3486    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_received",
3487		       GTK_SIGNAL_FUNC(selection_received_cb), NULL);
3488
3489    gui_gtk_set_selection_targets();
3490
3491    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_get",
3492		       GTK_SIGNAL_FUNC(selection_get_cb), NULL);
3493
3494    /* Pretend we don't have input focus, we will get an event if we do. */
3495    gui.in_focus = FALSE;
3496
3497    return OK;
3498}
3499
3500#if (defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)) || defined(PROTO)
3501/*
3502 * This is called from gui_start() after a fork() has been done.
3503 * We have to tell the session manager our new PID.
3504 */
3505    void
3506gui_mch_forked(void)
3507{
3508    if (using_gnome)
3509    {
3510	GnomeClient *client;
3511
3512	client = gnome_master_client();
3513
3514	if (client != NULL)
3515	    gnome_client_set_process_id(client, getpid());
3516    }
3517}
3518#endif /* FEAT_GUI_GNOME && FEAT_SESSION */
3519
3520/*
3521 * Called when the foreground or background color has been changed.
3522 * This used to change the graphics contexts directly but we are
3523 * currently manipulating them where desired.
3524 */
3525    void
3526gui_mch_new_colors(void)
3527{
3528    if (gui.drawarea != NULL && gui.drawarea->window != NULL)
3529    {
3530	GdkColor color = { 0, 0, 0, 0 };
3531
3532	color.pixel = gui.back_pixel;
3533	gdk_window_set_background(gui.drawarea->window, &color);
3534    }
3535}
3536
3537/*
3538 * This signal informs us about the need to rearrange our sub-widgets.
3539 */
3540    static gint
3541form_configure_event(GtkWidget *widget UNUSED,
3542		     GdkEventConfigure *event,
3543		     gpointer data UNUSED)
3544{
3545    int usable_height = event->height;
3546
3547    /* When in a GtkPlug, we can't guarantee valid heights (as a round
3548     * no. of char-heights), so we have to manually sanitise them.
3549     * Widths seem to sort themselves out, don't ask me why.
3550     */
3551    if (gtk_socket_id != 0)
3552	usable_height -= (gui.char_height - (gui.char_height/2)); /* sic. */
3553
3554    gtk_form_freeze(GTK_FORM(gui.formwin));
3555    gui_resize_shell(event->width, usable_height);
3556    gtk_form_thaw(GTK_FORM(gui.formwin));
3557
3558    return TRUE;
3559}
3560
3561/*
3562 * Function called when window already closed.
3563 * We can't do much more here than to trying to preserve what had been done,
3564 * since the window is already inevitably going away.
3565 */
3566    static void
3567mainwin_destroy_cb(GtkObject *object UNUSED, gpointer data UNUSED)
3568{
3569    /* Don't write messages to the GUI anymore */
3570    full_screen = FALSE;
3571
3572    gui.mainwin  = NULL;
3573    gui.drawarea = NULL;
3574
3575    if (!exiting) /* only do anything if the destroy was unexpected */
3576    {
3577	vim_strncpy(IObuff,
3578		(char_u *)_("Vim: Main window unexpectedly destroyed\n"),
3579		IOSIZE - 1);
3580	preserve_exit();
3581    }
3582}
3583
3584
3585/*
3586 * Bit of a hack to ensure we start GtkPlug windows with the correct window
3587 * hints (and thus the required size from -geom), but that after that we
3588 * put the hints back to normal (the actual minimum size) so we may
3589 * subsequently be resized smaller.  GtkSocket (the parent end) uses the
3590 * plug's window 'min hints to set *it's* minimum size, but that's also the
3591 * only way we have of making ourselves bigger (by set lines/columns).
3592 * Thus set hints at start-up to ensure correct init. size, then a
3593 * second after the final attempt to reset the real minimum hinst (done by
3594 * scrollbar init.), actually do the standard hinst and stop the timer.
3595 * We'll not let the default hints be set while this timer's active.
3596 */
3597    static gboolean
3598check_startup_plug_hints(gpointer data UNUSED)
3599{
3600    if (init_window_hints_state == 1)
3601    {
3602	/* Safe to use normal hints now */
3603	init_window_hints_state = 0;
3604	update_window_manager_hints(0, 0);
3605	return FALSE;   /* stop timer */
3606    }
3607
3608    /* Keep on trying */
3609    init_window_hints_state = 1;
3610    return TRUE;
3611}
3612
3613/*
3614 * Open the GUI window which was created by a call to gui_mch_init().
3615 */
3616    int
3617gui_mch_open(void)
3618{
3619    guicolor_T fg_pixel = INVALCOLOR;
3620    guicolor_T bg_pixel = INVALCOLOR;
3621    guint		pixel_width;
3622    guint		pixel_height;
3623
3624    /*
3625     * Allow setting a window role on the command line, or invent one
3626     * if none was specified.  This is mainly useful for GNOME session
3627     * support; allowing the WM to restore window placement.
3628     */
3629    if (role_argument != NULL)
3630    {
3631	gtk_window_set_role(GTK_WINDOW(gui.mainwin), role_argument);
3632    }
3633    else
3634    {
3635	char *role;
3636
3637	/* Invent a unique-enough ID string for the role */
3638	role = g_strdup_printf("vim-%u-%u-%u",
3639			       (unsigned)mch_get_pid(),
3640			       (unsigned)g_random_int(),
3641			       (unsigned)time(NULL));
3642
3643	gtk_window_set_role(GTK_WINDOW(gui.mainwin), role);
3644	g_free(role);
3645    }
3646
3647    if (gui_win_x != -1 && gui_win_y != -1)
3648	gtk_window_move(GTK_WINDOW(gui.mainwin), gui_win_x, gui_win_y);
3649
3650    /* Determine user specified geometry, if present. */
3651    if (gui.geom != NULL)
3652    {
3653	int		mask;
3654	unsigned int	w, h;
3655	int		x = 0;
3656	int		y = 0;
3657
3658	mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
3659
3660	if (mask & WidthValue)
3661	    Columns = w;
3662	if (mask & HeightValue)
3663	{
3664	    if (p_window > (long)h - 1 || !option_was_set((char_u *)"window"))
3665		p_window = h - 1;
3666	    Rows = h;
3667	}
3668
3669	pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width);
3670	pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height);
3671
3672	pixel_width  += get_menu_tool_width();
3673	pixel_height += get_menu_tool_height();
3674
3675	if (mask & (XValue | YValue))
3676	{
3677	    int ww, hh;
3678	    gui_mch_get_screen_dimensions(&ww, &hh);
3679	    hh += p_ghr + get_menu_tool_height();
3680	    ww += get_menu_tool_width();
3681	    if (mask & XNegative)
3682		x += ww - pixel_width;
3683	    if (mask & YNegative)
3684		y += hh - pixel_height;
3685	    gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
3686	}
3687	vim_free(gui.geom);
3688	gui.geom = NULL;
3689
3690	/* From now until everyone's stopped trying to set the window hints
3691	 * to their correct minimum values, stop them being set as we need
3692	 * them to remain at our required size for the parent GtkSocket to
3693	 * give us the right initial size.
3694	 */
3695	if (gtk_socket_id != 0  &&  (mask & WidthValue || mask & HeightValue))
3696	{
3697	    update_window_manager_hints(pixel_width, pixel_height);
3698	    init_window_hints_state = 1;
3699	    g_timeout_add(1000, check_startup_plug_hints, NULL);
3700	}
3701    }
3702
3703    pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width);
3704    pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height);
3705    /* For GTK2 changing the size of the form widget doesn't cause window
3706     * resizing. */
3707    if (gtk_socket_id == 0)
3708	gtk_window_resize(GTK_WINDOW(gui.mainwin), pixel_width, pixel_height);
3709    update_window_manager_hints(0, 0);
3710
3711    if (foreground_argument != NULL)
3712	fg_pixel = gui_get_color((char_u *)foreground_argument);
3713    if (fg_pixel == INVALCOLOR)
3714	fg_pixel = gui_get_color((char_u *)"Black");
3715
3716    if (background_argument != NULL)
3717	bg_pixel = gui_get_color((char_u *)background_argument);
3718    if (bg_pixel == INVALCOLOR)
3719	bg_pixel = gui_get_color((char_u *)"White");
3720
3721    if (found_reverse_arg)
3722    {
3723	gui.def_norm_pixel = bg_pixel;
3724	gui.def_back_pixel = fg_pixel;
3725    }
3726    else
3727    {
3728	gui.def_norm_pixel = fg_pixel;
3729	gui.def_back_pixel = bg_pixel;
3730    }
3731
3732    /* Get the colors from the "Normal" and "Menu" group (set in syntax.c or
3733     * in a vimrc file) */
3734    set_normal_colors();
3735
3736    /* Check that none of the colors are the same as the background color */
3737    gui_check_colors();
3738
3739    /* Get the colors for the highlight groups (gui_check_colors() might have
3740     * changed them). */
3741    highlight_gui_started();	/* re-init colors and fonts */
3742
3743    gtk_signal_connect(GTK_OBJECT(gui.mainwin), "destroy",
3744		       GTK_SIGNAL_FUNC(mainwin_destroy_cb), NULL);
3745
3746#ifdef FEAT_HANGULIN
3747    hangul_keyboard_set();
3748#endif
3749
3750    /*
3751     * Notify the fixed area about the need to resize the contents of the
3752     * gui.formwin, which we use for random positioning of the included
3753     * components.
3754     *
3755     * We connect this signal deferred finally after anything is in place,
3756     * since this is intended to handle resizements coming from the window
3757     * manager upon us and should not interfere with what VIM is requesting
3758     * upon startup.
3759     */
3760    gtk_signal_connect(GTK_OBJECT(gui.formwin), "configure_event",
3761		       GTK_SIGNAL_FUNC(form_configure_event), NULL);
3762
3763#ifdef FEAT_DND
3764    /* Set up for receiving DND items. */
3765    gui_gtk_set_dnd_targets();
3766
3767    gtk_signal_connect(GTK_OBJECT(gui.drawarea), "drag_data_received",
3768		       GTK_SIGNAL_FUNC(drag_data_received_cb), NULL);
3769#endif
3770
3771	/* With GTK+ 2, we need to iconify the window before calling show()
3772	 * to avoid mapping the window for a short time. */
3773	if (found_iconic_arg && gtk_socket_id == 0)
3774	    gui_mch_iconify();
3775
3776    {
3777#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU)
3778	unsigned long menu_handler = 0;
3779# ifdef FEAT_TOOLBAR
3780	unsigned long tool_handler = 0;
3781# endif
3782	/*
3783	 * Urgh hackish :/  For some reason BonoboDockLayout always forces a
3784	 * show when restoring the saved layout configuration.	We can't just
3785	 * hide the widgets again after gtk_widget_show(gui.mainwin) since it's
3786	 * a toplevel window and thus will be realized immediately.  Instead,
3787	 * connect signal handlers to hide the widgets just after they've been
3788	 * marked visible, but before the main window is realized.
3789	 */
3790	if (using_gnome && vim_strchr(p_go, GO_MENUS) == NULL)
3791	    menu_handler = g_signal_connect_after(gui.menubar_h, "show",
3792						  G_CALLBACK(&gtk_widget_hide),
3793						  NULL);
3794# ifdef FEAT_TOOLBAR
3795	if (using_gnome && vim_strchr(p_go, GO_TOOLBAR) == NULL
3796		&& (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS)))
3797	    tool_handler = g_signal_connect_after(gui.toolbar_h, "show",
3798						  G_CALLBACK(&gtk_widget_hide),
3799						  NULL);
3800# endif
3801#endif
3802	gtk_widget_show(gui.mainwin);
3803
3804#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU)
3805	if (menu_handler != 0)
3806	    g_signal_handler_disconnect(gui.menubar_h, menu_handler);
3807# ifdef FEAT_TOOLBAR
3808	if (tool_handler != 0)
3809	    g_signal_handler_disconnect(gui.toolbar_h, tool_handler);
3810# endif
3811#endif
3812    }
3813
3814    return OK;
3815}
3816
3817
3818    void
3819gui_mch_exit(int rc UNUSED)
3820{
3821    if (gui.mainwin != NULL)
3822	gtk_widget_destroy(gui.mainwin);
3823}
3824
3825/*
3826 * Get the position of the top left corner of the window.
3827 */
3828    int
3829gui_mch_get_winpos(int *x, int *y)
3830{
3831    gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y);
3832    return OK;
3833}
3834
3835/*
3836 * Set the position of the top left corner of the window to the given
3837 * coordinates.
3838 */
3839    void
3840gui_mch_set_winpos(int x, int y)
3841{
3842    gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
3843}
3844
3845#if 0
3846static int resize_idle_installed = FALSE;
3847/*
3848 * Idle handler to force resize.  Used by gui_mch_set_shellsize() to ensure
3849 * the shell size doesn't exceed the window size, i.e. if the window manager
3850 * ignored our size request.  Usually this happens if the window is maximized.
3851 *
3852 * FIXME: It'd be nice if we could find a little more orthodox solution.
3853 * See also the remark below in gui_mch_set_shellsize().
3854 *
3855 * DISABLED: When doing ":set lines+=1" this function would first invoke
3856 * gui_resize_shell() with the old size, then the normal callback would
3857 * report the new size through form_configure_event().  That caused the window
3858 * layout to be messed up.
3859 */
3860    static gboolean
3861force_shell_resize_idle(gpointer data)
3862{
3863    if (gui.mainwin != NULL
3864	    && GTK_WIDGET_REALIZED(gui.mainwin)
3865	    && GTK_WIDGET_VISIBLE(gui.mainwin))
3866    {
3867	int width;
3868	int height;
3869
3870	gtk_window_get_size(GTK_WINDOW(gui.mainwin), &width, &height);
3871
3872	width  -= get_menu_tool_width();
3873	height -= get_menu_tool_height();
3874
3875	gui_resize_shell(width, height);
3876    }
3877
3878    resize_idle_installed = FALSE;
3879    return FALSE; /* don't call me again */
3880}
3881#endif
3882
3883/*
3884 * Return TRUE if the main window is maximized.
3885 */
3886    int
3887gui_mch_maximized()
3888{
3889    return (gui.mainwin != NULL && gui.mainwin->window != NULL
3890	    && (gdk_window_get_state(gui.mainwin->window)
3891					       & GDK_WINDOW_STATE_MAXIMIZED));
3892}
3893
3894/*
3895 * Unmaximize the main window
3896 */
3897    void
3898gui_mch_unmaximize()
3899{
3900    if (gui.mainwin != NULL)
3901	gtk_window_unmaximize(GTK_WINDOW(gui.mainwin));
3902}
3903
3904/*
3905 * Set the windows size.
3906 */
3907    void
3908gui_mch_set_shellsize(int width, int height,
3909		      int min_width UNUSED,  int min_height UNUSED,
3910		      int base_width UNUSED, int base_height UNUSED,
3911		      int direction UNUSED)
3912{
3913    /* give GTK+ a chance to put all widget's into place */
3914    gui_mch_update();
3915
3916    /* this will cause the proper resizement to happen too */
3917    if (gtk_socket_id == 0)
3918	update_window_manager_hints(0, 0);
3919
3920    /* With GTK+ 2, changing the size of the form widget doesn't resize
3921     * the window.  So let's do it the other way around and resize the
3922     * main window instead. */
3923    width  += get_menu_tool_width();
3924    height += get_menu_tool_height();
3925
3926    if (gtk_socket_id == 0)
3927	gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height);
3928    else
3929	update_window_manager_hints(width, height);
3930
3931# if 0
3932    if (!resize_idle_installed)
3933    {
3934	g_idle_add_full(GDK_PRIORITY_EVENTS + 10,
3935			&force_shell_resize_idle, NULL, NULL);
3936	resize_idle_installed = TRUE;
3937    }
3938# endif
3939    /*
3940     * Wait until all events are processed to prevent a crash because the
3941     * real size of the drawing area doesn't reflect Vim's internal ideas.
3942     *
3943     * This is a bit of a hack, since Vim is a terminal application with a GUI
3944     * on top, while the GUI expects to be the boss.
3945     */
3946    gui_mch_update();
3947}
3948
3949
3950/*
3951 * The screen size is used to make sure the initial window doesn't get bigger
3952 * than the screen.  This subtracts some room for menubar, toolbar and window
3953 * decorations.
3954 */
3955    void
3956gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
3957{
3958#ifdef HAVE_GTK_MULTIHEAD
3959    GdkScreen* screen;
3960
3961    if (gui.mainwin != NULL && gtk_widget_has_screen(gui.mainwin))
3962	screen = gtk_widget_get_screen(gui.mainwin);
3963    else
3964	screen = gdk_screen_get_default();
3965
3966    *screen_w = gdk_screen_get_width(screen);
3967    *screen_h = gdk_screen_get_height(screen) - p_ghr;
3968#else
3969    *screen_w = gdk_screen_width();
3970    /* Subtract 'guiheadroom' from the height to allow some room for the
3971     * window manager (task list and window title bar). */
3972    *screen_h = gdk_screen_height() - p_ghr;
3973#endif
3974
3975    /*
3976     * FIXME: dirty trick: Because the gui_get_base_height() doesn't include
3977     * the toolbar and menubar for GTK, we subtract them from the screen
3978     * height, so that the window size can be made to fit on the screen.
3979     * This should be completely changed later.
3980     */
3981    *screen_w -= get_menu_tool_width();
3982    *screen_h -= get_menu_tool_height();
3983}
3984
3985#if defined(FEAT_TITLE) || defined(PROTO)
3986    void
3987gui_mch_settitle(char_u *title, char_u *icon UNUSED)
3988{
3989    if (title != NULL && output_conv.vc_type != CONV_NONE)
3990	title = string_convert(&output_conv, title, NULL);
3991
3992    gtk_window_set_title(GTK_WINDOW(gui.mainwin), (const char *)title);
3993
3994    if (output_conv.vc_type != CONV_NONE)
3995	vim_free(title);
3996}
3997#endif /* FEAT_TITLE */
3998
3999#if defined(FEAT_MENU) || defined(PROTO)
4000    void
4001gui_mch_enable_menu(int showit)
4002{
4003    GtkWidget *widget;
4004
4005# ifdef FEAT_GUI_GNOME
4006    if (using_gnome)
4007	widget = gui.menubar_h;
4008    else
4009# endif
4010	widget = gui.menubar;
4011
4012    /* Do not disable the menu while starting up, otherwise F10 doesn't work. */
4013    if (!showit != !GTK_WIDGET_VISIBLE(widget) && !gui.starting)
4014    {
4015	if (showit)
4016	    gtk_widget_show(widget);
4017	else
4018	    gtk_widget_hide(widget);
4019
4020	update_window_manager_hints(0, 0);
4021    }
4022}
4023#endif /* FEAT_MENU */
4024
4025#if defined(FEAT_TOOLBAR) || defined(PROTO)
4026    void
4027gui_mch_show_toolbar(int showit)
4028{
4029    GtkWidget *widget;
4030
4031    if (gui.toolbar == NULL)
4032	return;
4033
4034# ifdef FEAT_GUI_GNOME
4035    if (using_gnome)
4036	widget = gui.toolbar_h;
4037    else
4038# endif
4039	widget = gui.toolbar;
4040
4041    if (showit)
4042	set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
4043
4044    if (!showit != !GTK_WIDGET_VISIBLE(widget))
4045    {
4046	if (showit)
4047	    gtk_widget_show(widget);
4048	else
4049	    gtk_widget_hide(widget);
4050
4051	update_window_manager_hints(0, 0);
4052    }
4053}
4054#endif /* FEAT_TOOLBAR */
4055
4056/*
4057 * Check if a given font is a CJK font. This is done in a very crude manner. It
4058 * just see if U+04E00 for zh and ja and U+AC00 for ko are covered in a given
4059 * font. Consequently, this function cannot  be used as a general purpose check
4060 * for CJK-ness for which fontconfig APIs should be used.  This is only used by
4061 * gui_mch_init_font() to deal with 'CJK fixed width fonts'.
4062 */
4063    static int
4064is_cjk_font(PangoFontDescription *font_desc)
4065{
4066    static const char * const cjk_langs[] =
4067	{"zh_CN", "zh_TW", "zh_HK", "ja", "ko"};
4068
4069    PangoFont	*font;
4070    unsigned	i;
4071    int		is_cjk = FALSE;
4072
4073    font = pango_context_load_font(gui.text_context, font_desc);
4074
4075    if (font == NULL)
4076	return FALSE;
4077
4078    for (i = 0; !is_cjk && i < G_N_ELEMENTS(cjk_langs); ++i)
4079    {
4080	PangoCoverage	*coverage;
4081	gunichar	uc;
4082
4083	coverage = pango_font_get_coverage(
4084		font, pango_language_from_string(cjk_langs[i]));
4085
4086	if (coverage != NULL)
4087	{
4088	    uc = (cjk_langs[i][0] == 'k') ? 0xAC00 : 0x4E00;
4089	    is_cjk = (pango_coverage_get(coverage, uc) == PANGO_COVERAGE_EXACT);
4090	    pango_coverage_unref(coverage);
4091	}
4092    }
4093
4094    g_object_unref(font);
4095
4096    return is_cjk;
4097}
4098
4099/*
4100 * Adjust gui.char_height (after 'linespace' was changed).
4101 */
4102    int
4103gui_mch_adjust_charheight(void)
4104{
4105    PangoFontMetrics	*metrics;
4106    int			ascent;
4107    int			descent;
4108
4109    metrics = pango_context_get_metrics(gui.text_context, gui.norm_font,
4110				pango_context_get_language(gui.text_context));
4111    ascent  = pango_font_metrics_get_ascent(metrics);
4112    descent = pango_font_metrics_get_descent(metrics);
4113
4114    pango_font_metrics_unref(metrics);
4115
4116    gui.char_height = (ascent + descent + PANGO_SCALE - 1) / PANGO_SCALE
4117								+ p_linespace;
4118    /* LINTED: avoid warning: bitwise operation on signed value */
4119    gui.char_ascent = PANGO_PIXELS(ascent + p_linespace * PANGO_SCALE / 2);
4120
4121    /* A not-positive value of char_height may crash Vim.  Only happens
4122     * if 'linespace' is negative (which does make sense sometimes). */
4123    gui.char_ascent = MAX(gui.char_ascent, 0);
4124    gui.char_height = MAX(gui.char_height, gui.char_ascent + 1);
4125
4126    return OK;
4127}
4128
4129/*
4130 * Put up a font dialog and return the selected font name in allocated memory.
4131 * "oldval" is the previous value.  Return NULL when cancelled.
4132 * This should probably go into gui_gtk.c.  Hmm.
4133 * FIXME:
4134 * The GTK2 font selection dialog has no filtering API.  So we could either
4135 * a) implement our own (possibly copying the code from somewhere else) or
4136 * b) just live with it.
4137 */
4138    char_u *
4139gui_mch_font_dialog(char_u *oldval)
4140{
4141    GtkWidget	*dialog;
4142    int		response;
4143    char_u	*fontname = NULL;
4144    char_u	*oldname;
4145
4146    dialog = gtk_font_selection_dialog_new(NULL);
4147
4148    gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
4149    gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
4150
4151    if (oldval != NULL && oldval[0] != NUL)
4152    {
4153	if (output_conv.vc_type != CONV_NONE)
4154	    oldname = string_convert(&output_conv, oldval, NULL);
4155	else
4156	    oldname = oldval;
4157
4158	/* Annoying bug in GTK (or Pango): if the font name does not include a
4159	 * size, zero is used.  Use default point size ten. */
4160	if (!vim_isdigit(oldname[STRLEN(oldname) - 1]))
4161	{
4162	    char_u	*p = vim_strnsave(oldname, STRLEN(oldname) + 3);
4163
4164	    if (p != NULL)
4165	    {
4166		STRCPY(p + STRLEN(p), " 10");
4167		if (oldname != oldval)
4168		    vim_free(oldname);
4169		oldname = p;
4170	    }
4171	}
4172
4173	gtk_font_selection_dialog_set_font_name(
4174		GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname);
4175
4176	if (oldname != oldval)
4177	    vim_free(oldname);
4178    }
4179    else
4180	gtk_font_selection_dialog_set_font_name(
4181		GTK_FONT_SELECTION_DIALOG(dialog), DEFAULT_FONT);
4182
4183    response = gtk_dialog_run(GTK_DIALOG(dialog));
4184
4185    if (response == GTK_RESPONSE_OK)
4186    {
4187	char *name;
4188
4189	name = gtk_font_selection_dialog_get_font_name(
4190			    GTK_FONT_SELECTION_DIALOG(dialog));
4191	if (name != NULL)
4192	{
4193	    char_u  *p;
4194
4195	    /* Apparently some font names include a comma, need to escape
4196	     * that, because in 'guifont' it separates names. */
4197	    p = vim_strsave_escaped((char_u *)name, (char_u *)",");
4198	    g_free(name);
4199	    if (p != NULL && input_conv.vc_type != CONV_NONE)
4200	    {
4201		fontname = string_convert(&input_conv, p, NULL);
4202		vim_free(p);
4203	    }
4204	    else
4205		fontname = p;
4206	}
4207    }
4208
4209    if (response != GTK_RESPONSE_NONE)
4210	gtk_widget_destroy(dialog);
4211
4212    return fontname;
4213}
4214
4215/*
4216 * Some monospace fonts don't support a bold weight, and fall back
4217 * silently to the regular weight.  But this is no good since our text
4218 * drawing function can emulate bold by overstriking.  So let's try
4219 * to detect whether bold weight is actually available and emulate it
4220 * otherwise.
4221 *
4222 * Note that we don't need to check for italic style since Xft can
4223 * emulate italic on its own, provided you have a proper fontconfig
4224 * setup.  We wouldn't be able to emulate it in Vim anyway.
4225 */
4226    static void
4227get_styled_font_variants(void)
4228{
4229    PangoFontDescription    *bold_font_desc;
4230    PangoFont		    *plain_font;
4231    PangoFont		    *bold_font;
4232
4233    gui.font_can_bold = FALSE;
4234
4235    plain_font = pango_context_load_font(gui.text_context, gui.norm_font);
4236
4237    if (plain_font == NULL)
4238	return;
4239
4240    bold_font_desc = pango_font_description_copy_static(gui.norm_font);
4241    pango_font_description_set_weight(bold_font_desc, PANGO_WEIGHT_BOLD);
4242
4243    bold_font = pango_context_load_font(gui.text_context, bold_font_desc);
4244    /*
4245     * The comparison relies on the unique handle nature of a PangoFont*,
4246     * i.e. it's assumed that a different PangoFont* won't refer to the
4247     * same font.  Seems to work, and failing here isn't critical anyway.
4248     */
4249    if (bold_font != NULL)
4250    {
4251	gui.font_can_bold = (bold_font != plain_font);
4252	g_object_unref(bold_font);
4253    }
4254
4255    pango_font_description_free(bold_font_desc);
4256    g_object_unref(plain_font);
4257}
4258
4259static PangoEngineShape *default_shape_engine = NULL;
4260
4261/*
4262 * Create a map from ASCII characters in the range [32,126] to glyphs
4263 * of the current font.  This is used by gui_gtk2_draw_string() to skip
4264 * the itemize and shaping process for the most common case.
4265 */
4266    static void
4267ascii_glyph_table_init(void)
4268{
4269    char_u	    ascii_chars[128];
4270    PangoAttrList   *attr_list;
4271    GList	    *item_list;
4272    int		    i;
4273
4274    if (gui.ascii_glyphs != NULL)
4275	pango_glyph_string_free(gui.ascii_glyphs);
4276    if (gui.ascii_font != NULL)
4277	g_object_unref(gui.ascii_font);
4278
4279    gui.ascii_glyphs = NULL;
4280    gui.ascii_font   = NULL;
4281
4282    /* For safety, fill in question marks for the control characters. */
4283    for (i = 0; i < 32; ++i)
4284	ascii_chars[i] = '?';
4285    for (; i < 127; ++i)
4286	ascii_chars[i] = i;
4287    ascii_chars[i] = '?';
4288
4289    attr_list = pango_attr_list_new();
4290    item_list = pango_itemize(gui.text_context, (const char *)ascii_chars,
4291			      0, sizeof(ascii_chars), attr_list, NULL);
4292
4293    if (item_list != NULL && item_list->next == NULL) /* play safe */
4294    {
4295	PangoItem   *item;
4296	int	    width;
4297
4298	item  = (PangoItem *)item_list->data;
4299	width = gui.char_width * PANGO_SCALE;
4300
4301	/* Remember the shape engine used for ASCII. */
4302	default_shape_engine = item->analysis.shape_engine;
4303
4304	gui.ascii_font = item->analysis.font;
4305	g_object_ref(gui.ascii_font);
4306
4307	gui.ascii_glyphs = pango_glyph_string_new();
4308
4309	pango_shape((const char *)ascii_chars, sizeof(ascii_chars),
4310		    &item->analysis, gui.ascii_glyphs);
4311
4312	g_return_if_fail(gui.ascii_glyphs->num_glyphs == sizeof(ascii_chars));
4313
4314	for (i = 0; i < gui.ascii_glyphs->num_glyphs; ++i)
4315	{
4316	    PangoGlyphGeometry *geom;
4317
4318	    geom = &gui.ascii_glyphs->glyphs[i].geometry;
4319	    geom->x_offset += MAX(0, width - geom->width) / 2;
4320	    geom->width = width;
4321	}
4322    }
4323
4324    g_list_foreach(item_list, (GFunc)&pango_item_free, NULL);
4325    g_list_free(item_list);
4326    pango_attr_list_unref(attr_list);
4327}
4328
4329/*
4330 * Initialize Vim to use the font or fontset with the given name.
4331 * Return FAIL if the font could not be loaded, OK otherwise.
4332 */
4333    int
4334gui_mch_init_font(char_u *font_name, int fontset UNUSED)
4335{
4336    PangoFontDescription    *font_desc;
4337    PangoLayout		    *layout;
4338    int			    width;
4339
4340    /* If font_name is NULL, this means to use the default, which should
4341     * be present on all proper Pango/fontconfig installations. */
4342    if (font_name == NULL)
4343	font_name = (char_u *)DEFAULT_FONT;
4344
4345    font_desc = gui_mch_get_font(font_name, FALSE);
4346
4347    if (font_desc == NULL)
4348	return FAIL;
4349
4350    gui_mch_free_font(gui.norm_font);
4351    gui.norm_font = font_desc;
4352
4353    pango_context_set_font_description(gui.text_context, font_desc);
4354
4355    layout = pango_layout_new(gui.text_context);
4356    pango_layout_set_text(layout, "MW", 2);
4357    pango_layout_get_size(layout, &width, NULL);
4358    /*
4359     * Set char_width to half the width obtained from pango_layout_get_size()
4360     * for CJK fixed_width/bi-width fonts.  An unpatched version of Xft leads
4361     * Pango to use the same width for both non-CJK characters (e.g. Latin
4362     * letters and numbers) and CJK characters.  This results in 's p a c e d
4363     * o u t' rendering when a CJK 'fixed width' font is used. To work around
4364     * that, divide the width returned by Pango by 2 if cjk_width is equal to
4365     * width for CJK fonts.
4366     *
4367     * For related bugs, see:
4368     * http://bugzilla.gnome.org/show_bug.cgi?id=106618
4369     * http://bugzilla.gnome.org/show_bug.cgi?id=106624
4370     *
4371     * With this, for all four of the following cases, Vim works fine:
4372     *	   guifont=CJK_fixed_width_font
4373     *	   guifont=Non_CJK_fixed_font
4374     *	   guifont=Non_CJK_fixed_font,CJK_Fixed_font
4375     *	   guifont=Non_CJK_fixed_font guifontwide=CJK_fixed_font
4376     */
4377    if (is_cjk_font(gui.norm_font))
4378    {
4379	int cjk_width;
4380
4381	/* Measure the text extent of U+4E00 and U+4E8C */
4382	pango_layout_set_text(layout, "\344\270\200\344\272\214", -1);
4383	pango_layout_get_size(layout, &cjk_width, NULL);
4384
4385	if (width == cjk_width)  /* Xft not patched */
4386	    width /= 2;
4387    }
4388    g_object_unref(layout);
4389
4390    gui.char_width = (width / 2 + PANGO_SCALE - 1) / PANGO_SCALE;
4391
4392    /* A zero width may cause a crash.	Happens for semi-invalid fontsets. */
4393    if (gui.char_width <= 0)
4394	gui.char_width = 8;
4395
4396    gui_mch_adjust_charheight();
4397
4398    /* Set the fontname, which will be used for information purposes */
4399    hl_set_font_name(font_name);
4400
4401    get_styled_font_variants();
4402    ascii_glyph_table_init();
4403
4404    /* Avoid unnecessary overhead if 'guifontwide' is equal to 'guifont'. */
4405    if (gui.wide_font != NULL
4406	&& pango_font_description_equal(gui.norm_font, gui.wide_font))
4407    {
4408	pango_font_description_free(gui.wide_font);
4409	gui.wide_font = NULL;
4410    }
4411
4412    if (gui_mch_maximized())
4413    {
4414	int w, h;
4415
4416	/* Update lines and columns in accordance with the new font, keep the
4417	 * window maximized. */
4418	gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h);
4419	w -= get_menu_tool_width();
4420	h -= get_menu_tool_height();
4421	gui_resize_shell(w, h);
4422    }
4423    else
4424    {
4425	/* Preserve the logical dimensions of the screen. */
4426	update_window_manager_hints(0, 0);
4427    }
4428
4429    return OK;
4430}
4431
4432/*
4433 * Get a reference to the font "name".
4434 * Return zero for failure.
4435 */
4436    GuiFont
4437gui_mch_get_font(char_u *name, int report_error)
4438{
4439    PangoFontDescription    *font;
4440
4441    /* can't do this when GUI is not running */
4442    if (!gui.in_use || name == NULL)
4443	return NULL;
4444
4445    if (output_conv.vc_type != CONV_NONE)
4446    {
4447	char_u *buf;
4448
4449	buf = string_convert(&output_conv, name, NULL);
4450	if (buf != NULL)
4451	{
4452	    font = pango_font_description_from_string((const char *)buf);
4453	    vim_free(buf);
4454	}
4455	else
4456	    font = NULL;
4457    }
4458    else
4459	font = pango_font_description_from_string((const char *)name);
4460
4461    if (font != NULL)
4462    {
4463	PangoFont *real_font;
4464
4465	/* pango_context_load_font() bails out if no font size is set */
4466	if (pango_font_description_get_size(font) <= 0)
4467	    pango_font_description_set_size(font, 10 * PANGO_SCALE);
4468
4469	real_font = pango_context_load_font(gui.text_context, font);
4470
4471	if (real_font == NULL)
4472	{
4473	    pango_font_description_free(font);
4474	    font = NULL;
4475	}
4476	else
4477	    g_object_unref(real_font);
4478    }
4479
4480    if (font == NULL)
4481    {
4482	if (report_error)
4483	    EMSG2(_((char *)e_font), name);
4484	return NULL;
4485    }
4486
4487    return font;
4488}
4489
4490#if defined(FEAT_EVAL) || defined(PROTO)
4491/*
4492 * Return the name of font "font" in allocated memory.
4493 */
4494    char_u *
4495gui_mch_get_fontname(GuiFont font, char_u *name UNUSED)
4496{
4497    if (font != NOFONT)
4498    {
4499	char	*pangoname = pango_font_description_to_string(font);
4500
4501	if (pangoname != NULL)
4502	{
4503	    char_u	*s = vim_strsave((char_u *)pangoname);
4504
4505	    g_free(pangoname);
4506	    return s;
4507	}
4508    }
4509    return NULL;
4510}
4511#endif
4512
4513/*
4514 * If a font is not going to be used, free its structure.
4515 */
4516    void
4517gui_mch_free_font(GuiFont font)
4518{
4519    if (font != NOFONT)
4520	pango_font_description_free(font);
4521}
4522
4523/*
4524 * Return the Pixel value (color) for the given color name.  This routine was
4525 * pretty much taken from example code in the Silicon Graphics OSF/Motif
4526 * Programmer's Guide.
4527 * Return INVALCOLOR for error.
4528 */
4529    guicolor_T
4530gui_mch_get_color(char_u *name)
4531{
4532    /* A number of colors that some X11 systems don't have */
4533    static const char *const vimnames[][2] =
4534    {
4535	{"LightRed",	"#FFBBBB"},
4536	{"LightGreen",	"#88FF88"},
4537	{"LightMagenta","#FFBBFF"},
4538	{"DarkCyan",	"#008888"},
4539	{"DarkBlue",	"#0000BB"},
4540	{"DarkRed",	"#BB0000"},
4541	{"DarkMagenta", "#BB00BB"},
4542	{"DarkGrey",	"#BBBBBB"},
4543	{"DarkYellow",	"#BBBB00"},
4544	{"Gray10",	"#1A1A1A"},
4545	{"Grey10",	"#1A1A1A"},
4546	{"Gray20",	"#333333"},
4547	{"Grey20",	"#333333"},
4548	{"Gray30",	"#4D4D4D"},
4549	{"Grey30",	"#4D4D4D"},
4550	{"Gray40",	"#666666"},
4551	{"Grey40",	"#666666"},
4552	{"Gray50",	"#7F7F7F"},
4553	{"Grey50",	"#7F7F7F"},
4554	{"Gray60",	"#999999"},
4555	{"Grey60",	"#999999"},
4556	{"Gray70",	"#B3B3B3"},
4557	{"Grey70",	"#B3B3B3"},
4558	{"Gray80",	"#CCCCCC"},
4559	{"Grey80",	"#CCCCCC"},
4560	{"Gray90",	"#E5E5E5"},
4561	{"Grey90",	"#E5E5E5"},
4562	{NULL, NULL}
4563    };
4564
4565    if (!gui.in_use)		/* can't do this when GUI not running */
4566	return INVALCOLOR;
4567
4568    while (name != NULL)
4569    {
4570	GdkColor    color;
4571	int	    parsed;
4572	int	    i;
4573
4574	parsed = gdk_color_parse((const char *)name, &color);
4575
4576	if (parsed)
4577	{
4578	    gdk_colormap_alloc_color(gtk_widget_get_colormap(gui.drawarea),
4579				     &color, FALSE, TRUE);
4580	    return (guicolor_T)color.pixel;
4581	}
4582	/* add a few builtin names and try again */
4583	for (i = 0; ; ++i)
4584	{
4585	    if (vimnames[i][0] == NULL)
4586	    {
4587		name = NULL;
4588		break;
4589	    }
4590	    if (STRICMP(name, vimnames[i][0]) == 0)
4591	    {
4592		name = (char_u *)vimnames[i][1];
4593		break;
4594	    }
4595	}
4596    }
4597
4598    return INVALCOLOR;
4599}
4600
4601/*
4602 * Set the current text foreground color.
4603 */
4604    void
4605gui_mch_set_fg_color(guicolor_T color)
4606{
4607    gui.fgcolor->pixel = (unsigned long)color;
4608}
4609
4610/*
4611 * Set the current text background color.
4612 */
4613    void
4614gui_mch_set_bg_color(guicolor_T color)
4615{
4616    gui.bgcolor->pixel = (unsigned long)color;
4617}
4618
4619/*
4620 * Set the current text special color.
4621 */
4622    void
4623gui_mch_set_sp_color(guicolor_T color)
4624{
4625    gui.spcolor->pixel = (unsigned long)color;
4626}
4627
4628/*
4629 * Function-like convenience macro for the sake of efficiency.
4630 */
4631#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End)  \
4632    G_STMT_START{					    \
4633	PangoAttribute *tmp_attr_;			    \
4634	tmp_attr_ = (Attribute);			    \
4635	tmp_attr_->start_index = (Start);		    \
4636	tmp_attr_->end_index = (End);			    \
4637	pango_attr_list_insert((AttrList), tmp_attr_);	    \
4638    }G_STMT_END
4639
4640    static void
4641apply_wide_font_attr(char_u *s, int len, PangoAttrList *attr_list)
4642{
4643    char_u  *start = NULL;
4644    char_u  *p;
4645    int	    uc;
4646
4647    for (p = s; p < s + len; p += utf_byte2len(*p))
4648    {
4649	uc = utf_ptr2char(p);
4650
4651	if (start == NULL)
4652	{
4653	    if (uc >= 0x80 && utf_char2cells(uc) == 2)
4654		start = p;
4655	}
4656	else if (uc < 0x80 /* optimization shortcut */
4657		 || (utf_char2cells(uc) != 2 && !utf_iscomposing(uc)))
4658	{
4659	    INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
4660			      attr_list, start - s, p - s);
4661	    start = NULL;
4662	}
4663    }
4664
4665    if (start != NULL)
4666	INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
4667			  attr_list, start - s, len);
4668}
4669
4670    static int
4671count_cluster_cells(char_u *s, PangoItem *item,
4672		    PangoGlyphString* glyphs, int i,
4673		    int *cluster_width,
4674		    int *last_glyph_rbearing)
4675{
4676    char_u  *p;
4677    int	    next;	/* glyph start index of next cluster */
4678    int	    start, end; /* string segment of current cluster */
4679    int	    width;	/* real cluster width in Pango units */
4680    int	    uc;
4681    int	    cellcount = 0;
4682
4683    width = glyphs->glyphs[i].geometry.width;
4684
4685    for (next = i + 1; next < glyphs->num_glyphs; ++next)
4686    {
4687	if (glyphs->glyphs[next].attr.is_cluster_start)
4688	    break;
4689	else if (glyphs->glyphs[next].geometry.width > width)
4690	    width = glyphs->glyphs[next].geometry.width;
4691    }
4692
4693    start = item->offset + glyphs->log_clusters[i];
4694    end   = item->offset + ((next < glyphs->num_glyphs) ?
4695			    glyphs->log_clusters[next] : item->length);
4696
4697    for (p = s + start; p < s + end; p += utf_byte2len(*p))
4698    {
4699	uc = utf_ptr2char(p);
4700	if (uc < 0x80)
4701	    ++cellcount;
4702	else if (!utf_iscomposing(uc))
4703	    cellcount += utf_char2cells(uc);
4704    }
4705
4706    if (last_glyph_rbearing != NULL
4707	    && cellcount > 0 && next == glyphs->num_glyphs)
4708    {
4709	PangoRectangle ink_rect;
4710	/*
4711	 * If a certain combining mark had to be taken from a non-monospace
4712	 * font, we have to compensate manually by adapting x_offset according
4713	 * to the ink extents of the previous glyph.
4714	 */
4715	pango_font_get_glyph_extents(item->analysis.font,
4716				     glyphs->glyphs[i].glyph,
4717				     &ink_rect, NULL);
4718
4719	if (PANGO_RBEARING(ink_rect) > 0)
4720	    *last_glyph_rbearing = PANGO_RBEARING(ink_rect);
4721    }
4722
4723    if (cellcount > 0)
4724	*cluster_width = width;
4725
4726    return cellcount;
4727}
4728
4729/*
4730 * If there are only combining characters in the cluster, we cannot just
4731 * change the width of the previous glyph since there is none.	Therefore
4732 * some guesswork is needed.
4733 *
4734 * If ink_rect.x is negative Pango apparently has taken care of the composing
4735 * by itself.  Actually setting x_offset = 0 should be sufficient then, but due
4736 * to problems with composing from different fonts we still need to fine-tune
4737 * x_offset to avoid ugliness.
4738 *
4739 * If ink_rect.x is not negative, force overstriking by pointing x_offset to
4740 * the position of the previous glyph.	Apparently this happens only with old
4741 * X fonts which don't provide the special combining information needed by
4742 * Pango.
4743 */
4744    static void
4745setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph,
4746			 int last_cellcount, int last_cluster_width,
4747			 int last_glyph_rbearing)
4748{
4749    PangoRectangle  ink_rect;
4750    PangoRectangle  logical_rect;
4751    int		    width;
4752
4753    width = last_cellcount * gui.char_width * PANGO_SCALE;
4754    glyph->geometry.x_offset = -width + MAX(0, width - last_cluster_width) / 2;
4755    glyph->geometry.width = 0;
4756
4757    pango_font_get_glyph_extents(item->analysis.font,
4758				 glyph->glyph,
4759				 &ink_rect, &logical_rect);
4760    if (ink_rect.x < 0)
4761    {
4762	glyph->geometry.x_offset += last_glyph_rbearing;
4763	glyph->geometry.y_offset  = logical_rect.height
4764		- (gui.char_height - p_linespace) * PANGO_SCALE;
4765    }
4766    else
4767	/* If the accent width is smaller than the cluster width, position it
4768	 * in the middle. */
4769	glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2;
4770}
4771
4772    static void
4773draw_glyph_string(int row, int col, int num_cells, int flags,
4774		  PangoFont *font, PangoGlyphString *glyphs)
4775{
4776    if (!(flags & DRAW_TRANSP))
4777    {
4778	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
4779
4780	gdk_draw_rectangle(gui.drawarea->window,
4781			   gui.text_gc,
4782			   TRUE,
4783			   FILL_X(col),
4784			   FILL_Y(row),
4785			   num_cells * gui.char_width,
4786			   gui.char_height);
4787    }
4788
4789    gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
4790
4791    gdk_draw_glyphs(gui.drawarea->window,
4792		    gui.text_gc,
4793		    font,
4794		    TEXT_X(col),
4795		    TEXT_Y(row),
4796		    glyphs);
4797
4798    /* redraw the contents with an offset of 1 to emulate bold */
4799    if ((flags & DRAW_BOLD) && !gui.font_can_bold)
4800	gdk_draw_glyphs(gui.drawarea->window,
4801			gui.text_gc,
4802			font,
4803			TEXT_X(col) + 1,
4804			TEXT_Y(row),
4805			glyphs);
4806}
4807
4808/*
4809 * Draw underline and undercurl at the bottom of the character cell.
4810 */
4811    static void
4812draw_under(int flags, int row, int col, int cells)
4813{
4814    int			i;
4815    int			offset;
4816    static const int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
4817    int			y = FILL_Y(row + 1) - 1;
4818
4819    /* Undercurl: draw curl at the bottom of the character cell. */
4820    if (flags & DRAW_UNDERC)
4821    {
4822	gdk_gc_set_foreground(gui.text_gc, gui.spcolor);
4823	for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
4824	{
4825	    offset = val[i % 8];
4826	    gdk_draw_point(gui.drawarea->window, gui.text_gc, i, y - offset);
4827	}
4828	gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
4829    }
4830
4831    /* Underline: draw a line at the bottom of the character cell. */
4832    if (flags & DRAW_UNDERL)
4833    {
4834	/* When p_linespace is 0, overwrite the bottom row of pixels.
4835	 * Otherwise put the line just below the character. */
4836	if (p_linespace > 1)
4837	    y -= p_linespace - 1;
4838	gdk_draw_line(gui.drawarea->window, gui.text_gc,
4839		      FILL_X(col), y,
4840		      FILL_X(col + cells) - 1, y);
4841    }
4842}
4843
4844    int
4845gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
4846{
4847    GdkRectangle	area;		    /* area for clip mask	  */
4848    PangoGlyphString	*glyphs;	    /* glyphs of current item	  */
4849    int			column_offset = 0;  /* column offset in cells	  */
4850    int			i;
4851    char_u		*conv_buf = NULL;   /* result of UTF-8 conversion */
4852    char_u		*new_conv_buf;
4853    int			convlen;
4854    char_u		*sp, *bp;
4855    int			plen;
4856
4857    if (gui.text_context == NULL || gui.drawarea->window == NULL)
4858	return len;
4859
4860    if (output_conv.vc_type != CONV_NONE)
4861    {
4862	/*
4863	 * Convert characters from 'encoding' to 'termencoding', which is set
4864	 * to UTF-8 by gui_mch_init().	did_set_string_option() in option.c
4865	 * prohibits changing this to something else than UTF-8 if the GUI is
4866	 * in use.
4867	 */
4868	convlen = len;
4869	conv_buf = string_convert(&output_conv, s, &convlen);
4870	g_return_val_if_fail(conv_buf != NULL, len);
4871
4872	/* Correct for differences in char width: some chars are
4873	 * double-wide in 'encoding' but single-wide in utf-8.  Add a space to
4874	 * compensate for that. */
4875	for (sp = s, bp = conv_buf; sp < s + len && bp < conv_buf + convlen; )
4876	{
4877	    plen = utf_ptr2len(bp);
4878	    if ((*mb_ptr2cells)(sp) == 2 && utf_ptr2cells(bp) == 1)
4879	    {
4880		new_conv_buf = alloc(convlen + 2);
4881		if (new_conv_buf == NULL)
4882		    return len;
4883		plen += bp - conv_buf;
4884		mch_memmove(new_conv_buf, conv_buf, plen);
4885		new_conv_buf[plen] = ' ';
4886		mch_memmove(new_conv_buf + plen + 1, conv_buf + plen,
4887							  convlen - plen + 1);
4888		vim_free(conv_buf);
4889		conv_buf = new_conv_buf;
4890		++convlen;
4891		bp = conv_buf + plen;
4892		plen = 1;
4893	    }
4894	    sp += (*mb_ptr2len)(sp);
4895	    bp += plen;
4896	}
4897	s = conv_buf;
4898	len = convlen;
4899    }
4900
4901    /*
4902     * Restrict all drawing to the current screen line in order to prevent
4903     * fuzzy font lookups from messing up the screen.
4904     */
4905    area.x = gui.border_offset;
4906    area.y = FILL_Y(row);
4907    area.width	= gui.num_cols * gui.char_width;
4908    area.height = gui.char_height;
4909
4910    gdk_gc_set_clip_origin(gui.text_gc, 0, 0);
4911    gdk_gc_set_clip_rectangle(gui.text_gc, &area);
4912
4913    glyphs = pango_glyph_string_new();
4914
4915    /*
4916     * Optimization hack:  If possible, skip the itemize and shaping process
4917     * for pure ASCII strings.	This optimization is particularly effective
4918     * because Vim draws space characters to clear parts of the screen.
4919     */
4920    if (!(flags & DRAW_ITALIC)
4921	    && !((flags & DRAW_BOLD) && gui.font_can_bold)
4922	    && gui.ascii_glyphs != NULL)
4923    {
4924	char_u *p;
4925
4926	for (p = s; p < s + len; ++p)
4927	    if (*p & 0x80)
4928		goto not_ascii;
4929
4930	pango_glyph_string_set_size(glyphs, len);
4931
4932	for (i = 0; i < len; ++i)
4933	{
4934	    glyphs->glyphs[i] = gui.ascii_glyphs->glyphs[s[i]];
4935	    glyphs->log_clusters[i] = i;
4936	}
4937
4938	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs);
4939
4940	column_offset = len;
4941    }
4942    else
4943not_ascii:
4944    {
4945	PangoAttrList	*attr_list;
4946	GList		*item_list;
4947	int		cluster_width;
4948	int		last_glyph_rbearing;
4949	int		cells = 0;  /* cells occupied by current cluster */
4950
4951	/* Safety check: pango crashes when invoked with invalid utf-8
4952	 * characters. */
4953	if (!utf_valid_string(s, s + len))
4954	{
4955	    column_offset = len;
4956	    goto skipitall;
4957	}
4958
4959	/* original width of the current cluster */
4960	cluster_width = PANGO_SCALE * gui.char_width;
4961
4962	/* right bearing of the last non-composing glyph */
4963	last_glyph_rbearing = PANGO_SCALE * gui.char_width;
4964
4965	attr_list = pango_attr_list_new();
4966
4967	/* If 'guifontwide' is set then use that for double-width characters.
4968	 * Otherwise just go with 'guifont' and let Pango do its thing. */
4969	if (gui.wide_font != NULL)
4970	    apply_wide_font_attr(s, len, attr_list);
4971
4972	if ((flags & DRAW_BOLD) && gui.font_can_bold)
4973	    INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD),
4974			      attr_list, 0, len);
4975	if (flags & DRAW_ITALIC)
4976	    INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC),
4977			      attr_list, 0, len);
4978	/*
4979	 * Break the text into segments with consistent directional level
4980	 * and shaping engine.	Pure Latin text needs only a single segment,
4981	 * so there's no need to worry about the loop's efficiency.  Better
4982	 * try to optimize elsewhere, e.g. reducing exposes and stuff :)
4983	 */
4984	item_list = pango_itemize(gui.text_context,
4985				  (const char *)s, 0, len, attr_list, NULL);
4986
4987	while (item_list != NULL)
4988	{
4989	    PangoItem	*item;
4990	    int		item_cells = 0; /* item length in cells */
4991
4992	    item = (PangoItem *)item_list->data;
4993	    item_list = g_list_delete_link(item_list, item_list);
4994	    /*
4995	     * Increment the bidirectional embedding level by 1 if it is not
4996	     * even.  An odd number means the output will be RTL, but we don't
4997	     * want that since Vim handles right-to-left text on its own.  It
4998	     * would probably be sufficient to just set level = 0, but you can
4999	     * never know :)
5000	     *
5001	     * Unfortunately we can't take advantage of Pango's ability to
5002	     * render both LTR and RTL at the same time.  In order to support
5003	     * that, Vim's main screen engine would have to make use of Pango
5004	     * functionality.
5005	     */
5006	    item->analysis.level = (item->analysis.level + 1) & (~1U);
5007
5008	    /* HACK: Overrule the shape engine, we don't want shaping to be
5009	     * done, because drawing the cursor would change the display. */
5010	    item->analysis.shape_engine = default_shape_engine;
5011
5012	    pango_shape((const char *)s + item->offset, item->length,
5013			&item->analysis, glyphs);
5014	    /*
5015	     * Fixed-width hack: iterate over the array and assign a fixed
5016	     * width to each glyph, thus overriding the choice made by the
5017	     * shaping engine.	We use utf_char2cells() to determine the
5018	     * number of cells needed.
5019	     *
5020	     * Also perform all kind of dark magic to get composing
5021	     * characters right (and pretty too of course).
5022	     */
5023	    for (i = 0; i < glyphs->num_glyphs; ++i)
5024	    {
5025		PangoGlyphInfo *glyph;
5026
5027		glyph = &glyphs->glyphs[i];
5028
5029		if (glyph->attr.is_cluster_start)
5030		{
5031		    int cellcount;
5032
5033		    cellcount = count_cluster_cells(
5034			s, item, glyphs, i, &cluster_width,
5035			(item_list != NULL) ? &last_glyph_rbearing : NULL);
5036
5037		    if (cellcount > 0)
5038		    {
5039			int width;
5040
5041			width = cellcount * gui.char_width * PANGO_SCALE;
5042			glyph->geometry.x_offset +=
5043					    MAX(0, width - cluster_width) / 2;
5044			glyph->geometry.width = width;
5045		    }
5046		    else
5047		    {
5048			/* If there are only combining characters in the
5049			 * cluster, we cannot just change the width of the
5050			 * previous glyph since there is none.	Therefore
5051			 * some guesswork is needed. */
5052			setup_zero_width_cluster(item, glyph, cells,
5053						 cluster_width,
5054						 last_glyph_rbearing);
5055		    }
5056
5057		    item_cells += cellcount;
5058		    cells = cellcount;
5059		}
5060		else if (i > 0)
5061		{
5062		    int width;
5063
5064		    /* There is a previous glyph, so we deal with combining
5065		     * characters the canonical way.
5066		     * In some circumstances Pango uses a positive x_offset,
5067		     * then use the width of the previous glyph for this one
5068		     * and set the previous width to zero.
5069		     * Otherwise we get a negative x_offset, Pango has already
5070		     * positioned the combining char, keep the widths as they
5071		     * are.
5072		     * For both adjust the x_offset to position the glyph in
5073		     * the middle. */
5074		    if (glyph->geometry.x_offset >= 0)
5075		    {
5076			glyphs->glyphs[i].geometry.width =
5077					 glyphs->glyphs[i - 1].geometry.width;
5078			glyphs->glyphs[i - 1].geometry.width = 0;
5079		    }
5080		    width = cells * gui.char_width * PANGO_SCALE;
5081		    glyph->geometry.x_offset +=
5082					    MAX(0, width - cluster_width) / 2;
5083		}
5084		else /* i == 0 "cannot happen" */
5085		{
5086		    glyph->geometry.width = 0;
5087		}
5088	    }
5089
5090	    /*** Aaaaand action! ***/
5091	    draw_glyph_string(row, col + column_offset, item_cells,
5092			      flags, item->analysis.font, glyphs);
5093
5094	    pango_item_free(item);
5095
5096	    column_offset += item_cells;
5097	}
5098
5099	pango_attr_list_unref(attr_list);
5100    }
5101
5102skipitall:
5103    /* Draw underline and undercurl. */
5104    draw_under(flags, row, col, column_offset);
5105
5106    pango_glyph_string_free(glyphs);
5107    vim_free(conv_buf);
5108
5109    gdk_gc_set_clip_rectangle(gui.text_gc, NULL);
5110
5111    return column_offset;
5112}
5113
5114/*
5115 * Return OK if the key with the termcap name "name" is supported.
5116 */
5117    int
5118gui_mch_haskey(char_u *name)
5119{
5120    int i;
5121
5122    for (i = 0; special_keys[i].key_sym != 0; i++)
5123	if (name[0] == special_keys[i].code0
5124		&& name[1] == special_keys[i].code1)
5125	    return OK;
5126    return FAIL;
5127}
5128
5129#if defined(FEAT_TITLE) \
5130	|| defined(PROTO)
5131/*
5132 * Return the text window-id and display.  Only required for X-based GUI's
5133 */
5134    int
5135gui_get_x11_windis(Window *win, Display **dis)
5136{
5137    if (gui.mainwin != NULL && gui.mainwin->window != NULL)
5138    {
5139	*dis = GDK_WINDOW_XDISPLAY(gui.mainwin->window);
5140	*win = GDK_WINDOW_XWINDOW(gui.mainwin->window);
5141	return OK;
5142    }
5143
5144    *dis = NULL;
5145    *win = 0;
5146    return FAIL;
5147}
5148#endif
5149
5150#if defined(FEAT_CLIENTSERVER) \
5151	|| (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
5152
5153    Display *
5154gui_mch_get_display(void)
5155{
5156    if (gui.mainwin != NULL && gui.mainwin->window != NULL)
5157	return GDK_WINDOW_XDISPLAY(gui.mainwin->window);
5158    else
5159	return NULL;
5160}
5161#endif
5162
5163    void
5164gui_mch_beep(void)
5165{
5166#ifdef HAVE_GTK_MULTIHEAD
5167    GdkDisplay *display;
5168
5169    if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
5170	display = gtk_widget_get_display(gui.mainwin);
5171    else
5172	display = gdk_display_get_default();
5173
5174    if (display != NULL)
5175	gdk_display_beep(display);
5176#else
5177    gdk_beep();
5178#endif
5179}
5180
5181    void
5182gui_mch_flash(int msec)
5183{
5184    GdkGCValues	values;
5185    GdkGC	*invert_gc;
5186
5187    if (gui.drawarea->window == NULL)
5188	return;
5189
5190    values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel;
5191    values.background.pixel = gui.norm_pixel ^ gui.back_pixel;
5192    values.function = GDK_XOR;
5193    invert_gc = gdk_gc_new_with_values(gui.drawarea->window,
5194				       &values,
5195				       GDK_GC_FOREGROUND |
5196				       GDK_GC_BACKGROUND |
5197				       GDK_GC_FUNCTION);
5198    gdk_gc_set_exposures(invert_gc,
5199			 gui.visibility != GDK_VISIBILITY_UNOBSCURED);
5200    /*
5201     * Do a visual beep by changing back and forth in some undetermined way,
5202     * the foreground and background colors.  This is due to the fact that
5203     * there can't be really any prediction about the effects of XOR on
5204     * arbitrary X11 servers. However this seems to be enough for what we
5205     * intend it to do.
5206     */
5207    gdk_draw_rectangle(gui.drawarea->window, invert_gc,
5208		       TRUE,
5209		       0, 0,
5210		       FILL_X((int)Columns) + gui.border_offset,
5211		       FILL_Y((int)Rows) + gui.border_offset);
5212
5213    gui_mch_flush();
5214    ui_delay((long)msec, TRUE);	/* wait so many msec */
5215
5216    gdk_draw_rectangle(gui.drawarea->window, invert_gc,
5217		       TRUE,
5218		       0, 0,
5219		       FILL_X((int)Columns) + gui.border_offset,
5220		       FILL_Y((int)Rows) + gui.border_offset);
5221
5222    gdk_gc_destroy(invert_gc);
5223}
5224
5225/*
5226 * Invert a rectangle from row r, column c, for nr rows and nc columns.
5227 */
5228    void
5229gui_mch_invert_rectangle(int r, int c, int nr, int nc)
5230{
5231    GdkGCValues values;
5232    GdkGC *invert_gc;
5233
5234    if (gui.drawarea->window == NULL)
5235	return;
5236
5237    values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel;
5238    values.background.pixel = gui.norm_pixel ^ gui.back_pixel;
5239    values.function = GDK_XOR;
5240    invert_gc = gdk_gc_new_with_values(gui.drawarea->window,
5241				       &values,
5242				       GDK_GC_FOREGROUND |
5243				       GDK_GC_BACKGROUND |
5244				       GDK_GC_FUNCTION);
5245    gdk_gc_set_exposures(invert_gc, gui.visibility !=
5246						   GDK_VISIBILITY_UNOBSCURED);
5247    gdk_draw_rectangle(gui.drawarea->window, invert_gc,
5248		       TRUE,
5249		       FILL_X(c), FILL_Y(r),
5250		       (nc) * gui.char_width, (nr) * gui.char_height);
5251    gdk_gc_destroy(invert_gc);
5252}
5253
5254/*
5255 * Iconify the GUI window.
5256 */
5257    void
5258gui_mch_iconify(void)
5259{
5260    gtk_window_iconify(GTK_WINDOW(gui.mainwin));
5261}
5262
5263#if defined(FEAT_EVAL) || defined(PROTO)
5264/*
5265 * Bring the Vim window to the foreground.
5266 */
5267    void
5268gui_mch_set_foreground(void)
5269{
5270    gtk_window_present(GTK_WINDOW(gui.mainwin));
5271}
5272#endif
5273
5274/*
5275 * Draw a cursor without focus.
5276 */
5277    void
5278gui_mch_draw_hollow_cursor(guicolor_T color)
5279{
5280    int		i = 1;
5281
5282    if (gui.drawarea->window == NULL)
5283	return;
5284
5285    gui_mch_set_fg_color(color);
5286
5287    gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5288    if (mb_lefthalve(gui.row, gui.col))
5289	i = 2;
5290    gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
5291	    FALSE,
5292	    FILL_X(gui.col), FILL_Y(gui.row),
5293	    i * gui.char_width - 1, gui.char_height - 1);
5294}
5295
5296/*
5297 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
5298 * color "color".
5299 */
5300    void
5301gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
5302{
5303    if (gui.drawarea->window == NULL)
5304	return;
5305
5306    gui_mch_set_fg_color(color);
5307
5308    gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5309    gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
5310	    TRUE,
5311#ifdef FEAT_RIGHTLEFT
5312	    /* vertical line should be on the right of current point */
5313	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
5314#endif
5315	    FILL_X(gui.col),
5316	    FILL_Y(gui.row) + gui.char_height - h,
5317	    w, h);
5318}
5319
5320
5321/*
5322 * Catch up with any queued X11 events.  This may put keyboard input into the
5323 * input buffer, call resize call-backs, trigger timers etc.  If there is
5324 * nothing in the X11 event queue (& no timers pending), then we return
5325 * immediately.
5326 */
5327    void
5328gui_mch_update(void)
5329{
5330    while (g_main_context_pending(NULL) && !vim_is_input_buf_full())
5331	g_main_context_iteration(NULL, TRUE);
5332}
5333
5334    static gint
5335input_timer_cb(gpointer data)
5336{
5337    int *timed_out = (int *) data;
5338
5339    /* Just inform the caller about the occurrence of it */
5340    *timed_out = TRUE;
5341
5342    return FALSE;		/* don't happen again */
5343}
5344
5345#ifdef FEAT_SNIFF
5346/*
5347 * Callback function, used when data is available on the SNiFF connection.
5348 */
5349    static void
5350sniff_request_cb(
5351    gpointer	data,
5352    gint	source_fd,
5353    GdkInputCondition condition)
5354{
5355    static char_u bytes[3] = {CSI, (int)KS_EXTRA, (int)KE_SNIFF};
5356
5357    add_to_input_buf(bytes, 3);
5358}
5359#endif
5360
5361/*
5362 * GUI input routine called by gui_wait_for_chars().  Waits for a character
5363 * from the keyboard.
5364 *  wtime == -1     Wait forever.
5365 *  wtime == 0	    This should never happen.
5366 *  wtime > 0	    Wait wtime milliseconds for a character.
5367 * Returns OK if a character was found to be available within the given time,
5368 * or FAIL otherwise.
5369 */
5370    int
5371gui_mch_wait_for_chars(long wtime)
5372{
5373    int focus;
5374    guint timer;
5375    static int timed_out;
5376#ifdef FEAT_SNIFF
5377    static int	sniff_on = 0;
5378    static gint	sniff_input_id = 0;
5379#endif
5380
5381#ifdef FEAT_SNIFF
5382    if (sniff_on && !want_sniff_request)
5383    {
5384	if (sniff_input_id)
5385	    gdk_input_remove(sniff_input_id);
5386	sniff_on = 0;
5387    }
5388    else if (!sniff_on && want_sniff_request)
5389    {
5390	/* Add fd_from_sniff to watch for available data in main loop. */
5391	sniff_input_id = gdk_input_add(fd_from_sniff,
5392			       GDK_INPUT_READ, sniff_request_cb, NULL);
5393	sniff_on = 1;
5394    }
5395#endif
5396
5397    timed_out = FALSE;
5398
5399    /* this timeout makes sure that we will return if no characters arrived in
5400     * time */
5401
5402    if (wtime > 0)
5403	timer = gtk_timeout_add((guint32)wtime, input_timer_cb, &timed_out);
5404    else
5405	timer = 0;
5406
5407    focus = gui.in_focus;
5408
5409    do
5410    {
5411	/* Stop or start blinking when focus changes */
5412	if (gui.in_focus != focus)
5413	{
5414	    if (gui.in_focus)
5415		gui_mch_start_blink();
5416	    else
5417		gui_mch_stop_blink();
5418	    focus = gui.in_focus;
5419	}
5420
5421#if defined(FEAT_NETBEANS_INTG)
5422	/* Process the queued netbeans messages. */
5423	netbeans_parse_messages();
5424#endif
5425
5426	/*
5427	 * Loop in GTK+ processing  until a timeout or input occurs.
5428	 * Skip this if input is available anyway (can happen in rare
5429	 * situations, sort of race condition).
5430	 */
5431	if (!input_available())
5432	    g_main_context_iteration(NULL, TRUE);
5433
5434	/* Got char, return immediately */
5435	if (input_available())
5436	{
5437	    if (timer != 0 && !timed_out)
5438		gtk_timeout_remove(timer);
5439	    return OK;
5440	}
5441    } while (wtime < 0 || !timed_out);
5442
5443    /*
5444     * Flush all eventually pending (drawing) events.
5445     */
5446    gui_mch_update();
5447
5448    return FAIL;
5449}
5450
5451
5452/****************************************************************************
5453 * Output drawing routines.
5454 ****************************************************************************/
5455
5456
5457/* Flush any output to the screen */
5458    void
5459gui_mch_flush(void)
5460{
5461#ifdef HAVE_GTK_MULTIHEAD
5462    if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
5463	gdk_display_sync(gtk_widget_get_display(gui.mainwin));
5464#else
5465    gdk_flush(); /* historical misnomer: calls XSync(), not XFlush() */
5466#endif
5467    /* This happens to actually do what gui_mch_flush() is supposed to do,
5468     * according to the comment above. */
5469    if (gui.drawarea != NULL && gui.drawarea->window != NULL)
5470	gdk_window_process_updates(gui.drawarea->window, FALSE);
5471}
5472
5473/*
5474 * Clear a rectangular region of the screen from text pos (row1, col1) to
5475 * (row2, col2) inclusive.
5476 */
5477    void
5478gui_mch_clear_block(int row1, int col1, int row2, int col2)
5479{
5480    GdkColor color;
5481
5482    if (gui.drawarea->window == NULL)
5483	return;
5484
5485    color.pixel = gui.back_pixel;
5486
5487    gdk_gc_set_foreground(gui.text_gc, &color);
5488
5489    /* Clear one extra pixel at the far right, for when bold characters have
5490     * spilled over to the window border. */
5491    gdk_draw_rectangle(gui.drawarea->window, gui.text_gc, TRUE,
5492		       FILL_X(col1), FILL_Y(row1),
5493		       (col2 - col1 + 1) * gui.char_width
5494						      + (col2 == Columns - 1),
5495		       (row2 - row1 + 1) * gui.char_height);
5496}
5497
5498    void
5499gui_mch_clear_all(void)
5500{
5501    if (gui.drawarea->window != NULL)
5502	gdk_window_clear(gui.drawarea->window);
5503}
5504
5505/*
5506 * Redraw any text revealed by scrolling up/down.
5507 */
5508    static void
5509check_copy_area(void)
5510{
5511    GdkEvent	*event;
5512    int		expose_count;
5513
5514    if (gui.visibility != GDK_VISIBILITY_PARTIAL)
5515	return;
5516
5517    /* Avoid redrawing the cursor while scrolling or it'll end up where
5518     * we don't want it to be.	I'm not sure if it's correct to call
5519     * gui_dont_update_cursor() at this point but it works as a quick
5520     * fix for now. */
5521    gui_dont_update_cursor();
5522
5523    do
5524    {
5525	/* Wait to check whether the scroll worked or not. */
5526	event = gdk_event_get_graphics_expose(gui.drawarea->window);
5527
5528	if (event == NULL)
5529	    break; /* received NoExpose event */
5530
5531	gui_redraw(event->expose.area.x, event->expose.area.y,
5532		   event->expose.area.width, event->expose.area.height);
5533
5534	expose_count = event->expose.count;
5535	gdk_event_free(event);
5536    }
5537    while (expose_count > 0); /* more events follow */
5538
5539    gui_can_update_cursor();
5540}
5541
5542/*
5543 * Delete the given number of lines from the given row, scrolling up any
5544 * text further down within the scroll region.
5545 */
5546    void
5547gui_mch_delete_lines(int row, int num_lines)
5548{
5549    if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
5550	return;			/* Can't see the window */
5551
5552    gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5553    gdk_gc_set_background(gui.text_gc, gui.bgcolor);
5554
5555    /* copy one extra pixel, for when bold has spilled over */
5556    gdk_window_copy_area(gui.drawarea->window, gui.text_gc,
5557	    FILL_X(gui.scroll_region_left), FILL_Y(row),
5558	    gui.drawarea->window,
5559	    FILL_X(gui.scroll_region_left),
5560	    FILL_Y(row + num_lines),
5561	    gui.char_width * (gui.scroll_region_right
5562					    - gui.scroll_region_left + 1) + 1,
5563	    gui.char_height * (gui.scroll_region_bot - row - num_lines + 1));
5564
5565    gui_clear_block(gui.scroll_region_bot - num_lines + 1,
5566						       gui.scroll_region_left,
5567		    gui.scroll_region_bot, gui.scroll_region_right);
5568    check_copy_area();
5569}
5570
5571/*
5572 * Insert the given number of lines before the given row, scrolling down any
5573 * following text within the scroll region.
5574 */
5575    void
5576gui_mch_insert_lines(int row, int num_lines)
5577{
5578    if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
5579	return;			/* Can't see the window */
5580
5581    gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5582    gdk_gc_set_background(gui.text_gc, gui.bgcolor);
5583
5584    /* copy one extra pixel, for when bold has spilled over */
5585    gdk_window_copy_area(gui.drawarea->window, gui.text_gc,
5586	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
5587	    gui.drawarea->window,
5588	    FILL_X(gui.scroll_region_left), FILL_Y(row),
5589	    gui.char_width * (gui.scroll_region_right
5590					    - gui.scroll_region_left + 1) + 1,
5591	    gui.char_height * (gui.scroll_region_bot - row - num_lines + 1));
5592
5593    gui_clear_block(row, gui.scroll_region_left,
5594				row + num_lines - 1, gui.scroll_region_right);
5595    check_copy_area();
5596}
5597
5598/*
5599 * X Selection stuff, for cutting and pasting text to other windows.
5600 */
5601    void
5602clip_mch_request_selection(VimClipboard *cbd)
5603{
5604    GdkAtom	target;
5605    unsigned	i;
5606    time_t	start;
5607
5608    for (i = 0; i < N_SELECTION_TARGETS; ++i)
5609    {
5610	if (!clip_html && selection_targets[i].info == TARGET_HTML)
5611	    continue;
5612	received_selection = RS_NONE;
5613	target = gdk_atom_intern(selection_targets[i].target, FALSE);
5614
5615	gtk_selection_convert(gui.drawarea,
5616			      cbd->gtk_sel_atom, target,
5617			      (guint32)GDK_CURRENT_TIME);
5618
5619	/* Hack: Wait up to three seconds for the selection.  A hang was
5620	 * noticed here when using the netrw plugin combined with ":gui"
5621	 * during the FocusGained event. */
5622	start = time(NULL);
5623	while (received_selection == RS_NONE && time(NULL) < start + 3)
5624	    g_main_context_iteration(NULL, TRUE);	/* wait for selection_received_cb */
5625
5626	if (received_selection != RS_FAIL)
5627	    return;
5628    }
5629
5630    /* Final fallback position - use the X CUT_BUFFER0 store */
5631    yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gui.mainwin->window), cbd);
5632}
5633
5634/*
5635 * Disown the selection.
5636 */
5637    void
5638clip_mch_lose_selection(VimClipboard *cbd UNUSED)
5639{
5640    /* WEIRD: when using NULL to actually disown the selection, we lose the
5641     * selection the first time we own it. */
5642    /*
5643    gtk_selection_owner_set(NULL, cbd->gtk_sel_atom, (guint32)GDK_CURRENT_TIME);
5644    gui_mch_update();
5645     */
5646}
5647
5648/*
5649 * Own the selection and return OK if it worked.
5650 */
5651    int
5652clip_mch_own_selection(VimClipboard *cbd)
5653{
5654    int success;
5655
5656    success = gtk_selection_owner_set(gui.drawarea, cbd->gtk_sel_atom,
5657				      clipboard_event_time);
5658    gui_mch_update();
5659    return (success) ? OK : FAIL;
5660}
5661
5662/*
5663 * Send the current selection to the clipboard.  Do nothing for X because we
5664 * will fill in the selection only when requested by another app.
5665 */
5666    void
5667clip_mch_set_selection(VimClipboard *cbd UNUSED)
5668{
5669}
5670
5671
5672#if defined(FEAT_MENU) || defined(PROTO)
5673/*
5674 * Make a menu item appear either active or not active (grey or not grey).
5675 */
5676    void
5677gui_mch_menu_grey(vimmenu_T *menu, int grey)
5678{
5679    if (menu->id == NULL)
5680	return;
5681
5682    if (menu_is_separator(menu->name))
5683	grey = TRUE;
5684
5685    gui_mch_menu_hidden(menu, FALSE);
5686    /* Be clever about bitfields versus true booleans here! */
5687    if (!GTK_WIDGET_SENSITIVE(menu->id) == !grey)
5688    {
5689	gtk_widget_set_sensitive(menu->id, !grey);
5690	gui_mch_update();
5691    }
5692}
5693
5694/*
5695 * Make menu item hidden or not hidden.
5696 */
5697    void
5698gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
5699{
5700    if (menu->id == 0)
5701	return;
5702
5703    if (hidden)
5704    {
5705	if (GTK_WIDGET_VISIBLE(menu->id))
5706	{
5707	    gtk_widget_hide(menu->id);
5708	    gui_mch_update();
5709	}
5710    }
5711    else
5712    {
5713	if (!GTK_WIDGET_VISIBLE(menu->id))
5714	{
5715	    gtk_widget_show(menu->id);
5716	    gui_mch_update();
5717	}
5718    }
5719}
5720
5721/*
5722 * This is called after setting all the menus to grey/hidden or not.
5723 */
5724    void
5725gui_mch_draw_menubar(void)
5726{
5727    /* just make sure that the visual changes get effect immediately */
5728    gui_mch_update();
5729}
5730#endif /* FEAT_MENU */
5731
5732/*
5733 * Scrollbar stuff.
5734 */
5735    void
5736gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
5737{
5738    if (sb->id == NULL)
5739	return;
5740
5741    if (flag)
5742	gtk_widget_show(sb->id);
5743    else
5744	gtk_widget_hide(sb->id);
5745
5746    update_window_manager_hints(0, 0);
5747}
5748
5749
5750/*
5751 * Return the RGB value of a pixel as long.
5752 */
5753    long_u
5754gui_mch_get_rgb(guicolor_T pixel)
5755{
5756    GdkColor color;
5757    gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
5758			     (unsigned long)pixel, &color);
5759
5760    return (((unsigned)color.red   & 0xff00) << 8)
5761	 |  ((unsigned)color.green & 0xff00)
5762	 | (((unsigned)color.blue  & 0xff00) >> 8);
5763}
5764
5765/*
5766 * Get current mouse coordinates in text window.
5767 */
5768    void
5769gui_mch_getmouse(int *x, int *y)
5770{
5771    gdk_window_get_pointer(gui.drawarea->window, x, y, NULL);
5772}
5773
5774    void
5775gui_mch_setmouse(int x, int y)
5776{
5777    /* Sorry for the Xlib call, but we can't avoid it, since there is no
5778     * internal GDK mechanism present to accomplish this.  (and for good
5779     * reason...) */
5780    XWarpPointer(GDK_WINDOW_XDISPLAY(gui.drawarea->window),
5781		 (Window)0, GDK_WINDOW_XWINDOW(gui.drawarea->window),
5782		 0, 0, 0U, 0U, x, y);
5783}
5784
5785
5786#ifdef FEAT_MOUSESHAPE
5787/* The last set mouse pointer shape is remembered, to be used when it goes
5788 * from hidden to not hidden. */
5789static int last_shape = 0;
5790#endif
5791
5792/*
5793 * Use the blank mouse pointer or not.
5794 *
5795 * hide: TRUE = use blank ptr, FALSE = use parent ptr
5796 */
5797    void
5798gui_mch_mousehide(int hide)
5799{
5800    if (gui.pointer_hidden != hide)
5801    {
5802	gui.pointer_hidden = hide;
5803	if (gui.drawarea->window && gui.blank_pointer != NULL)
5804	{
5805	    if (hide)
5806		gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
5807	    else
5808#ifdef FEAT_MOUSESHAPE
5809		mch_set_mouse_shape(last_shape);
5810#else
5811		gdk_window_set_cursor(gui.drawarea->window, NULL);
5812#endif
5813	}
5814    }
5815}
5816
5817#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
5818
5819/* Table for shape IDs.  Keep in sync with the mshape_names[] table in
5820 * misc2.c! */
5821static const int mshape_ids[] =
5822{
5823    GDK_LEFT_PTR,		/* arrow */
5824    GDK_CURSOR_IS_PIXMAP,	/* blank */
5825    GDK_XTERM,			/* beam */
5826    GDK_SB_V_DOUBLE_ARROW,	/* updown */
5827    GDK_SIZING,			/* udsizing */
5828    GDK_SB_H_DOUBLE_ARROW,	/* leftright */
5829    GDK_SIZING,			/* lrsizing */
5830    GDK_WATCH,			/* busy */
5831    GDK_X_CURSOR,		/* no */
5832    GDK_CROSSHAIR,		/* crosshair */
5833    GDK_HAND1,			/* hand1 */
5834    GDK_HAND2,			/* hand2 */
5835    GDK_PENCIL,			/* pencil */
5836    GDK_QUESTION_ARROW,		/* question */
5837    GDK_RIGHT_PTR,		/* right-arrow */
5838    GDK_CENTER_PTR,		/* up-arrow */
5839    GDK_LEFT_PTR		/* last one */
5840};
5841
5842    void
5843mch_set_mouse_shape(int shape)
5844{
5845    int		   id;
5846    GdkCursor	   *c;
5847
5848    if (gui.drawarea->window == NULL)
5849	return;
5850
5851    if (shape == MSHAPE_HIDE || gui.pointer_hidden)
5852	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
5853    else
5854    {
5855	if (shape >= MSHAPE_NUMBERED)
5856	{
5857	    id = shape - MSHAPE_NUMBERED;
5858	    if (id >= GDK_LAST_CURSOR)
5859		id = GDK_LEFT_PTR;
5860	    else
5861		id &= ~1;	/* they are always even (why?) */
5862	}
5863	else if (shape < (int)(sizeof(mshape_ids) / sizeof(int)))
5864	    id = mshape_ids[shape];
5865	else
5866	    return;
5867# ifdef HAVE_GTK_MULTIHEAD
5868	c = gdk_cursor_new_for_display(
5869		gtk_widget_get_display(gui.drawarea), (GdkCursorType)id);
5870# else
5871	c = gdk_cursor_new((GdkCursorType)id);
5872# endif
5873	gdk_window_set_cursor(gui.drawarea->window, c);
5874	gdk_cursor_destroy(c); /* Unref, actually.  Bloody GTK+ 1. */
5875    }
5876    if (shape != MSHAPE_HIDE)
5877	last_shape = shape;
5878}
5879#endif /* FEAT_MOUSESHAPE */
5880
5881
5882#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
5883/*
5884 * Signs are currently always 2 chars wide.  With GTK+ 2, the image will be
5885 * scaled down if the current font is not big enough, or scaled up if the image
5886 * size is less than 3/4 of the maximum sign size.  With GTK+ 1, the pixmap
5887 * will be cut off if the current font is not big enough, or centered if it's
5888 * too small.
5889 */
5890# define SIGN_WIDTH  (2 * gui.char_width)
5891# define SIGN_HEIGHT (gui.char_height)
5892# define SIGN_ASPECT ((double)SIGN_HEIGHT / (double)SIGN_WIDTH)
5893
5894    void
5895gui_mch_drawsign(int row, int col, int typenr)
5896{
5897    GdkPixbuf *sign;
5898
5899    sign = (GdkPixbuf *)sign_get_image(typenr);
5900
5901    if (sign != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
5902    {
5903	int width;
5904	int height;
5905	int xoffset;
5906	int yoffset;
5907	int need_scale;
5908
5909	width  = gdk_pixbuf_get_width(sign);
5910	height = gdk_pixbuf_get_height(sign);
5911	/*
5912	 * Decide whether we need to scale.  Allow one pixel of border
5913	 * width to be cut off, in order to avoid excessive scaling for
5914	 * tiny differences in font size.
5915	 */
5916	need_scale = (width > SIGN_WIDTH + 2
5917		      || height > SIGN_HEIGHT + 2
5918		      || (width < 3 * SIGN_WIDTH / 4
5919			  && height < 3 * SIGN_HEIGHT / 4));
5920	if (need_scale)
5921	{
5922	    double aspect;
5923
5924	    /* Keep the original aspect ratio */
5925	    aspect = (double)height / (double)width;
5926	    width  = (double)SIGN_WIDTH * SIGN_ASPECT / aspect;
5927	    width  = MIN(width, SIGN_WIDTH);
5928	    height = (double)width * aspect;
5929
5930	    /* This doesn't seem to be worth caching, and doing so
5931	     * would complicate the code quite a bit. */
5932	    sign = gdk_pixbuf_scale_simple(sign, width, height,
5933					   GDK_INTERP_BILINEAR);
5934	    if (sign == NULL)
5935		return; /* out of memory */
5936	}
5937
5938	/* The origin is the upper-left corner of the pixmap.  Therefore
5939	 * these offset may become negative if the pixmap is smaller than
5940	 * the 2x1 cells reserved for the sign icon. */
5941	xoffset = (width  - SIGN_WIDTH)  / 2;
5942	yoffset = (height - SIGN_HEIGHT) / 2;
5943
5944	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
5945
5946	gdk_draw_rectangle(gui.drawarea->window,
5947			   gui.text_gc,
5948			   TRUE,
5949			   FILL_X(col),
5950			   FILL_Y(row),
5951			   SIGN_WIDTH,
5952			   SIGN_HEIGHT);
5953
5954	gdk_pixbuf_render_to_drawable_alpha(sign,
5955					    gui.drawarea->window,
5956					    MAX(0, xoffset),
5957					    MAX(0, yoffset),
5958					    FILL_X(col) - MIN(0, xoffset),
5959					    FILL_Y(row) - MIN(0, yoffset),
5960					    MIN(width,	SIGN_WIDTH),
5961					    MIN(height, SIGN_HEIGHT),
5962					    GDK_PIXBUF_ALPHA_BILEVEL,
5963					    127,
5964					    GDK_RGB_DITHER_NORMAL,
5965					    0, 0);
5966	if (need_scale)
5967	    g_object_unref(sign);
5968    }
5969}
5970
5971    void *
5972gui_mch_register_sign(char_u *signfile)
5973{
5974    if (signfile[0] != NUL && signfile[0] != '-' && gui.in_use)
5975    {
5976	GdkPixbuf   *sign;
5977	GError	    *error = NULL;
5978	char_u	    *message;
5979
5980	sign = gdk_pixbuf_new_from_file((const char *)signfile, &error);
5981
5982	if (error == NULL)
5983	    return sign;
5984
5985	message = (char_u *)error->message;
5986
5987	if (message != NULL && input_conv.vc_type != CONV_NONE)
5988	    message = string_convert(&input_conv, message, NULL);
5989
5990	if (message != NULL)
5991	{
5992	    /* The error message is already translated and will be more
5993	     * descriptive than anything we could possibly do ourselves. */
5994	    EMSG2("E255: %s", message);
5995
5996	    if (input_conv.vc_type != CONV_NONE)
5997		vim_free(message);
5998	}
5999	g_error_free(error);
6000    }
6001
6002    return NULL;
6003}
6004
6005    void
6006gui_mch_destroy_sign(void *sign)
6007{
6008    if (sign != NULL)
6009	g_object_unref(sign);
6010}
6011
6012#endif /* FEAT_SIGN_ICONS */
6013