]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 7.4.1402 v7.4.1402
authorBram Moolenaar <Bram@vim.org>
Tue, 23 Feb 2016 16:14:37 +0000 (17:14 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 23 Feb 2016 16:14:37 +0000 (17:14 +0100)
Problem:    GTK 3 is not supported.
Solution:   Add GTK 3 support. (Kazunobu Kuriyama)

20 files changed:
runtime/doc/eval.txt
runtime/doc/gui.txt
runtime/doc/gui_x11.txt
src/auto/configure
src/channel.c
src/config.h.in
src/configure.in
src/eval.c
src/gui.h
src/gui_beval.c
src/gui_beval.h
src/gui_gtk.c
src/gui_gtk_f.c
src/gui_gtk_f.h
src/gui_gtk_x11.c
src/if_mzsch.c
src/mbyte.c
src/netbeans.c
src/structs.h
src/version.c

index 022a5da7a54372b1b577310404b419669198c707..abc022d39276d26dc416fb0e8d5b18830d8acd23 100644 (file)
@@ -7315,6 +7315,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.
index d77976330b6a61a613a2ded8dd9ca3373f60b790..520eba910bf8235e572bed31b9cede6c14a6440e 100644 (file)
@@ -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+ 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
index f085e2f2cbedb2e5157147d73ec6d357a64f812e..605b998af44518065bc1683fcb08fc3f332286e2 100644 (file)
@@ -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 snippet of CSS code 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
index 69aec67c65b180c8f61f363221bf10aed36a578e..1b3b0c70fc9745a5134f73f422a126ed12a0d9dc 100755 (executable)
@@ -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; }
@@ -9546,7 +9798,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
index e9068fee5e0b322c166cbc1036b2e49b3cd39295..c59758d85304e84aeb173685fb9ebcf716bf7846 100644 (file)
@@ -361,6 +361,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)
+{
+    channel_read_fd(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,
@@ -368,6 +379,7 @@ messageFromNetbeans(gpointer clientData,
 {
     channel_read_fd((int)(long)clientData);
 }
+# endif
 #endif
 
     static void
@@ -388,12 +400,27 @@ channel_gui_register_one(channel_T *channel, int part)
     /* Tell gdk we are interested in being called when there
      * is input on the editor connection socket. */
     if (channel->ch_part[part].ch_inputHandler == 0)
+#   if GTK_CHECK_VERSION(3,0,0)
+    {
+       GIOChannel *chnnl = g_io_channel_unix_new(
+               (gint)channel->ch_part[part].ch_fd);
+
+       channel->ch_part[part].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_part[part].ch_fd));
+
+       g_io_channel_unref(chnnl);
+    }
+#   else
        channel->ch_part[part].ch_inputHandler = gdk_input_add(
                (gint)channel->ch_part[part].ch_fd,
                (GdkInputCondition)
                             ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
                messageFromNetbeans,
                (gpointer)(long)channel->ch_part[part].ch_fd);
+#   endif
 #  else
 #   ifdef FEAT_GUI_W32
     /* Tell Windows we are interested in receiving message when there
@@ -457,7 +484,11 @@ channel_gui_unregister(channel_T *channel)
 #  ifdef FEAT_GUI_GTK
        if (channel->ch_part[part].ch_inputHandler != 0)
        {
+#   if GTK_CHECK_VERSION(3,0,0)
+           g_source_remove(channel->ch_part[part].ch_inputHandler);
+#   else
            gdk_input_remove(channel->ch_part[part].ch_inputHandler);
+#   endif
            channel->ch_part[part].ch_inputHandler = 0;
        }
 #  else
@@ -606,7 +637,7 @@ channel_open(
            fd_set              wfds;
 #if defined(__APPLE__) && __APPLE__ == 1
 # define PASS_RFDS
-           fd_set          rfds;
+           fd_set          rfds;
 
            FD_ZERO(&rfds);
            FD_SET(sd, &rfds);
index 8e115f5f16d0af1ddfee0c9db21e4200a842c3a9..fd45c7332ba4fcab45e7b7bf831c6b16b5e1c4c9 100644 (file)
 
 /* 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
index 9f19c137b66786aca499ee16dc403849701fb577..930278c7de9fb581153b8737285191f4b1e3d83f 100644 (file)
@@ -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
@@ -2823,7 +2891,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"
index 3b1172ecd8879c991c4adbc0672b7d419bfb64de..1471568184cb3f5e5901da257d5c680796c480b9 100644 (file)
@@ -13671,7 +13671,11 @@ f_has(typval_T *argvars, typval_T *rettv)
 #endif
 #ifdef FEAT_GUI_GTK
        "gui_gtk",
+# ifdef USE_GTK3
+       "gui_gtk3",
+# else
        "gui_gtk2",
+# endif
 #endif
 #ifdef FEAT_GUI_GNOME
        "gui_gnome",
index d026664d375fd918766e09f38696e9f29a707842..f119de5c1294863c146600fa825a22647ace3ff0 100644 (file)
--- a/src/gui.h
+++ b/src/gui.h
@@ -359,7 +359,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 */
@@ -381,7 +383,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 */
index e96e67d3fd8dbbf509c90d1a8221e8bf7504135d..258ba8aeb743b19e9e22e512d976604dc7657464 100644 (file)
@@ -122,7 +122,11 @@ 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>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
 # include <gtk/gtk.h>
 #else
 # include <X11/keysym.h>
@@ -164,8 +168,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 +471,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 +489,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 +505,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 +553,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 +655,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 +698,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 +718,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 +761,7 @@ balloon_expose_event_cb(GtkWidget *widget,
 
     return FALSE; /* continue emission */
 }
+# endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 #else /* !FEAT_GUI_GTK */
 
@@ -957,8 +1044,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) / 255.0 * 65535;
+               color.green = ((pixel & g_mask) >> g_shift) / 255.0 * 65535;
+               color.blue = ((pixel & b_mask) >> b_shift) / 255.0 * 65535;
+           }
+       }
+# else
            gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
                                     (unsigned long)pixel, &color);
+# endif
 
        pdest = buf;
        p = text;
@@ -1059,8 +1175,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 +1199,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 +1225,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 +1256,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 +1272,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);
 
index 859c08193f5d0a2d35e98e3b119fcc0f372dc14a..ead0fb172f180ee5b9eb4075e5f58b3feec304cd 100644 (file)
 #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>
index 99e4d70dff43fe9ea16caa20e1ba51a9e244fff9..6decec0df3c8ad7692be1f1dc6b8db39bbcab735 100644 (file)
  *
  * 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>
+ *
+ * With the help of Marius Gedminas and the word of Bram Moolenaar,
+ * "Let's give this some time to mature."
  */
 
+#include "vim.h"
+
 #ifdef FEAT_GUI_GTK
 # include "gui_gtk_f.h"
 #endif
@@ -37,8 +46,6 @@
 # undef MAX
 #endif
 
-#include "vim.h"
-
 #ifdef FEAT_GUI_GNOME
 /* Gnome redefines _() and N_().  Grrr... */
 # ifdef _
 #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 +115,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",           /* sub. GTK_STOCK_NEW */
+    /* 01 */ "document-open",          /* sub. GTK_STOCK_OPEN */
+    /* 02 */ "document-save",          /* sub. GTK_STOCK_SAVE */
+    /* 03 */ "edit-undo",              /* sub. GTK_STOCK_UNDO */
+    /* 04 */ "edit-redo",              /* sub. GTK_STOCK_REDO */
+    /* 05 */ "edit-cut",               /* sub. GTK_STOCK_CUT */
+    /* 06 */ "edit-copy",              /* sub. GTK_STOCK_COPY */
+    /* 07 */ "edit-paste",             /* sub. GTK_STOCK_PASTE */
+    /* 08 */ "document-print",         /* sub. GTK_STOCK_PRINT */
+    /* 09 */ "help-browser",           /* sub. GTK_STOCK_HELP */
+    /* 10 */ "edit-find",              /* sub. 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",             /* sub. GTK_STOCK_EXECUTE */
+    /* 16 */ "edit-find-replace",      /* sub. GTK_STOCK_FIND_AND_REPLACE */
+    /* 17 */ "window-close",           /* sub. 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",            /* sub. GTK_STOCK_GO_BACK */
+    /* 23 */ "go-next",                        /* sub. 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",            /* sub. GTK_STOCK_CONVERT */
+    /* 26 */ "go-jump",                        /* sub. 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 +214,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 +239,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 +260,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 +292,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 +358,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 +379,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 +399,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 +425,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,35 +446,91 @@ 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 /* defined(USE_GRESOURCE) */
     const char * const path_prefix = "/org/vim/gui/icon";
+#  if GTK_CHECK_VERSION(3,14,0)
+    GdkScreen    *screen = NULL;
+    GtkIconTheme *icon_theme = NULL;
+
+    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];
-        GdkPixbuf *pixbuf;
-
-        vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
-        pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
-        if (pixbuf != NULL)
-        {
-            GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
-            gtk_icon_factory_add(factory, names->icon_name, icon_set);
-            gtk_icon_set_unref(icon_set);
-            g_object_unref(pixbuf);
-        }
+       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);
+       }
     }
-#endif
+#  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++)
+    {
+       char path[MAXPATHL];
+       GdkPixbuf *pixbuf;
+
+       vim_snprintf(path, MAXPATHL, "%s/%s", path_prefix, names->file_name);
+       pixbuf = gdk_pixbuf_new_from_resource(path, NULL);
+       if (pixbuf != NULL)
+       {
+           GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf(pixbuf);
+           gtk_icon_factory_add(factory, names->icon_name, icon_set);
+           gtk_icon_set_unref(icon_set);
+           g_object_unref(pixbuf);
+       }
+    }
+
     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 +601,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 +663,17 @@ 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 +699,17 @@ 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 +724,24 @@ 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 +751,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 +784,12 @@ 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 +797,21 @@ 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 +846,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 +863,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)
@@ -644,10 +908,15 @@ gui_mch_menu_set_tip(vimmenu_T *menu)
        char_u *tooltip;
 
        tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]);
-       if (tooltip == NULL || utf_valid_string(tooltip, NULL))
+       if (tooltip != NULL && utf_valid_string(tooltip, NULL))
+# if GTK_CHECK_VERSION(3,0,0)
            /* 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);
+           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 +945,20 @@ 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 = NULL;
+
+           item = gtk_toolbar_get_nth_item(GTK_TOOLBAR(gui.toolbar),
+                                           get_menu_position(menu));
+           if (item != NULL)
+               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 +992,42 @@ 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 +1055,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 +1103,7 @@ adjustment_value_changed(GtkAdjustment *adjustment, gpointer data)
            }
        }
     }
-
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
     gui_drag_scrollbar(sb, value, dragging);
 }
 
@@ -802,23 +1112,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 +1261,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);
@@ -991,7 +1325,7 @@ gui_mch_browse(int saving UNUSED,
     }
     gtk_widget_destroy(GTK_WIDGET(fc));
 
-#else
+#else /* !USE_FILE_CHOOSER */
 
     if (gui.filedlg == NULL)
     {
@@ -1027,7 +1361,7 @@ gui_mch_browse(int saving UNUSED,
 
     gtk_widget_show(gui.filedlg);
     gtk_main();
-#endif
+#endif /* !USE_FILE_CHOOSER */
     g_log_remove_handler(domain, log_handler);
 
     CONVERT_TO_UTF8_FREE(title);
@@ -1062,8 +1396,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);
@@ -1096,10 +1435,10 @@ gui_mch_browsedir(
     g_free(dirname);
     return p;
 
-# else
+# else /* !defined(GTK_FILE_CHOOSER) */
     /* For GTK 2.2 and earlier: fall back to ordinary file selector. */
     return gui_mch_browse(0, title, NULL, NULL, initdir, NULL);
-# endif
+# endif /* !defined(GTK_FILE_CHOOSER) */
 }
 
 
@@ -1266,6 +1605,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 +1618,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 +1653,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 +1671,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 +1764,32 @@ 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)
+       {
+           GtkWidget * const vbox
+               = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+           gtk_box_pack_start(GTK_BOX(vbox),
+                   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 +1847,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 +1874,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 +1900,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 +1914,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 +1997,13 @@ find_key_press_event(
 }
 
     static GtkWidget *
-create_image_button(const char *stock_id, const char *label)
+#if GTK_CHECK_VERSION(3,10,0)
+create_image_button(const char *stock_id UNUSED,
+                   const char *label)
+#else
+create_image_button(const char *stock_id,
+                   const char *label)
+#endif
 {
     char_u     *text;
     GtkWidget  *box;
@@ -1621,18 +2012,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 +2103,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 +2121,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 +2140,402 @@ 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)
+    {
+       GtkWidget * const dialog_vbox
+           = gtk_dialog_get_content_area(GTK_DIALOG(frdp->dialog));
+       gtk_container_add(GTK_CONTAINER(dialog_vbox), 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", &align_val);
+
+       g_value_set_float(&align_val, 0.5);
+       g_object_set_property(G_OBJECT(tmp), "yalign", &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:")));
+#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", &align_val);
+
+           g_value_set_float(&align_val, 0.5);
+           g_object_set_property(G_OBJECT(tmp), "yalign", &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, 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 +2585,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);
index 0eb50652ca4db545475a145d2002ebef26a5f5f5..838a4ca8f127a8ec8aae26b0cc19d4c82a3a3778 100644 (file)
  * 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,12 @@ gtk_form_put(GtkForm       *form,
     child->window = NULL;
     child->x = x;
     child->y = y;
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_size_request(child->widget, -1, -1);
+#else
     child->widget->requisition.width = 0;
     child->widget->requisition.height = 0;
+#endif
     child->mapped = FALSE;
 
     form->children = g_list_append(form->children, child);
@@ -131,13 +163,24 @@ 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 +236,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 +259,7 @@ gtk_form_get_type(void)
     }
     return form_type;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
     static void
 gtk_form_class_init(GtkFormClass *klass)
@@ -223,14 +270,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;
@@ -239,15 +297,22 @@ gtk_form_class_init(GtkFormClass *klass)
     static void
 gtk_form_init(GtkForm *form)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_set_has_window(GTK_WIDGET(form), TRUE);
+#endif
     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 +332,92 @@ 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.event_mask = GDK_EXPOSURE_MASK;
+#else
     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)
+    {
+       GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
+
+       gtk_style_context_add_class(sctx, "gtk-form");
+       gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
+# if !GTK_CHECK_VERSION(3,18,0)
+       gtk_style_context_set_background(sctx, gtk_widget_get_window(widget));
+       gtk_style_context_set_background(sctx, form->bin_window);
+# endif
+    }
+#else
     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);
+#endif
 
+#if !GTK_CHECK_VERSION(3,0,0)
     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 +425,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 +453,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 +503,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 +528,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,7 +566,36 @@ 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)
@@ -418,17 +603,34 @@ 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 +646,81 @@ 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 void
+gtk_form_render_background(GtkWidget *widget, cairo_t *cr)
+{
+    gtk_render_background(gtk_widget_get_style_context(widget), cr,
+                         0, 0,
+                         gtk_widget_get_allocated_width(widget),
+                         gtk_widget_get_allocated_height(widget));
+}
+
+    static gboolean
+gtk_form_draw(GtkWidget *widget, cairo_t *cr)
+{
+    GList   *tmp_list = NULL;
+    GtkForm *form     = NULL;
+
+    g_return_val_if_fail(GTK_IS_FORM(widget), FALSE);
+
+    gtk_form_render_background(widget, cr);
+
+    form = GTK_FORM(widget);
+    for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next)
+    {
+       GtkFormChild * const formchild = tmp_list->data;
+
+       if (!gtk_widget_get_has_window(formchild->widget) &&
+               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);
+
+           gtk_form_render_background(formchild->widget, cr);
+       }
+    }
+
+    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 +760,7 @@ gtk_form_expose(GtkWidget *widget, GdkEventExpose *event)
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 /* Container method
  */
@@ -522,12 +786,22 @@ gtk_form_remove(GtkContainer *container, GtkWidget *widget)
 
     if (tmp_list)
     {
+#if GTK_CHECK_VERSION(3,0,0)
+       const gboolean was_visible = gtk_widget_get_visible(widget);
+#endif
        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
@@ -536,7 +810,10 @@ gtk_form_remove(GtkContainer *container, GtkWidget *widget)
            gdk_window_destroy(child->window);
        }
        gtk_widget_unparent(widget);
-
+#if GTK_CHECK_VERSION(3,0,0)
+       if (was_visible)
+           gtk_widget_queue_resize(GTK_WIDGET(container));
+#endif
        form->children = g_list_remove_link(form->children, tmp_list);
        g_list_free_1(tmp_list);
        g_free(child);
@@ -577,7 +854,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 +869,75 @@ 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)
+       {
+           GtkStyleContext * const sctx = gtk_widget_get_style_context(widget);
+
+           gtk_style_context_set_state(sctx, GTK_STATE_FLAG_NORMAL);
+# if !GTK_CHECK_VERSION(3,18,0)
+           gtk_style_context_set_background(sctx, child->window);
+# endif
+       }
+#else
        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 +949,14 @@ 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 */
+# if GTK_CHECK_VERSION(3,0,0)
+       gtk_form_set_static_gravity(gtk_widget_get_window(child->widget), TRUE);
+# else
        gtk_form_set_static_gravity(child->widget->window, TRUE);
+# endif
+#endif
 }
 
     static void
@@ -646,9 +974,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 +996,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 +1032,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 +1049,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 +1079,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 +1146,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 +1156,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 +1181,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 +1224,3 @@ gtk_form_child_unmap(GtkWidget *widget UNUSED, gpointer user_data)
     child->mapped = FALSE;
     gdk_window_hide(child->window);
 }
-
index 73b0024f8cfceb07f84cdcb6ae9c5ccf202fb980..fa0f40aa300e61681e8904767025bc1cef2d7733 100644 (file)
@@ -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 ())
+#ifdef 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,13 +44,17 @@ struct _GtkForm
 
     GList *children;
 
+#ifndef USE_GTK3
     guint width;
     guint height;
+#endif
 
     GdkWindow *bin_window;
 
+#ifndef USE_GTK3
     GdkVisibilityState visibility;
     gulong configure_serial;
+#endif
 
     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);
 
index 9f6775fb8c83adc7b5d29709b9c3af7a18eb25c0..440b401ab16b00784e1b9d338235af7030345c2a 100644 (file)
  *
  * (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,14 +79,18 @@ 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>
 # else
 #  include <gdk/gdkx.h>
 # endif
-
 # include <gtk/gtk.h>
 # include "gui_gtk_f.h"
 #endif
@@ -580,6 +588,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 +610,93 @@ 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 is_key_pressed = FALSE;
+
+static gboolean gui_gtk_is_blink_on(void);
+static gboolean gui_gtk_is_no_blink(void);
+static void gui_gtk_window_clear(GdkWindow *win);
+
+    static void
+gui_gtk3_redraw(int x, int y, int width, int height)
+{
+    gui_redraw_block(Y_2_ROW(y), X_2_COL(x),
+           Y_2_ROW(y + height - 1), X_2_COL(x + width - 1),
+           GUI_MON_NOCLEAR);
+}
+
+    static void
+gui_gtk3_update_cursor(cairo_t *cr)
+{
+    if (gui.row == gui.cursor_row)
+    {
+       gui.by_signal = TRUE;
+       gui_update_cursor(TRUE, TRUE);
+       gui.by_signal = FALSE;
+       cairo_paint(cr);
+    }
+}
+
+    static gboolean
+gui_gtk3_should_draw_cursor(void)
+{
+    unsigned int cond = 0;
+    cond |= gui_gtk_is_blink_on();
+    cond |= is_key_pressed;
+    cond |= gui.in_focus == FALSE;
+    cond |= gui_gtk_is_no_blink();
+    return  cond;
+}
+
+    static gboolean
+draw_event(GtkWidget *widget,
+          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 */
+                               /* for GTK+ 3, may induce other draw events. */
+
+    cairo_set_source_surface(cr, gui.surface, 0, 0);
+
+    /* Draw the window without the cursor. */
+    gui.by_signal = TRUE;
+    {
+       cairo_rectangle_list_t *list = NULL;
+
+       gui_gtk_window_clear(gtk_widget_get_window(widget));
+
+       list = cairo_copy_clip_rectangle_list(cr);
+       if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
+       {
+           int i;
+           for (i = 0; i < list->num_rectangles; i++)
+           {
+               const cairo_rectangle_t rect = list->rectangles[i];
+               gui_gtk3_redraw(rect.x, rect.y, rect.width, rect.height);
+           }
+       }
+       cairo_rectangle_list_destroy(list);
+
+       cairo_paint(cr);
+    }
+    gui.by_signal = FALSE;
+
+    /* Add the cursor to the window if necessary.*/
+    if (gui_gtk3_should_draw_cursor())
+       gui_gtk3_update_cursor(cr);
+
+    return FALSE;
+}
+#else /* !GTK_CHECK_VERSION(3,0,0) */
     static gint
 expose_event(GtkWidget *widget UNUSED,
             GdkEventExpose *event,
@@ -631,6 +723,7 @@ expose_event(GtkWidget *widget UNUSED,
 
     return FALSE;
 }
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
 
 #ifdef FEAT_CLIENTSERVER
 /*
@@ -643,7 +736,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,11 +750,16 @@ 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;
 }
-#endif
+#endif /* defined(FEAT_CLIENTSERVER) */
 
 
 /****************************************************************************
@@ -682,6 +784,20 @@ static long_u blink_ontime = 400;
 static long_u blink_offtime = 250;
 static guint blink_timer = 0;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    static gboolean
+gui_gtk_is_blink_on(void)
+{
+    return blink_state == BLINK_ON;
+}
+
+    static gboolean
+gui_gtk_is_no_blink(void)
+{
+    return blink_waittime == 0 || blink_ontime == 0 || blink_offtime == 0;
+}
+#endif
+
     void
 gui_mch_set_blinking(long waittime, long on, long off)
 {
@@ -698,7 +814,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 +826,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 +870,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 +901,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;
@@ -938,6 +1085,11 @@ key_press_event(GtkWidget *widget UNUSED,
     guint      state;
     char_u     *s, *d;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    is_key_pressed = TRUE;
+    gui_mch_stop_blink();
+#endif
+
     gui.event_time = event->time;
     key_sym = event->keyval;
     state = event->state;
@@ -1127,12 +1279,17 @@ key_press_event(GtkWidget *widget UNUSED,
     return TRUE;
 }
 
-#if defined(FEAT_XIM)
+#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
     static gboolean
 key_release_event(GtkWidget *widget UNUSED,
                  GdkEventKey *event,
                  gpointer data UNUSED)
 {
+# if GTK_CHECK_VERSION(3,0,0)
+    is_key_pressed = FALSE;
+    gui_mch_start_blink();
+# endif
+# if defined(FEAT_XIM)
     gui.event_time = event->time;
     /*
      * GTK+ 2 input methods may do fancy stuff on key release events too.
@@ -1140,6 +1297,9 @@ key_release_event(GtkWidget *widget UNUSED,
      * by holding down CTRL-SHIFT and typing hexadecimal digits.
      */
     return xim_queue_key_press_event(event, FALSE);
+# else
+    return TRUE;
+# endif
 }
 #endif
 
@@ -1179,13 +1339,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 +1364,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 +1468,12 @@ 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 +1542,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 +1596,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 +1681,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 +1697,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 +1737,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 +1758,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 +1789,66 @@ 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 GdkDevice *
+gui_gtk_get_pointer_device(GtkWidget *widget)
+{
+    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);
+    return gdk_device_manager_get_client_pointer(mngr);
+}
+
+    static GdkWindow *
+gui_gtk_get_pointer(GtkWidget       *widget,
+                   gint            *x,
+                   gint            *y,
+                   GdkModifierType *state)
+{
+    GdkWindow * const win = gtk_widget_get_window(widget);
+    GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
+    return gdk_window_get_device_position(win, dev , x, y, state);
+}
+
+    static GdkWindow *
+gui_gtk_window_at_position(GtkWidget *widget,
+                          gint      *x,
+                          gint      *y)
+{
+    GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
+    return gdk_device_get_window_at_position(dev, x, y);
+}
+#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 +1893,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 +1928,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 +1948,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 +2003,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 +2058,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 +2177,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 +2210,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 +2258,21 @@ drag_data_received_cb(GtkWidget         *widget,
     GdkModifierType state;
 
     /* Guard against trash */
+# if GTK_CHECK_VERSION(3,0,0)
+    const guchar * const data_data = gtk_selection_data_get_data(data);
+    const gint data_length = gtk_selection_data_get_length(data);
+    const gint data_format = gtk_selection_data_get_format(data);
+
+    if (data_data == NULL
+           || data_length <= 0
+           || data_format != 8
+           || data_data[data_length] != '\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 +2280,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 +2564,7 @@ setup_save_yourself(void)
     Atom    *existing_atoms = NULL;
     int            count = 0;
 
-#ifdef USE_XSMP
+# ifdef USE_XSMP
     if (xsmp_icefd != -1)
     {
        /*
@@ -2264,16 +2575,25 @@ setup_save_yourself(void)
 
        g_io_add_watch(g_io, G_IO_IN | G_IO_ERR | G_IO_HUP,
                                  local_xsmp_handle_requests, (gpointer)g_io);
+       g_io_channel_unref(g_io);
     }
     else
-#endif
+# endif
     {
        /* Fall back to old method */
 
        /* first get the existing value */
+# if GTK_CHECK_VERSION(3,0,0)
+       GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+
+       if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
+                   GDK_WINDOW_XID(mainwin_win),
+                   &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 +2615,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(mainwin_win),
+                           GDK_WINDOW_XID(mainwin_win),
+# 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 +2666,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;
     }
@@ -2376,10 +2706,18 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
 #undef magick
 # undef static
 
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
+#endif
+
     /* 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(mainwin_win));
+#else
        printf("WID: %ld\n", (long)GDK_WINDOW_XWINDOW(gui.mainwin->window));
+#endif
        fflush(stdout);
     }
 
@@ -2416,10 +2754,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(mainwin_win);
+
+       (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win),
+                                serverDelayedStartName);
+# else
        commWindow = GDK_WINDOW_XWINDOW(gui.mainwin->window);
 
        (void)serverRegisterName(GDK_WINDOW_XDISPLAY(gui.mainwin->window),
                                 serverDelayedStartName);
+# endif
     }
     else
     {
@@ -2428,12 +2773,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(mainwin_win),
+                                    GDK_WINDOW_XID(mainwin_win));
+# 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 +2796,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 +2867,22 @@ 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 +2913,54 @@ 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 +2988,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 +3015,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 +3066,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 +3085,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 +3266,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 +3290,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 +3319,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 +3353,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 +3380,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 +3430,20 @@ on_tabline_menu(GtkWidget *widget, GdkEvent *event)
           )
            return TRUE;
 
+# if GTK_CHECK_VERSION(3,0,0)
+       tabwin = gui_gtk_window_at_position(gui.mainwin, &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 +3474,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 +3503,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 +3555,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 +3577,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 +3596,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 +3609,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 +3633,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 +3749,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 +3785,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 +3824,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 +3852,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 +3920,17 @@ 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. */
+    /* N.B.  Since the default value of GtkToolbar::button-relief is
+     * GTK_RELIEF_NONE, there's no need to specify that, probably. */
+# 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 +3972,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,18 +4064,35 @@ 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);
-#if defined(FEAT_XIM)
+#endif
+#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
     /* Also forward key release events for the benefit of GTK+ 2 input
      * modules.  Try CTRL-SHIFT-xdigits to enter a Unicode code point. */
     g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
                                          : G_OBJECT(gui.drawarea),
-                    "key_release_event",
+                    "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 +4100,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 +4113,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 +4132,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 +4148,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 +4167,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 +4225,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 +4278,67 @@ 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;
+}
+
+/* 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);
+}
+
+    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 /* GTK_CHECK_VERSION(3,0,0) */
+
 /*
  * Called when the foreground or background color has been changed.
  * This used to change the graphics contexts directly but we are
@@ -3580,12 +4347,39 @@ gui_mch_forked(void)
     void
 gui_mch_new_colors(void)
 {
+#if GTK_CHECK_VERSION(3,0,0)
+    GdkWindow * const da_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(da_win, pat);
+               cairo_pattern_destroy(pat);
+           }
+           else
+               gdk_window_set_background_rgba(da_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(da_win, &color);
+# else
        gdk_window_set_background(gui.drawarea->window, &color);
+# endif
+#endif /* !GTK_CHECK_VERSION(3,4,0) */
     }
 }
 
@@ -3618,8 +4412,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 +4598,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 +4620,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 +4715,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 +4752,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 +4761,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 +4824,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 +4908,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 +4943,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 +5032,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 +5060,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 +5093,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 +5119,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;
@@ -4636,17 +5499,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 +5713,26 @@ 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
+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 +5742,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 +5758,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 +5797,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 +5815,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 +5825,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 +5854,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 +5915,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 +5949,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 +6111,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 +6130,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 +6173,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 +6201,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 +6219,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 +6238,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 +6281,7 @@ gui_mch_flash(int msec)
                       FILL_Y((int)Rows) + gui.border_offset);
 
     gdk_gc_destroy(invert_gc);
+#endif
 }
 
 /*
@@ -5301,6 +6290,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 +6315,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 +6345,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 +6392,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 +6445,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 +6518,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 +6556,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 +6584,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 +6611,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 +6656,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 +6730,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 +6759,26 @@ 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);
+    gui_gtk3_redraw(
+           FILL_X(gui.scroll_region_left), FILL_Y(row),
+           gui.char_width * ncols + 1,     gui.char_height * nrows);
+    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 +6799,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 +6809,26 @@ 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);
+    gui_gtk3_redraw(
+           FILL_X(gui.scroll_region_left), FILL_Y(row),
+           gui.char_width * ncols + 1,     gui.char_height * nrows);
+    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 +6847,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 +6883,12 @@ 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 +6946,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 +6968,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 +6980,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 +7012,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 +7032,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 +7056,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 +7069,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 +7098,19 @@ 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 +7152,20 @@ 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 +7186,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 +7222,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 +7291,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(gui.surface,
+                   cairo_surface_get_content(gui.surface),
+                   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(gui.surface,
+                   cairo_surface_get_content(gui.surface),
+                   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 +7356,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);
     }
index 0a3c201f37be22f268d9a174f837301ffad240e4..4be3c7201b5dec985f75766e534965f2302d485c 100644 (file)
@@ -852,7 +852,11 @@ static int mz_threads_allow = 0;
 static void CALLBACK timer_proc(HWND, UINT, UINT, DWORD);
 static UINT timer_id = 0;
 #elif defined(FEAT_GUI_GTK)
+# if GTK_CHECK_VERSION(3,0,0)
+static gboolean timer_proc(gpointer);
+# else
 static gint timer_proc(gpointer);
+# endif
 static guint timer_id = 0;
 #elif defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)
 static void timer_proc(XtPointer, XtIntervalId *);
@@ -892,7 +896,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 +927,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 +947,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)
index 16092843b88351e568be0fcdbaa2e9317c3cccb9..b44168ded39ae2088beb335175ae250855dc741d 100644 (file)
 #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,21 @@ 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;
index ca0278eeeb5e5d6722664726c6cac84da31b6c32..44a725e1edc6a530143eea80228d04d223a32f98 100644 (file)
@@ -3055,17 +3055,56 @@ netbeans_draw_multisign_indicator(int row)
     int i;
     int y;
     int x;
+#if GTK_CHECK_VERSION(3,0,0)
+    cairo_t *cr = NULL;
+#else
     GdkDrawable *drawable = gui.drawarea->window;
+#endif
 
     if (!NETBEANS_OPEN)
        return;
 
+#if GTK_CHECK_VERSION(3,0,0)
+    cr = cairo_create(gui.surface);
+    {
+       GdkVisual *visual = NULL;
+       guint32 r_mask, g_mask, b_mask;
+       gint r_shift, g_shift, b_shift;
+
+       visual = gdk_window_get_visual(gtk_widget_get_window(gui.drawarea));
+       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 */
 
index c47eef31ae4bf7748065a16003706a7d0e1b5234..95b33c3a0d0f781bc3a7ecc712f835eaa7fd7790 100644 (file)
@@ -2736,7 +2736,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
index f3fc7c9427dd527c13d569304f8e77ed36bb1bc8..94f7b36e774e284721b88b2f2063912dca0a21fa 100644 (file)
@@ -748,6 +748,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1402,
 /**/
     1401,
 /**/
@@ -3824,11 +3826,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."));