# HG changeset patch # User Christian Brabandt # Date 1456244105 -3600 # Node ID 3456e2ebebd4241a99c7e20779d85c9fb935aa01 # Parent c52abf35df881e2e7cdb8edbd68e33435d95a135 commit https://github.com/vim/vim/commit/9892189d2e7ab94b750f99e6da4cbfc3c8014517 Author: Bram Moolenaar Date: Tue Feb 23 17:14:37 2016 +0100 patch 7.4.1402 Problem: GTK 3 is not supported. Solution: Add GTK 3 support. (Kazunobu Kuriyama) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -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. diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -25,7 +25,7 @@ 1. Starting the GUI *gui-start* *E229 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 to execute it This does require the |+menu| feature enabled at compile time. *tear-off-menus* -GTK+ and Motif support Tear-off menus. These are sort of sticky menus or +GTK+ 2 and Motif support Tear-off menus. These are sort of sticky menus or pop-up menus that are present all the time. If the resizing does not work correctly, this may be caused by using something like "Vim*geometry" in the defaults. Use "Vim.geometry" instead. +As to GTK+ 3, tear-off menus have been deprecated since GTK+ 3.4. +Accordingly, they are disabled if gvim is linked against GTK+ 3.4 or later. + The Win32 GUI version emulates Motif's tear-off menus. Actually, a Motif user will spot the differences easily, but hopefully they're just as useful. You can also use the |:tearoff| command together with |hidden-menus| to create @@ -650,8 +653,8 @@ When no or zero priority is given, 500 i The priority for the PopUp menu is not used. The Help menu will be placed on the far right side of the menu bar on systems -which support this (Motif and GTK+). For GTK+ 2, this is not done anymore -because right-aligning the Help menu is now discouraged UI design. +which support this (Motif and GTK+). For GTK+ 2 and 3, this is not done +anymore because right-aligning the Help menu is now discouraged UI design. You can use a priority higher than 9999, to make it go after the Help menu, but that is non-standard and is discouraged. The highest possible priority is diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt --- a/runtime/doc/gui_x11.txt +++ b/runtime/doc/gui_x11.txt @@ -369,6 +369,16 @@ Write this in the file ~/.gtkrc and it w 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 diff --git a/src/auto/configure b/src/auto/configure --- a/src/auto/configure +++ b/src/auto/configure @@ -821,6 +821,7 @@ with_x enable_gui enable_gtk2_check enable_gnome_check +enable_gtk3_check enable_motif_check enable_athena_check enable_nextaw_check @@ -1481,9 +1482,10 @@ Optional Features: --enable-hangulinput Include Hangul input support. --enable-xim Include XIM input support. --enable-fontset Include X fontset output support. - --enable-gui=OPTS X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/motif/athena/neXtaw/photon/carbon + --enable-gui=OPTS X11 GUI default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/athena/neXtaw/photon/carbon --enable-gtk2-check If auto-select GUI, check for GTK+ 2 default=yes --enable-gnome-check If GTK GUI, check for GNOME default=no + --enable-gtk3-check If auto-select GUI, check for GTK+ 3 default=yes --enable-motif-check If auto-select GUI, check for Motif default=yes --enable-athena-check If auto-select GUI, check for Athena default=yes --enable-nextaw-check If auto-select GUI, check for neXtaw default=yes @@ -4355,7 +4357,7 @@ fi if test "x$CARBON" = "xyes"; then - if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2; then + if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xathena -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then with_x=no fi fi @@ -8606,6 +8608,9 @@ else $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 @@ fi 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 @@ fi 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 @@ fi 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 @@ fi 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 +#include +#if STDC_HEADERS +# include +# include +#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, µ) != 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 diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -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 *chan /* 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 *channe # 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); diff --git a/src/config.h.in b/src/config.h.in --- a/src/config.h.in +++ b/src/config.h.in @@ -460,3 +460,6 @@ /* Define if GResource is used to load icons */ #undef USE_GRESOURCE + +/* Define if GTK+ GUI is to be linked against GTK+ 3 */ +#undef USE_GTK3 diff --git a/src/configure.in b/src/configure.in --- a/src/configure.in +++ b/src/configure.in @@ -213,7 +213,7 @@ if test "`(uname) 2>/dev/null`" = Darwin 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" != " 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 "$en 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" diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -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", diff --git a/src/gui.h b/src/gui.h --- 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 */ diff --git a/src/gui_beval.c b/src/gui_beval.c --- a/src/gui_beval.c +++ b/src/gui_beval.c @@ -122,7 +122,11 @@ general_beval_cb(BalloonEval *beval, int #if !defined(FEAT_GUI_W32) || defined(PROTO) #ifdef FEAT_GUI_GTK -# include +# if GTK_CHECK_VERSION(3,0,0) +# include +# else +# include +# endif # include #else # include @@ -164,8 +168,16 @@ static gint target_event_cb(GtkWidget *, 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, Ballo * 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, Ballo 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, Ballo 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, GdkEv * 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, } 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 k 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 *widge 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 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 *bev beval->balloonShell = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_set_app_paintable(beval->balloonShell, TRUE); +# if GTK_CHECK_VERSION(3,0,0) + gtk_window_set_resizable(GTK_WINDOW(beval->balloonShell), FALSE); +# else gtk_window_set_policy(GTK_WINDOW(beval->balloonShell), FALSE, FALSE, TRUE); +# endif gtk_widget_set_name(beval->balloonShell, "gtk-tooltips"); +# if GTK_CHECK_VERSION(3,0,0) + gtk_container_set_border_width(GTK_CONTAINER(beval->balloonShell), 4); +# else gtk_container_border_width(GTK_CONTAINER(beval->balloonShell), 4); +# endif +# if GTK_CHECK_VERSION(3,0,0) + g_signal_connect(G_OBJECT(beval->balloonShell), "draw", + G_CALLBACK(balloon_draw_event_cb), NULL); +# else gtk_signal_connect((GtkObject*)(beval->balloonShell), "expose_event", GTK_SIGNAL_FUNC(balloon_expose_event_cb), NULL); +# endif beval->balloonLabel = gtk_label_new(NULL); gtk_label_set_line_wrap(GTK_LABEL(beval->balloonLabel), FALSE); gtk_label_set_justify(GTK_LABEL(beval->balloonLabel), GTK_JUSTIFY_LEFT); +# if GTK_CHECK_VERSION(3,16,0) + gtk_label_set_xalign(GTK_LABEL(beval->balloonLabel), 0.5); + gtk_label_set_yalign(GTK_LABEL(beval->balloonLabel), 0.5); +# elif GTK_CHECK_VERSION(3,14,0) + GValue align_val = G_VALUE_INIT; + g_value_init(&align_val, G_TYPE_FLOAT); + g_value_set_float(&align_val, 0.5); + g_object_set_property(G_OBJECT(beval->balloonLabel), "xalign", &align_val); + g_object_set_property(G_OBJECT(beval->balloonLabel), "yalign", &align_val); + g_value_unset(&align_val); +# else gtk_misc_set_alignment(GTK_MISC(beval->balloonLabel), 0.5f, 0.5f); +# endif gtk_widget_set_name(beval->balloonLabel, "vim-balloon-label"); gtk_widget_show(beval->balloonLabel); diff --git a/src/gui_beval.h b/src/gui_beval.h --- a/src/gui_beval.h +++ b/src/gui_beval.h @@ -11,7 +11,11 @@ #define GUI_BEVAL_H #ifdef FEAT_GUI_GTK -# include +# ifdef USE_GTK3 +# include +# else +# include +# endif #else # if defined(FEAT_GUI_X11) # include diff --git a/src/gui_gtk.c b/src/gui_gtk.c --- a/src/gui_gtk.c +++ b/src/gui_gtk.c @@ -22,8 +22,17 @@ * * Best supporting actor (He helped somewhat, aesthetically speaking): * Maxime Romano + * + * Support for GTK+ 3 was added by: + * + * 2016 Kazunobu Kuriyama + * + * 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 _ @@ -63,7 +70,11 @@ #endif #ifdef FEAT_GUI_GTK -# include +# if GTK_CHECK_VERSION(3,0,0) +# include +# else +# include +# endif # include # ifdef WIN3264 # include @@ -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, c 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, GtkIcon 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, GtkIco /* 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, GtkIco 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 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; + char path[MAXPATHL]; - 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); - } + 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 * 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 id 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, i 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, i * 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, i 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, i 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, i /* 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 va 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 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 * #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 * } } } - +#endif /* !GTK_CHECK_VERSION(3,0,0) */ gui_drag_scrollbar(sb, value, dragging); } @@ -802,23 +1112,42 @@ adjustment_value_changed(GtkAdjustment * 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, ch /* 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, ch 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, ch */ 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, ch 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 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 U 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 U *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 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, 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, } 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, 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 } find_text = (char_u *)gtk_entry_get_text(GTK_ENTRY(sfr->what)); +#if GTK_CHECK_VERSION(3,0,0) + direction_down = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->down)); +#else direction_down = GTK_TOGGLE_BUTTON(sfr->down)->active; +#endif +#if GTK_CHECK_VERSION(3,0,0) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->wword))) +#else if (GTK_TOGGLE_BUTTON(sfr->wword)->active) +#endif flags |= FRD_WHOLE_WORD; +#if GTK_CHECK_VERSION(3,0,0) + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sfr->mcase))) +#else if (GTK_TOGGLE_BUTTON(sfr->mcase)->active) +#endif flags |= FRD_MATCH_CASE; repl_text = CONVERT_FROM_UTF8(repl_text); diff --git a/src/gui_gtk_f.c b/src/gui_gtk_f.c --- a/src/gui_gtk_f.c +++ b/src/gui_gtk_f.c @@ -19,13 +19,19 @@ * children at arbitrary positions width arbitrary sizes. This finally puts * an end on our resize problems with which we where struggling for such a * long time. + * + * Support for GTK+ 3 was added by: + * + * 2016 Kazunobu Kuriyama */ #include "vim.h" #include /* without this it compiles, but gives errors at runtime! */ #include "gui_gtk_f.h" -#include +#if !GTK_CHECK_VERSION(3,0,0) +# include +#endif #ifdef WIN3264 # include #else @@ -52,10 +58,23 @@ static void gtk_form_unrealize(GtkWidget 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(GtkF 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,25 +566,71 @@ gtk_form_size_request(GtkWidget *widget, gtk_widget_size_request(child->widget, NULL); tmp_list = tmp_list->next; } +#endif } +#if GTK_CHECK_VERSION(3,0,0) + static void +gtk_form_get_preferred_width(GtkWidget *widget, + gint *minimal_width, + gint *natural_width) +{ + GtkRequisition requisition; + + gtk_form_size_request(widget, &requisition); + + *minimal_width = requisition.width; + *natural_width = requisition.width; +} + + static void +gtk_form_get_preferred_height(GtkWidget *widget, + gint *minimal_height, + gint *natural_height) +{ + GtkRequisition requisition; + + gtk_form_size_request(widget, &requisition); + + *minimal_height = requisition.height; + *natural_height = requisition.height; +} +#endif /* GTK_CHECK_VERSION(3,0,0) */ + static void gtk_form_size_allocate(GtkWidget *widget, GtkAllocation *allocation) { GList *tmp_list; GtkForm *form; gboolean need_reposition; +#if GTK_CHECK_VERSION(3,0,0) + GtkAllocation cur_alloc; +#endif g_return_if_fail(GTK_IS_FORM(widget)); +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_get_allocation(widget, &cur_alloc); + + if (cur_alloc.x == allocation->x + && cur_alloc.y == allocation->y + && cur_alloc.width == allocation->width + && cur_alloc.height == allocation->height) +#else if (widget->allocation.x == allocation->x && widget->allocation.y == allocation->y && widget->allocation.width == allocation->width && widget->allocation.height == allocation->height) +#endif return; +#if GTK_CHECK_VERSION(3,0,0) + need_reposition = cur_alloc.width != allocation->width + || cur_alloc.height != allocation->height; +#else need_reposition = widget->allocation.width != allocation->width || widget->allocation.height != allocation->height; +#endif form = GTK_FORM(widget); if (need_reposition) @@ -444,20 +646,81 @@ gtk_form_size_allocate(GtkWidget *widget } } +#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, GdkEv return FALSE; } +#endif /* !GTK_CHECK_VERSION(3,0,0) */ /* Container method */ @@ -522,12 +786,22 @@ gtk_form_remove(GtkContainer *container, 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(>k_form_child_map), child); + g_signal_handlers_disconnect_by_func(G_OBJECT(child->widget), + G_CALLBACK(>k_form_child_unmap), child); +#else gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget), GTK_SIGNAL_FUNC(>k_form_child_map), child); gtk_signal_disconnect_by_func(GTK_OBJECT(child->widget), GTK_SIGNAL_FUNC(>k_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, 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 *fo 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 *fo 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(>k_form_child_map), child); + g_signal_connect(G_OBJECT(child->widget), "unmap", + G_CALLBACK(>k_form_child_unmap), child); +#else gtk_signal_connect(GTK_OBJECT(child->widget), "map", GTK_SIGNAL_FUNC(>k_form_child_map), child); gtk_signal_connect(GTK_OBJECT(child->widget), "unmap", GTK_SIGNAL_FUNC(>k_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, Gt 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, G { 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, G 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, G 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, G { 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_xeve } 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 *w * 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 U child->mapped = FALSE; gdk_window_hide(child->window); } - diff --git a/src/gui_gtk_f.h b/src/gui_gtk_f.h --- a/src/gui_gtk_f.h +++ b/src/gui_gtk_f.h @@ -9,8 +9,12 @@ #ifndef __GTK_FORM_H__ #define __GTK_FORM_H__ +#ifdef USE_GTK3 +#include +#else #include #include +#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); diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -19,6 +19,10 @@ * * (C) 2002,2003 Jason Hildebrand * Daniel Elstner + * + * Support for GTK+ 3 was added by: + * + * 2016 Kazunobu Kuriyama */ #include "vim.h" @@ -75,14 +79,18 @@ extern void bonobo_dock_item_set_behavio # define GdkEventConfigure int # define GdkEventClient int #else -# include +# if GTK_CHECK_VERSION(3,0,0) +# include +# include +# else +# include +# endif # include # ifdef WIN3264 # include # else # include # endif - # include # 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 UNUSE 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 UNU 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 UNUS * 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 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 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 U 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 U 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 U 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, GdkM /* * 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, GdkM /* 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, GdkM /* 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 U 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 *con 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 *con 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 #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 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 * 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 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 *wi * 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 *wi 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 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 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, G { 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, G 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 *wid { 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 *wid 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, c 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, GdkEv ) 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, GdkEv 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 U * 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 hei 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 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 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 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, /* 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, 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, * 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, c 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, c 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, c 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 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, i 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 co 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, (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_li 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_li 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_li 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 } /* 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 g 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 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 } 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 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 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 t 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 t 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 t 127, GDK_RGB_DITHER_NORMAL, 0, 0); +# endif /* !GTK_CHECK_VERSION(3,0,0) */ if (need_scale) g_object_unref(sign); } diff --git a/src/if_mzsch.c b/src/if_mzsch.c --- a/src/if_mzsch.c +++ b/src/if_mzsch.c @@ -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) diff --git a/src/mbyte.c b/src/mbyte.c --- a/src/mbyte.c +++ b/src/mbyte.c @@ -109,7 +109,11 @@ #endif #if defined(FEAT_GUI_GTK) && defined(FEAT_XIM) -# include +# if GTK_CHECK_VERSION(3,0,0) +# include +# else +# include +# endif # ifdef WIN3264 # include # 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 keyv # ifdef HAVE_GTK_MULTIHEAD event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS); +# if GTK_CHECK_VERSION(3,0,0) + g_object_ref(gtk_widget_get_window(gui.drawarea)); + /* unreffed by gdk_event_free() */ +# else g_object_ref(gui.drawarea->window); /* unreffed by gdk_event_free() */ +# endif # else event = (GdkEventKey *)g_malloc0((gulong)sizeof(GdkEvent)); event->type = GDK_KEY_PRESS; # endif +# if GTK_CHECK_VERSION(3,0,0) + event->window = gtk_widget_get_window(gui.drawarea); +# else event->window = gui.drawarea->window; +# endif event->send_event = TRUE; event->time = GDK_CURRENT_TIME; event->state = state; diff --git a/src/netbeans.c b/src/netbeans.c --- a/src/netbeans.c +++ b/src/netbeans.c @@ -3055,17 +3055,56 @@ netbeans_draw_multisign_indicator(int ro 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 ro gdk_draw_point(drawable, gui.text_gc, x+2, y); gdk_draw_point(drawable, gui.text_gc, x+3, y++); gdk_draw_point(drawable, gui.text_gc, x+2, y); +#endif + +#if GTK_CHECK_VERSION(3,0,0) + cairo_destroy(cr); +#endif } #endif /* FEAT_GUI_GTK */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1402, +/**/ 1401, /**/ 1400, @@ -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."));