diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 63438c9..4ae2f88 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -7233,6 +7233,7 @@ gui_athena		Compiled with Athena GUI.
 gui_gnome		Compiled with Gnome support (gui_gtk is also defined).
 gui_gtk			Compiled with GTK+ GUI (any version).
 gui_gtk2		Compiled with GTK+ 2 GUI (gui_gtk is also defined).
+gui_gtk3		Compiled with GTK+ 3 GUI (gui_gtk is also defined).
 gui_mac			Compiled with Macintosh GUI.
 gui_motif		Compiled with Motif GUI.
 gui_photon		Compiled with Photon GUI.
diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt
index d779763..520eba9 100644
--- a/runtime/doc/gui.txt
+++ b/runtime/doc/gui.txt
@@ -25,7 +25,7 @@ Other GUI documentation:
 
 First you must make sure you actually have a version of Vim with the GUI code
 included.  You can check this with the ":version" command, it says "with xxx
-GUI", where "xxx" is X11-Motif, X11-Athena, Photon, GTK, GTK2, etc., or
+GUI", where "xxx" is X11-Motif, X11-Athena, Photon, GTK2, GTK3, etc., or
 "MS-Windows 32 bit GUI version".
 
 How to start the GUI depends on the system used.  Mostly you can run the
@@ -514,11 +514,14 @@ a menu entry.  Hit <Enter> to execute it.  Hit <Esc> if you want to cancel.
 This does require the |+menu| feature enabled at compile time.
 
 							*tear-off-menus*
-GTK+ and Motif support Tear-off menus.  These are sort of sticky menus or
+GTK+ 2 and Motif support Tear-off menus.  These are sort of sticky menus or
 pop-up menus that are present all the time.  If the resizing does not work
 correctly, this may be caused by using something like "Vim*geometry" in the
 defaults.  Use "Vim.geometry" instead.
 
+As to GTK+ 3, tear-off menus have been deprecated since GTK+ 3.4.
+Accordingly, they are disabled if gvim is linked against GTK+ 3.4 or later.
+
 The Win32 GUI version emulates Motif's tear-off menus.  Actually, a Motif user
 will spot the differences easily, but hopefully they're just as useful.  You
 can also use the |:tearoff| command together with |hidden-menus| to create
@@ -650,8 +653,8 @@ When no or zero priority is given, 500 is used.
 The priority for the PopUp menu is not used.
 
 The Help menu will be placed on the far right side of the menu bar on systems
-which support this (Motif and GTK+).  For GTK+ 2, this is not done anymore
-because right-aligning the Help menu is now discouraged UI design.
+which support this (Motif and GTK+).  For GTK+ 2 and 3, this is not done
+anymore because right-aligning the Help menu is now discouraged UI design.
 
 You can use a priority higher than 9999, to make it go after the Help menu,
 but that is non-standard and is discouraged.  The highest possible priority is
diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt
index f085e2f..ff9179d 100644
--- a/runtime/doc/gui_x11.txt
+++ b/runtime/doc/gui_x11.txt
@@ -369,6 +369,16 @@ Write this in the file ~/.gtkrc and it will be used by GTK+.  For GTK+ 2
 you might have to use the file ~/.gtkrc-2.0 instead, depending on your
 distribution.
 
+For GTK+ 3, an effect similar to the above can be obtained by adding the
+following snipped to $XDG_HOME_DIR/gtk-3.0/gtk.css (usually,
+$HOME/.config/gtk-3.0/gtk.css):
+ >
+	.tooltip {
+		background-color: #ffffcc;
+		color: #000000;
+	}
+<
+
 Using Vim as a GTK+ plugin				*gui-gtk-socketid*
 
 When the GTK+ version of Vim starts up normally, it creates its own top level
diff --git a/src/auto/configure b/src/auto/configure
index fea1f2c..bf7215a 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -821,6 +821,7 @@ with_x
 enable_gui
 enable_gtk2_check
 enable_gnome_check
+enable_gtk3_check
 enable_motif_check
 enable_athena_check
 enable_nextaw_check
@@ -1481,9 +1482,10 @@ Optional Features:
   --enable-hangulinput    Include Hangul input support.
   --enable-xim            Include XIM input support.
   --enable-fontset        Include X fontset output support.
-  --enable-gui=OPTS     X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/motif/athena/neXtaw/photon/carbon
+  --enable-gui=OPTS     X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon
   --enable-gtk2-check     If auto-select GUI, check for GTK+ 2 default=yes
   --enable-gnome-check    If GTK GUI, check for GNOME default=no
+  --enable-gtk3-check     If auto-select GUI, check for GTK+ 3 default=yes
   --enable-motif-check    If auto-select GUI, check for Motif default=yes
   --enable-athena-check   If auto-select GUI, check for Athena default=yes
   --enable-nextaw-check   If auto-select GUI, check for neXtaw default=yes
@@ -4355,7 +4357,7 @@ fi
 
 
     if test "x$CARBON" = "xyes"; then
-      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2; then
+      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then
 	with_x=no
       fi
     fi
@@ -8606,6 +8608,9 @@ $as_echo "GTK+ 2.x GUI support" >&6; }
 $as_echo "GNOME 2.x GUI support" >&6; }
 		SKIP_GNOME=
 		SKIP_GTK2=;;
+    gtk3)	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: GTK+ 3.x GUI support" >&5
+$as_echo "GTK+ 3.x GUI support" >&6; }
+		SKIP_GTK3=;;
     motif)	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Motif GUI support" >&5
 $as_echo "Motif GUI support" >&6; }
 		SKIP_MOTIF=;;
@@ -8657,6 +8662,23 @@ $as_echo "$enable_gnome_check" >&6; }
   fi
 fi
 
+if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 3" >&5
+$as_echo_n "checking whether or not to look for GTK+ 3... " >&6; }
+  # Check whether --enable-gtk3-check was given.
+if test "${enable_gtk3_check+set}" = set; then :
+  enableval=$enable_gtk3_check;
+else
+  enable_gtk3_check="yes"
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gtk3_check" >&5
+$as_echo "$enable_gtk3_check" >&6; }
+  if test "x$enable_gtk3_check" = "xno"; then
+    SKIP_GTK3=YES
+  fi
+fi
+
 if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether or not to look for Motif" >&5
 $as_echo_n "checking whether or not to look for Motif... " >&6; }
@@ -8831,13 +8853,13 @@ fi
 
   if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
   {
-    min_gtk_version=2.2.0
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
-$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
     no_gtk=""
     if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
 	  && $PKG_CONFIG --exists gtk+-2.0; then
     {
+      min_gtk_version=2.2.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
                         GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0`
       GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0`
       GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0`
@@ -8848,6 +8870,23 @@ $as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
       gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
 	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
     }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=2.2.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
     else
       no_gtk=yes
     fi
@@ -8943,6 +8982,7 @@ $as_echo "no" >&6; }
   rm -f conf.gtktest
 
     if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK3=YES
       SKIP_ATHENA=YES
       SKIP_NEXTAW=YES
       SKIP_MOTIF=YES
@@ -9044,6 +9084,218 @@ $as_echo "not found" >&6; }
   fi
 fi
 
+
+if test -z "$SKIP_GTK3"; then
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5
+$as_echo_n "checking --disable-gtktest argument... " >&6; }
+  # Check whether --enable-gtktest was given.
+if test "${enable_gtktest+set}" = set; then :
+  enableval=$enable_gtktest;
+else
+  enable_gtktest=yes
+fi
+
+  if test "x$enable_gtktest" = "xyes" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test enabled" >&5
+$as_echo "gtk test enabled" >&6; }
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: gtk test disabled" >&5
+$as_echo "gtk test disabled" >&6; }
+  fi
+
+  if test "X$PKG_CONFIG" = "X"; then
+    # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $PKG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no"
+  ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+
+  if test "x$PKG_CONFIG" != "xno"; then
+
+  if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
+  {
+    no_gtk=""
+    if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-2.0; then
+    {
+      min_gtk_version=3.0.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+                        GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-2.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-2.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-2.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=3.0.0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5
+$as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; }
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'`
+    }
+    else
+      no_gtk=yes
+    fi
+
+    if test "x$enable_gtktest" = "xyes" -a "x$no_gtk" = "x"; then
+    {
+      ac_save_CFLAGS="$CFLAGS"
+      ac_save_LIBS="$LIBS"
+      CFLAGS="$CFLAGS $GTK_CFLAGS"
+      LIBS="$LIBS $GTK_LIBS"
+
+                        rm -f conf.gtktest
+      if test "$cross_compiling" = yes; then :
+  echo $ac_n "cross compiling; assumed OK... $ac_c"
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#endif
+
+int
+main ()
+{
+int major, minor, micro;
+char *tmp_version;
+
+system ("touch conf.gtktest");
+
+/* HP/UX 9 (%@#!) writes to sscanf strings */
+tmp_version = g_strdup("$min_gtk_version");
+if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, &micro) != 3) {
+   printf("%s, bad version string\n", "$min_gtk_version");
+   exit(1);
+ }
+
+if ((gtk_major_version > major) ||
+    ((gtk_major_version == major) && (gtk_minor_version > minor)) ||
+    ((gtk_major_version == major) && (gtk_minor_version == minor) &&
+				     (gtk_micro_version >= micro)))
+{
+    return 0;
+}
+return 1;
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+  no_gtk=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+      CFLAGS="$ac_save_CFLAGS"
+      LIBS="$ac_save_LIBS"
+    }
+    fi
+    if test "x$no_gtk" = x ; then
+      if test "x$enable_gtktest" = "xyes"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5
+$as_echo "yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; }
+      else
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5
+$as_echo "found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; }
+      fi
+      GUI_LIB_LOC="$GTK_LIBDIR"
+		 GTK_LIBNAME="$GTK_LIBS"
+		GUI_INC_LOC="$GTK_CFLAGS"
+    else
+    {
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+      GTK_CFLAGS=""
+      GTK_LIBS=""
+      :
+    }
+    fi
+  }
+  else
+    GTK_CFLAGS=""
+    GTK_LIBS=""
+    :
+  fi
+
+
+  rm -f conf.gtktest
+
+    if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK2=YES
+      SKIP_GNOME=YES
+      SKIP_ATHENA=YES
+      SKIP_NEXTAW=YES
+      SKIP_MOTIF=YES
+      GUITYPE=GTK
+
+      $as_echo "#define HAVE_GTK_MULTIHEAD 1" >>confdefs.h
+
+      $as_echo "#define USE_GTK3 1" >>confdefs.h
+
+    fi
+  fi
+fi
+
 if test "x$GUITYPE" = "xGTK"; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking version of Gdk-Pixbuf" >&5
 $as_echo_n "checking version of Gdk-Pixbuf... " >&6; }
@@ -9545,7 +9797,7 @@ done
 fi
 
 
-if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2"; then
+if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then
     cppflags_save=$CPPFLAGS
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
   for ac_header in X11/xpm.h X11/Sunkeysym.h
@@ -11692,7 +11944,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -11738,7 +11990,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -11762,7 +12014,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -11807,7 +12059,7 @@ else
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -11831,7 +12083,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
+#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
diff --git a/src/channel.c b/src/channel.c
index dc05587..e8915f3 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -308,6 +308,17 @@ messageFromNetbeans(XtPointer clientData,
 #endif
 
 #ifdef FEAT_GUI_GTK
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+messageFromNetbeans(GIOChannel *unused1 UNUSED,
+                    GIOCondition unused2 UNUSED,
+                    gpointer clientData UNUSED)
+{
+    channel_read_netbeans(GPOINTER_TO_INT(clientData));
+    return TRUE; /* Return FALSE instead in case the event source is to
+                  * be removed after this function returns. */
+}
+# else
     static void
 messageFromNetbeans(gpointer clientData,
 		    gint unused1 UNUSED,
@@ -315,6 +326,7 @@ messageFromNetbeans(gpointer clientData,
 {
     channel_read_netbeans((int)(long)clientData);
 }
+# endif
 #endif
 
     static void
@@ -340,10 +352,22 @@ channel_gui_register(channel_T *channel)
      * is input on the editor connection socket
      */
     if (channel->ch_inputHandler == 0)
+#   if GTK_CHECK_VERSION(3,0,0)
+    {
+        GIOChannel *chnnl = g_io_channel_unix_new(channel->ch_sock);
+        channel->ch_inputHandler
+            = g_io_add_watch(chnnl,
+                                G_IO_IN|G_IO_HUP|G_IO_ERR|G_IO_PRI,
+                                    messageFromNetbeans,
+                                        GINT_TO_POINTER(channel->ch_id));
+        g_io_channel_unref(chnnl);
+    }
+#   else
 	channel->ch_inputHandler =
 	    gdk_input_add((gint)channel->ch_sock, (GdkInputCondition)
 			     ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
 			 messageFromNetbeans, (gpointer)(long)channel->ch_id);
+#   endif
 #  else
 #   ifdef FEAT_GUI_W32
     /*
@@ -387,7 +411,11 @@ channel_gui_unregister(channel_T *channel)
 #  ifdef FEAT_GUI_GTK
     if (channel->ch_inputHandler != 0)
     {
+#   if GTK_CHECK_VERSION(3,0,0)
+        g_source_remove(channel->ch_inputHandler);
+#   else
 	gdk_input_remove(channel->ch_inputHandler);
+#   endif
 	channel->ch_inputHandler = 0;
     }
 #  else
diff --git a/src/config.h.in b/src/config.h.in
index 8e115f5..fd45c73 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -460,3 +460,6 @@
 
 /* Define if GResource is used to load icons */
 #undef USE_GRESOURCE
+
+/* Define if GTK+ GUI is to be linked against GTK+ 3 */
+#undef USE_GTK3
diff --git a/src/configure.in b/src/configure.in
index 92a1bb5..8ea2eda 100644
--- a/src/configure.in
+++ b/src/configure.in
@@ -213,7 +213,7 @@ if test "`(uname) 2>/dev/null`" = Darwin; then
     dnl or Motif, Athena or GTK GUI is used.
     AC_CHECK_HEADER(Carbon/Carbon.h, CARBON=yes)
     if test "x$CARBON" = "xyes"; then
-      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2; then
+      if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then
 	with_x=no
       fi
     fi
@@ -2198,7 +2198,7 @@ test "x$with_x" = xno -a "x$MACOSX" != "xyes" -a "x$QNX" != "xyes" && enable_gui
 
 AC_MSG_CHECKING(--enable-gui argument)
 AC_ARG_ENABLE(gui,
- [  --enable-gui[=OPTS]     X11 GUI [default=auto] [OPTS=auto/no/gtk2/gnome2/motif/athena/neXtaw/photon/carbon]], , enable_gui="auto")
+ [  --enable-gui[=OPTS]     X11 GUI [default=auto] [OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon]], , enable_gui="auto")
 
 dnl Canonicalize the --enable-gui= argument so that it can be easily compared.
 dnl Do not use character classes for portability with old tools.
@@ -2256,6 +2256,8 @@ else
     gnome2)	AC_MSG_RESULT(GNOME 2.x GUI support)
 		SKIP_GNOME=
 		SKIP_GTK2=;;
+    gtk3)	AC_MSG_RESULT(GTK+ 3.x GUI support)
+		SKIP_GTK3=;;
     motif)	AC_MSG_RESULT(Motif GUI support)
 		SKIP_MOTIF=;;
     athena)	AC_MSG_RESULT(Athena GUI support)
@@ -2291,6 +2293,17 @@ if test "x$SKIP_GNOME" != "xYES" -a "$enable_gui_canon" != "gnome2"; then
   fi
 fi
 
+if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then
+  AC_MSG_CHECKING(whether or not to look for GTK+ 3)
+  AC_ARG_ENABLE(gtk3-check,
+	[  --enable-gtk3-check     If auto-select GUI, check for GTK+ 3 [default=yes]],
+	, enable_gtk3_check="yes")
+  AC_MSG_RESULT($enable_gtk3_check)
+  if test "x$enable_gtk3_check" = "xno"; then
+    SKIP_GTK3=YES
+  fi
+fi
+
 if test "x$SKIP_MOTIF" != "xYES" -a "$enable_gui_canon" != "motif"; then
   AC_MSG_CHECKING(whether or not to look for Motif)
   AC_ARG_ENABLE(motif-check,
@@ -2379,12 +2392,12 @@ AC_DEFUN(AM_PATH_GTK,
 [
   if test "X$GTK_CONFIG" != "Xno" -o "X$PKG_CONFIG" != "Xno"; then
   {
-    min_gtk_version=ifelse([$1], ,2.2.0,$1)
-    AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
     no_gtk=""
     if (test "X$SKIP_GTK2" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
 	  && $PKG_CONFIG --exists gtk+-2.0; then
     {
+      min_gtk_version=ifelse([$1], ,2.2.0,$1)
+      AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
       dnl We should be using PKG_CHECK_MODULES() instead of this hack.
       dnl But I guess the dependency on pkgconfig.m4 is not wanted or
       dnl something like that.
@@ -2398,6 +2411,22 @@ AC_DEFUN(AM_PATH_GTK,
       gtk_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \
 	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
     }
+    elif (test "X$SKIP_GTK3" != "XYES" -a "X$PKG_CONFIG" != "Xno") \
+	  && $PKG_CONFIG --exists gtk+-3.0; then
+    {
+      min_gtk_version=ifelse([$1], ,3.0.0,$1)
+      AC_MSG_CHECKING(for GTK - version >= $min_gtk_version)
+
+      GTK_CFLAGS=`$PKG_CONFIG --cflags gtk+-3.0`
+      GTK_LIBDIR=`$PKG_CONFIG --libs-only-L gtk+-3.0`
+      GTK_LIBS=`$PKG_CONFIG --libs gtk+-3.0`
+      gtk_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'`
+      gtk_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'`
+      gtk_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \
+	     sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'`
+    }
     else
       no_gtk=yes
     fi
@@ -2573,6 +2602,7 @@ if test -z "$SKIP_GTK2"; then
 		 GTK_LIBNAME="$GTK_LIBS"
 		GUI_INC_LOC="$GTK_CFLAGS"], )
     if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK3=YES
       SKIP_ATHENA=YES
       SKIP_NEXTAW=YES
       SKIP_MOTIF=YES
@@ -2601,6 +2631,44 @@ if test -z "$SKIP_GTK2"; then
   fi
 fi
 
+
+dnl ---------------------------------------------------------------------------
+dnl Check for GTK3.
+dnl ---------------------------------------------------------------------------
+if test -z "$SKIP_GTK3"; then
+
+  AC_MSG_CHECKING(--disable-gtktest argument)
+  AC_ARG_ENABLE(gtktest, [  --disable-gtktest       Do not try to compile and run a test GTK program],
+	, enable_gtktest=yes)
+  if test "x$enable_gtktest" = "xyes" ; then
+    AC_MSG_RESULT(gtk test enabled)
+  else
+    AC_MSG_RESULT(gtk test disabled)
+  fi
+
+  if test "X$PKG_CONFIG" = "X"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "x$PKG_CONFIG" != "xno"; then
+    AM_PATH_GTK(3.0.0,
+		[GUI_LIB_LOC="$GTK_LIBDIR"
+		 GTK_LIBNAME="$GTK_LIBS"
+		GUI_INC_LOC="$GTK_CFLAGS"], )
+    if test "x$GTK_CFLAGS" != "x"; then
+      SKIP_GTK2=YES
+      SKIP_GNOME=YES
+      SKIP_ATHENA=YES
+      SKIP_NEXTAW=YES
+      SKIP_MOTIF=YES
+      GUITYPE=GTK
+      AC_SUBST(GTK_LIBNAME)
+      AC_DEFINE(HAVE_GTK_MULTIHEAD)
+      AC_DEFINE(USE_GTK3)
+    fi
+  fi
+fi
+
 dnl Check the version of Gdk-Pixbuf.  If the version is 2.31 or later and
 dnl glib-compile-resources is found in PATH, use GResource.
 if test "x$GUITYPE" = "xGTK"; then
@@ -2822,7 +2890,7 @@ if test "$enable_xsmp" = "yes"; then
 fi
 
 
-if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2"; then
+if test -z "$SKIP_ATHENA" -o -z "$SKIP_NEXTAW" -o -z "$SKIP_MOTIF" -o -z "$SKIP_GTK2" -o -z "$SKIP_GTK3"; then
   dnl Check for X11/xpm.h and X11/Sunkeysym.h with the GUI include path
   cppflags_save=$CPPFLAGS
   CPPFLAGS="$CPPFLAGS $X_CFLAGS"
diff --git a/src/eval.c b/src/eval.c
index 75f5e54..034f4c4 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -13239,8 +13239,13 @@ f_has(typval_T *argvars, typval_T *rettv)
 # endif
 #endif
 #ifdef FEAT_GUI_GTK
+# ifdef USE_GTK3
+	"gui_gtk",
+        "gui_gtk3",
+# else
 	"gui_gtk",
 	"gui_gtk2",
+# endif
 #endif
 #ifdef FEAT_GUI_GNOME
 	"gui_gnome",
diff --git a/src/gui.h b/src/gui.h
index b2fdc6d..6bac6c8 100644
--- a/src/gui.h
+++ b/src/gui.h
@@ -364,7 +364,9 @@ typedef struct Gui
 #endif
 
 #ifdef FEAT_GUI_GTK
+# ifndef USE_GTK3
     int		visibility;	    /* Is shell partially/fully obscured? */
+# endif
     GdkCursor	*blank_pointer;	    /* Blank pointer */
 
     /* X Resources */
@@ -386,7 +388,12 @@ typedef struct Gui
     GdkColor	*fgcolor;	    /* GDK-styled foreground color */
     GdkColor	*bgcolor;	    /* GDK-styled background color */
     GdkColor	*spcolor;	    /* GDK-styled special color */
+# ifdef USE_GTK3
+    cairo_surface_t *surface;       /* drawarea surface */
+    gboolean         by_signal;     /* cause of draw operation */
+# else
     GdkGC	*text_gc;	    /* cached GC for normal text */
+# endif
     PangoContext     *text_context; /* the context used for all text */
     PangoFont	     *ascii_font;   /* cached font for ASCII strings */
     PangoGlyphString *ascii_glyphs; /* cached code point -> glyph map */
diff --git a/src/gui_beval.c b/src/gui_beval.c
index e96e67d..7962724 100644
--- a/src/gui_beval.c
+++ b/src/gui_beval.c
@@ -122,7 +122,12 @@ general_beval_cb(BalloonEval *beval, int state UNUSED)
 #if !defined(FEAT_GUI_W32) || defined(PROTO)
 
 #ifdef FEAT_GUI_GTK
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+#  include <gdk/gdkx.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gtk/gtk.h>
 #else
 # include <X11/keysym.h>
@@ -164,8 +169,16 @@ static gint target_event_cb(GtkWidget *, GdkEvent *, gpointer);
 static gint mainwin_event_cb(GtkWidget *, GdkEvent *, gpointer);
 static void pointer_event(BalloonEval *, int, int, unsigned);
 static void key_event(BalloonEval *, unsigned, int);
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean timeout_cb(gpointer);
+# else
 static gint timeout_cb(gpointer);
-static gint balloon_expose_event_cb(GtkWidget *, GdkEventExpose *, gpointer);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean balloon_draw_event_cb (GtkWidget *, cairo_t *, gpointer);
+# else
+static gint balloon_expose_event_cb (GtkWidget *, GdkEventExpose *, gpointer);
+# endif
 #else
 static void addEventHandler(Widget, BalloonEval *);
 static void removeEventHandler(BalloonEval *);
@@ -459,10 +472,16 @@ addEventHandler(GtkWidget *target, BalloonEval *beval)
      * This allows us to catch events independently of the signal handlers
      * in gui_gtk_x11.c.
      */
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(target), "event",
+                     G_CALLBACK(target_event_cb),
+                     beval);
+# else
     /* Should use GTK_OBJECT() here, but that causes a lint warning... */
     gtk_signal_connect((GtkObject*)(target), "event",
 		       GTK_SIGNAL_FUNC(target_event_cb),
 		       beval);
+# endif
     /*
      * Nasty:  Key press events go to the main window thus the drawing area
      * will never see them.  This means we have to connect to the main window
@@ -471,9 +490,15 @@ addEventHandler(GtkWidget *target, BalloonEval *beval)
     if (gtk_socket_id == 0 && gui.mainwin != NULL
 	    && gtk_widget_is_ancestor(target, gui.mainwin))
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.mainwin), "event",
+                         G_CALLBACK(mainwin_event_cb),
+                         beval);
+# else
 	gtk_signal_connect((GtkObject*)(gui.mainwin), "event",
 			   GTK_SIGNAL_FUNC(mainwin_event_cb),
 			   beval);
+# endif
     }
 }
 
@@ -481,17 +506,29 @@ addEventHandler(GtkWidget *target, BalloonEval *beval)
 removeEventHandler(BalloonEval *beval)
 {
     /* LINTED: avoid warning: dubious operation on enum */
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_handlers_disconnect_by_func(G_OBJECT(beval->target),
+                                         G_CALLBACK(target_event_cb),
+                                         beval);
+# else
     gtk_signal_disconnect_by_func((GtkObject*)(beval->target),
 				  GTK_SIGNAL_FUNC(target_event_cb),
 				  beval);
+# endif
 
     if (gtk_socket_id == 0 && gui.mainwin != NULL
 	    && gtk_widget_is_ancestor(beval->target, gui.mainwin))
     {
 	/* LINTED: avoid warning: dubious operation on enum */
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handlers_disconnect_by_func(G_OBJECT(gui.mainwin),
+                                             G_CALLBACK(mainwin_event_cb),
+                                             beval);
+# else
 	gtk_signal_disconnect_by_func((GtkObject*)(gui.mainwin),
 				      GTK_SIGNAL_FUNC(mainwin_event_cb),
 				      beval);
+# endif
     }
 }
 
@@ -517,7 +554,17 @@ target_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
 		 * GDK_POINTER_MOTION_HINT_MASK is set, thus we cannot obtain
 		 * the coordinates from the GdkEventMotion struct directly.
 		 */
+# if GTK_CHECK_VERSION(3,0,0)
+                {
+                    GdkWindow * const win = gtk_widget_get_window(widget);
+                    GdkDisplay * const dpy = gdk_window_get_display(win);
+                    GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
+                    GdkDevice * const dev = gdk_device_manager_get_client_pointer(mngr);
+                    gdk_window_get_device_position(win, dev , &x, &y, &state);
+                }
+# else
 		gdk_window_get_pointer(widget->window, &x, &y, &state);
+# endif
 		pointer_event(beval, x, y, (unsigned int)state);
 	    }
 	    else
@@ -609,8 +656,13 @@ pointer_event(BalloonEval *beval, int x, int y, unsigned state)
 	    }
 	    else
 	    {
+# if GTK_CHECK_VERSION(3,0,0)
+		beval->timerID = g_timeout_add((guint)p_bdlay,
+                                               &timeout_cb, beval);
+# else
 		beval->timerID = gtk_timeout_add((guint32)p_bdlay,
 						 &timeout_cb, beval);
+# endif
 	    }
 	}
     }
@@ -647,7 +699,11 @@ key_event(BalloonEval *beval, unsigned keyval, int is_keypress)
 	cancelBalloon(beval);
 }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+# else
     static gint
+# endif
 timeout_cb(gpointer data)
 {
     BalloonEval *beval = (BalloonEval *)data;
@@ -663,6 +719,37 @@ timeout_cb(gpointer data)
     return FALSE; /* don't call me again */
 }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+balloon_draw_event_cb(GtkWidget *widget,
+                      cairo_t   *cr,
+                      gpointer   data UNUSED)
+{
+    GtkStyleContext *context = NULL;
+    gint width = -1, height = -1;
+
+    if (widget == NULL)
+        return TRUE;
+
+    context = gtk_widget_get_style_context(widget);
+    width = gtk_widget_get_allocated_width(widget);
+    height = gtk_widget_get_allocated_height(widget);
+
+    gtk_style_context_save(context);
+
+    gtk_style_context_add_class(context, "tooltip");
+    gtk_style_context_set_state(context, GTK_STATE_FLAG_NORMAL);
+
+    cairo_save(cr);
+    gtk_render_frame(context, cr, 0, 0, width, height);
+    gtk_render_background(context, cr, 0, 0, width, height);
+    cairo_restore(cr);
+
+    gtk_style_context_restore(context);
+
+    return FALSE;
+}
+# else
     static gint
 balloon_expose_event_cb(GtkWidget *widget,
 			GdkEventExpose *event,
@@ -675,6 +762,7 @@ balloon_expose_event_cb(GtkWidget *widget,
 
     return FALSE; /* continue emission */
 }
+# endif
 
 #else /* !FEAT_GUI_GTK */
 
@@ -957,8 +1045,37 @@ set_printable_label_text(GtkLabel *label, char_u *text)
 	aep = syn_gui_attr2entry(hl_attr(HLF_8));
 	pixel = (aep != NULL) ? aep->ae_u.gui.fg_color : INVALCOLOR;
 	if (pixel != INVALCOLOR)
+# if GTK_CHECK_VERSION(3,0,0)
+        {
+            GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+
+            if (visual == NULL)
+            {
+                color.red = 0;
+                color.green = 0;
+                color.blue = 0;
+            }
+            else
+            {
+                guint32 r_mask, g_mask, b_mask;
+                gint r_shift, g_shift, b_shift;
+
+                gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift,
+                                                 NULL);
+                gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift,
+                                                   NULL);
+                gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift,
+                                                  NULL);
+
+                color.red = ((pixel & r_mask) >> r_shift) << 8;
+                color.green = ((pixel & g_mask) >> g_shift) << 8;
+                color.blue = ((pixel & b_mask) >> b_shift) << 8;
+            }
+        }
+# else
 	    gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
 				     (unsigned long)pixel, &color);
+# endif
 
 	pdest = buf;
 	p = text;
@@ -1059,8 +1176,10 @@ drawBalloon(BalloonEval *beval)
 	screen_w = gdk_screen_width();
 	screen_h = gdk_screen_height();
 # endif
+# if !GTK_CHECK_VERSION(3,0,0)
 	gtk_widget_ensure_style(beval->balloonShell);
 	gtk_widget_ensure_style(beval->balloonLabel);
+# endif
 
 	set_printable_label_text(GTK_LABEL(beval->balloonLabel), beval->msg);
 	/*
@@ -1081,10 +1200,18 @@ drawBalloon(BalloonEval *beval)
 				    MAX(20, screen_w - 20)));
 
 	/* Calculate the balloon's width and height. */
+# if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_get_preferred_size(beval->balloonShell, &requisition, NULL);
+# else
 	gtk_widget_size_request(beval->balloonShell, &requisition);
+# endif
 
 	/* Compute position of the balloon area */
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_get_origin(gtk_widget_get_window(beval->target), &x, &y);
+# else
 	gdk_window_get_origin(beval->target->window, &x, &y);
+# endif
 	x += beval->x;
 	y += beval->y;
 
@@ -1099,7 +1226,11 @@ drawBalloon(BalloonEval *beval)
 	y = CLAMP(y + y_offset, 0, MAX(0, screen_h - requisition.height));
 
 	/* Show the balloon */
+# if GTK_CHECK_VERSION(3,0,0)
+        gtk_window_move(GTK_WINDOW(beval->balloonShell), x, y);
+# else
 	gtk_widget_set_uposition(beval->balloonShell, x, y);
+# endif
 	gtk_widget_show(beval->balloonShell);
 
 	beval->showState = ShS_SHOWING;
@@ -1126,7 +1257,11 @@ cancelBalloon(BalloonEval *beval)
 
     if (beval->timerID != 0)
     {
+# if GTK_CHECK_VERSION(3,0,0)
+        g_source_remove(beval->timerID);
+# else
 	gtk_timeout_remove(beval->timerID);
+# endif
 	beval->timerID = 0;
     }
     beval->showState = ShS_NEUTRAL;
@@ -1138,17 +1273,42 @@ createBalloonEvalWindow(BalloonEval *beval)
     beval->balloonShell = gtk_window_new(GTK_WINDOW_POPUP);
 
     gtk_widget_set_app_paintable(beval->balloonShell, TRUE);
+# if GTK_CHECK_VERSION(3,0,0)
+    gtk_window_set_resizable(GTK_WINDOW(beval->balloonShell), FALSE);
+# else
     gtk_window_set_policy(GTK_WINDOW(beval->balloonShell), FALSE, FALSE, TRUE);
+# endif
     gtk_widget_set_name(beval->balloonShell, "gtk-tooltips");
+# if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(beval->balloonShell), 4);
+# else
     gtk_container_border_width(GTK_CONTAINER(beval->balloonShell), 4);
+# endif
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(beval->balloonShell), "draw",
+                     G_CALLBACK(balloon_draw_event_cb), NULL);
+# else
     gtk_signal_connect((GtkObject*)(beval->balloonShell), "expose_event",
 		       GTK_SIGNAL_FUNC(balloon_expose_event_cb), NULL);
+# endif
     beval->balloonLabel = gtk_label_new(NULL);
 
     gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), FALSE);
     gtk_label_set_justify(GTK_LABEL(beval->balloonLabel), GTK_JUSTIFY_LEFT);
+# if GTK_CHECK_VERSION(3,16,0)
+    gtk_label_set_xalign(GTK_LABEL(beval->balloonLabel), 0.5);
+    gtk_label_set_yalign(GTK_LABEL(beval->balloonLabel), 0.5);
+# elif GTK_CHECK_VERSION(3,14,0)
+    GValue align_val = G_VALUE_INIT;
+    g_value_init(&align_val, G_TYPE_FLOAT);
+    g_value_set_float(&align_val, 0.5);
+    g_object_set_property(G_OBJECT(beval->balloonLabel), "xalign", &align_val);
+    g_object_set_property(G_OBJECT(beval->balloonLabel), "yalign", &align_val);
+    g_value_unset(&align_val);
+# else
     gtk_misc_set_alignment(GTK_MISC(beval->balloonLabel), 0.5f, 0.5f);
+# endif
     gtk_widget_set_name(beval->balloonLabel, "vim-balloon-label");
     gtk_widget_show(beval->balloonLabel);
 
diff --git a/src/gui_beval.h b/src/gui_beval.h
index 859c081..ead0fb1 100644
--- a/src/gui_beval.h
+++ b/src/gui_beval.h
@@ -11,7 +11,11 @@
 #define GUI_BEVAL_H
 
 #ifdef FEAT_GUI_GTK
-# include <gtk/gtkwidget.h>
+# ifdef USE_GTK3
+#  include <gtk/gtk.h>
+# else
+#  include <gtk/gtkwidget.h>
+# endif
 #else
 # if defined(FEAT_GUI_X11)
 #  include <X11/Intrinsic.h>
diff --git a/src/gui_gtk.c b/src/gui_gtk.c
index 99e4d70..ca5caee 100644
--- a/src/gui_gtk.c
+++ b/src/gui_gtk.c
@@ -22,8 +22,14 @@
  *
  * Best supporting actor (He helped somewhat, aesthetically speaking):
  * Maxime Romano <verbophobe@hotmail.com>
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
  */
 
+#include "vim.h"
+
 #ifdef FEAT_GUI_GTK
 # include "gui_gtk_f.h"
 #endif
@@ -37,8 +43,6 @@
 # undef MAX
 #endif
 
-#include "vim.h"
-
 #ifdef FEAT_GUI_GNOME
 /* Gnome redefines _() and N_().  Grrr... */
 # ifdef _
@@ -63,7 +67,11 @@
 #endif
 
 #ifdef FEAT_GUI_GTK
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gdk/gdk.h>
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
@@ -104,6 +112,70 @@ static void recent_func_log_func(
  * match toolbar_names[] in menu.c!  All stock icons including the "vim-*"
  * ones can be overridden in your gtkrc file.
  */
+# if GTK_CHECK_VERSION(3,10,0)
+static const char * const menu_themed_names[] =
+{
+    /* 00 */ "document-new",            /* GTK_STOCK_NEW */
+    /* 01 */ "document-open",           /* GTK_STOCK_OPEN */
+    /* 02 */ "document-save",           /* GTK_STOCK_SAVE */
+    /* 03 */ "edit-undo",               /* GTK_STOCK_UNDO */
+    /* 04 */ "edit-redo",               /* GTK_STOCK_REDO */
+    /* 05 */ "edit-cut",                /* GTK_STOCK_CUT */
+    /* 06 */ "edit-copy",               /* GTK_STOCK_COPY */
+    /* 07 */ "edit-paste",              /* GTK_STOCK_PASTE */
+    /* 08 */ "document-print",          /* GTK_STOCK_PRINT */
+    /* 09 */ "help-browser",            /* GTK_STOCK_HELP */
+    /* 10 */ "edit-find",               /* GTK_STOCK_FIND */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* Use the file names in gui_gtk_res.xml, cutting off the extension.
+     * Similar changes follow. */
+    /* 11 */ "stock_vim_save_all",
+    /* 12 */ "stock_vim_session_save",
+    /* 13 */ "stock_vim_session_new",
+    /* 14 */ "stock_vim_session_load",
+#  else
+    /* 11 */ "vim-save-all",
+    /* 12 */ "vim-session-save",
+    /* 13 */ "vim-session-new",
+    /* 14 */ "vim-session-load",
+#  endif
+    /* 15 */ "system-run",              /* GTK_STOCK_EXECUTE */
+    /* 16 */ "edit-find-replace",       /* GTK_STOCK_FIND_AND_REPLACE */
+    /* 17 */ "window-close",            /* GTK_STOCK_CLOSE, FIXME: fuzzy */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 18 */ "stock_vim_window_maximize",
+    /* 19 */ "stock_vim_window_minimize",
+    /* 20 */ "stock_vim_window_split",
+    /* 21 */ "stock_vim_shell",
+#  else
+    /* 18 */ "vim-window-maximize",
+    /* 19 */ "vim-window-minimize",
+    /* 20 */ "vim-window-split",
+    /* 21 */ "vim-shell",
+#  endif
+    /* 22 */ "go-previous",             /* GTK_STOCK_GO_BACK */
+    /* 23 */ "go-next",                 /* GTK_STOCK_GO_FORWARD */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 24 */ "stock_vim_find_help",
+#  else
+    /* 24 */ "vim-find-help",
+#  endif
+    /* 25 */ "gtk-convert",             /* GTK_STOCK_CONVERT */
+    /* 26 */ "go-jump",                 /* GTK_STOCK_JUMP_TO */
+#  if GTK_CHECK_VERSION(3,14,0)
+    /* 27 */ "stock_vim_build_tags",
+    /* 28 */ "stock_vim_window_split_vertical",
+    /* 29 */ "stock_vim_window_maximize_width",
+    /* 30 */ "stock_vim_window_minimize_width",
+#  else
+    /* 27 */ "vim-build-tags",
+    /* 28 */ "vim-window-split-vertical",
+    /* 29 */ "vim-window-maximize-width",
+    /* 30 */ "vim-window-minimize-width",
+#  endif
+    /* 31 */ "application-exit",        /* GTK_STOCK_QUIT */
+};
+# else /* !GTK_CHECK_VERSION(3,10,0) */
 static const char * const menu_stock_ids[] =
 {
     /* 00 */ GTK_STOCK_NEW,
@@ -139,8 +211,10 @@ static const char * const menu_stock_ids[] =
     /* 30 */ "vim-window-minimize-width",
     /* 31 */ GTK_STOCK_QUIT
 };
+# endif /* !GTK_CHECK_VERSION(3,10,0) */
 
-#ifdef USE_GRESOURCE
+# ifdef USE_GRESOURCE
+#  if !GTK_CHECK_VERSION(3,10,0)
 typedef struct IconNames {
     const char *icon_name;
     const char *file_name;
@@ -162,9 +236,10 @@ static IconNames stock_vim_icons[] = {
     { "vim-window-split-vertical", "stock_vim_window_split_vertical.png" },
     { NULL, NULL }
 };
-#endif
+#  endif
+# endif /* USE_G_RESOURCE */
 
-#ifndef USE_GRESOURCE
+# ifndef USE_GRESOURCE
     static void
 add_stock_icon(GtkIconFactory	*factory,
 	       const char	*stock_id,
@@ -182,7 +257,7 @@ add_stock_icon(GtkIconFactory	*factory,
     gtk_icon_set_unref(icon_set);
     g_object_unref(pixbuf);
 }
-#endif
+# endif
 
     static int
 lookup_menu_iconfile(char_u *iconfile, char_u *dest)
@@ -214,6 +289,52 @@ lookup_menu_iconfile(char_u *iconfile, char_u *dest)
 load_menu_iconfile(char_u *name, GtkIconSize icon_size)
 {
     GtkWidget	    *image = NULL;
+# if GTK_CHECK_VERSION(3,10,0)
+    int              pixel_size = -1;
+
+    switch (icon_size)
+    {
+        case GTK_ICON_SIZE_MENU:
+            pixel_size = 16;
+            break;
+        case GTK_ICON_SIZE_SMALL_TOOLBAR:
+            pixel_size = 16;
+            break;
+        case GTK_ICON_SIZE_LARGE_TOOLBAR:
+            pixel_size = 24;
+            break;
+        case GTK_ICON_SIZE_BUTTON:
+            pixel_size = 16;
+            break;
+        case GTK_ICON_SIZE_DND:
+            pixel_size = 32;
+            break;
+        case GTK_ICON_SIZE_DIALOG:
+            pixel_size = 48;
+            break;
+        case GTK_ICON_SIZE_INVALID:
+            /* FALLTHROUGH */
+        default:
+            pixel_size = 0;
+            break;
+    }
+
+    if (pixel_size > 0 || pixel_size == -1)
+    {
+        GdkPixbuf * const pixbuf
+            = gdk_pixbuf_new_from_file_at_scale((const char *)name,
+                pixel_size, pixel_size, TRUE, NULL);
+        if (pixbuf != NULL)
+        {
+            image = gtk_image_new_from_pixbuf(pixbuf);
+            g_object_unref(pixbuf);
+        }
+    }
+    if (image == NULL)
+        image = gtk_image_new_from_icon_name("image-missing", icon_size);
+
+    return image;
+# else /* !GTK_CHECK_VERSION(3,10,0) */
     GtkIconSet	    *icon_set;
     GtkIconSource   *icon_source;
 
@@ -234,6 +355,7 @@ load_menu_iconfile(char_u *name, GtkIconSize icon_size)
     gtk_icon_set_unref(icon_set);
 
     return image;
+# endif /* !GTK_CHECK_VERSION(3,10,0) */
 }
 
     static GtkWidget *
@@ -254,6 +376,17 @@ create_menu_icon(vimmenu_T *menu, GtkIconSize icon_size)
     /* Still not found?  Then use a builtin icon, a blank one as fallback. */
     if (image == NULL)
     {
+# if GTK_CHECK_VERSION(3,10,0)
+        const char *icon_name = NULL;
+        const int   n_names = G_N_ELEMENTS(menu_themed_names);
+
+	if (menu->iconidx >= 0 && menu->iconidx < n_names)
+	    icon_name = menu_themed_names[menu->iconidx];
+        if (icon_name == NULL)
+	    icon_name = "image-missing";
+
+        image = gtk_image_new_from_icon_name(icon_name, icon_size);
+# else
 	const char  *stock_id;
 	const int   n_ids = G_N_ELEMENTS(menu_stock_ids);
 
@@ -263,6 +396,7 @@ create_menu_icon(vimmenu_T *menu, GtkIconSize icon_size)
 	    stock_id = GTK_STOCK_MISSING_IMAGE;
 
 	image = gtk_image_new_from_stock(stock_id, icon_size);
+# endif
     }
 
     return image;
@@ -288,12 +422,12 @@ toolbar_button_focus_in_event(GtkWidget *widget UNUSED,
     void
 gui_gtk_register_stock_icons(void)
 {
-#ifndef USE_GRESOURCE
-# include "../pixmaps/stock_icons.h"
+# ifndef USE_GRESOURCE
+#  include "../pixmaps/stock_icons.h"
     GtkIconFactory *factory;
 
     factory = gtk_icon_factory_new();
-# define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
+#  define ADD_ICON(Name, Data) add_stock_icon(factory, Name, Data, (int)sizeof(Data))
 
     ADD_ICON("vim-build-tags",		  stock_vim_build_tags);
     ADD_ICON("vim-find-help",		  stock_vim_find_help);
@@ -309,10 +443,65 @@ gui_gtk_register_stock_icons(void)
     ADD_ICON("vim-window-split",	  stock_vim_window_split);
     ADD_ICON("vim-window-split-vertical", stock_vim_window_split_vertical);
 
-# undef ADD_ICON
-#else
-    GtkIconFactory * const factory = gtk_icon_factory_new();
+#  undef ADD_ICON
+
+    gtk_icon_factory_add_default(factory);
+    g_object_unref(factory);
+# else
     const char * const path_prefix = "/org/vim/gui/icon";
+#  if GTK_CHECK_VERSION(3,14,0)
+    GdkScreen *screen;
+    GtkIconTheme *icon_theme;
+
+    if (GTK_IS_WIDGET(gui.mainwin))
+        screen = gtk_widget_get_screen(gui.mainwin);
+    else
+        screen = gdk_screen_get_default();
+    icon_theme = gtk_icon_theme_get_for_screen(screen);
+    gtk_icon_theme_add_resource_path(icon_theme, path_prefix);
+#  elif GTK_CHECK_VERSION(3,0,0)
+    IconNames *names;
+
+    for (names = stock_vim_icons; names->icon_name != NULL; names++)
+    {
+        char path[MAXPATHL];
+
+        vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
+        GdkPixbuf *pixbuf = NULL;
+        pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
+        if (pixbuf != NULL)
+        {
+            const gint size = MAX(gdk_pixbuf_get_width(pixbuf),
+                                  gdk_pixbuf_get_height(pixbuf));
+            if (size > 16)
+            {
+                /* An icon theme is supposed to provide fixed-size
+                 * image files for each size, e.g., 16, 22, 24, ...
+                 * Naturally, in contrast to GtkIconSet, GtkIconTheme
+                 * won't prepare size variants for us out of a single
+                 * fixed-size image.
+                 *
+                 * Currently, Vim provides 24x24 images only while the
+                 * icon size on the menu and the toolbar is set to 16x16
+                 * by default.
+                 *
+                 * Resize them by ourselves until we have our own fully
+                 * fledged icon theme. */
+                GdkPixbuf *src = pixbuf;
+                pixbuf = gdk_pixbuf_scale_simple(src,
+                                                 16, 16,
+                                                 GDK_INTERP_BILINEAR);
+                if (pixbuf == NULL)
+                    pixbuf = src;
+                else
+                    g_object_unref(src);
+            }
+            gtk_icon_theme_add_builtin_icon(names->icon_name, size, pixbuf);
+            g_object_unref(pixbuf);
+        }
+    }
+#  else /* !GTK_CHECK_VERSION(3,0.0) */
+    GtkIconFactory * const factory = gtk_icon_factory_new();
     IconNames *names;
 
     for (names = stock_vim_icons; names->icon_name != NULL; names++)
@@ -330,14 +519,15 @@ gui_gtk_register_stock_icons(void)
             g_object_unref(pixbuf);
         }
     }
-#endif
+
     gtk_icon_factory_add_default(factory);
     g_object_unref(factory);
+#  endif /* !GTK_CHECK_VERSION(3,0,0) */
+# endif /* !defined(USE_GRESOURCE) */
 }
 
 #endif /* FEAT_TOOLBAR */
 
-
 #if defined(FEAT_MENU) || defined(PROTO)
 
 /*
@@ -408,7 +598,12 @@ menu_item_new(vimmenu_T *menu, GtkWidget *parent_widget)
      * changes to Vim's menu system.  Not to mention that all the translations
      * had to be updated. */
     menu->id = gtk_menu_item_new();
+# if GTK_CHECK_VERSION(3,2,0)
+    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
+    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
+# else
     box = gtk_hbox_new(FALSE, 20);
+# endif
 
     use_mnemonic = (p_wak[0] != 'n' || !GTK_IS_MENU_BAR(parent_widget));
     text = translate_mnemonic_tag(menu->name, use_mnemonic);
@@ -465,10 +660,16 @@ gui_mch_add_menu(vimmenu_T *menu, int idx)
     gtk_menu_set_accel_group(GTK_MENU(menu->submenu_id), gui.accel_group);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu->id), menu->submenu_id);
 
+# if !GTK_CHECK_VERSION(3,4,0)
     menu->tearoff_handle = gtk_tearoff_menu_item_new();
     if (vim_strchr(p_go, GO_TEAROFF) != NULL)
 	gtk_widget_show(menu->tearoff_handle);
+#  if GTK_CHECK_VERSION(3,0,0)
+    gtk_menu_shell_prepend(GTK_MENU_SHELL(menu->submenu_id), menu->tearoff_handle);
+#  else
     gtk_menu_prepend(GTK_MENU(menu->submenu_id), menu->tearoff_handle);
+#  endif
+# endif
 }
 
     static void
@@ -494,7 +695,16 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 
 	if (menu_is_separator(menu->name))
 	{
+#  if GTK_CHECK_VERSION(3,0,0)
+            GtkToolItem *item = gtk_separator_tool_item_new();
+            gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(item), TRUE);
+            gtk_tool_item_set_expand(GTK_TOOL_ITEM(item), FALSE);
+            gtk_widget_show(GTK_WIDGET(item));
+
+            gtk_toolbar_insert(toolbar, item, idx);
+#  else
 	    gtk_toolbar_insert_space(toolbar, idx);
+#  endif
 	    menu->id = NULL;
 	}
 	else
@@ -509,6 +719,23 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 		 * a nasty GTK error message, skip the tooltip. */
 		CONVERT_TO_UTF8_FREE(tooltip);
 
+#  if GTK_CHECK_VERSION(3,0,0)
+            {
+                GtkWidget *icon;
+                GtkToolItem *item;
+
+                icon = create_menu_icon(menu, gtk_toolbar_get_icon_size(toolbar));
+                item = gtk_tool_button_new(icon, (const gchar *)text);
+                gtk_tool_item_set_tooltip_text(item, (const gchar *)tooltip);
+                g_signal_connect(G_OBJECT(item), "clicked",
+                                 G_CALLBACK(&menu_item_activate), menu);
+                gtk_widget_show_all(GTK_WIDGET(item));
+
+                gtk_toolbar_insert(toolbar, item, idx);
+
+                menu->id = GTK_WIDGET(item);
+            }
+#  else
 	    menu->id = gtk_toolbar_insert_item(
 		    toolbar,
 		    (const char *)text,
@@ -518,10 +745,16 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 		    G_CALLBACK(&menu_item_activate),
 		    menu,
 		    idx);
+#  endif
 
 	    if (gtk_socket_id != 0)
+#  if GTK_CHECK_VERSION(3,0,0)
+		g_signal_connect(G_OBJECT(menu->id), "focus-in-event",
+			G_CALLBACK(toolbar_button_focus_in_event), NULL);
+#  else
 		gtk_signal_connect(GTK_OBJECT(menu->id), "focus_in_event",
 			GTK_SIGNAL_FUNC(toolbar_button_focus_in_event), NULL);
+#  endif
 
 	    CONVERT_TO_UTF8_FREE(text);
 	    CONVERT_TO_UTF8_FREE(tooltip);
@@ -545,7 +778,11 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 	    menu->id = gtk_menu_item_new();
 	    gtk_widget_set_sensitive(menu->id, FALSE);
 	    gtk_widget_show(menu->id);
+# if GTK_CHECK_VERSION(3,0,0)
+            gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id), menu->id, idx);
+# else
 	    gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
+# endif
 
 	    return;
 	}
@@ -553,11 +790,20 @@ gui_mch_add_menu_item(vimmenu_T *menu, int idx)
 	/* Add textual menu item. */
 	menu_item_new(menu, parent->submenu_id);
 	gtk_widget_show(menu->id);
+# if GTK_CHECK_VERSION(3,0,0)
+        gtk_menu_shell_insert(GTK_MENU_SHELL(parent->submenu_id), menu->id, idx);
+# else
 	gtk_menu_insert(GTK_MENU(parent->submenu_id), menu->id, idx);
+# endif
 
 	if (menu->id != NULL)
+# if GTK_CHECK_VERSION(3,0,0)
+            g_signal_connect(G_OBJECT(menu->id), "activate",
+                             G_CALLBACK(menu_item_activate), menu);
+# else
 	    gtk_signal_connect(GTK_OBJECT(menu->id), "activate",
 			       GTK_SIGNAL_FUNC(menu_item_activate), menu);
+# endif
     }
 }
 #endif /* FEAT_MENU */
@@ -592,6 +838,7 @@ gui_gtk_set_mnemonics(int enable)
     }
 }
 
+# if !GTK_CHECK_VERSION(3,4,0)
     static void
 recurse_tearoffs(vimmenu_T *menu, int val)
 {
@@ -608,12 +855,21 @@ recurse_tearoffs(vimmenu_T *menu, int val)
 	recurse_tearoffs(menu->children, val);
     }
 }
+# endif
 
+# if GTK_CHECK_VERSION(3,4,0)
+    void
+gui_mch_toggle_tearoffs(int enable UNUSED)
+{
+    /* Do nothing */
+}
+# else
     void
 gui_mch_toggle_tearoffs(int enable)
 {
     recurse_tearoffs(root_menu, enable);
 }
+# endif
 #endif /* FEAT_MENU */
 
 #if defined(FEAT_TOOLBAR)
@@ -645,9 +901,13 @@ gui_mch_menu_set_tip(vimmenu_T *menu)
 
 	tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
 	if (tooltip == NULL || utf_valid_string(tooltip, NULL))
+# if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_tooltip_text(menu->id, (const gchar *)tooltip);
+# else
 	    /* Only set the tooltip when it's valid utf-8. */
 	gtk_tooltips_set_tip(GTK_TOOLBAR(gui.toolbar)->tooltips,
 			     menu->id, (const char *)tooltip, NULL);
+# endif
 	CONVERT_TO_UTF8_FREE(tooltip);
     }
 }
@@ -676,8 +936,18 @@ gui_mch_destroy_menu(vimmenu_T *menu)
     if (menu->parent != NULL && menu_is_toolbar(menu->parent->name))
     {
 	if (menu_is_separator(menu->name))
+#  if GTK_CHECK_VERSION(3,0,0)
+        {
+            GtkToolItem *item;
+
+            item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(gui.toolbar),
+                                            get_menu_position(menu));
+            gtk_container_remove(GTK_CONTAINER(gui.toolbar), GTK_WIDGET(item));
+        }
+#  else
 	    gtk_toolbar_remove_space(GTK_TOOLBAR(gui.toolbar),
 				     get_menu_position(menu));
+#  endif
 	else if (menu->id != NULL)
 	    gtk_widget_destroy(menu->id);
     }
@@ -711,18 +981,41 @@ gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max)
 
 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
 
+#if GTK_CHECK_VERSION(3,0,0)
+        gtk_adjustment_set_lower(adjustment, 0.0);
+        gtk_adjustment_set_value(adjustment, val);
+        gtk_adjustment_set_upper(adjustment, max + 1);
+        gtk_adjustment_set_page_size(adjustment, size);
+        gtk_adjustment_set_page_increment(adjustment, size < 3L ? 1L : size - 2L);
+        gtk_adjustment_set_step_increment(adjustment, 1.0);
+#else
 	adjustment->lower = 0.0;
 	adjustment->value = val;
 	adjustment->upper = max + 1;
 	adjustment->page_size = size;
 	adjustment->page_increment = size < 3L ? 1L : size - 2L;
 	adjustment->step_increment = 1.0;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handler_block(G_OBJECT(adjustment),
+						      (gulong)sb->handler_id);
+#else
 	g_signal_handler_block(GTK_OBJECT(adjustment),
 						      (gulong)sb->handler_id);
+#endif
+
+#if !GTK_CHECK_VERSION(3,18,0)
 	gtk_adjustment_changed(adjustment);
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_handler_unblock(G_OBJECT(adjustment),
+						      (gulong)sb->handler_id);
+#else
 	g_signal_handler_unblock(GTK_OBJECT(adjustment),
 						      (gulong)sb->handler_id);
+#endif
     }
 }
 
@@ -750,7 +1043,12 @@ adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
 #endif
 
     sb = gui_find_scrollbar((long)data);
+#if GTK_CHECK_VERSION(3,0,0)
+    value = gtk_adjustment_get_value(adjustment);
+#else
     value = (long)adjustment->value;
+#endif
+#if !GTK_CHECK_VERSION(3,0,0)
     /*
      * The dragging argument must be right for the scrollbar to work with
      * closed folds.  This isn't documented, hopefully this will keep on
@@ -793,7 +1091,7 @@ adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
 	    }
 	}
     }
-
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
     gui_drag_scrollbar(sb, value, dragging);
 }
 
@@ -802,23 +1100,42 @@ adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
 gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
 {
     if (orient == SBAR_HORIZ)
+#if GTK_CHECK_VERSION(3,2,0)
+        sb->id = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL);
+#else
 	sb->id = gtk_hscrollbar_new(NULL);
+#endif
     else if (orient == SBAR_VERT)
+#if GTK_CHECK_VERSION(3,2,0)
+        sb->id = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL);
+#else
 	sb->id = gtk_vscrollbar_new(NULL);
+#endif
 
     if (sb->id != NULL)
     {
 	GtkAdjustment *adjustment;
 
+#if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_can_focus(sb->id, FALSE);
+#else
 	GTK_WIDGET_UNSET_FLAGS(sb->id, GTK_CAN_FOCUS);
+#endif
 	gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0);
 
 	adjustment = gtk_range_get_adjustment(GTK_RANGE(sb->id));
 
+#if GTK_CHECK_VERSION(3,0,0)
+	sb->handler_id = g_signal_connect(
+			     G_OBJECT(adjustment), "value-changed",
+			     G_CALLBACK(adjustment_value_changed),
+			     GINT_TO_POINTER(sb->ident));
+#else
 	sb->handler_id = gtk_signal_connect(
 			     GTK_OBJECT(adjustment), "value_changed",
 			     GTK_SIGNAL_FUNC(adjustment_value_changed),
 			     GINT_TO_POINTER(sb->ident));
+#endif
 	gui_mch_update();
     }
 }
@@ -932,8 +1249,13 @@ gui_mch_browse(int saving UNUSED,
 	    GTK_WINDOW(gui.mainwin),
 	    saving ? GTK_FILE_CHOOSER_ACTION_SAVE
 					   : GTK_FILE_CHOOSER_ACTION_OPEN,
+# if GTK_CHECK_VERSION(3,10,0)
+	    _("_Cancel"), GTK_RESPONSE_CANCEL,
+	    saving ? _("_Save") : _("_Open"), GTK_RESPONSE_ACCEPT,
+# else
 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 	    saving ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+# endif
 	    NULL);
     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fc),
 						       (const gchar *)dirbuf);
@@ -1062,8 +1384,13 @@ gui_mch_browsedir(
 	    (const gchar *)title,
 	    GTK_WINDOW(gui.mainwin),
 	    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+#  if GTK_CHECK_VERSION(3,10,0)
+	    _("_Cancel"), GTK_RESPONSE_CANCEL,
+	    _("_OK"), GTK_RESPONSE_ACCEPT,
+#  else
 	    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 	    GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+#  endif
 	    NULL);
 
     CONVERT_TO_UTF8_FREE(title);
@@ -1266,6 +1593,11 @@ dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
     /* Check 'v' flag in 'guioptions': vertical button placement. */
     if (vim_strchr(p_go, GO_VERTICAL) != NULL)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+        /* Add GTK+ 3 code if necessary. */
+	/* N.B. GTK+ 3 doesn't allow you to access vbox and action_area via
+	 * the C API. */
+#else
 	GtkWidget	*vbutton_box;
 
 	vbutton_box = gtk_vbutton_box_new();
@@ -1274,6 +1606,7 @@ dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
 						 vbutton_box, TRUE, FALSE, 0);
 	/* Overrule the "action_area" value, hopefully this works... */
 	GTK_DIALOG(dialog)->action_area = vbutton_box;
+#endif
     }
 
     /*
@@ -1308,6 +1641,16 @@ dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
 	 */
 	if (ok != NULL && ync != NULL) /* almost impossible to fail */
 	{
+#if GTK_CHECK_VERSION(3,10,0)
+	    if	    (button_equal(label, ok[0]))    label = _("OK");
+	    else if (button_equal(label, ync[0]))   label = _("Yes");
+	    else if (button_equal(label, ync[1]))   label = _("No");
+	    else if (button_equal(label, ync[2]))   label = _("Cancel");
+	    else if (button_equal(label, "Ok"))     label = _("OK");
+	    else if (button_equal(label, "Yes"))    label = _("Yes");
+	    else if (button_equal(label, "No"))     label = _("No");
+	    else if (button_equal(label, "Cancel")) label = _("Canccl");
+#else
 	    if	    (button_equal(label, ok[0]))    label = GTK_STOCK_OK;
 	    else if (button_equal(label, ync[0]))   label = GTK_STOCK_YES;
 	    else if (button_equal(label, ync[1]))   label = GTK_STOCK_NO;
@@ -1316,6 +1659,7 @@ dialog_add_buttons(GtkDialog *dialog, char_u *button_string)
 	    else if (button_equal(label, "Yes"))    label = GTK_STOCK_YES;
 	    else if (button_equal(label, "No"))     label = GTK_STOCK_NO;
 	    else if (button_equal(label, "Cancel")) label = GTK_STOCK_CANCEL;
+#endif
 	}
 	label8 = CONVERT_TO_UTF8((char_u *)label);
 	gtk_dialog_add_button(dialog, (const gchar *)label8, idx);
@@ -1408,14 +1752,28 @@ gui_mch_dialog(int	type,	    /* type of dialog */
 	gtk_entry_set_text(GTK_ENTRY(entry), (const char *)text);
 	CONVERT_TO_UTF8_FREE(text);
 
+#if GTK_CHECK_VERSION(3,14,0)
+        gtk_widget_set_halign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
+        gtk_widget_set_valign(GTK_WIDGET(entry), GTK_ALIGN_CENTER);
+        gtk_widget_set_hexpand(GTK_WIDGET(entry), TRUE);
+        gtk_widget_set_vexpand(GTK_WIDGET(entry), TRUE);
+
+        alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+#else
 	alignment = gtk_alignment_new((float)0.5, (float)0.5,
 						      (float)1.0, (float)1.0);
+#endif
 	gtk_container_add(GTK_CONTAINER(alignment), entry);
 	gtk_container_set_border_width(GTK_CONTAINER(alignment), 5);
 	gtk_widget_show(alignment);
 
+#if GTK_CHECK_VERSION(3,0,0)
+	gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
+			   alignment, TRUE, FALSE, 0);
+#else
 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
 			   alignment, TRUE, FALSE, 0);
+#endif
 	dialoginfo.noalt = FALSE;
     }
     else
@@ -1473,6 +1831,7 @@ gui_mch_show_popupmenu(vimmenu_T *menu)
      * Append a submenu for selecting an input method.	This is
      * currently the only way to switch input methods at runtime.
      */
+#  if !GTK_CHECK_VERSION(3,10,0)
     if (xic != NULL && g_object_get_data(G_OBJECT(menu->submenu_id),
 					 "vim-has-im-menu") == NULL)
     {
@@ -1499,6 +1858,7 @@ gui_mch_show_popupmenu(vimmenu_T *menu)
 	g_object_set_data(G_OBJECT(menu->submenu_id),
 			  "vim-has-im-menu", GINT_TO_POINTER(TRUE));
     }
+#  endif
 # endif /* FEAT_XIM */
 
     gtk_menu_popup(GTK_MENU(menu->submenu_id),
@@ -1524,7 +1884,11 @@ popup_menu_position_func(GtkMenu *menu UNUSED,
 			 gboolean *push_in UNUSED,
 			 gpointer user_data UNUSED)
 {
+# if GTK_CHECK_VERSION(3,0,0)
+    gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), x, y);
+# else
     gdk_window_get_origin(gui.drawarea->window, x, y);
+# endif
 
     if (popup_mouse_pos)
     {
@@ -1534,7 +1898,12 @@ popup_menu_position_func(GtkMenu *menu UNUSED,
 	*x += mx;
 	*y += my;
     }
+# if GTK_CHECK_VERSION(3,0,0)
+    else if (curwin != NULL && gui.drawarea != NULL &&
+             gtk_widget_get_window(gui.drawarea) != NULL)
+# else
     else if (curwin != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
+# endif
     {
 	/* Find the cursor position in the current window */
 	*x += FILL_X(W_WINCOL(curwin) + curwin->w_wcol + 1) + 1;
@@ -1612,7 +1981,8 @@ find_key_press_event(
 }
 
     static GtkWidget *
-create_image_button(const char *stock_id, const char *label)
+create_image_button(const char *stock_id UNUSED,
+                    const char *label)
 {
     char_u	*text;
     GtkWidget	*box;
@@ -1621,18 +1991,35 @@ create_image_button(const char *stock_id, const char *label)
 
     text = CONVERT_TO_UTF8((char_u *)label);
 
+#if GTK_CHECK_VERSION(3,2,0)
+    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
+    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
+#else
     box = gtk_hbox_new(FALSE, 3);
-    gtk_box_pack_start(GTK_BOX(box),
-		       gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
-		       FALSE, FALSE, 0);
+#endif
+#if !GTK_CHECK_VERSION(3,10,0)
+    if (stock_id != NULL)
+        gtk_box_pack_start(GTK_BOX(box),
+                           gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_BUTTON),
+                           FALSE, FALSE, 0);
+#endif
     gtk_box_pack_start(GTK_BOX(box),
 		       gtk_label_new((const char *)text),
 		       FALSE, FALSE, 0);
 
     CONVERT_TO_UTF8_FREE(text);
 
+#if GTK_CHECK_VERSION(3,14,0)
+    gtk_widget_set_halign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
+    gtk_widget_set_valign(GTK_WIDGET(box), GTK_ALIGN_CENTER);
+    gtk_widget_set_hexpand(GTK_WIDGET(box), TRUE);
+    gtk_widget_set_vexpand(GTK_WIDGET(box), TRUE);
+
+    alignment = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+#else
     alignment = gtk_alignment_new((float)0.5, (float)0.5,
 						      (float)0.0, (float)0.0);
+#endif
     gtk_container_add(GTK_CONTAINER(alignment), box);
     gtk_widget_show_all(alignment);
 
@@ -1695,10 +2082,17 @@ find_replace_dialog_create(char_u *arg, int do_replace)
 	if (entry_text != NULL)
 	{
 	    gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
+#if GTK_CHECK_VERSION(3,0,0)
+	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
+							     (gboolean)wword);
+	    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
+							     (gboolean)mcase);
+#else
 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
 							     (gboolean)wword);
 	    gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
 							     (gboolean)mcase);
+#endif
 	}
 	gtk_window_present(GTK_WINDOW(frdp->dialog));
 	vim_free(entry_text);
@@ -1706,7 +2100,11 @@ find_replace_dialog_create(char_u *arg, int do_replace)
     }
 
     frdp->dialog = gtk_dialog_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    /* Nothing equivalent to gtk_dialog_set_has_separator() in GTK+ 3. */
+#else
     gtk_dialog_set_has_separator(GTK_DIALOG(frdp->dialog), FALSE);
+#endif
     gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), GTK_WINDOW(gui.mainwin));
     gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE);
 
@@ -1721,164 +2119,390 @@ find_replace_dialog_create(char_u *arg, int do_replace)
 			     CONV(_("VIM - Search...")));
     }
 
+#if GTK_CHECK_VERSION(3,2,0)
+    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
+#else
     hbox = gtk_hbox_new(FALSE, 0);
+#endif
     gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_add(
+        GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(frdp->dialog))), hbox
+    );
+#else
     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(frdp->dialog)->vbox), hbox);
+#endif
 
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+        table = gtk_grid_new();
+#else
 	table = gtk_table_new(1024, 4, FALSE);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+        table = gtk_grid_new();
+#else
 	table = gtk_table_new(1024, 3, FALSE);
+#endif
     gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(table), 4);
+#else
     gtk_container_border_width(GTK_CONTAINER(table), 4);
+#endif
 
     tmp = gtk_label_new(CONV(_("Find what:")));
+#if GTK_CHECK_VERSION(3,16,0)
+    gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
+    gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
+#elif GTK_CHECK_VERSION(3,14,0)
+    GValue align_val = G_VALUE_INIT;
+    g_value_init(&align_val, G_TYPE_FLOAT);
+    g_value_set_float(&align_val, 0.0);
+    g_object_set_property(G_OBJECT(tmp), "xalign_val", &align_val);
+    g_object_set_property(G_OBJECT(tmp), "yalign_val", &align_val);
+    g_value_unset(&align_val);
+#else
     gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+    gtk_grid_attach(GTK_GRID(table), tmp, 0, 0, 2, 1);
+#else
     gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 0, 1,
 		     GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     frdp->what = gtk_entry_new();
     sensitive = (entry_text != NULL && entry_text[0] != NUL);
     if (entry_text != NULL)
 	gtk_entry_set_text(GTK_ENTRY(frdp->what), (char *)entry_text);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(frdp->what), "changed",
+                     G_CALLBACK(entry_changed_cb), frdp->dialog);
+    g_signal_connect_after(G_OBJECT(frdp->what), "key-press-event",
+				 G_CALLBACK(find_key_press_event),
+				 (gpointer) frdp);
+#else
     gtk_signal_connect(GTK_OBJECT(frdp->what), "changed",
 		       GTK_SIGNAL_FUNC(entry_changed_cb), frdp->dialog);
     gtk_signal_connect_after(GTK_OBJECT(frdp->what), "key_press_event",
 				 GTK_SIGNAL_FUNC(find_key_press_event),
 				 (gpointer) frdp);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+    gtk_grid_attach(GTK_GRID(table), frdp->what, 2, 0, 5, 1);
+#else
     gtk_table_attach(GTK_TABLE(table), frdp->what, 1, 1024, 0, 1,
 		     GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     if (do_replace)
     {
 	tmp = gtk_label_new(CONV(_("Replace with:")));
-	gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
+#if GTK_CHECK_VERSION(3,16,0)
+        gtk_label_set_xalign(GTK_LABEL(tmp), 0.0);
+        gtk_label_set_yalign(GTK_LABEL(tmp), 0.5);
+#elif GTK_CHECK_VERSION(3,14,0)
+        GValue xalign = G_VALUE_INIT;
+        GValue yalign = G_VALUE_INIT;
+        g_value_init(&xalign, G_TYPE_FLOAT);
+        g_value_init(&yalign, G_TYPE_FLOAT);
+        g_value_set_float(&xalign, 0.0);
+        g_value_set_float(&yalign, 0.5);
+        g_object_set_property(G_OBJECT(tmp), "xalign", &xalign);
+        g_object_set_property(G_OBJECT(tmp), "yalign", &yalign);
+        g_value_unset(&xalign);
+        g_value_unset(&yalign);
+#else
+        gtk_misc_set_alignment(GTK_MISC(tmp), (gfloat)0.0, (gfloat)0.5);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), tmp, 0, 1, 2, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 0, 1, 1, 2,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 	frdp->with = gtk_entry_new();
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->with), "activate",
+                         G_CALLBACK(find_replace_cb),
+                         GINT_TO_POINTER(FRD_R_FINDNEXT));
+	g_signal_connect_after(G_OBJECT(frdp->with), "key-press-event",
+                               G_CALLBACK(find_key_press_event),
+                               (gpointer) frdp);
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->with), "activate",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_R_FINDNEXT));
 	gtk_signal_connect_after(GTK_OBJECT(frdp->with), "key_press_event",
 				 GTK_SIGNAL_FUNC(find_key_press_event),
 				 (gpointer) frdp);
+#endif
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), frdp->with, 2, 1, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->with, 1, 1024, 1, 2,
 			 GTK_EXPAND | GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
 	/*
 	 * Make the entry activation only change the input focus onto the
 	 * with item.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->what), "activate",
+                         G_CALLBACK(entry_activate_cb), frdp->with);
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
 			   GTK_SIGNAL_FUNC(entry_activate_cb), frdp->with);
+#endif
     }
     else
     {
 	/*
 	 * Make the entry activation do the search.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->what), "activate",
+                         G_CALLBACK(find_replace_cb),
+                         GINT_TO_POINTER(FRD_FINDNEXT));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->what), "activate",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_FINDNEXT));
+#endif
     }
 
     /* whole word only button */
     frdp->wword = gtk_check_button_new_with_label(CONV(_("Match whole word only")));
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->wword),
+							(gboolean)wword);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->wword),
 							(gboolean)wword);
+#endif
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 2, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 2, 3,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), frdp->wword, 0, 3, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->wword, 0, 1023, 1, 2,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     /* match case button */
     frdp->mcase = gtk_check_button_new_with_label(CONV(_("Match case")));
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->mcase),
+							     (gboolean)mcase);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->mcase),
 							     (gboolean)mcase);
+#endif
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 3, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 3, 4,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), frdp->mcase, 0, 4, 5, 1);
+#else
 	gtk_table_attach(GTK_TABLE(table), frdp->mcase, 0, 1023, 2, 3,
 			 GTK_FILL, GTK_EXPAND, 2, 2);
+#endif
 
     tmp = gtk_frame_new(CONV(_("Direction")));
     if (do_replace)
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 2, 4);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 2, 4,
 			 GTK_FILL, GTK_FILL, 2, 2);
+#endif
     else
+#if GTK_CHECK_VERSION(3,4,0)
+        gtk_grid_attach(GTK_GRID(table), tmp, 5, 2, 1, 3);
+#else
 	gtk_table_attach(GTK_TABLE(table), tmp, 1023, 1024, 1, 3,
 			 GTK_FILL, GTK_FILL, 2, 2);
+#endif
+#if GTK_CHECK_VERSION(3,2,0)
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
     vbox = gtk_vbox_new(FALSE, 0);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(vbox), 0);
+#endif
     gtk_container_add(GTK_CONTAINER(tmp), vbox);
 
     /* 'Up' and 'Down' buttons */
     frdp->up = gtk_radio_button_new_with_label(NULL, CONV(_("Up")));
     gtk_box_pack_start(GTK_BOX(vbox), frdp->up, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    frdp->down = gtk_radio_button_new_with_label(
+			gtk_radio_button_get_group(GTK_RADIO_BUTTON(frdp->up)),
+			CONV(_("Down")));
+#else
     frdp->down = gtk_radio_button_new_with_label(
 			gtk_radio_button_group(GTK_RADIO_BUTTON(frdp->up)),
 			CONV(_("Down")));
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
+#else
     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(frdp->down), TRUE);
+#endif
     gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
     gtk_box_pack_start(GTK_BOX(vbox), frdp->down, TRUE, TRUE, 0);
 
     /* vbox to hold the action buttons */
+#if GTK_CHECK_VERSION(3,2,0)
+    actionarea = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
+#else
     actionarea = gtk_vbutton_box_new();
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(actionarea), 2);
+#else
     gtk_container_border_width(GTK_CONTAINER(actionarea), 2);
+#endif
     gtk_box_pack_end(GTK_BOX(hbox), actionarea, FALSE, FALSE, 0);
 
     /* 'Find Next' button */
+#if GTK_CHECK_VERSION(3,10,0)
+    frdp->find = create_image_button(NULL, _("Find Next"));
+#else
     frdp->find = create_image_button(GTK_STOCK_FIND, _("Find Next"));
+#endif
     gtk_widget_set_sensitive(frdp->find, sensitive);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(frdp->find), "clicked",
+                     G_CALLBACK(find_replace_cb),
+                     (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
+				  : GINT_TO_POINTER(FRD_FINDNEXT));
+#else
     gtk_signal_connect(GTK_OBJECT(frdp->find), "clicked",
 		       GTK_SIGNAL_FUNC(find_replace_cb),
 		       (do_replace) ? GINT_TO_POINTER(FRD_R_FINDNEXT)
 				    : GINT_TO_POINTER(FRD_FINDNEXT));
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_can_default(frdp->find, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(frdp->find, GTK_CAN_DEFAULT);
+#endif
     gtk_box_pack_start(GTK_BOX(actionarea), frdp->find, FALSE, FALSE, 0);
     gtk_widget_grab_default(frdp->find);
 
     if (do_replace)
     {
 	/* 'Replace' button */
+#if GTK_CHECK_VERSION(3,10,0)
+	frdp->replace = create_image_button(NULL, _("Replace"));
+#else
 	frdp->replace = create_image_button(GTK_STOCK_CONVERT, _("Replace"));
+#endif
 	gtk_widget_set_sensitive(frdp->replace, sensitive);
+#if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_can_default(frdp->find, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(frdp->replace, GTK_CAN_DEFAULT);
+#endif
 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->replace, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->replace), "clicked",
+                         G_CALLBACK(find_replace_cb),
+                         GINT_TO_POINTER(FRD_REPLACE));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->replace), "clicked",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_REPLACE));
+#endif
 
 	/* 'Replace All' button */
+#if GTK_CHECK_VERSION(3,10,0)
+	frdp->all = create_image_button(NULL, _("Replace All"));
+#else
 	frdp->all = create_image_button(GTK_STOCK_CONVERT, _("Replace All"));
+#endif
 	gtk_widget_set_sensitive(frdp->all, sensitive);
+#if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_can_default(frdp->all, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(frdp->all, GTK_CAN_DEFAULT);
+#endif
 	gtk_box_pack_start(GTK_BOX(actionarea), frdp->all, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(frdp->all), "clicked",
+                         G_CALLBACK(find_replace_cb),
+                         GINT_TO_POINTER(FRD_REPLACEALL));
+#else
 	gtk_signal_connect(GTK_OBJECT(frdp->all), "clicked",
 			   GTK_SIGNAL_FUNC(find_replace_cb),
 			   GINT_TO_POINTER(FRD_REPLACEALL));
+#endif
     }
 
     /* 'Cancel' button */
+#if GTK_CHECK_VERSION(3,10,0)
+    tmp = gtk_button_new_with_mnemonic(_("_Close"));
+#else
     tmp = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_can_default(tmp, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(tmp, GTK_CAN_DEFAULT);
+#endif
     gtk_box_pack_end(GTK_BOX(actionarea), tmp, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect_swapped(G_OBJECT(tmp),
+			      "clicked", G_CALLBACK(gtk_widget_hide),
+			      G_OBJECT(frdp->dialog));
+    g_signal_connect_swapped(G_OBJECT(frdp->dialog),
+			      "delete_event", G_CALLBACK(gtk_widget_hide_on_delete),
+			      G_OBJECT(frdp->dialog));
+#else
     gtk_signal_connect_object(GTK_OBJECT(tmp),
 			      "clicked", GTK_SIGNAL_FUNC(gtk_widget_hide),
 			      GTK_OBJECT(frdp->dialog));
     gtk_signal_connect_object(GTK_OBJECT(frdp->dialog),
 			      "delete_event", GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete),
 			      GTK_OBJECT(frdp->dialog));
+#endif
 
+#if GTK_CHECK_VERSION(3,2,0)
+    tmp = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
+#else
     tmp = gtk_vseparator_new();
+#endif
     gtk_box_pack_end(GTK_BOX(hbox), tmp, FALSE, FALSE, 10);
 
     /* Suppress automatic show of the unused action area */
+#if GTK_CHECK_VERSION(3,0,0)
+#if !GTK_CHECK_VERSION(3,12,0)
+    gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(frdp->dialog)));
+#endif
+#else
     gtk_widget_hide(GTK_DIALOG(frdp->dialog)->action_area);
+#endif
     gtk_widget_show_all(hbox);
     gtk_widget_show(frdp->dialog);
 
@@ -1928,11 +2552,23 @@ find_replace_cb(GtkWidget *widget UNUSED, gpointer data)
     }
 
     find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what));
+#if GTK_CHECK_VERSION(3,0,0)
+    direction_down = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->down));
+#else
     direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->wword)))
+#else
     if (GTK_TOGGLE_BUTTON(sfr->wword)->active)
+#endif
 	flags |= FRD_WHOLE_WORD;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->mcase)))
+#else
     if (GTK_TOGGLE_BUTTON(sfr->mcase)->active)
+#endif
 	flags |= FRD_MATCH_CASE;
 
     repl_text = CONVERT_FROM_UTF8(repl_text);
diff --git a/src/gui_gtk_f.c b/src/gui_gtk_f.c
index 0eb5065..3f63b96 100644
--- a/src/gui_gtk_f.c
+++ b/src/gui_gtk_f.c
@@ -19,13 +19,19 @@
  * children at arbitrary positions width arbitrary sizes.  This finally puts
  * an end on our resize problems with which we where struggling for such a
  * long time.
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
  */
 
 #include "vim.h"
 #include <gtk/gtk.h>	/* without this it compiles, but gives errors at
 			   runtime! */
 #include "gui_gtk_f.h"
-#include <gtk/gtksignal.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+# include <gtk/gtksignal.h>
+#endif
 #ifdef WIN3264
 # include <gdk/gdkwin32.h>
 #else
@@ -52,10 +58,23 @@ static void gtk_form_unrealize(GtkWidget *widget);
 static void gtk_form_map(GtkWidget *widget);
 static void gtk_form_size_request(GtkWidget *widget,
 				  GtkRequisition *requisition);
+#if GTK_CHECK_VERSION(3,0,0)
+static void gtk_form_get_preferred_width(GtkWidget *widget,
+                                         gint *minimal_width,
+                                         gint *natural_width);
+static void gtk_form_get_preferred_height(GtkWidget *widget,
+                                          gint *minimal_height,
+                                          gint *natural_height);
+#endif
 static void gtk_form_size_allocate(GtkWidget *widget,
 				   GtkAllocation *allocation);
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean gtk_form_draw(GtkWidget *widget,
+                              cairo_t   *cr);
+#else
 static gint gtk_form_expose(GtkWidget *widget,
 			    GdkEventExpose *event);
+#endif
 
 static void gtk_form_remove(GtkContainer *container,
 			    GtkWidget *widget);
@@ -73,22 +92,27 @@ static void gtk_form_position_child(GtkForm *form,
 				    gboolean force_allocate);
 static void gtk_form_position_children(GtkForm *form);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static GdkFilterReturn gtk_form_filter(GdkXEvent *gdk_xevent,
 				       GdkEvent *event,
 				       gpointer data);
 static GdkFilterReturn gtk_form_main_filter(GdkXEvent *gdk_xevent,
 					    GdkEvent *event,
 					    gpointer data);
-
+#endif
+#if !GTK_CHECK_VERSION(3,16,0)
 static void gtk_form_set_static_gravity(GdkWindow *window,
 					gboolean use_static);
+#endif
 
 static void gtk_form_send_configure(GtkForm *form);
 
 static void gtk_form_child_map(GtkWidget *widget, gpointer user_data);
 static void gtk_form_child_unmap(GtkWidget *widget, gpointer user_data);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 static GtkWidgetClass *parent_class = NULL;
+#endif
 
 /* Public interface
  */
@@ -98,7 +122,11 @@ gtk_form_new(void)
 {
     GtkForm *form;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    form = g_object_new(GTK_TYPE_FORM, NULL);
+#else
     form = gtk_type_new(gtk_form_get_type());
+#endif
 
     return GTK_WIDGET(form);
 }
@@ -120,8 +148,10 @@ gtk_form_put(GtkForm	*form,
     child->window = NULL;
     child->x = x;
     child->y = y;
+#if !GTK_CHECK_VERSION(3,0,0)
     child->widget->requisition.width = 0;
     child->widget->requisition.height = 0;
+#endif
     child->mapped = FALSE;
 
     form->children = g_list_append(form->children, child);
@@ -131,13 +161,23 @@ gtk_form_put(GtkForm	*form,
      * that gtk_widget_set_parent() realizes the widget if it's visible
      * and its parent is mapped.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(GTK_WIDGET(form)))
+#else
     if (GTK_WIDGET_REALIZED(form))
+#endif
 	gtk_form_attach_child_window(form, child);
 
     gtk_widget_set_parent(child_widget, GTK_WIDGET(form));
+#if !GTK_CHECK_VERSION(3,0,0)
     gtk_widget_size_request(child->widget, NULL);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(GTK_WIDGET(form)) && !gtk_widget_get_realized(child_widget))
+#else
     if (GTK_WIDGET_REALIZED(form) && !GTK_WIDGET_REALIZED(child_widget))
+#endif
 	gtk_form_realize_child(form, child);
 
     gtk_form_position_child(form, child, TRUE);
@@ -193,6 +233,9 @@ gtk_form_thaw(GtkForm *form)
 
 /* Basic Object handling procedures
  */
+#if GTK_CHECK_VERSION(3,0,0)
+G_DEFINE_TYPE(GtkForm, gtk_form, GTK_TYPE_CONTAINER)
+#else
     GtkType
 gtk_form_get_type(void)
 {
@@ -213,6 +256,7 @@ gtk_form_get_type(void)
     }
     return form_type;
 }
+#endif
 
     static void
 gtk_form_class_init(GtkFormClass *klass)
@@ -223,14 +267,25 @@ gtk_form_class_init(GtkFormClass *klass)
     widget_class = (GtkWidgetClass *) klass;
     container_class = (GtkContainerClass *) klass;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     parent_class = gtk_type_class(gtk_container_get_type());
+#endif
 
     widget_class->realize = gtk_form_realize;
     widget_class->unrealize = gtk_form_unrealize;
     widget_class->map = gtk_form_map;
+#if GTK_CHECK_VERSION(3,0,0)
+    widget_class->get_preferred_width = gtk_form_get_preferred_width;
+    widget_class->get_preferred_height = gtk_form_get_preferred_height;
+#else
     widget_class->size_request = gtk_form_size_request;
+#endif
     widget_class->size_allocate = gtk_form_size_allocate;
+#if GTK_CHECK_VERSION(3,0,0)
+    widget_class->draw = gtk_form_draw;
+#else
     widget_class->expose_event = gtk_form_expose;
+#endif
 
     container_class->remove = gtk_form_remove;
     container_class->forall = gtk_form_forall;
@@ -241,13 +296,17 @@ gtk_form_init(GtkForm *form)
 {
     form->children = NULL;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form->width = 1;
     form->height = 1;
+#endif
 
     form->bin_window = NULL;
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form->configure_serial = 0;
     form->visibility = GDK_VISIBILITY_PARTIAL;
+#endif
 
     form->freeze_count = 0;
 }
@@ -267,40 +326,77 @@ gtk_form_realize(GtkWidget *widget)
     g_return_if_fail(GTK_IS_FORM(widget));
 
     form = GTK_FORM(widget);
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_realized(widget, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(form, GTK_REALIZED);
+#endif
 
     attributes.window_type = GDK_WINDOW_CHILD;
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        GtkAllocation allocation;
+        gtk_widget_get_allocation(widget, &allocation);
+        attributes.x = allocation.x;
+        attributes.y = allocation.y;
+        attributes.width = allocation.width;
+        attributes.height = allocation.height;
+    }
+#else
     attributes.x = widget->allocation.x;
     attributes.y = widget->allocation.y;
     attributes.width = widget->allocation.width;
     attributes.height = widget->allocation.height;
+#endif
     attributes.wclass = GDK_INPUT_OUTPUT;
     attributes.visual = gtk_widget_get_visual(widget);
+#if !GTK_CHECK_VERSION(3,0,0)
     attributes.colormap = gtk_widget_get_colormap(widget);
     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
     attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_window(widget,
+                          gdk_window_new(gtk_widget_get_parent_window(widget),
+                                         &attributes, attributes_mask));
+    gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
+#else
     widget->window = gdk_window_new(gtk_widget_get_parent_window(widget),
 				    &attributes, attributes_mask);
     gdk_window_set_user_data(widget->window, widget);
+#endif
 
     attributes.x = 0;
     attributes.y = 0;
     attributes.event_mask = gtk_widget_get_events(widget);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    form->bin_window = gdk_window_new(gtk_widget_get_window(widget),
+				      &attributes, attributes_mask);
+#else
     form->bin_window = gdk_window_new(widget->window,
 				      &attributes, attributes_mask);
+#endif
     gdk_window_set_user_data(form->bin_window, widget);
 
+#if !GTK_CHECK_VERSION(3,16,0)
     gtk_form_set_static_gravity(form->bin_window, TRUE);
+#endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
     widget->style = gtk_style_attach(widget->style, widget->window);
     gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
     gtk_style_set_background(widget->style, form->bin_window, GTK_STATE_NORMAL);
 
     gdk_window_add_filter(widget->window, gtk_form_main_filter, form);
     gdk_window_add_filter(form->bin_window, gtk_form_filter, form);
+#endif
 
     for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
     {
@@ -308,7 +404,11 @@ gtk_form_realize(GtkWidget *widget)
 
 	gtk_form_attach_child_window(form, child);
 
+#if GTK_CHECK_VERSION(3,0,0)
+        if (gtk_widget_get_visible(child->widget))
+#else
 	if (GTK_WIDGET_VISIBLE(child->widget))
+#endif
 	    gtk_form_realize_child(form, child);
     }
 }
@@ -332,17 +432,30 @@ gtk_form_map(GtkWidget *widget)
 
     form = GTK_FORM(widget);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_mapped(widget, TRUE);
+#else
     GTK_WIDGET_SET_FLAGS(widget, GTK_MAPPED);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gdk_window_show(gtk_widget_get_window(widget));
+#else
     gdk_window_show(widget->window);
+#endif
     gdk_window_show(form->bin_window);
 
     for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
     {
 	GtkFormChild *child = tmp_list->data;
 
+#if GTK_CHECK_VERSION(3,0,0)
+        if (gtk_widget_get_visible(child->widget)
+                && !gtk_widget_get_mapped(child->widget))
+#else
 	if (GTK_WIDGET_VISIBLE(child->widget)
 		&& !GTK_WIDGET_MAPPED(child->widget))
+#endif
 	    gtk_widget_map(child->widget);
     }
 }
@@ -369,12 +482,21 @@ gtk_form_unrealize(GtkWidget *widget)
 
 	if (child->window != NULL)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+                                                 G_CALLBACK(gtk_form_child_map),
+                                                 child);
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+                                                 G_CALLBACK(gtk_form_child_unmap),
+                                                 child);
+#else
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(gtk_form_child_map),
 					  child);
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(gtk_form_child_unmap),
 					  child);
+#endif
 
 	    gdk_window_set_user_data(child->window, NULL);
 	    gdk_window_destroy(child->window);
@@ -385,20 +507,33 @@ gtk_form_unrealize(GtkWidget *widget)
 	tmp_list = tmp_list->next;
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize)
+         (* GTK_WIDGET_CLASS (gtk_form_parent_class)->unrealize) (widget);
+#else
     if (GTK_WIDGET_CLASS (parent_class)->unrealize)
 	 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+#endif
 }
 
     static void
 gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
 {
+#if !GTK_CHECK_VERSION(3,0,0)
     GList *tmp_list;
     GtkForm *form;
+#endif
 
     g_return_if_fail(GTK_IS_FORM(widget));
 
+#if !GTK_CHECK_VERSION(3,0,0)
     form = GTK_FORM(widget);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    requisition->width = 1;
+    requisition->height = 1;
+#else
     requisition->width = form->width;
     requisition->height = form->height;
 
@@ -410,25 +545,71 @@ gtk_form_size_request(GtkWidget *widget, GtkRequisition *requisition)
 	gtk_widget_size_request(child->widget, NULL);
 	tmp_list = tmp_list->next;
     }
+#endif
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gtk_form_get_preferred_width(GtkWidget *widget,
+                             gint      *minimal_width,
+                             gint      *natural_width)
+{
+    GtkRequisition requisition;
+
+    gtk_form_size_request(widget, &requisition);
+
+    *minimal_width = requisition.width;
+    *natural_width = requisition.width;
 }
 
     static void
+gtk_form_get_preferred_height(GtkWidget *widget,
+                              gint      *minimal_height,
+                              gint      *natural_height)
+{
+    GtkRequisition requisition;
+
+    gtk_form_size_request(widget, &requisition);
+
+    *minimal_height = requisition.height;
+    *natural_height = requisition.height;
+}
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
+    static void
 gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
 {
     GList *tmp_list;
     GtkForm *form;
     gboolean need_reposition;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation cur_alloc;
+#endif
 
     g_return_if_fail(GTK_IS_FORM(widget));
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(widget, &cur_alloc);
+
+    if (cur_alloc.x == allocation->x
+	    && cur_alloc.y == allocation->y
+	    && cur_alloc.width == allocation->width
+	    && cur_alloc.height == allocation->height)
+#else
     if (widget->allocation.x == allocation->x
 	    && widget->allocation.y == allocation->y
 	    && widget->allocation.width == allocation->width
 	    && widget->allocation.height == allocation->height)
+#endif
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    need_reposition = cur_alloc.width != allocation->width
+		   || cur_alloc.height != allocation->height;
+#else
     need_reposition = widget->allocation.width != allocation->width
 		   || widget->allocation.height != allocation->height;
+#endif
     form = GTK_FORM(widget);
 
     if (need_reposition)
@@ -444,20 +625,70 @@ gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
 	}
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_realized(widget))
+#else
     if (GTK_WIDGET_REALIZED(widget))
+#endif
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_move_resize(gtk_widget_get_window(widget),
+			       allocation->x, allocation->y,
+			       allocation->width, allocation->height);
+#else
 	gdk_window_move_resize(widget->window,
 			       allocation->x, allocation->y,
 			       allocation->width, allocation->height);
+#endif
 	gdk_window_move_resize(GTK_FORM(widget)->bin_window,
 			       0, 0,
 			       allocation->width, allocation->height);
     }
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_allocation(widget, allocation);
+#else
     widget->allocation = *allocation;
+#endif
     if (need_reposition)
 	gtk_form_send_configure(form);
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+gtk_form_draw(GtkWidget *widget, cairo_t *cr)
+{
+    GList *tmp_list;
+    GtkForm *form;
+
+    g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
+
+    form = GTK_FORM(widget);
+
+    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
+    {
+	GtkFormChild	*formchild = tmp_list->data;
+	GtkWidget	*child	   = formchild->widget;
+
+	if (!gtk_widget_get_has_window(child) &&
+		gtk_cairo_should_draw_window(cr, formchild->window))
+	{
+	    /* To get gtk_widget_draw() to work, it is required to call
+	     * gtk_widget_size_allocate() in advance with a well-posed
+	     * allocation for a given child widget in order to set a
+	     * certain private GtkWidget variable, called
+	     * widget->priv->alloc_need, to the proper value; othewise,
+	     * gtk_widget_draw() fails and the relevant scrollbar won't
+	     * appear on the screen.
+	     *
+	     * Calling gtk_form_position_child() like this is one of ways
+	     * to make sure of that. */
+	    gtk_form_position_child(form, formchild, TRUE);
+	}
+    }
+
+    return GTK_WIDGET_CLASS(gtk_form_parent_class)->draw(widget, cr);
+}
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     static gint
 gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
 {
@@ -497,6 +728,7 @@ gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /* Container method
  */
@@ -524,10 +756,17 @@ gtk_form_remove(GtkContainer *container, GtkWidget *widget)
     {
 	if (child->window)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+					  G_CALLBACK(&gtk_form_child_map), child);
+	    g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget),
+					  G_CALLBACK(&gtk_form_child_unmap), child);
+#else
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(&gtk_form_child_map), child);
 	    gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget),
 					  GTK_SIGNAL_FUNC(&gtk_form_child_unmap), child);
+#endif
 
 	    /* FIXME: This will cause problems for reparenting NO_WINDOW
 	     * widgets out of a GtkForm
@@ -577,7 +816,11 @@ gtk_form_attach_child_window(GtkForm *form, GtkFormChild *child)
     if (child->window != NULL)
 	return; /* been there, done that */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (!gtk_widget_get_has_window(child->widget))
+#else
     if (GTK_WIDGET_NO_WINDOW(child->widget))
+#endif
     {
 	GtkWidget	*widget;
 	GdkWindowAttr	attributes;
@@ -588,34 +831,66 @@ gtk_form_attach_child_window(GtkForm *form, GtkFormChild *child)
 	attributes.window_type = GDK_WINDOW_CHILD;
 	attributes.x = child->x;
 	attributes.y = child->y;
+#if GTK_CHECK_VERSION(3,0,0)
+        {
+            GtkRequisition requisition;
+
+	    gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
+
+            attributes.width = requisition.width;
+            attributes.height = requisition.height;
+        }
+#else
 	attributes.width = child->widget->requisition.width;
 	attributes.height = child->widget->requisition.height;
+#endif
 	attributes.wclass = GDK_INPUT_OUTPUT;
 	attributes.visual = gtk_widget_get_visual(widget);
+#if !GTK_CHECK_VERSION(3,0,0)
 	attributes.colormap = gtk_widget_get_colormap(widget);
+#endif
 	attributes.event_mask = GDK_EXPOSURE_MASK;
 
+#if GTK_CHECK_VERSION(3,0,0)
+	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+#else
 	attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+#endif
 	child->window = gdk_window_new(form->bin_window,
 				       &attributes, attributes_mask);
 	gdk_window_set_user_data(child->window, widget);
 
+#if !GTK_CHECK_VERSION(3,0,0)
 	gtk_style_set_background(widget->style,
 				 child->window,
 				 GTK_STATE_NORMAL);
+#endif
 
 	gtk_widget_set_parent_window(child->widget, child->window);
+#if !GTK_CHECK_VERSION(3,16,0)
 	gtk_form_set_static_gravity(child->window, TRUE);
+#endif
 	/*
 	 * Install signal handlers to map/unmap child->window
 	 * alongside with the actual widget.
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(child->widget), "map",
+                         G_CALLBACK(&gtk_form_child_map), child);
+        g_signal_connect(G_OBJECT(child->widget), "unmap",
+                         G_CALLBACK(&gtk_form_child_unmap), child);
+#else
 	gtk_signal_connect(GTK_OBJECT(child->widget), "map",
 			   GTK_SIGNAL_FUNC(&gtk_form_child_map), child);
 	gtk_signal_connect(GTK_OBJECT(child->widget), "unmap",
 			   GTK_SIGNAL_FUNC(&gtk_form_child_unmap), child);
+#endif
     }
+#if GTK_CHECK_VERSION(3,0,0)
+    else if (!gtk_widget_get_realized(child->widget))
+#else
     else if (!GTK_WIDGET_REALIZED(child->widget))
+#endif
     {
 	gtk_widget_set_parent_window(child->widget, form->bin_window);
     }
@@ -627,8 +902,10 @@ gtk_form_realize_child(GtkForm *form, GtkFormChild *child)
     gtk_form_attach_child_window(form, child);
     gtk_widget_realize(child->widget);
 
+#if !GTK_CHECK_VERSION(3,16,0)
     if (child->window == NULL) /* might be already set, see above */
 	gtk_form_set_static_gravity(child->widget->window, TRUE);
+#endif
 }
 
     static void
@@ -646,9 +923,18 @@ gtk_form_position_child(GtkForm *form, GtkFormChild *child,
     {
 	if (!child->mapped)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+            if (gtk_widget_get_mapped(GTK_WIDGET(form)) &&
+		gtk_widget_get_visible(child->widget))
+#else
 	    if (GTK_WIDGET_MAPPED(form) && GTK_WIDGET_VISIBLE(child->widget))
+#endif
 	    {
+#if GTK_CHECK_VERSION(3,0,0)
+                if (!gtk_widget_get_mapped(child->widget))
+#else
 		if (!GTK_WIDGET_MAPPED(child->widget))
+#endif
 		    gtk_widget_map(child->widget);
 
 		child->mapped = TRUE;
@@ -659,15 +945,31 @@ gtk_form_position_child(GtkForm *form, GtkFormChild *child,
 	if (force_allocate)
 	{
 	    GtkAllocation allocation;
+#if GTK_CHECK_VERSION(3,0,0)
+            GtkRequisition requisition;
+
+            gtk_widget_get_preferred_size(child->widget, &requisition, NULL);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+            if (!gtk_widget_get_has_window(child->widget))
+#else
 	    if (GTK_WIDGET_NO_WINDOW(child->widget))
+#endif
 	    {
 		if (child->window)
 		{
+#if GTK_CHECK_VERSION(3,0,0)
+		    gdk_window_move_resize(child->window,
+			    x, y,
+			    requisition.width,
+			    requisition.height);
+#else
 		    gdk_window_move_resize(child->window,
 			    x, y,
 			    child->widget->requisition.width,
 			    child->widget->requisition.height);
+#endif
 		}
 
 		allocation.x = 0;
@@ -679,8 +981,13 @@ gtk_form_position_child(GtkForm *form, GtkFormChild *child,
 		allocation.y = y;
 	    }
 
+#if GTK_CHECK_VERSION(3,0,0)
+	    allocation.width = requisition.width;
+	    allocation.height = requisition.height;
+#else
 	    allocation.width = child->widget->requisition.width;
 	    allocation.height = child->widget->requisition.height;
+#endif
 
 	    gtk_widget_size_allocate(child->widget, &allocation);
 	}
@@ -691,7 +998,11 @@ gtk_form_position_child(GtkForm *form, GtkFormChild *child,
 	{
 	    child->mapped = FALSE;
 
+#if GTK_CHECK_VERSION(3,0,0)
+            if (gtk_widget_get_mapped(child->widget))
+#else
 	    if (GTK_WIDGET_MAPPED(child->widget))
+#endif
 		gtk_widget_unmap(child->widget);
 	}
     }
@@ -717,6 +1028,7 @@ gtk_form_position_children(GtkForm *form)
  * them or discards them, depending on whether we are obscured
  * or not.
  */
+#if !GTK_CHECK_VERSION(3,0,0)
     static GdkFilterReturn
 gtk_form_filter(GdkXEvent *gdk_xevent, GdkEvent *event UNUSED, gpointer data)
 {
@@ -783,7 +1095,9 @@ gtk_form_main_filter(GdkXEvent *gdk_xevent,
     }
     return GDK_FILTER_CONTINUE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
+#if !GTK_CHECK_VERSION(3,16,0)
     static void
 gtk_form_set_static_gravity(GdkWindow *window, gboolean use_static)
 {
@@ -791,13 +1105,18 @@ gtk_form_set_static_gravity(GdkWindow *window, gboolean use_static)
      * results in an annoying assertion error message. */
     gdk_window_set_static_gravities(window, use_static);
 }
+#endif /* !GTK_CHECK_VERSION(3,16,0) */
 
     void
 gtk_form_move_resize(GtkForm *form, GtkWidget *widget,
 		     gint x, gint y, gint w, gint h)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_size_request(widget, w, h);
+#else
     widget->requisition.width  = w;
     widget->requisition.height = h;
+#endif
 
     gtk_form_move(form, widget, x, y);
 }
@@ -811,11 +1130,24 @@ gtk_form_send_configure(GtkForm *form)
     widget = GTK_WIDGET(form);
 
     event.type = GDK_CONFIGURE;
+#if GTK_CHECK_VERSION(3,0,0)
+    event.window = gtk_widget_get_window(widget);
+    {
+        GtkAllocation allocation;
+
+        gtk_widget_get_allocation(widget, &allocation);
+        event.x = allocation.x;
+        event.y = allocation.y;
+        event.width = allocation.width;
+        event.height = allocation.height;
+    }
+#else
     event.window = widget->window;
     event.x = widget->allocation.x;
     event.y = widget->allocation.y;
     event.width = widget->allocation.width;
     event.height = widget->allocation.height;
+#endif
 
     gtk_main_do_event((GdkEvent*)&event);
 }
@@ -841,4 +1173,3 @@ gtk_form_child_unmap(GtkWidget *widget UNUSED, gpointer user_data)
     child->mapped = FALSE;
     gdk_window_hide(child->window);
 }
-
diff --git a/src/gui_gtk_f.h b/src/gui_gtk_f.h
index 73b0024..703659c 100644
--- a/src/gui_gtk_f.h
+++ b/src/gui_gtk_f.h
@@ -9,8 +9,12 @@
 #ifndef __GTK_FORM_H__
 #define __GTK_FORM_H__
 
+#ifdef USE_GTK3
+#include <gtk/gtk.h>
+#else
 #include <gdk/gdk.h>
 #include <gtk/gtkcontainer.h>
+#endif
 
 
 #ifdef __cplusplus
@@ -18,10 +22,17 @@ extern "C" {
 #endif
 
 #define GTK_TYPE_FORM		       (gtk_form_get_type ())
+#if USE_GTK3
+#define GTK_FORM(obj)                  (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_FORM, GtkForm))
+#define GTK_FORM_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_FORM, GtkFormClass))
+#define GTK_IS_FORM(obj)               (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_FORM))
+#define GTK_IS_FORM_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_FORM))
+#else
 #define GTK_FORM(obj)		       (GTK_CHECK_CAST ((obj), GTK_TYPE_FORM, GtkForm))
 #define GTK_FORM_CLASS(klass)	       (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_FORM, GtkFormClass))
 #define GTK_IS_FORM(obj)	       (GTK_CHECK_TYPE ((obj), GTK_TYPE_FORM))
 #define GTK_IS_FORM_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FORM))
+#endif
 
 
 typedef struct _GtkForm GtkForm;
@@ -33,12 +44,16 @@ struct _GtkForm
 
     GList *children;
 
+#ifndef USE_GTK3
     guint width;
     guint height;
+#endif
 
     GdkWindow *bin_window;
 
+#ifndef USE_GTK3
     GdkVisibilityState visibility;
+#endif
     gulong configure_serial;
 
     gint freeze_count;
@@ -49,7 +64,11 @@ struct _GtkFormClass
     GtkContainerClass parent_class;
 };
 
+#ifdef USE_GTK3
+GType gtk_form_get_type(void);
+#else
 GtkType gtk_form_get_type(void);
+#endif
 
 GtkWidget *gtk_form_new(void);
 
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 9f6775f..6b6cede 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -19,6 +19,10 @@
  *
  * (C) 2002,2003  Jason Hildebrand  <jason@peaceworks.ca>
  *		  Daniel Elstner  <daniel.elstner@gmx.net>
+ *
+ * Support for GTK+ 3 was added by:
+ *
+ * 2016  Kazunobu Kuriyama  <kazunobu.kuriyama@gmail.com>
  */
 
 #include "vim.h"
@@ -75,7 +79,12 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockI
 # define GdkEventConfigure int
 # define GdkEventClient int
 #else
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+#  include <gtk/gtkx.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gdk/gdk.h>
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
@@ -580,6 +589,7 @@ gui_mch_free_all(void)
 }
 #endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
 /*
  * This should be maybe completely removed.
  * Doesn't seem possible, since check_copy_area() relies on
@@ -601,10 +611,41 @@ visibility_event(GtkWidget *widget UNUSED,
 			     gui.visibility != GDK_VISIBILITY_UNOBSCURED);
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /*
  * Redraw the corresponding portions of the screen.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+draw_event(GtkWidget *widget UNUSED,
+           cairo_t   *cr,
+           gpointer   user_data UNUSED)
+{
+    /* Skip this when the GUI isn't set up yet, will redraw later. */
+    if (gui.starting)
+	return FALSE;
+
+    out_flush();		/* make sure all output has been processed */
+
+    cairo_set_source_surface(cr, gui.surface, 0, 0);
+    cairo_paint(cr);
+#if 0
+    {
+	GdkRectangle rect;
+
+	if (gdk_cairo_get_clip_rectangle(cr, &rect))
+	{
+	    gui.by_signal = TRUE;
+	    gui_redraw(rect.x, rect.y, rect.width, rect.height);
+	    gui.by_signal = FALSE;
+	}
+    }
+#endif
+
+    return FALSE;
+}
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     static gint
 expose_event(GtkWidget *widget UNUSED,
 	     GdkEventExpose *event,
@@ -631,6 +672,7 @@ expose_event(GtkWidget *widget UNUSED,
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 #ifdef FEAT_CLIENTSERVER
 /*
@@ -643,7 +685,11 @@ property_event(GtkWidget *widget,
 {
     if (event->type == GDK_PROPERTY_NOTIFY
 	    && event->state == (int)GDK_PROPERTY_NEW_VALUE
+# if GTK_CHECK_VERSION(3,0,0)
+            && GDK_WINDOW_XID(event->window) == commWindow
+# else
 	    && GDK_WINDOW_XWINDOW(event->window) == commWindow
+# endif
 	    && GET_X_ATOM(event->atom) == commProperty)
     {
 	XEvent xev;
@@ -653,7 +699,11 @@ property_event(GtkWidget *widget,
 	xev.xproperty.atom = commProperty;
 	xev.xproperty.window = commWindow;
 	xev.xproperty.state = PropertyNewValue;
+# if GTK_CHECK_VERSION(3,0,0)
+	serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)), &xev, 0);
+# else
 	serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev, 0);
+# endif
     }
     return FALSE;
 }
@@ -698,7 +748,11 @@ gui_mch_stop_blink(void)
 {
     if (blink_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+        g_source_remove(blink_timer);
+#else
 	gtk_timeout_remove(blink_timer);
+#endif
 	blink_timer = 0;
     }
     if (blink_state == BLINK_OFF)
@@ -706,22 +760,36 @@ gui_mch_stop_blink(void)
     blink_state = BLINK_NONE;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 blink_cb(gpointer data UNUSED)
 {
     if (blink_state == BLINK_ON)
     {
 	gui_undraw_cursor();
 	blink_state = BLINK_OFF;
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_offtime,
+                                   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_offtime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
     }
     else
     {
 	gui_update_cursor(TRUE, FALSE);
 	blink_state = BLINK_ON;
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_ontime,
+                                   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_ontime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
     }
 
     return FALSE;		/* don't happen again */
@@ -736,14 +804,23 @@ gui_mch_start_blink(void)
 {
     if (blink_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(blink_timer);
+#else
 	gtk_timeout_remove(blink_timer);
+#endif
 	blink_timer = 0;
     }
     /* Only switch blinking on if none of the times is zero */
     if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	blink_timer = g_timeout_add((guint)blink_waittime,
+                                   (GSourceFunc) blink_cb, NULL);
+#else
 	blink_timer = gtk_timeout_add((guint32)blink_waittime,
 				   (GtkFunction) blink_cb, NULL);
+#endif
 	blink_state = BLINK_ON;
 	gui_update_cursor(TRUE, FALSE);
     }
@@ -758,7 +835,11 @@ enter_notify_event(GtkWidget *widget UNUSED,
 	gui_mch_start_blink();
 
     /* make sure keyboard input goes there */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id == 0 || !gtk_widget_has_focus(gui.drawarea))
+#else
     if (gtk_socket_id == 0 || !GTK_WIDGET_HAS_FOCUS(gui.drawarea))
+#endif
 	gtk_widget_grab_focus(gui.drawarea);
 
     return FALSE;
@@ -1179,13 +1260,22 @@ selection_received_cb(GtkWidget		*widget UNUSED,
     int		    len;
     int		    motion_type = MAUTO;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_selection(data) == clip_plus.gtk_sel_atom)
+#else
     if (data->selection == clip_plus.gtk_sel_atom)
+#endif
 	cbd = &clip_plus;
     else
 	cbd = &clip_star;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    text = (char_u *)gtk_selection_data_get_data(data);
+    len = gtk_selection_data_get_length(data);
+#else
     text = (char_u *)data->data;
     len  = data->length;
+#endif
 
     if (text == NULL || len <= 0)
     {
@@ -1195,13 +1285,20 @@ selection_received_cb(GtkWidget		*widget UNUSED,
 	return;
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_data_type(data) == vim_atom)
+#else
     if (data->type == vim_atom)
+#endif
     {
 	motion_type = *text++;
 	--len;
     }
-
+#if GTK_CHECK_VERSION(3,0,0)
+    else if (gtk_selection_data_get_data_type(data) == vimenc_atom)
+#else
     else if (data->type == vimenc_atom)
+#endif
     {
 	char_u		*enc;
 	vimconv_T	conv;
@@ -1292,7 +1389,11 @@ selection_get_cb(GtkWidget	    *widget UNUSED,
     GdkAtom	    type;
     VimClipboard    *cbd;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_selection(selection_data) == clip_plus.gtk_sel_atom)
+#else
     if (selection_data->selection == clip_plus.gtk_sel_atom)
+#endif
 	cbd = &clip_plus;
     else
 	cbd = &clip_star;
@@ -1361,8 +1462,12 @@ selection_get_cb(GtkWidget	    *widget UNUSED,
 	    string = tmpbuf;
 	    length += 2;
 
+#if !GTK_CHECK_VERSION(3,0,0)
+            /* Looks redandunt even for GTK2 because these values are
+             * overwritten by gtk_selection_data_set() that follows. */
 	    selection_data->type = selection_data->target;
 	    selection_data->format = 16;	/* 16 bits per char */
+#endif
 	    gtk_selection_data_set(selection_data, html_atom, 16,
 							      string, length);
 	    vim_free(string);
@@ -1411,9 +1516,12 @@ selection_get_cb(GtkWidget	    *widget UNUSED,
 
     if (string != NULL)
     {
+#if !GTK_CHECK_VERSION(3,0,0)
+        /* Looks redandunt even for GTK2 because these values are
+         * overwritten by gtk_selection_data_set() that follows. */
 	selection_data->type = selection_data->target;
 	selection_data->format = 8;	/* 8 bits per char */
-
+#endif
 	gtk_selection_data_set(selection_data, type, 8, string, length);
 	vim_free(string);
     }
@@ -1493,7 +1601,11 @@ static int mouse_timed_out = TRUE;
 /*
  * Timer used to recognize multiple clicks of the mouse button
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 mouse_click_timer_cb(gpointer data)
 {
     /* we don't use this information currently */
@@ -1505,13 +1617,20 @@ mouse_click_timer_cb(gpointer data)
 
 static guint motion_repeat_timer  = 0;
 static int   motion_repeat_offset = FALSE;
+#ifdef GTK_DEST_DEFAULT_ALL
+static gboolean  motion_repeat_timer_cb(gpointer);
+#else
 static gint  motion_repeat_timer_cb(gpointer);
+#endif
 
     static void
 process_motion_notify(int x, int y, GdkModifierType state)
 {
     int	    button;
     int_u   vim_modifiers;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation allocation;
+#endif
 
     button = (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
 		       GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
@@ -1538,9 +1657,17 @@ process_motion_notify(int x, int y, GdkModifierType state)
     /*
      * Auto repeat timer handling.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(gui.drawarea, &allocation);
+
+    if (x < 0 || y < 0
+	    || x >= allocation.width
+	    || y >= allocation.height)
+#else
     if (x < 0 || y < 0
 	    || x >= gui.drawarea->allocation.width
 	    || y >= gui.drawarea->allocation.height)
+#endif
     {
 
 	int dx;
@@ -1551,8 +1678,13 @@ process_motion_notify(int x, int y, GdkModifierType state)
 	/* Calculate the maximal distance of the cursor from the drawing area.
 	 * (offshoot can't become negative here!).
 	 */
+#if GTK_CHECK_VERSION(3,0,0)
+	dx = x < 0 ? -x : x - allocation.width;
+	dy = y < 0 ? -y : y - allocation.height;
+#else
 	dx = x < 0 ? -x : x - gui.drawarea->allocation.width;
 	dy = y < 0 ? -y : y - gui.drawarea->allocation.height;
+#endif
 
 	offshoot = dx > dy ? dx : dy;
 
@@ -1577,22 +1709,50 @@ process_motion_notify(int x, int y, GdkModifierType state)
 
 	/* shoot again */
 	if (!motion_repeat_timer)
+#if GTK_CHECK_VERSION(3,0,0)
+	    motion_repeat_timer = g_timeout_add((guint)delay,
+						motion_repeat_timer_cb, NULL);
+#else
 	    motion_repeat_timer = gtk_timeout_add((guint32)delay,
 						motion_repeat_timer_cb, NULL);
+#endif
     }
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static GdkWindow *
+gui_gtk_get_pointer(GtkWidget       *widget,
+                    gint            *x,
+                    gint            *y,
+                    GdkModifierType *state)
+{
+    GdkWindow * const win = gtk_widget_get_window(widget);
+    GdkDisplay * const dpy = gdk_window_get_display(win);
+    GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
+    GdkDevice * const dev = gdk_device_manager_get_client_pointer(mngr);
+    return gdk_window_get_device_position(win, dev , x, y, state);
+}
+#endif
+
 /*
  * Timer used to recognize multiple clicks of the mouse button.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 motion_repeat_timer_cb(gpointer data UNUSED)
 {
     int		    x;
     int		    y;
     GdkModifierType state;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(gui.drawarea, &x, &y, &state);
+#else
     gdk_window_get_pointer(gui.drawarea->window, &x, &y, &state);
+#endif
 
     if (!(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
 		   GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
@@ -1637,7 +1797,11 @@ motion_notify_event(GtkWidget *widget,
 	int		y;
 	GdkModifierType	state;
 
+#if GTK_CHECK_VERSION(3,0,0)
+        gui_gtk_get_pointer(widget, &x, &y, &state);
+#else
 	gdk_window_get_pointer(widget->window, &x, &y, &state);
+#endif
 	process_motion_notify(x, y, state);
     }
     else
@@ -1668,7 +1832,11 @@ button_press_event(GtkWidget *widget,
     gui.event_time = event->time;
 
     /* Make sure we have focus now we've been selected */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
+#else
     if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
+#endif
 	gtk_widget_grab_focus(widget);
 
     /*
@@ -1684,14 +1852,23 @@ button_press_event(GtkWidget *widget,
     /* Handle multiple clicks */
     if (!mouse_timed_out && mouse_click_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(mouse_click_timer);
+#else
 	gtk_timeout_remove(mouse_click_timer);
+#endif
 	mouse_click_timer = 0;
 	repeated_click = TRUE;
     }
 
     mouse_timed_out = FALSE;
+#if GTK_CHECK_VERSION(3,0,0)
+    mouse_click_timer = g_timeout_add((guint)p_mouset,
+				  mouse_click_timer_cb, &mouse_timed_out);
+#else
     mouse_click_timer = gtk_timeout_add((guint32)p_mouset,
 				  mouse_click_timer_cb, &mouse_timed_out);
+#endif
 
     switch (event->button)
     {
@@ -1730,7 +1907,11 @@ scroll_event(GtkWidget *widget,
     int	    button;
     int_u   vim_modifiers;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
+#else
     if (gtk_socket_id != 0 && !GTK_WIDGET_HAS_FOCUS(widget))
+#endif
 	gtk_widget_grab_focus(widget);
 
     switch (event->direction)
@@ -1781,7 +1962,11 @@ button_release_event(GtkWidget *widget UNUSED,
        area .*/
     if (motion_repeat_timer)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_source_remove(motion_repeat_timer);
+#else
 	gtk_timeout_remove(motion_repeat_timer);
+#endif
 	motion_repeat_timer = 0;
     }
 
@@ -1896,7 +2081,13 @@ drag_handle_uri_list(GdkDragContext	*context,
     char_u  **fnames;
     int	    nfiles = 0;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    fnames = parse_uri_list(&nfiles,
+                            (char_u *)gtk_selection_data_get_data(data),
+                            gtk_selection_data_get_length(data));
+# else
     fnames = parse_uri_list(&nfiles, data->data, data->length);
+# endif
 
     if (fnames != NULL && nfiles > 0)
     {
@@ -1923,10 +2114,19 @@ drag_handle_text(GdkDragContext	    *context,
     int	    len;
     char_u  *tmpbuf = NULL;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    text = (char_u *)gtk_selection_data_get_data(data);
+    len = gtk_selection_data_get_length(data);
+# else
     text = data->data;
     len  = data->length;
+# endif
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_data_type(data) == utf8_string_atom)
+# else
     if (data->type == utf8_string_atom)
+# endif
     {
 	if (input_conv.vc_type != CONV_NONE)
 	    tmpbuf = string_convert(&input_conv, text, &len);
@@ -1962,10 +2162,17 @@ drag_data_received_cb(GtkWidget		*widget,
     GdkModifierType state;
 
     /* Guard against trash */
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_selection_data_get_data(data) == NULL
+            || gtk_selection_data_get_length(data) <= 0
+            || gtk_selection_data_get_format(data) != 8
+            || gtk_selection_data_get_data(data)[gtk_selection_data_get_length(data)] != '\0')
+# else
     if (data->data == NULL
 	    || data->length <= 0
 	    || data->format != 8
 	    || data->data[data->length] != '\0')
+# endif
     {
 	gtk_drag_finish(context, FALSE, FALSE, time_);
 	return;
@@ -1973,7 +2180,11 @@ drag_data_received_cb(GtkWidget		*widget,
 
     /* Get the current modifier state for proper distinguishment between
      * different operations later. */
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(widget, NULL, NULL, &state);
+# else
     gdk_window_get_pointer(widget->window, NULL, NULL, &state);
+# endif
 
     /* Not sure about the role of "text/plain" here... */
     if (info == (guint)TARGET_TEXT_URI_LIST)
@@ -2253,7 +2464,7 @@ setup_save_yourself(void)
     Atom    *existing_atoms = NULL;
     int	    count = 0;
 
-#ifdef USE_XSMP
+# ifdef USE_XSMP
     if (xsmp_icefd != -1)
     {
 	/*
@@ -2266,14 +2477,20 @@ setup_save_yourself(void)
 				  local_xsmp_handle_requests, (gpointer)g_io);
     }
     else
-#endif
+# endif
     {
 	/* Fall back to old method */
 
 	/* first get the existing value */
+# if GTK_CHECK_VERSION(3,0,0)
+	if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+                    GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)),
+		    &existing_atoms, &count))
+# else
 	if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
 		    &existing_atoms, &count))
+# endif
 	{
 	    Atom	*new_atoms;
 	    Atom	save_yourself_xatom;
@@ -2295,8 +2512,13 @@ setup_save_yourself(void)
 		{
 		    memcpy(new_atoms, existing_atoms, count * sizeof(Atom));
 		    new_atoms[count] = save_yourself_xatom;
+# if GTK_CHECK_VERSION(3,0,0)
+		    XSetWMProtocols(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+			    GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)),
+# else
 		    XSetWMProtocols(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 			    GDK_WINDOW_XWINDOW(gui.mainwin->window),
+# endif
 			    new_atoms, count + 1);
 		    vim_free(new_atoms);
 		}
@@ -2341,8 +2563,13 @@ global_event_filter(GdkXEvent *xev,
 	 * know we are done saving ourselves.  We don't want to be
 	 * restarted, thus set argv to NULL.
 	 */
+# if GTK_CHECK_VERSION(3,0,0)
+	XSetCommand(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+		    GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)),
+# else
 	XSetCommand(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 		    GDK_WINDOW_XWINDOW(gui.mainwin->window),
+# endif
 		    NULL, 0);
 	return GDK_FILTER_REMOVE;
     }
@@ -2379,7 +2606,11 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
     /* When started with "--echo-wid" argument, write window ID on stdout. */
     if (echo_wid_arg)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	printf("WID: %ld\n", (long)GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)));
+#else
 	printf("WID: %ld\n", (long)GDK_WINDOW_XWINDOW(gui.mainwin->window));
+#endif
 	fflush(stdout);
     }
 
@@ -2416,10 +2647,17 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
     if (serverName == NULL && serverDelayedStartName != NULL)
     {
 	/* This is a :gui command in a plain vim with no previous server */
+# if GTK_CHECK_VERSION(3,0,0)
+	commWindow = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin));
+
+	(void)serverRegisterName(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+				 serverDelayedStartName);
+# else
 	commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window);
 
 	(void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 				 serverDelayedStartName);
+# endif
     }
     else
     {
@@ -2428,12 +2666,22 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
 	 * have to change the "server" registration to that of the main window
 	 * If we have not registered a name yet, remember the window
 	 */
+# if GTK_CHECK_VERSION(3,0,0)
+	serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
+				     GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)));
+# else
 	serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
 				     GDK_WINDOW_XWINDOW(gui.mainwin->window));
+# endif
     }
     gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event",
+                     G_CALLBACK(property_event), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "property_notify_event",
 		       GTK_SIGNAL_FUNC(property_event), NULL);
+# endif
 #endif
 }
 
@@ -2441,21 +2689,60 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
 create_blank_pointer(void)
 {
     GdkWindow	*root_window = NULL;
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkPixbuf   *blank_mask;
+#else
     GdkPixmap	*blank_mask;
+#endif
     GdkCursor	*cursor;
     GdkColor	color = { 0, 0, 0, 0 };
+#if !GTK_CHECK_VERSION(3,0,0)
     char	blank_data[] = { 0x0 };
+#endif
 
 #ifdef HAVE_GTK_MULTIHEAD
+# if GTK_CHECK_VERSION(3,12,0)
+    {
+	GdkWindow * const win = gtk_widget_get_window(gui.mainwin);
+	GdkScreen * const scrn = gdk_window_get_screen(win);
+	root_window = gdk_screen_get_root_window(scrn);
+    }
+# else
     root_window = gtk_widget_get_root_window(gui.mainwin);
+# endif
 #endif
 
     /* Create a pseudo blank pointer, which is in fact one pixel by one pixel
      * in size. */
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        cairo_surface_t *surf;
+        cairo_t         *cr;
+
+        surf = cairo_image_surface_create(CAIRO_FORMAT_A1, 1, 1);
+        cr = cairo_create(surf);
+
+        cairo_set_source_rgb(cr,
+                             color.red / 65535.0,
+                             color.green / 65535.0,
+                             color.blue / 65535.0);
+        cairo_rectangle(cr, 0, 0, 1, 1);
+        cairo_fill(cr);
+        cairo_destroy(cr);
+
+        blank_mask = gdk_pixbuf_get_from_surface(surf, 0, 0, 1, 1);
+        cairo_surface_destroy(surf);
+
+        cursor = gdk_cursor_new_from_pixbuf(gdk_window_get_display(root_window),
+                                            blank_mask, 0, 0);
+        g_object_unref(blank_mask);
+    }
+#else
     blank_mask = gdk_bitmap_create_from_data(root_window, blank_data, 1, 1);
     cursor = gdk_cursor_new_from_pixmap(blank_mask, blank_mask,
 					&color, &color, 0, 0);
     gdk_bitmap_unref(blank_mask);
+#endif
 
     return cursor;
 }
@@ -2473,12 +2760,21 @@ mainwin_screen_changed_cb(GtkWidget  *widget,
      * Recreate the invisible mouse cursor.
      */
     if (gui.blank_pointer != NULL)
+# if GTK_CHECK_VERSION(3,0,0)
+        g_object_unref(G_OBJECT(gui.blank_pointer));
+# else
 	gdk_cursor_unref(gui.blank_pointer);
+# endif
 
     gui.blank_pointer = create_blank_pointer();
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.pointer_hidden && gtk_widget_get_window(gui.drawarea) != NULL)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), gui.blank_pointer);
+# else
     if (gui.pointer_hidden && gui.drawarea->window != NULL)
 	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+# endif
 
     /*
      * Create a new PangoContext for this screen, and initialize it
@@ -2509,28 +2805,53 @@ mainwin_screen_changed_cb(GtkWidget  *widget,
 drawarea_realize_cb(GtkWidget *widget, gpointer data UNUSED)
 {
     GtkWidget *sbar;
+#if GTK_CHECK_VERSION(3,0,0)
+    GtkAllocation allocation;
+#endif
 
 #ifdef FEAT_XIM
     xim_init();
 #endif
     gui_mch_new_colors();
+#if GTK_CHECK_VERSION(3,0,0)
+    gui.surface = gdk_window_create_similar_surface(gtk_widget_get_window(widget),
+            CAIRO_CONTENT_COLOR_ALPHA,
+            gtk_widget_get_allocated_width(widget),
+            gtk_widget_get_allocated_height(widget));
+#else
     gui.text_gc = gdk_gc_new(gui.drawarea->window);
+#endif
 
     gui.blank_pointer = create_blank_pointer();
     if (gui.pointer_hidden)
+#if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(widget), gui.blank_pointer);
+#else
 	gdk_window_set_cursor(widget->window, gui.blank_pointer);
+#endif
 
     /* get the actual size of the scrollbars, if they are realized */
     sbar = firstwin->w_scrollbars[SBAR_LEFT].id;
     if (!sbar || (!gui.which_scrollbars[SBAR_LEFT]
 				    && firstwin->w_scrollbars[SBAR_RIGHT].id))
 	sbar = firstwin->w_scrollbars[SBAR_RIGHT].id;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_get_allocation(sbar, &allocation);
+    if (sbar && gtk_widget_get_realized(sbar) && allocation.width)
+	gui.scrollbar_width = allocation.width;
+#else
     if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.width)
 	gui.scrollbar_width = sbar->allocation.width;
+#endif
 
     sbar = gui.bottom_sbar.id;
+#if GTK_CHECK_VERSION(3,0,0)
+    if (sbar && gtk_widget_get_realized(sbar) && allocation.height)
+	gui.scrollbar_height = allocation.height;
+#else
     if (sbar && GTK_WIDGET_REALIZED(sbar) && sbar->allocation.height)
 	gui.scrollbar_height = sbar->allocation.height;
+#endif
 }
 
 /*
@@ -2558,10 +2879,22 @@ drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
     g_object_unref(gui.text_context);
     gui.text_context = NULL;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.surface != NULL)
+    {
+        cairo_surface_destroy(gui.surface);
+        gui.surface = NULL;
+    }
+#else
     g_object_unref(gui.text_gc);
     gui.text_gc = NULL;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_object_unref(G_OBJECT(gui.blank_pointer));
+#else
     gdk_cursor_unref(gui.blank_pointer);
+#endif
     gui.blank_pointer = NULL;
 }
 
@@ -2573,6 +2906,38 @@ drawarea_style_set_cb(GtkWidget	*widget UNUSED,
     gui_mch_new_colors();
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+drawarea_configure_event_cb(GtkWidget         *widget,
+                            GdkEventConfigure *event,
+                            gpointer           data UNUSED)
+{
+    static int cur_width = 0;
+    static int cur_height = 0;
+
+    g_return_val_if_fail(event &&
+	    event->width >= 1 && event->height >= 1, TRUE);
+
+    if (event->width == cur_width && event->height == cur_height)
+	return TRUE;
+
+    cur_width = event->width;
+    cur_height = event->height;
+
+    if (gui.surface != NULL)
+	cairo_surface_destroy(gui.surface);
+
+    gui.surface = gdk_window_create_similar_surface(
+	    gtk_widget_get_window(widget),
+	    CAIRO_CONTENT_COLOR_ALPHA,
+	    event->width, event->height);
+
+    gtk_widget_queue_draw(widget);
+
+    return TRUE;
+}
+#endif
+
 /*
  * Callback routine for the "delete_event" signal on the toplevel window.
  * Tries to vim gracefully, or refuses to exit with changed buffers.
@@ -2592,7 +2957,7 @@ get_item_dimensions(GtkWidget *widget, GtkOrientation orientation)
 {
     GtkOrientation item_orientation = GTK_ORIENTATION_HORIZONTAL;
 
-#ifdef FEAT_GUI_GNOME
+# ifdef FEAT_GUI_GNOME
     if (using_gnome && widget != NULL)
     {
 	GtkWidget *parent;
@@ -2611,16 +2976,34 @@ get_item_dimensions(GtkWidget *widget, GtkOrientation orientation)
 	    item_orientation = bonobo_dock_item_get_orientation(dockitem);
 	}
     }
-#endif
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+    if (widget != NULL
+	    && item_orientation == orientation
+	    && gtk_widget_get_realized(widget)
+	    && gtk_widget_get_visible(widget))
+# else
     if (widget != NULL
 	    && item_orientation == orientation
 	    && GTK_WIDGET_REALIZED(widget)
 	    && GTK_WIDGET_VISIBLE(widget))
+# endif
     {
+# if GTK_CHECK_VERSION(3,0,0)
+        GtkAllocation allocation;
+
+        gtk_widget_get_allocation(widget, &allocation);
+
+	if (orientation == GTK_ORIENTATION_HORIZONTAL)
+	    return allocation.height;
+	else
+	    return allocation.width;
+# else
 	if (orientation == GTK_ORIENTATION_HORIZONTAL)
 	    return widget->allocation.height;
 	else
 	    return widget->allocation.width;
+# endif
     }
     return 0;
 }
@@ -2774,6 +3157,17 @@ icon_size_changed_foreach(GtkWidget *widget, gpointer user_data)
     {
 	GtkImage *image = (GtkImage *)widget;
 
+# if GTK_CHECK_VERSION(3,10,0)
+        if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_NAME)
+        {
+            const GtkIconSize icon_size = GPOINTER_TO_INT(user_data);
+            const gchar *icon_name;
+
+            gtk_image_get_icon_name(image, &icon_name, NULL);
+
+            gtk_image_set_from_icon_name(image, icon_name, icon_size);
+        }
+# else
 	/* User-defined icons are stored in a GtkIconSet */
 	if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_SET)
 	{
@@ -2787,6 +3181,7 @@ icon_size_changed_foreach(GtkWidget *widget, gpointer user_data)
 	    gtk_image_set_from_icon_set(image, icon_set, icon_size);
 	    gtk_icon_set_unref(icon_set);
 	}
+# endif
     }
     else if (GTK_IS_CONTAINER(widget))
     {
@@ -2815,7 +3210,9 @@ set_toolbar_style(GtkToolbar *toolbar)
 	style = GTK_TOOLBAR_ICONS;
 
     gtk_toolbar_set_style(toolbar, style);
+# if !GTK_CHECK_VERSION(3,0,0)
     gtk_toolbar_set_tooltips(toolbar, (toolbar_flags & TOOLBAR_TOOLTIPS) != 0);
+# endif
 
     switch (tbis_flags)
     {
@@ -2847,7 +3244,9 @@ set_toolbar_style(GtkToolbar *toolbar)
 #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
 static int ignore_tabline_evt = FALSE;
 static GtkWidget *tabline_menu;
+# if !GTK_CHECK_VERSION(3,0,0)
 static GtkTooltips *tabline_tooltip;
+# endif
 static int clicked_page;	    /* page clicked in tab line */
 
 /*
@@ -2872,9 +3271,15 @@ add_tabline_menu_item(GtkWidget *menu, char_u *text, int resp)
     CONVERT_TO_UTF8_FREE(utf_text);
 
     gtk_container_add(GTK_CONTAINER(menu), item);
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(item), "activate",
+	    G_CALLBACK(tabline_menu_handler),
+	    GINT_TO_POINTER(resp));
+# else
     gtk_signal_connect(GTK_OBJECT(item), "activate",
 	    GTK_SIGNAL_FUNC(tabline_menu_handler),
 	    (gpointer)(long)resp);
+# endif
 }
 
 /*
@@ -2916,10 +3321,26 @@ on_tabline_menu(GtkWidget *widget, GdkEvent *event)
 	   )
 	    return TRUE;
 
+# if GTK_CHECK_VERSION(3,0,0)
+        {
+            GdkWindow * const win = gtk_widget_get_window(gui.mainwin);
+            GdkDisplay * const dpy = gdk_window_get_display(win);
+            GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
+            GdkDevice * const dev = gdk_device_manager_get_client_pointer(mngr);
+            tabwin = gdk_device_get_window_at_position(dev, &x, &y);
+        }
+# else
 	tabwin = gdk_window_at_pointer(&x, &y);
+# endif
+
 	gdk_window_get_user_data(tabwin, (gpointer)&tabwidget);
+# if GTK_CHECK_VERSION(3,0,0)
+	clicked_page = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tabwidget),
+                                                         "tab_num"));
+# else
 	clicked_page = (int)(long)gtk_object_get_user_data(
 						       GTK_OBJECT(tabwidget));
+# endif
 
 	/* If the event was generated for 3rd button popup the menu. */
 	if (bevent->button == 3)
@@ -2950,7 +3371,11 @@ on_tabline_menu(GtkWidget *widget, GdkEvent *event)
     static void
 on_select_tab(
 	GtkNotebook	*notebook UNUSED,
+# if GTK_CHECK_VERSION(3,0,0)
+        gpointer        *page UNUSED,
+# else
 	GtkNotebookPage *page UNUSED,
+# endif
 	gint		idx,
 	gpointer	data UNUSED)
 {
@@ -2975,7 +3400,11 @@ gui_mch_show_tabline(int showit)
 	gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit);
 	update_window_manager_hints(0, 0);
 	if (showit)
+# if GTK_CHECK_VERSION(3,0,0)
+            gtk_widget_set_can_focus(GTK_WIDGET(gui.tabline), FALSE);
+# else
 	    GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(gui.tabline), GTK_CAN_FOCUS);
+# endif
     }
 
     gui_mch_update();
@@ -3023,12 +3452,19 @@ gui_mch_update_tabline(void)
 	if (page == NULL)
 	{
 	    /* Add notebook page */
+# if GTK_CHECK_VERSION(3,2,0)
+            page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+            gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
+# else
 	    page = gtk_vbox_new(FALSE, 0);
+# endif
 	    gtk_widget_show(page);
 	    event_box = gtk_event_box_new();
 	    gtk_widget_show(event_box);
 	    label = gtk_label_new("-Empty-");
+# if !GTK_CHECK_VERSION(3,14,0)
 	    gtk_misc_set_padding(GTK_MISC(label), 2, 2);
+# endif
 	    gtk_container_add(GTK_CONTAINER(event_box), label);
 	    gtk_widget_show(label);
 	    gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline),
@@ -3038,9 +3474,18 @@ gui_mch_update_tabline(void)
 	}
 
 	event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page);
+# if GTK_CHECK_VERSION(3,0,0)
+        g_object_set_data(G_OBJECT(event_box), "tab_num",
+                                                     GINT_TO_POINTER(tab_num));
+# else
 	gtk_object_set_user_data(GTK_OBJECT(event_box),
 						     (gpointer)(long)tab_num);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+        label = gtk_bin_get_child(GTK_BIN(event_box));
+# else
 	label = GTK_BIN(event_box)->child;
+# endif
 	get_tabline_label(tp, FALSE);
 	labeltext = CONVERT_TO_UTF8(NameBuff);
 	gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext);
@@ -3048,8 +3493,12 @@ gui_mch_update_tabline(void)
 
 	get_tabline_label(tp, TRUE);
 	labeltext = CONVERT_TO_UTF8(NameBuff);
+# if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_tooltip_text(event_box, (const gchar *)labeltext);
+# else
 	gtk_tooltips_set_tip(GTK_TOOLTIPS(tabline_tooltip), event_box,
 			     (const char *)labeltext, NULL);
+# endif
 	CONVERT_TO_UTF8_FREE(labeltext);
     }
 
@@ -3057,8 +3506,13 @@ gui_mch_update_tabline(void)
     while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL)
 	gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr);
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
+        gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
+# else
     if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
 	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
+# endif
 
     /* Make sure everything is in place before drawing text. */
     gui_mch_update();
@@ -3076,8 +3530,13 @@ gui_mch_set_curtab(int nr)
 	return;
 
     ignore_tabline_evt = TRUE;
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
+	gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
+# else
     if (gtk_notebook_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
 	gtk_notebook_set_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
+# endif
     ignore_tabline_evt = FALSE;
 }
 
@@ -3187,8 +3646,10 @@ gui_mch_init(void)
 #endif
     /* FIXME: Need to install the classic icons and a gtkrc.classic file.
      * The hard part is deciding install locations and the Makefile magic. */
-#if 0
+#if !GTK_CHECK_VERSION(3,0,0)
+# if 0
     gtk_rc_parse("gtkrc");
+# endif
 #endif
 
     /* Initialize values */
@@ -3221,7 +3682,11 @@ gui_mch_init(void)
 #else
 	plug = gtk_plug_new(gtk_socket_id);
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+	if (plug != NULL && gtk_plug_get_socket_window(GTK_PLUG(plug)) != NULL)
+#else
 	if (plug != NULL && GTK_PLUG(plug)->socket_window != NULL)
+#endif
 	{
 	    gui.mainwin = plug;
 	}
@@ -3256,14 +3721,26 @@ gui_mch_init(void)
     gui.text_context = gtk_widget_create_pango_context(gui.mainwin);
     pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(gui.mainwin), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(gui.mainwin), 0);
+#endif
     gtk_widget_add_events(gui.mainwin, GDK_VISIBILITY_NOTIFY_MASK);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "delete-event",
+                     G_CALLBACK(&delete_event_cb), NULL);
+
+    g_signal_connect(G_OBJECT(gui.mainwin), "realize",
+                     G_CALLBACK(&mainwin_realize), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "delete_event",
 		       GTK_SIGNAL_FUNC(&delete_event_cb), NULL);
 
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "realize",
 		       GTK_SIGNAL_FUNC(&mainwin_realize), NULL);
+#endif
 #ifdef HAVE_GTK_MULTIHEAD
     g_signal_connect(G_OBJECT(gui.mainwin), "screen_changed",
 		     G_CALLBACK(&mainwin_screen_changed_cb), NULL);
@@ -3272,7 +3749,12 @@ gui_mch_init(void)
     gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group);
 
     /* A vertical box holds the menubar, toolbar and main text window. */
+#if GTK_CHECK_VERSION(3,2,0)
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
+#else
     vbox = gtk_vbox_new(FALSE, 0);
+#endif
 
 #ifdef FEAT_GUI_GNOME
     if (using_gnome)
@@ -3335,11 +3817,15 @@ gui_mch_init(void)
      * Create the toolbar and handle
      */
     /* some aesthetics on the toolbar */
+# ifdef USE_GTK3
+    /* TODO: Add GTK+ 3 code here using GtkCssProvider if neccessary. */
+# else
     gtk_rc_parse_string(
 	    "style \"vim-toolbar-style\" {\n"
 	    "  GtkToolbar::button_relief = GTK_RELIEF_NONE\n"
 	    "}\n"
 	    "widget \"*.vim-toolbar\" style \"vim-toolbar-style\"\n");
+# endif
     gui.toolbar = gtk_toolbar_new();
     gtk_widget_set_name(gui.toolbar, "vim-toolbar");
     set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
@@ -3381,42 +3867,77 @@ gui_mch_init(void)
     gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE);
     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE);
     gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE);
+# if !GTK_CHECK_VERSION(3,0,0)
     gtk_notebook_set_tab_border(GTK_NOTEBOOK(gui.tabline), FALSE);
+# endif
 
+# if !GTK_CHECK_VERSION(3,0,0)
     tabline_tooltip = gtk_tooltips_new();
     gtk_tooltips_enable(GTK_TOOLTIPS(tabline_tooltip));
+# endif
 
     {
 	GtkWidget *page, *label, *event_box;
 
 	/* Add the first tab. */
+# if GTK_CHECK_VERSION(3,2,0)
+        page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+        gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
+# else
 	page = gtk_vbox_new(FALSE, 0);
+# endif
 	gtk_widget_show(page);
 	gtk_container_add(GTK_CONTAINER(gui.tabline), page);
 	label = gtk_label_new("-Empty-");
 	gtk_widget_show(label);
 	event_box = gtk_event_box_new();
 	gtk_widget_show(event_box);
+# if GTK_CHECK_VERSION(3,0,0)
+	g_object_set_data(G_OBJECT(event_box), "tab_num", GINT_TO_POINTER(1L));
+# else
 	gtk_object_set_user_data(GTK_OBJECT(event_box), (gpointer)1L);
+# endif
+# if !GTK_CHECK_VERSION(3,14,0)
 	gtk_misc_set_padding(GTK_MISC(label), 2, 2);
+# endif
 	gtk_container_add(GTK_CONTAINER(event_box), label);
 	gtk_notebook_set_tab_label(GTK_NOTEBOOK(gui.tabline), page, event_box);
     }
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.tabline), "switch-page",
+                     G_CALLBACK(on_select_tab), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.tabline), "switch_page",
 		       GTK_SIGNAL_FUNC(on_select_tab), NULL);
+# endif
 
     /* Create a popup menu for the tab line and connect it. */
     tabline_menu = create_tabline_menu();
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect_swapped(G_OBJECT(gui.tabline), "button-press-event",
+	    G_CALLBACK(on_tabline_menu), G_OBJECT(tabline_menu));
+# else
     gtk_signal_connect_object(GTK_OBJECT(gui.tabline), "button_press_event",
 	    GTK_SIGNAL_FUNC(on_tabline_menu), GTK_OBJECT(tabline_menu));
-#endif
+# endif
+#endif /* FEAT_GUI_TABLINE */
 
     gui.formwin = gtk_form_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_container_set_border_width(GTK_CONTAINER(gui.formwin), 0);
+#else
     gtk_container_border_width(GTK_CONTAINER(gui.formwin), 0);
+#endif
+#if !GTK_CHECK_VERSION(3,0,0)
     gtk_widget_set_events(gui.formwin, GDK_EXPOSURE_MASK);
+#endif
 
     gui.drawarea = gtk_drawing_area_new();
+#if GTK_CHECK_VERSION(3,0,0)
+    gui.surface = NULL;
+    gui.by_signal = FALSE;
+#endif
 
     /* Determine which events we will filter. */
     gtk_widget_set_events(gui.drawarea,
@@ -3438,10 +3959,17 @@ gui_mch_init(void)
 
     /* For GtkSockets, key-presses must go to the focus widget (drawarea)
      * and not the window. */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
+				          : G_OBJECT(gui.drawarea),
+		       "key-press-event",
+		       G_CALLBACK(key_press_event), NULL);
+#else
     gtk_signal_connect((gtk_socket_id == 0) ? GTK_OBJECT(gui.mainwin)
 					    : GTK_OBJECT(gui.drawarea),
 		       "key_press_event",
 		       GTK_SIGNAL_FUNC(key_press_event), NULL);
+#endif
 #if defined(FEAT_XIM)
     /* Also forward key release events for the benefit of GTK+ 2 input
      * modules.  Try CTRL-SHIFT-xdigits to enter a Unicode code point. */
@@ -3450,6 +3978,16 @@ gui_mch_init(void)
 		     "key_release_event",
 		     G_CALLBACK(&key_release_event), NULL);
 #endif
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "realize",
+                     G_CALLBACK(drawarea_realize_cb), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "unrealize",
+                     G_CALLBACK(drawarea_unrealize_cb), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "configure-event",
+            G_CALLBACK(drawarea_configure_event_cb), NULL);
+    g_signal_connect_after(G_OBJECT(gui.drawarea), "style-set",
+                           G_CALLBACK(&drawarea_style_set_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "realize",
 		       GTK_SIGNAL_FUNC(drawarea_realize_cb), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "unrealize",
@@ -3457,8 +3995,11 @@ gui_mch_init(void)
 
     gtk_signal_connect_after(GTK_OBJECT(gui.drawarea), "style_set",
 			     GTK_SIGNAL_FUNC(&drawarea_style_set_cb), NULL);
+#endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
     gui.visibility = GDK_VISIBILITY_UNOBSCURED;
+#endif
 
 #if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
     wm_protocols_atom = gdk_atom_intern("WM_PROTOCOLS", FALSE);
@@ -3467,7 +4008,11 @@ gui_mch_init(void)
 
     if (gtk_socket_id != 0)
 	/* make sure keyboard input can go to the drawarea */
+#if GTK_CHECK_VERSION(3,0,0)
+        gtk_widget_set_can_focus(gui.drawarea, TRUE);
+#else
 	GTK_WIDGET_SET_FLAGS(gui.drawarea, GTK_CAN_FOCUS);
+#endif
 
     /*
      * Set clipboard specific atoms
@@ -3482,10 +4027,15 @@ gui_mch_init(void)
      */
     gui.border_offset = gui.border_width;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "draw",
+                     G_CALLBACK(draw_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "visibility_notify_event",
 		       GTK_SIGNAL_FUNC(visibility_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "expose_event",
 		       GTK_SIGNAL_FUNC(expose_event), NULL);
+#endif
 
     /*
      * Only install these enter/leave callbacks when 'p' in 'guioptions'.
@@ -3493,10 +4043,17 @@ gui_mch_init(void)
      */
     if (vim_strchr(p_go, GO_POINTER) != NULL)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.drawarea), "leave-notify-event",
+                         G_CALLBACK(leave_notify_event), NULL);
+	g_signal_connect(G_OBJECT(gui.drawarea), "enter-notify-event",
+                         G_CALLBACK(enter_notify_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "leave_notify_event",
 			   GTK_SIGNAL_FUNC(leave_notify_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "enter_notify_event",
 			   GTK_SIGNAL_FUNC(enter_notify_event), NULL);
+#endif
     }
 
     /* Real windows can get focus ... GtkPlug, being a mere container can't,
@@ -3505,25 +4062,56 @@ gui_mch_init(void)
      */
     if (gtk_socket_id == 0)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.mainwin), "focus-out-event",
+                         G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.mainwin), "focus-in-event",
+                         G_CALLBACK(focus_in_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.mainwin), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+#endif
     }
     else
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.drawarea), "focus-out-event",
+                         G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.drawarea), "focus-in-event",
+                         G_CALLBACK(focus_in_event), NULL);
+#else
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.drawarea), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+#endif
 #ifdef FEAT_GUI_TABLINE
+# if GTK_CHECK_VERSION(3,0,0)
+	g_signal_connect(G_OBJECT(gui.tabline), "focus-out-event",
+                         G_CALLBACK(focus_out_event), NULL);
+	g_signal_connect(G_OBJECT(gui.tabline), "focus-in-event",
+                         G_CALLBACK(focus_in_event), NULL);
+# else
 	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_out_event",
 			       GTK_SIGNAL_FUNC(focus_out_event), NULL);
 	gtk_signal_connect(GTK_OBJECT(gui.tabline), "focus_in_event",
 			       GTK_SIGNAL_FUNC(focus_in_event), NULL);
+# endif
 #endif /* FEAT_GUI_TABLINE */
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "motion-notify-event",
+                     G_CALLBACK(motion_notify_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "button-press-event",
+                     G_CALLBACK(button_press_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "button-release-event",
+                     G_CALLBACK(button_release_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "scroll-event",
+		     G_CALLBACK(&scroll_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "motion_notify_event",
 		       GTK_SIGNAL_FUNC(motion_notify_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "button_press_event",
@@ -3532,19 +4120,32 @@ gui_mch_init(void)
 		       GTK_SIGNAL_FUNC(button_release_event), NULL);
     g_signal_connect(G_OBJECT(gui.drawarea), "scroll_event",
 		     G_CALLBACK(&scroll_event), NULL);
+#endif
 
     /*
      * Add selection handler functions.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-clear-event",
+                     G_CALLBACK(selection_clear_event), NULL);
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-received",
+                     G_CALLBACK(selection_received_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_clear_event",
 		       GTK_SIGNAL_FUNC(selection_clear_event), NULL);
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_received",
 		       GTK_SIGNAL_FUNC(selection_received_cb), NULL);
+#endif
 
     gui_gtk_set_selection_targets();
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "selection-get",
+                     G_CALLBACK(selection_get_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "selection_get",
 		       GTK_SIGNAL_FUNC(selection_get_cb), NULL);
+#endif
 
     /* Pretend we don't have input focus, we will get an event if we do. */
     gui.in_focus = FALSE;
@@ -3572,6 +4173,34 @@ gui_mch_forked(void)
 }
 #endif /* FEAT_GUI_GNOME && FEAT_SESSION */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_get_rgb_from_pixel(guint32 pixel, GdkRGBA *result)
+{
+    GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+    guint32 r_mask, g_mask, b_mask;
+    gint r_shift, g_shift, b_shift;
+
+    if (visual == NULL)
+    {
+        result->red = 0.0;
+        result->green = 0.0;
+        result->blue = 0.0;
+        result->alpha = 0.0;
+        return;
+    }
+
+    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+    result->red = ((pixel & r_mask) >> r_shift) / 255.0;
+    result->green = ((pixel & g_mask) >> g_shift) / 255.0;
+    result->blue = ((pixel & b_mask) >> b_shift) / 255.0;
+    result->alpha = 1.0;
+}
+#endif
+
 /*
  * Called when the foreground or background color has been changed.
  * This used to change the graphics contexts directly but we are
@@ -3580,12 +4209,39 @@ gui_mch_forked(void)
     void
 gui_mch_new_colors(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow * const win = gtk_widget_get_window(gui.drawarea);
+
+    if (gui.drawarea != NULL && gtk_widget_get_window(gui.drawarea) != NULL)
+#else
     if (gui.drawarea != NULL && gui.drawarea->window != NULL)
+#endif
     {
+#if GTK_CHECK_VERSION(3,4,0)
+        GdkRGBA color;
+
+        gui_gtk_get_rgb_from_pixel(gui.back_pixel, &color);
+	{
+	    cairo_pattern_t * const pat = cairo_pattern_create_rgba(
+		    color.red, color.green, color.blue, color.alpha);
+	    if (pat != NULL)
+	    {
+		gdk_window_set_background_pattern(win, pat);
+		cairo_pattern_destroy(pat);
+	    }
+	    else
+		gdk_window_set_background_rgba(win, &color);
+	}
+#else /* !GTK_CHECK_VERSION(3,4,0) */
 	GdkColor color = { 0, 0, 0, 0 };
 
 	color.pixel = gui.back_pixel;
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_background(win, &color);
+# else
 	gdk_window_set_background(gui.drawarea->window, &color);
+# endif
+#endif /* !GTK_CHECK_VERSION(3,4,0) */
     }
 }
 
@@ -3618,8 +4274,13 @@ form_configure_event(GtkWidget *widget UNUSED,
  * We can't do much more here than to trying to preserve what had been done,
  * since the window is already inevitably going away.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+mainwin_destroy_cb(GObject *object UNUSED, gpointer data UNUSED)
+#else
     static void
 mainwin_destroy_cb(GtkObject *object UNUSED, gpointer data UNUSED)
+#endif
 {
     /* Don't write messages to the GUI anymore */
     full_screen = FALSE;
@@ -3799,8 +4460,13 @@ gui_mch_open(void)
      * changed them). */
     highlight_gui_started();	/* re-init colors and fonts */
 
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.mainwin), "destroy",
+                     G_CALLBACK(mainwin_destroy_cb), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.mainwin), "destroy",
 		       GTK_SIGNAL_FUNC(mainwin_destroy_cb), NULL);
+#endif
 
 #ifdef FEAT_HANGULIN
     hangul_keyboard_set();
@@ -3816,15 +4482,25 @@ gui_mch_open(void)
      * manager upon us and should not interfere with what VIM is requesting
      * upon startup.
      */
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.formwin), "configure-event",
+                     G_CALLBACK(form_configure_event), NULL);
+#else
     gtk_signal_connect(GTK_OBJECT(gui.formwin), "configure_event",
 		       GTK_SIGNAL_FUNC(form_configure_event), NULL);
+#endif
 
 #ifdef FEAT_DND
     /* Set up for receiving DND items. */
     gui_gtk_set_dnd_targets();
 
+# if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received",
+                     G_CALLBACK(drag_data_received_cb), NULL);
+# else
     gtk_signal_connect(GTK_OBJECT(gui.drawarea), "drag_data_received",
 		       GTK_SIGNAL_FUNC(drag_data_received_cb), NULL);
+# endif
 #endif
 
 	/* With GTK+ 2, we need to iconify the window before calling show()
@@ -3901,7 +4577,8 @@ gui_mch_set_winpos(int x, int y)
     gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
 }
 
-#if 0
+#if !GTK_CHECK_VERSION(3,0,0)
+# if 0
 static int resize_idle_installed = FALSE;
 /*
  * Idle handler to force resize.  Used by gui_mch_set_shellsize() to ensure
@@ -3937,7 +4614,8 @@ force_shell_resize_idle(gpointer data)
     resize_idle_installed = FALSE;
     return FALSE; /* don't call me again */
 }
-#endif
+# endif
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /*
  * Return TRUE if the main window is maximized.
@@ -3945,9 +4623,15 @@ force_shell_resize_idle(gpointer data)
     int
 gui_mch_maximized(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    return (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL
+	    && (gdk_window_get_state(gtk_widget_get_window(gui.mainwin))
+					       & GDK_WINDOW_STATE_MAXIMIZED));
+#else
     return (gui.mainwin != NULL && gui.mainwin->window != NULL
 	    && (gdk_window_get_state(gui.mainwin->window)
 					       & GDK_WINDOW_STATE_MAXIMIZED));
+#endif
 }
 
 /*
@@ -4002,14 +4686,16 @@ gui_mch_set_shellsize(int width, int height,
     else
 	update_window_manager_hints(width, height);
 
-# if 0
+# if !GTK_CHECK_VERSION(3,0,0)
+#  if 0
     if (!resize_idle_installed)
     {
 	g_idle_add_full(GDK_PRIORITY_EVENTS + 10,
 			&force_shell_resize_idle, NULL, NULL);
 	resize_idle_installed = TRUE;
     }
-# endif
+#  endif
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
     /*
      * Wait until all events are processed to prevent a crash because the
      * real size of the drawing area doesn't reflect Vim's internal ideas.
@@ -4084,7 +4770,11 @@ gui_mch_enable_menu(int showit)
 	widget = gui.menubar;
 
     /* Do not disable the menu while starting up, otherwise F10 doesn't work. */
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!showit != !gtk_widget_get_visible(widget) && !gui.starting)
+# else
     if (!showit != !GTK_WIDGET_VISIBLE(widget) && !gui.starting)
+# endif
     {
 	if (showit)
 	    gtk_widget_show(widget);
@@ -4115,7 +4805,11 @@ gui_mch_show_toolbar(int showit)
     if (showit)
 	set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!showit != !gtk_widget_get_visible(widget))
+# else
     if (!showit != !GTK_WIDGET_VISIBLE(widget))
+# endif
     {
 	if (showit)
 	    gtk_widget_show(widget);
@@ -4200,6 +4894,17 @@ gui_mch_adjust_charheight(void)
     return OK;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+/* Callback function used in gui_mch_font_dialog() */
+    static gboolean
+font_filter(const PangoFontFamily *family,
+            const PangoFontFace   *face UNUSED,
+            gpointer               data UNUSED)
+{
+    return pango_font_family_is_monospace((PangoFontFamily *)family);
+}
+#endif
+
 /*
  * Put up a font dialog and return the selected font name in allocated memory.
  * "oldval" is the previous value.  Return NULL when cancelled.
@@ -4217,7 +4922,13 @@ gui_mch_font_dialog(char_u *oldval)
     char_u	*fontname = NULL;
     char_u	*oldname;
 
+#if GTK_CHECK_VERSION(3,2,0)
+    dialog = gtk_font_chooser_dialog_new(NULL, NULL);
+    gtk_font_chooser_set_filter_func(GTK_FONT_CHOOSER(dialog), font_filter,
+                                      NULL, NULL);
+#else
     dialog = gtk_font_selection_dialog_new(NULL);
+#endif
 
     gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
     gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
@@ -4244,15 +4955,25 @@ gui_mch_font_dialog(char_u *oldval)
 	    }
 	}
 
+#if GTK_CHECK_VERSION(3,2,0)
+        gtk_font_chooser_set_font(
+                GTK_FONT_CHOOSER(dialog), (const gchar *)oldname);
+#else
 	gtk_font_selection_dialog_set_font_name(
 		GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname);
+#endif
 
 	if (oldname != oldval)
 	    vim_free(oldname);
     }
     else
+#if GTK_CHECK_VERSION(3,2,0)
+        gtk_font_chooser_set_font(
+		GTK_FONT_CHOOSER(dialog), DEFAULT_FONT);
+#else
 	gtk_font_selection_dialog_set_font_name(
 		GTK_FONT_SELECTION_DIALOG(dialog), DEFAULT_FONT);
+#endif
 
     response = gtk_dialog_run(GTK_DIALOG(dialog));
 
@@ -4260,8 +4981,12 @@ gui_mch_font_dialog(char_u *oldval)
     {
 	char *name;
 
+#if GTK_CHECK_VERSION(3,2,0)
+        name = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
+#else
 	name = gtk_font_selection_dialog_get_font_name(
 			    GTK_FONT_SELECTION_DIALOG(dialog));
+#endif
 	if (name != NULL)
 	{
 	    char_u  *p;
@@ -4589,6 +5314,33 @@ gui_mch_free_font(GuiFont font)
 	pango_font_description_free(font);
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+/* Convert a GdRGBA into a pixel value using drawarea's visual */
+    static guint32
+gui_gtk_get_pixel_from_rgb(const GdkRGBA *rgba)
+{
+    GdkVisual * const visual = gtk_widget_get_visual(gui.drawarea);
+    guint32 r_mask, g_mask, b_mask;
+    gint r_shift, g_shift, b_shift;
+    guint32 r, g, b;
+
+    if (visual == NULL)
+        return 0;
+
+    gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+    gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+    gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+    r = rgba->red * 65535;
+    g = rgba->green * 65535;
+    b = rgba->blue * 65535;
+
+    return ((r << r_shift) & r_mask) |
+           ((g << g_shift) & g_mask) |
+           ((b << b_shift) & b_mask);
+}
+#endif
+
 /*
  * Return the Pixel value (color) for the given color name.  This routine was
  * pretty much taken from example code in the Silicon Graphics OSF/Motif
@@ -4636,17 +5388,29 @@ gui_mch_get_color(char_u *name)
 
     while (name != NULL)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+        GdkRGBA     color;
+#else
 	GdkColor    color;
+#endif
 	int	    parsed;
 	int	    i;
 
+#if GTK_CHECK_VERSION(3,0,0)
+        parsed = gdk_rgba_parse(&color, (const gchar *)name);
+#else
 	parsed = gdk_color_parse((const char *)name, &color);
+#endif
 
 	if (parsed)
 	{
+#if GTK_CHECK_VERSION(3,0,0)
+            return (guicolor_T)gui_gtk_get_pixel_from_rgb(&color);
+#else
 	    gdk_colormap_alloc_color(gtk_widget_get_colormap(gui.drawarea),
 				     &color, FALSE, TRUE);
 	    return (guicolor_T)color.pixel;
+#endif
 	}
 	/* add a few builtin names and try again */
 	for (i = 0; ; ++i)
@@ -4838,12 +5602,36 @@ setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph,
 	glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2;
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+set_cairo_source_rgb_from_pixel(cairo_t *cr, guint32 pixel)
+{
+    GdkRGBA result;
+    gui_gtk_get_rgb_from_pixel(pixel, &result);
+    cairo_set_source_rgb(cr, result.red, result.green, result.blue);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+draw_glyph_string(int row, int col, int num_cells, int flags,
+		  PangoFont *font, PangoGlyphString *glyphs,
+                  cairo_t *cr)
+#else
     static void
 draw_glyph_string(int row, int col, int num_cells, int flags,
 		  PangoFont *font, PangoGlyphString *glyphs)
+#endif
 {
     if (!(flags & DRAW_TRANSP))
     {
+#if GTK_CHECK_VERSION(3,0,0)
+        set_cairo_source_rgb_from_pixel(cr, gui.bgcolor->pixel);
+        cairo_rectangle(cr,
+                        FILL_X(col), FILL_Y(row),
+                        num_cells * gui.char_width, gui.char_height);
+        cairo_fill(cr);
+#else
 	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
 
 	gdk_draw_rectangle(gui.drawarea->window,
@@ -4853,8 +5641,14 @@ draw_glyph_string(int row, int col, int num_cells, int flags,
 			   FILL_Y(row),
 			   num_cells * gui.char_width,
 			   gui.char_height);
+#endif
     }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+    cairo_move_to(cr, TEXT_X(col), TEXT_Y(row));
+    pango_cairo_show_glyph_string(cr, font, glyphs);
+#else
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
 
     gdk_draw_glyphs(gui.drawarea->window,
@@ -4863,22 +5657,36 @@ draw_glyph_string(int row, int col, int num_cells, int flags,
 		    TEXT_X(col),
 		    TEXT_Y(row),
 		    glyphs);
+#endif
 
     /* redraw the contents with an offset of 1 to emulate bold */
     if ((flags & DRAW_BOLD) && !gui.font_can_bold)
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+        cairo_move_to(cr, TEXT_X(col) + 1, TEXT_Y(row));
+        pango_cairo_show_glyph_string(cr, font, glyphs);
+    }
+#else
 	gdk_draw_glyphs(gui.drawarea->window,
 			gui.text_gc,
 			font,
 			TEXT_X(col) + 1,
 			TEXT_Y(row),
 			glyphs);
+#endif
 }
 
 /*
  * Draw underline and undercurl at the bottom of the character cell.
  */
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+draw_under(int flags, int row, int col, int cells, cairo_t *cr)
+#else
     static void
 draw_under(int flags, int row, int col, int cells)
+#endif
 {
     int			i;
     int			offset;
@@ -4888,6 +5696,17 @@ draw_under(int flags, int row, int col, int cells)
     /* Undercurl: draw curl at the bottom of the character cell. */
     if (flags & DRAW_UNDERC)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+        cairo_set_line_width(cr, 1.0);
+        cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+        set_cairo_source_rgb_from_pixel(cr, gui.spcolor->pixel);
+	for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
+	{
+	    offset = val[i % 8];
+	    cairo_line_to(cr, i, y - offset + 0.5);
+	}
+        cairo_stroke(cr);
+#else
 	gdk_gc_set_foreground(gui.text_gc, gui.spcolor);
 	for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
 	{
@@ -4895,6 +5714,7 @@ draw_under(int flags, int row, int col, int cells)
 	    gdk_draw_point(gui.drawarea->window, gui.text_gc, i, y - offset);
 	}
 	gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
+#endif
     }
 
     /* Underline: draw a line at the bottom of the character cell. */
@@ -4904,9 +5724,20 @@ draw_under(int flags, int row, int col, int cells)
 	 * Otherwise put the line just below the character. */
 	if (p_linespace > 1)
 	    y -= p_linespace - 1;
+#if GTK_CHECK_VERSION(3,0,0)
+        {
+            cairo_set_line_width(cr, 1.0);
+            cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+            set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+            cairo_move_to(cr, FILL_X(col), y + 0.5);
+            cairo_line_to(cr, FILL_X(col + cells), y + 0.5);
+            cairo_stroke(cr);
+        }
+#else
 	gdk_draw_line(gui.drawarea->window, gui.text_gc,
 		      FILL_X(col), y,
 		      FILL_X(col + cells) - 1, y);
+#endif
     }
 }
 
@@ -4922,8 +5753,15 @@ gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
     int			convlen;
     char_u		*sp, *bp;
     int			plen;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t             *cr;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.text_context == NULL || gui.drawarea->window == NULL)
+#endif
 	return len;
 
     if (output_conv.vc_type != CONV_NONE)
@@ -4976,8 +5814,14 @@ gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
     area.width	= gui.num_cols * gui.char_width;
     area.height = gui.char_height;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+    cairo_rectangle(cr, area.x, area.y, area.width, area.height);
+    cairo_clip(cr);
+#else
     gdk_gc_set_clip_origin(gui.text_gc, 0, 0);
     gdk_gc_set_clip_rectangle(gui.text_gc, &area);
+#endif
 
     glyphs = pango_glyph_string_new();
 
@@ -5004,7 +5848,11 @@ gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
 	    glyphs->log_clusters[i] = i;
 	}
 
+#if GTK_CHECK_VERSION(3,0,0)
+	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs, cr);
+#else
 	draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs);
+#endif
 
 	column_offset = len;
     }
@@ -5162,8 +6010,14 @@ not_ascii:
 	    }
 
 	    /*** Aaaaand action! ***/
+#if GTK_CHECK_VERSION(3,0,0)
+	    draw_glyph_string(row, col + column_offset, item_cells,
+			      flags, item->analysis.font, glyphs,
+                              cr);
+#else
 	    draw_glyph_string(row, col + column_offset, item_cells,
 			      flags, item->analysis.font, glyphs);
+#endif
 
 	    pango_item_free(item);
 
@@ -5175,12 +6029,23 @@ not_ascii:
 
 skipitall:
     /* Draw underline and undercurl. */
+#if GTK_CHECK_VERSION(3,0,0)
+    draw_under(flags, row, col, column_offset, cr);
+#else
     draw_under(flags, row, col, column_offset);
+#endif
 
     pango_glyph_string_free(glyphs);
     vim_free(conv_buf);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_destroy(cr);
+    if (!gui.by_signal)
+        gdk_window_invalidate_rect(gtk_widget_get_window(gui.drawarea),
+		&area, FALSE);
+#else
     gdk_gc_set_clip_rectangle(gui.text_gc, NULL);
+#endif
 
     return column_offset;
 }
@@ -5207,10 +6072,19 @@ gui_mch_haskey(char_u *name)
     int
 gui_get_x11_windis(Window *win, Display **dis)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+#else
     if (gui.mainwin != NULL && gui.mainwin->window != NULL)
+#endif
     {
+#if GTK_CHECK_VERSION(3,0,0)
+	*dis = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
+	*win = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin));
+#else
 	*dis = GDK_WINDOW_XDISPLAY(gui.mainwin->window);
 	*win = GDK_WINDOW_XWINDOW(gui.mainwin->window);
+#endif
 	return OK;
     }
 
@@ -5226,8 +6100,13 @@ gui_get_x11_windis(Window *win, Display **dis)
     Display *
 gui_mch_get_display(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
+	return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
+#else
     if (gui.mainwin != NULL && gui.mainwin->window != NULL)
 	return GDK_WINDOW_XDISPLAY(gui.mainwin->window);
+#endif
     else
 	return NULL;
 }
@@ -5239,7 +6118,11 @@ gui_mch_beep(void)
 #ifdef HAVE_GTK_MULTIHEAD
     GdkDisplay *display;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
+# else
     if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
+# endif
 	display = gtk_widget_get_display(gui.mainwin);
     else
 	display = gdk_display_get_default();
@@ -5254,6 +6137,10 @@ gui_mch_beep(void)
     void
 gui_mch_flash(int msec)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    /* TODO Replace GdkGC with Cairo */
+    (void)msec;
+#else
     GdkGCValues	values;
     GdkGC	*invert_gc;
 
@@ -5293,6 +6180,7 @@ gui_mch_flash(int msec)
 		       FILL_Y((int)Rows) + gui.border_offset);
 
     gdk_gc_destroy(invert_gc);
+#endif
 }
 
 /*
@@ -5301,6 +6189,10 @@ gui_mch_flash(int msec)
     void
 gui_mch_invert_rectangle(int r, int c, int nr, int nc)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    /* TODO Replace GdkGC with Cairo */
+    (void)r; (void)c; (void)nr; (void)nc;
+#else
     GdkGCValues values;
     GdkGC *invert_gc;
 
@@ -5322,6 +6214,7 @@ gui_mch_invert_rectangle(int r, int c, int nr, int nc)
 		       FILL_X(c), FILL_Y(r),
 		       (nc) * gui.char_width, (nr) * gui.char_height);
     gdk_gc_destroy(invert_gc);
+#endif
 }
 
 /*
@@ -5351,19 +6244,44 @@ gui_mch_set_foreground(void)
 gui_mch_draw_hollow_cursor(guicolor_T color)
 {
     int		i = 1;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t    *cr;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.drawarea->window == NULL)
+#endif
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+#endif
+
     gui_mch_set_fg_color(color);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+#else
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
+#endif
     if (mb_lefthalve(gui.row, gui.col))
 	i = 2;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_set_line_width(cr, 1.0);
+    cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+    cairo_rectangle(cr,
+	    FILL_X(gui.col) + 0.5, FILL_Y(gui.row) + 0.5,
+	    i * gui.char_width - 1, gui.char_height - 1);
+    cairo_stroke(cr);
+    cairo_destroy(cr);
+#else
     gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
 	    FALSE,
 	    FILL_X(gui.col), FILL_Y(gui.row),
 	    i * gui.char_width - 1, gui.char_height - 1);
+#endif
 }
 
 /*
@@ -5373,21 +6291,43 @@ gui_mch_draw_hollow_cursor(guicolor_T color)
     void
 gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+#else
     if (gui.drawarea->window == NULL)
+#endif
 	return;
 
     gui_mch_set_fg_color(color);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+        cairo_t *cr;
+
+        cr = cairo_create(gui.surface);
+        set_cairo_source_rgb_from_pixel(cr, gui.fgcolor->pixel);
+        cairo_rectangle(cr,
+# ifdef FEAT_RIGHTLEFT
+	    /* vertical line should be on the right of current point */
+	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
+# endif
+	    FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h,
+	    w, h);
+        cairo_fill(cr);
+        cairo_destroy(cr);
+    }
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
     gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
 	    TRUE,
-#ifdef FEAT_RIGHTLEFT
+# ifdef FEAT_RIGHTLEFT
 	    /* vertical line should be on the right of current point */
 	    CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
-#endif
+# endif
 	    FILL_X(gui.col),
 	    FILL_Y(gui.row) + gui.char_height - h,
 	    w, h);
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 
@@ -5404,7 +6344,11 @@ gui_mch_update(void)
 	g_main_context_iteration(NULL, TRUE);
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 input_timer_cb(gpointer data)
 {
     int *timed_out = (int *) data;
@@ -5473,7 +6417,11 @@ gui_mch_wait_for_chars(long wtime)
      * time */
 
     if (wtime > 0)
+#if GTK_CHECK_VERSION(3,0,0)
+	timer = g_timeout_add((guint)wtime, input_timer_cb, &timed_out);
+#else
 	timer = gtk_timeout_add((guint32)wtime, input_timer_cb, &timed_out);
+#endif
     else
 	timer = 0;
 
@@ -5507,7 +6455,11 @@ gui_mch_wait_for_chars(long wtime)
 	if (input_available())
 	{
 	    if (timer != 0 && !timed_out)
+#if GTK_CHECK_VERSION(3,0,0)
+		g_source_remove(timer);
+#else
 		gtk_timeout_remove(timer);
+#endif
 	    return OK;
 	}
     } while (wtime < 0 || !timed_out);
@@ -5531,15 +6483,24 @@ gui_mch_wait_for_chars(long wtime)
 gui_mch_flush(void)
 {
 #ifdef HAVE_GTK_MULTIHEAD
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
+# else
     if (gui.mainwin != NULL && GTK_WIDGET_REALIZED(gui.mainwin))
+# endif
 	gdk_display_sync(gtk_widget_get_display(gui.mainwin));
 #else
     gdk_flush(); /* historical misnomer: calls XSync(), not XFlush() */
 #endif
     /* This happens to actually do what gui_mch_flush() is supposed to do,
      * according to the comment above. */
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gui.drawarea != NULL && gtk_widget_get_window(gui.drawarea) != NULL)
+	gdk_window_process_updates(gtk_widget_get_window(gui.drawarea), FALSE);
+#else
     if (gui.drawarea != NULL && gui.drawarea->window != NULL)
 	gdk_window_process_updates(gui.drawarea->window, FALSE);
+#endif
 }
 
 /*
@@ -5549,13 +6510,42 @@ gui_mch_flush(void)
     void
 gui_mch_clear_block(int row1, int col1, int row2, int col2)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+	return;
+#else
     GdkColor color;
 
     if (gui.drawarea->window == NULL)
 	return;
 
     color.pixel = gui.back_pixel;
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    {
+	/* Add one pixel to the far right column in case a double-stroked
+	 * bold glyph may sit there. */
+        const GdkRectangle rect = {
+            FILL_X(col1), FILL_Y(row1),
+            (col2 - col1 + 1) * gui.char_width + (col2 == Columns - 1),
+            (row2 - row1 + 1) * gui.char_height
+        };
+        GdkWindow * const win = gtk_widget_get_window(gui.drawarea);
+        cairo_t * const cr = cairo_create(gui.surface);
+        cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
+        if (pat != NULL)
+            cairo_set_source(cr, pat);
+        else
+            set_cairo_source_rgb_from_pixel(cr, gui.back_pixel);
+        gdk_cairo_rectangle(cr, &rect);
+        cairo_fill(cr);
+        cairo_destroy(cr);
+
+        if (!gui.by_signal)
+            gdk_window_invalidate_rect(win, &rect, FALSE);
+    }
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     gdk_gc_set_foreground(gui.text_gc, &color);
 
     /* Clear one extra pixel at the far right, for when bold characters have
@@ -5565,15 +6555,44 @@ gui_mch_clear_block(int row1, int col1, int row2, int col2)
 		       (col2 - col1 + 1) * gui.char_width
 						      + (col2 == Columns - 1),
 		       (row2 - row1 + 1) * gui.char_height);
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_window_clear(GdkWindow *win)
+{
+    const GdkRectangle rect = {
+        0, 0, gdk_window_get_width(win), gdk_window_get_height(win)
+    };
+    cairo_t * const cr = cairo_create(gui.surface);
+    cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
+    if (pat != NULL)
+        cairo_set_source(cr, pat);
+    else
+        set_cairo_source_rgb_from_pixel(cr, gui.back_pixel);
+    gdk_cairo_rectangle(cr, &rect);
+    cairo_fill(cr);
+    cairo_destroy(cr);
+
+    if (!gui.by_signal)
+        gdk_window_invalidate_rect(win, &rect, FALSE);
+}
+#endif
+
     void
 gui_mch_clear_all(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) != NULL)
+        gui_gtk_window_clear(gtk_widget_get_window(gui.drawarea));
+#else
     if (gui.drawarea->window != NULL)
 	gdk_window_clear(gui.drawarea->window);
+#endif
 }
 
+#if !GTK_CHECK_VERSION(3,0,0)
 /*
  * Redraw any text revealed by scrolling up/down.
  */
@@ -5610,6 +6629,27 @@ check_copy_area(void)
 
     gui_can_update_cursor();
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
+
+#if GTK_CHECK_VERSION(3,0,0)
+    static void
+gui_gtk_surface_copy_rect(int dest_x, int dest_y,
+	                  int src_x,  int src_y,
+			  int width,  int height)
+{
+    cairo_t * const cr = cairo_create(gui.surface);
+
+    cairo_rectangle(cr, dest_x, dest_y, width, height);
+    cairo_clip(cr);
+    cairo_push_group(cr);
+    cairo_set_source_surface(cr, gui.surface, dest_x - src_x, dest_y - src_y);
+    cairo_paint(cr);
+    cairo_pop_group_to_source(cr);
+    cairo_paint(cr);
+
+    cairo_destroy(cr);
+}
+#endif
 
 /*
  * Delete the given number of lines from the given row, scrolling up any
@@ -5618,6 +6658,23 @@ check_copy_area(void)
     void
 gui_mch_delete_lines(int row, int num_lines)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
+    const int nrows = gui.scroll_region_bot - row + 1;
+    const int src_nrows = nrows - num_lines;
+
+    gui_gtk_surface_copy_rect(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
+	    gui.char_width * ncols + 1,     gui.char_height * src_nrows);
+    gui_clear_block(
+	    gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left,
+	    gui.scroll_region_bot,                 gui.scroll_region_right);
+    if (!gui.by_signal)
+	gtk_widget_queue_draw_area(gui.drawarea,
+		FILL_X(gui.scroll_region_left), FILL_Y(row),
+		gui.char_width * ncols + 1,     gui.char_height * nrows);
+#else
     if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
 	return;			/* Can't see the window */
 
@@ -5638,6 +6695,7 @@ gui_mch_delete_lines(int row, int num_lines)
 						       gui.scroll_region_left,
 		    gui.scroll_region_bot, gui.scroll_region_right);
     check_copy_area();
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 /*
@@ -5647,6 +6705,23 @@ gui_mch_delete_lines(int row, int num_lines)
     void
 gui_mch_insert_lines(int row, int num_lines)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
+    const int nrows = gui.scroll_region_bot - row + 1;
+    const int src_nrows = nrows - num_lines;
+
+    gui_gtk_surface_copy_rect(
+	    FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
+	    FILL_X(gui.scroll_region_left), FILL_Y(row),
+	    gui.char_width * ncols + 1,     gui.char_height * src_nrows);
+    gui_mch_clear_block(
+	    row,                 gui.scroll_region_left,
+	    row + num_lines - 1, gui.scroll_region_right);
+    if (!gui.by_signal)
+	gtk_widget_queue_draw_area(gui.drawarea,
+		FILL_X(gui.scroll_region_left), FILL_Y(row),
+		gui.char_width * ncols + 1,     gui.char_height * nrows);
+#else
     if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
 	return;			/* Can't see the window */
 
@@ -5665,6 +6740,7 @@ gui_mch_insert_lines(int row, int num_lines)
     gui_clear_block(row, gui.scroll_region_left,
 				row + num_lines - 1, gui.scroll_region_right);
     check_copy_area();
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 }
 
 /*
@@ -5700,7 +6776,11 @@ clip_mch_request_selection(VimClipboard *cbd)
     }
 
     /* Final fallback position - use the X CUT_BUFFER0 store */
+#if GTK_CHECK_VERSION(3,0,0)
+    yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)), cbd);
+#else
     yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gui.mainwin->window), cbd);
+#endif
 }
 
 /*
@@ -5758,7 +6838,11 @@ gui_mch_menu_grey(vimmenu_T *menu, int grey)
 
     gui_mch_menu_hidden(menu, FALSE);
     /* Be clever about bitfields versus true booleans here! */
+# if GTK_CHECK_VERSION(3,0,0)
+    if (!gtk_widget_get_sensitive(menu->id) == !grey)
+# else
     if (!GTK_WIDGET_SENSITIVE(menu->id) == !grey)
+# endif
     {
 	gtk_widget_set_sensitive(menu->id, !grey);
 	gui_mch_update();
@@ -5776,7 +6860,11 @@ gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
 
     if (hidden)
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_visible(menu->id))
+# else
 	if (GTK_WIDGET_VISIBLE(menu->id))
+# endif
 	{
 	    gtk_widget_hide(menu->id);
 	    gui_mch_update();
@@ -5784,7 +6872,11 @@ gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
     }
     else
     {
+# if GTK_CHECK_VERSION(3,0,0)
+	if (!gtk_widget_get_visible(menu->id))
+# else
 	if (!GTK_WIDGET_VISIBLE(menu->id))
+# endif
 	{
 	    gtk_widget_show(menu->id);
 	    gui_mch_update();
@@ -5812,10 +6904,14 @@ gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
     if (sb->id == NULL)
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_visible(sb->id, flag);
+#else
     if (flag)
 	gtk_widget_show(sb->id);
     else
 	gtk_widget_hide(sb->id);
+#endif
 
     update_window_manager_hints(0, 0);
 }
@@ -5828,8 +6924,18 @@ gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
 gui_mch_get_rgb(guicolor_T pixel)
 {
     GdkColor color;
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkRGBA rgba;
+
+    gui_gtk_get_rgb_from_pixel(pixel, &rgba);
+
+    color.red = rgba.red * 65535;
+    color.green = rgba.green * 65535;
+    color.blue = rgba.blue * 65535;
+#else
     gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
 			     (unsigned long)pixel, &color);
+#endif
 
     return (((unsigned)color.red   & 0xff00) << 8)
 	 |  ((unsigned)color.green & 0xff00)
@@ -5842,7 +6948,11 @@ gui_mch_get_rgb(guicolor_T pixel)
     void
 gui_mch_getmouse(int *x, int *y)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gui_gtk_get_pointer(gui.drawarea, x, y, NULL);
+#else
     gdk_window_get_pointer(gui.drawarea->window, x, y, NULL);
+#endif
 }
 
     void
@@ -5851,9 +6961,15 @@ gui_mch_setmouse(int x, int y)
     /* Sorry for the Xlib call, but we can't avoid it, since there is no
      * internal GDK mechanism present to accomplish this.  (and for good
      * reason...) */
+#if GTK_CHECK_VERSION(3,0,0)
+    XWarpPointer(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.drawarea)),
+		 (Window)0, GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)),
+		 0, 0, 0U, 0U, x, y);
+#else
     XWarpPointer(GDK_WINDOW_XDISPLAY(gui.drawarea->window),
 		 (Window)0, GDK_WINDOW_XWINDOW(gui.drawarea->window),
 		 0, 0, 0U, 0U, x, y);
+#endif
 }
 
 
@@ -5874,10 +6990,18 @@ gui_mch_mousehide(int hide)
     if (gui.pointer_hidden != hide)
     {
 	gui.pointer_hidden = hide;
+#if GTK_CHECK_VERSION(3,0,0)
+	if (gtk_widget_get_window(gui.drawarea) && gui.blank_pointer != NULL)
+#else
 	if (gui.drawarea->window && gui.blank_pointer != NULL)
+#endif
 	{
 	    if (hide)
+#if GTK_CHECK_VERSION(3,0,0)
+		gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), gui.blank_pointer);
+#else
 		gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+#endif
 	    else
 #ifdef FEAT_MOUSESHAPE
 		mch_set_mouse_shape(last_shape);
@@ -5919,11 +7043,19 @@ mch_set_mouse_shape(int shape)
     int		   id;
     GdkCursor	   *c;
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (gtk_widget_get_window(gui.drawarea) == NULL)
+# else
     if (gui.drawarea->window == NULL)
+# endif
 	return;
 
     if (shape == MSHAPE_HIDE || gui.pointer_hidden)
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), gui.blank_pointer);
+# else
 	gdk_window_set_cursor(gui.drawarea->window, gui.blank_pointer);
+# endif
     else
     {
 	if (shape >= MSHAPE_NUMBERED)
@@ -5944,8 +7076,16 @@ mch_set_mouse_shape(int shape)
 # else
 	c = gdk_cursor_new((GdkCursorType)id);
 # endif
+# if GTK_CHECK_VERSION(3,0,0)
+	gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), c);
+# else
 	gdk_window_set_cursor(gui.drawarea->window, c);
+# endif
+# if GTK_CHECK_VERSION(3,0,0)
+        g_object_unref(G_OBJECT(c));
+# else
 	gdk_cursor_destroy(c); /* Unref, actually.  Bloody GTK+ 1. */
+# endif
     }
     if (shape != MSHAPE_HIDE)
 	last_shape = shape;
@@ -5972,7 +7112,12 @@ gui_mch_drawsign(int row, int col, int typenr)
 
     sign = (GdkPixbuf *)sign_get_image(typenr);
 
+# if GTK_CHECK_VERSION(3,0,0)
+    if (sign != NULL && gui.drawarea != NULL &&
+        gtk_widget_get_window(gui.drawarea) != NULL)
+# else
     if (sign != NULL && gui.drawarea != NULL && gui.drawarea->window != NULL)
+# endif
     {
 	int width;
 	int height;
@@ -6036,6 +7181,49 @@ gui_mch_drawsign(int row, int col, int typenr)
 	xoffset = (width  - SIGN_WIDTH)  / 2;
 	yoffset = (height - SIGN_HEIGHT) / 2;
 
+# if GTK_CHECK_VERSION(3,0,0)
+        {
+            cairo_t         *cr;
+            cairo_surface_t *bg_surf;
+            cairo_t         *bg_cr;
+            cairo_surface_t *sign_surf;
+            cairo_t         *sign_cr;
+
+            cr = cairo_create(gui.surface);
+
+            bg_surf = cairo_surface_create_similar(cairo_get_target(cr),
+                                                   CAIRO_CONTENT_COLOR,
+                                                   SIGN_WIDTH, SIGN_HEIGHT);
+            bg_cr = cairo_create(bg_surf);
+            set_cairo_source_rgb_from_pixel(bg_cr, gui.bgcolor->pixel);
+            cairo_paint(bg_cr);
+
+            sign_surf = cairo_surface_create_similar(cairo_get_target(cr),
+                                                     CAIRO_CONTENT_COLOR,
+                                                     SIGN_WIDTH, SIGN_HEIGHT);
+            sign_cr = cairo_create(sign_surf);
+            gdk_cairo_set_source_pixbuf(sign_cr, sign, -xoffset, -yoffset);
+            cairo_paint(sign_cr);
+
+            cairo_set_operator(sign_cr, CAIRO_OPERATOR_DEST_OVER);
+            cairo_set_source_surface(sign_cr, bg_surf, 0, 0);
+            cairo_paint(sign_cr);
+
+            cairo_set_source_surface(cr, sign_surf, FILL_X(col), FILL_Y(row));
+            cairo_paint(cr);
+
+            cairo_destroy(sign_cr);
+            cairo_surface_destroy(sign_surf);
+            cairo_destroy(bg_cr);
+            cairo_surface_destroy(bg_surf);
+            cairo_destroy(cr);
+
+            if (!gui.by_signal)
+                gtk_widget_queue_draw_area(gui.drawarea,
+                        FILL_X(col), FILL_Y(col), width, height);
+
+        }
+# else /* !GTK_CHECK_VERSION(3,0,0) */
 	gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
 
 	gdk_draw_rectangle(gui.drawarea->window,
@@ -6058,6 +7246,7 @@ gui_mch_drawsign(int row, int col, int typenr)
 					    127,
 					    GDK_RGB_DITHER_NORMAL,
 					    0, 0);
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
 	if (need_scale)
 	    g_object_unref(sign);
     }
diff --git a/src/if_mzsch.c b/src/if_mzsch.c
index f291da7..935f3f5 100644
--- a/src/if_mzsch.c
+++ b/src/if_mzsch.c
@@ -892,7 +892,11 @@ static void remove_timer(void);
     static void CALLBACK
 timer_proc(HWND hwnd UNUSED, UINT uMsg UNUSED, UINT idEvent UNUSED, DWORD dwTime UNUSED)
 # elif defined(FEAT_GUI_GTK)
+#  if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+#else
     static gint
+#endif
 timer_proc(gpointer data UNUSED)
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     static void
@@ -919,7 +923,11 @@ setup_timer(void)
 # if defined(FEAT_GUI_W32)
     timer_id = SetTimer(NULL, 0, p_mzq, timer_proc);
 # elif defined(FEAT_GUI_GTK)
+#  if GTK_CHECK_VERSION(3,0,0)
+    timer_id = g_timeout_add((guint)p_mzq, (GSourceFunc)timer_proc, NULL);
+#  else
     timer_id = gtk_timeout_add((guint32)p_mzq, (GtkFunction)timer_proc, NULL);
+#  endif
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     timer_id = XtAppAddTimeOut(app_context, p_mzq, timer_proc, NULL);
 # elif defined(FEAT_GUI_MAC)
@@ -935,7 +943,11 @@ remove_timer(void)
 # if defined(FEAT_GUI_W32)
     KillTimer(NULL, timer_id);
 # elif defined(FEAT_GUI_GTK)
+   #if GTK_CHECK_VERSION(3,0,0)
+    g_source_remove(timer_id);
+   #else
     gtk_timeout_remove(timer_id);
+   #endif
 # elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
     XtRemoveTimeOut(timer_id);
 # elif defined(FEAT_GUI_MAC)
diff --git a/src/mbyte.c b/src/mbyte.c
index 8f7bbb2..1ee8639 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -109,7 +109,11 @@
 #endif
 
 #if defined(FEAT_GUI_GTK) && defined(FEAT_XIM)
-# include <gdk/gdkkeysyms.h>
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # ifdef WIN3264
 #  include <gdk/gdkwin32.h>
 # else
@@ -4941,7 +4945,11 @@ xim_init(void)
 #endif
 
     g_return_if_fail(gui.drawarea != NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL);
+#else
     g_return_if_fail(gui.drawarea->window != NULL);
+#endif
 
     xic = gtk_im_multicontext_new();
     g_object_ref(xic);
@@ -4955,7 +4963,11 @@ xim_init(void)
     g_signal_connect(G_OBJECT(xic), "preedit_end",
 		     G_CALLBACK(&im_preedit_end_cb), NULL);
 
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea));
+#else
     gtk_im_context_set_client_window(xic, gui.drawarea->window);
+#endif
 }
 
     void
@@ -5054,12 +5066,20 @@ im_synthesize_keypress(unsigned int keyval, unsigned int state)
 
 #  ifdef HAVE_GTK_MULTIHEAD
     event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS);
+#   if GTK_CHECK_VERSION(3,0,0)
+    g_object_ref(gtk_widget_get_window(gui.drawarea)); /* unreffed by gdk_event_free() */
+#   else
     g_object_ref(gui.drawarea->window); /* unreffed by gdk_event_free() */
+#   endif
 #  else
     event = (GdkEventKey *)g_malloc0((gulong)sizeof(GdkEvent));
     event->type = GDK_KEY_PRESS;
 #  endif
+#  if GTK_CHECK_VERSION(3,0,0)
+    event->window = gtk_widget_get_window(gui.drawarea);
+#  else
     event->window = gui.drawarea->window;
+#  endif
     event->send_event = TRUE;
     event->time = GDK_CURRENT_TIME;
     event->state  = state;
diff --git a/src/netbeans.c b/src/netbeans.c
index 759ffa6..f054cf0 100644
--- a/src/netbeans.c
+++ b/src/netbeans.c
@@ -426,7 +426,6 @@ netbeans_parse_messages(void)
 	}
     }
 }
-
 /*
  * Handle one NUL terminated command.
  *
@@ -3055,17 +3054,57 @@ netbeans_draw_multisign_indicator(int row)
     int i;
     int y;
     int x;
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow   *window = gtk_widget_get_window(gui.drawarea);
+    cairo_t     *cr = NULL;
+#else
     GdkDrawable *drawable = gui.drawarea->window;
+#endif
 
     if (!NETBEANS_OPEN)
 	return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = gdk_cairo_create(window);
+    {
+        GdkVisual *visual = NULL;
+        guint32 r_mask, g_mask, b_mask;
+        gint r_shift, g_shift, b_shift;
+
+        visual = gdk_window_get_visual(window);
+        if (visual != NULL)
+        {
+            gdk_visual_get_red_pixel_details(visual, &r_mask, &r_shift, NULL);
+            gdk_visual_get_green_pixel_details(visual, &g_mask, &g_shift, NULL);
+            gdk_visual_get_blue_pixel_details(visual, &b_mask, &b_shift, NULL);
+
+            cairo_set_source_rgb(cr,
+                                 ((gui.fgcolor->red & r_mask) >> r_shift) / 255.0,
+                                 ((gui.fgcolor->green & g_mask) >> g_shift) / 255.0,
+                                 ((gui.fgcolor->blue & b_mask) >> b_shift) / 255.0);
+        }
+    }
+#endif
+
     x = 0;
     y = row * gui.char_height + 2;
 
     for (i = 0; i < gui.char_height - 3; i++)
+#if GTK_CHECK_VERSION(3,0,0)
+        cairo_rectangle(cr, x+2, y++, 1, 1);
+#else
 	gdk_draw_point(drawable, gui.text_gc, x+2, y++);
+#endif
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_rectangle(cr, x+0, y, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+    cairo_rectangle(cr, x+4, y++, 1, 1);
+    cairo_rectangle(cr, x+1, y, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+    cairo_rectangle(cr, x+3, y++, 1, 1);
+    cairo_rectangle(cr, x+2, y, 1, 1);
+#else
     gdk_draw_point(drawable, gui.text_gc, x+0, y);
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
     gdk_draw_point(drawable, gui.text_gc, x+4, y++);
@@ -3073,6 +3112,11 @@ netbeans_draw_multisign_indicator(int row)
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
     gdk_draw_point(drawable, gui.text_gc, x+3, y++);
     gdk_draw_point(drawable, gui.text_gc, x+2, y);
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_destroy(cr);
+#endif
 }
 #endif /* FEAT_GUI_GTK */
 
diff --git a/src/structs.h b/src/structs.h
index c8334cb..8cb1e89 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2669,7 +2669,9 @@ struct VimMenu
 #ifdef FEAT_GUI_GTK
     GtkWidget	*id;		    /* Manage this to enable item */
     GtkWidget	*submenu_id;	    /* If this is submenu, add children here */
+#if defined(GTK_CHECK_VERSION) && !GTK_CHECK_VERSION(3,4,0)
     GtkWidget	*tearoff_handle;
+#endif
     GtkWidget   *label;		    /* Used by "set wak=" code. */
 #endif
 #ifdef FEAT_GUI_MOTIF
diff --git a/src/version.c b/src/version.c
index f1abf12..b9b4eee 100644
--- a/src/version.c
+++ b/src/version.c
@@ -3665,11 +3665,15 @@ list_version(void)
     MSG_PUTS(_("without GUI."));
 #else
 # ifdef FEAT_GUI_GTK
-#  ifdef FEAT_GUI_GNOME
-    MSG_PUTS(_("with GTK2-GNOME GUI."));
+#  ifdef USE_GTK3
+    MSG_PUTS(_("with GTK3 GUI."));
 #  else
-    MSG_PUTS(_("with GTK2 GUI."));
-#  endif
+#   ifdef FEAT_GUI_GNOME
+     MSG_PUTS(_("with GTK2-GNOME GUI."));
+#   else
+     MSG_PUTS(_("with GTK2 GUI."));
+#   endif
+# endif
 # else
 #  ifdef FEAT_GUI_MOTIF
     MSG_PUTS(_("with X11-Motif GUI."));
