[evolution-data-server/wip/mcrha/gtk4: 97/97] Port most of the libedataserverui to gtk4




commit ce752d776e39e710b472bfecac44bccf079580a1
Author: Milan Crha <mcrha redhat com>
Date:   Fri Oct 8 12:48:59 2021 +0200

    Port most of the libedataserverui to gtk4

 CMakeLists.txt                                     |  50 ++-
 config.h.in                                        |   7 +-
 src/CMakeLists.txt                                 |   5 +-
 src/libedataserverui/CMakeLists.txt                | 175 +++++++++-
 src/libedataserverui/e-buffer-tagger.c             | 386 ++++++++++++++++-----
 src/libedataserverui/e-cell-renderer-color.c       | 121 ++++++-
 .../e-credentials-prompter-impl-oauth2.c           | 146 ++++++--
 .../e-credentials-prompter-impl-password.c         |  82 ++++-
 src/libedataserverui/e-reminders-widget.c          | 126 ++++++-
 src/libedataserverui/e-trust-prompt.c              |  66 +++-
 src/libedataserverui/e-webdav-discover-widget.c    |  63 +++-
 src/libedataserverui/libedataserverui-private.c    | 103 ++++++
 src/libedataserverui/libedataserverui-private.h    |   3 +
 src/libedataserverui/libedataserverui.pc.in        |  10 +-
 src/vala/CMakeLists.txt                            |  35 +-
 15 files changed, 1196 insertions(+), 182 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f84bcb455..3fd5a02e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,11 @@ set(LIBEDATASERVERUI_CURRENT 3)
 set(LIBEDATASERVERUI_REVISION 0)
 set(LIBEDATASERVERUI_AGE 0)
 
+set(LIBEDATASERVERUI4_API_VERSION 1.0)
+set(LIBEDATASERVERUI4_CURRENT 0)
+set(LIBEDATASERVERUI4_REVISION 0)
+set(LIBEDATASERVERUI4_AGE 0)
+
 set(LIBEBOOK_CURRENT 20)
 set(LIBEBOOK_REVISION 1)
 set(LIBEBOOK_AGE 3)
@@ -89,8 +94,12 @@ set(glib_minimum_version 2.46)
 set(glib_encoded_version GLIB_VERSION_2_46)
 
 # Keep these two definitions in agreement.
-set(gdk_minimum_version 3.16)
-set(gdk_encoded_version GDK_VERSION_3_16)
+set(gdk_minimum_version 3.20)
+set(gdk_encoded_version GDK_VERSION_3_20)
+
+# Keep these two definitions in agreement.
+set(gdk4_minimum_version 4.4)
+set(gdk4_encoded_version GDK_VERSION_4_4)
 
 # Keep these two definitions in agreement.
 set(soup_minimum_version 2.58)
@@ -98,7 +107,6 @@ set(soup_encoded_version SOUP_VERSION_2_58)
 
 # Warn about API usage that violates our minimum requirements.
 add_definitions(-DGLIB_VERSION_MAX_ALLOWED=${glib_encoded_version})
-add_definitions(-DGDK_VERSION_MAX_ALLOWED=${gdk_encoded_version})
 add_definitions(-DSOUP_VERSION_MAX_ALLOWED=${soup_encoded_version})
 
 # These will suppress warnings about newly-deprecated symbols. Ideally
@@ -106,7 +114,6 @@ add_definitions(-DSOUP_VERSION_MAX_ALLOWED=${soup_encoded_version})
 # up any new deprecation warnings after bumping our minimum requirements.
 # But if the warnings get to be overwhelming, use fixed versions instead.
 add_definitions(-DGLIB_VERSION_MIN_REQUIRED=${glib_encoded_version})
-add_definitions(-DGDK_VERSION_MIN_REQUIRED=${gdk_encoded_version})
 add_definitions(-DSOUP_VERSION_MIN_REQUIRED=${soup_encoded_version})
 
 set(gcr_minimum_version 3.4)
@@ -124,6 +131,7 @@ set(libaccounts_glib_minimum_version 1.4)
 set(libsignon_glib_minimum_version 1.8)
 set(json_glib_minimum_version 1.0.4)
 set(webkit2gtk_minimum_version 2.28.0)
+set(webkit2gtk4_minimum_version 2.36.0)
 set(libcanberra_gtk_minimum_version 0.25)
 
 # Load modules from the source tree
@@ -392,19 +400,39 @@ if(ENABLE_GTK)
        set(HAVE_GTK 1)
 endif(ENABLE_GTK)
 
+add_printable_option(ENABLE_GTK4 "Enable gtk4 support" ON)
+if(ENABLE_GTK4)
+       pkg_check_modules_for_option(ENABLE_GTK4 "gtk4 support" GTK4 gtk4>=${gdk4_minimum_version})
+       #pkg_check_modules_for_option(ENABLE_GTK4 "gtk4 support" GCR4 gcr-4>=${gcr4_minimum_version})
+
+       set(HAVE_GTK4 1)
+endif(ENABLE_GTK4)
+
 # **************************************************************
 # Check for WebKitGTK+ and json-glib for OAuth2 authentications
 # **************************************************************
 
 pkg_check_modules(JSON_GLIB REQUIRED json-glib-1.0>=${json_glib_minimum_version})
 
-add_printable_option(ENABLE_OAUTH2_WEBKITGTK "Enable WebKitGTK for built-in OAuth2 authentications" ON)
+if(ENABLE_GTK)
+       add_printable_option(ENABLE_OAUTH2_WEBKITGTK3 "Enable WebKitGTK 3 for built-in OAuth2 
authentications" ON)
 
-if(ENABLE_OAUTH2_WEBKITGTK)
-       pkg_check_modules_for_option(ENABLE_OAUTH2_WEBKITGTK "WebKitGTK for built-in OAuth2 authentications" 
OAUTH2_WEBKITGTK
-               webkit2gtk-4.0>=${webkit2gtk_minimum_version}
-       )
-endif(ENABLE_OAUTH2_WEBKITGTK)
+       if(ENABLE_OAUTH2_WEBKITGTK3)
+               pkg_check_modules_for_option(ENABLE_OAUTH2_WEBKITGTK3 "WebKitGTK 3 for built-in OAuth2 
authentications" OAUTH2_WEBKITGTK3
+                       webkit2gtk-4.0>=${webkit2gtk_minimum_version}
+               )
+       endif(ENABLE_OAUTH2_WEBKITGTK3)
+endif(ENABLE_GTK)
+
+if(ENABLE_GTK4)
+       add_printable_option(ENABLE_OAUTH2_WEBKITGTK4 "Enable WebKitGTK 4 for built-in OAuth2 
authentications" ON)
+
+       if(ENABLE_OAUTH2_WEBKITGTK4)
+               pkg_check_modules_for_option(ENABLE_OAUTH2_WEBKITGTK4 "WebKitGTK 4 for built-in OAuth2 
authentications" OAUTH2_WEBKITGTK4
+                       webkit2gtk-4.1>=${webkit2gtk4_minimum_version}
+               )
+       endif(ENABLE_OAUTH2_WEBKITGTK4)
+endif(ENABLE_GTK4)
 
 add_printable_variable(WITH_GOOGLE_CLIENT_ID "Google OAuth 2.0 client id" "")
 add_printable_variable(WITH_GOOGLE_CLIENT_SECRET "Google OAuth 2.0 client secret" "")
@@ -449,7 +477,7 @@ add_printable_option(ENABLE_EXAMPLES "Enable the build of examples" ON)
 if(ENABLE_EXAMPLES)
        pkg_check_modules_for_option(ENABLE_EXAMPLES "build the example program(s)" EXAMPLES
                gtk+-3.0>=3.10
-               glib-2.0>=2.38
+               glib-2.0>=${glib_minimum_version}
        )
 
        set(BUILD_EXAMPLES 1)
diff --git a/config.h.in b/config.h.in
index 0ec3eaa71..afc4fda9f 100644
--- a/config.h.in
+++ b/config.h.in
@@ -72,8 +72,11 @@
 /* Define to 1 if you have the gtk+-3.0 package. */
 #cmakedefine HAVE_GTK 1
 
-/* Define to 1 to use WebKitGTK for OAuth2 authentication. */
-#cmakedefine ENABLE_OAUTH2_WEBKITGTK 1
+/* Define to 1 to use WebKitGTK 3 for OAuth2 authentication. */
+#cmakedefine ENABLE_OAUTH2_WEBKITGTK3 1
+
+/* Define to 1 to use WebKitGTK 4 for OAuth2 authentication. */
+#cmakedefine ENABLE_OAUTH2_WEBKITGTK4 1
 
 /* Define to 1 if the examples should be built. */
 #cmakedefine BUILD_EXAMPLES 1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 50ab1cdc9..7861be3eb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,15 +3,12 @@ add_subdirectory(libedataserver)
 add_subdirectory(libebackend)
 add_subdirectory(addressbook)
 add_subdirectory(calendar)
+add_subdirectory(libedataserverui)
 add_subdirectory(modules)
 add_subdirectory(private)
 add_subdirectory(services)
 add_subdirectory(tools)
 
-if(HAVE_GTK)
-       add_subdirectory(libedataserverui)
-endif(HAVE_GTK)
-
 if(ENABLE_EXAMPLES)
        add_subdirectory(examples)
 endif(ENABLE_EXAMPLES)
diff --git a/src/libedataserverui/CMakeLists.txt b/src/libedataserverui/CMakeLists.txt
index def120959..67c3ca289 100644
--- a/src/libedataserverui/CMakeLists.txt
+++ b/src/libedataserverui/CMakeLists.txt
@@ -1,4 +1,8 @@
-add_pkgconfig_file(libedataserverui.pc.in libedataserverui-${API_VERSION}.pc)
+if(ENABLE_GTK)
+set(GTK_DEPENDENCY gtk+-3.0)
+set(UI_VERSION)
+set(UI_API_VERSION ${API_VERSION})
+add_pkgconfig_file(libedataserverui.pc.in libedataserverui-${UI_API_VERSION}.pc)
 
 set(SOURCES
        e-buffer-tagger.c
@@ -45,7 +49,7 @@ add_dependencies(edataserverui
 set_target_properties(edataserverui PROPERTIES
        VERSION "${LIBEDATASERVERUI_CURRENT}.${LIBEDATASERVERUI_REVISION}.${LIBEDATASERVERUI_AGE}"
        SOVERSION ${LIBEDATASERVERUI_CURRENT}
-       OUTPUT_NAME edataserverui-${API_VERSION}
+       OUTPUT_NAME edataserverui-${UI_API_VERSION}
 )
 
 target_compile_definitions(edataserverui PRIVATE
@@ -54,6 +58,8 @@ target_compile_definitions(edataserverui PRIVATE
        -DE_DATA_SERVER_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"
        -DE_DATA_SERVER_UIMODULEDIR=\"${uimoduledir}\"
        -DE_DATA_SERVER_ICONDIR=\"${icondir}\"
+       -DGDK_VERSION_MAX_ALLOWED=${gdk_encoded_version}
+       -DGDK_VERSION_MIN_REQUIRED=${gdk_encoded_version}
 )
 
 target_compile_options(edataserverui PUBLIC
@@ -65,7 +71,7 @@ target_compile_options(edataserverui PUBLIC
        ${GCR_CFLAGS}
        ${GTK_CFLAGS}
        ${JSON_GLIB_CFLAGS}
-       ${OAUTH2_WEBKITGTK_CFLAGS}
+       ${OAUTH2_WEBKITGTK3_CFLAGS}
 )
 
 target_include_directories(edataserverui PUBLIC
@@ -81,7 +87,7 @@ target_include_directories(edataserverui PUBLIC
        ${GCR_INCLUDE_DIRS}
        ${GTK_INCLUDE_DIRS}
        ${JSON_GLIB_INCLUDE_DIRS}
-       ${OAUTH2_WEBKITGTK_INCLUDE_DIRS}
+       ${OAUTH2_WEBKITGTK3_INCLUDE_DIRS}
 )
 
 target_link_libraries(edataserverui
@@ -94,7 +100,7 @@ target_link_libraries(edataserverui
        ${GCR_LDFLAGS}
        ${GTK_LDFLAGS}
        ${JSON_GLIB_LDFLAGS}
-       ${OAUTH2_WEBKITGTK_LDFLAGS}
+       ${OAUTH2_WEBKITGTK3_LDFLAGS}
 )
 
 install(TARGETS edataserverui
@@ -134,7 +140,7 @@ set(gir_deps
 gir_add_introspection_simple(
        EDataServerUI
        libedataserverui
-       ${API_VERSION}
+       ${UI_API_VERSION}
        "libedataserverui/libedataserverui.h"
        gir_identifies_prefixes
        gir_includes
@@ -144,3 +150,160 @@ gir_add_introspection_simple(
        gir_deps
        gir_sources
 )
+endif(ENABLE_GTK)
+
+#-----------------------------------------------------------------------------
+
+if(ENABLE_GTK4)
+set(GTK_DEPENDENCY gtk4)
+set(UI_VERSION 4)
+set(UI_API_VERSION ${LIBEDATASERVERUI4_API_VERSION})
+add_pkgconfig_file(libedataserverui.pc.in libedataserverui${UI_VERSION}-${UI_API_VERSION}.pc)
+
+set(SOURCES
+       e-buffer-tagger.c
+       e-cell-renderer-color.c
+       e-credentials-prompter.c
+       e-credentials-prompter-impl.c
+       e-credentials-prompter-impl-oauth2.c
+       e-credentials-prompter-impl-password.c
+       e-reminders-widget.c
+       e-trust-prompt.c
+       e-webdav-discover-widget.c
+       libedataserverui-private.h
+       libedataserverui-private.c
+)
+
+set(HEADERS
+       libedataserverui.h
+       e-buffer-tagger.h
+       e-cell-renderer-color.h
+       e-credentials-prompter.h
+       e-credentials-prompter-impl.h
+       e-credentials-prompter-impl-oauth2.h
+       e-credentials-prompter-impl-password.h
+       e-reminders-widget.h
+       e-trust-prompt.h
+       e-webdav-discover-widget.h
+)
+
+set(DEPENDENCIES
+       camel
+       ecal
+       edataserver
+)
+
+add_library(edataserverui${UI_VERSION} SHARED
+       ${SOURCES}
+       ${HEADERS}
+)
+
+add_dependencies(edataserverui${UI_VERSION}
+       ${DEPENDENCIES}
+)
+
+set_target_properties(edataserverui${UI_VERSION} PROPERTIES
+       VERSION "${LIBEDATASERVERUI4_CURRENT}.${LIBEDATASERVERUI4_REVISION}.${LIBEDATASERVERUI4_AGE}"
+       SOVERSION ${LIBEDATASERVERUI4_CURRENT}
+       OUTPUT_NAME edataserverui${UI_VERSION}-${UI_API_VERSION}
+)
+
+target_compile_definitions(edataserverui${UI_VERSION} PRIVATE
+       -DG_LOG_DOMAIN=\"e-data-server-ui${UI_VERSION}\"
+       -DLIBEDATASERVERUI_COMPILATION
+       -DE_DATA_SERVER_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"
+       -DE_DATA_SERVER_UIMODULEDIR=\"${uimoduledir}${UI_VERSION}\"
+       -DE_DATA_SERVER_ICONDIR=\"${icondir}\"
+       -DGDK_VERSION_MAX_ALLOWED=${gdk4_encoded_version}
+       -DGDK_VERSION_MIN_REQUIRED=${gdk4_encoded_version}
+)
+
+target_compile_options(edataserverui${UI_VERSION} PUBLIC
+       ${DATA_SERVER_CFLAGS}
+       ${CAMEL_CFLAGS}
+       ${CODE_COVERAGE_CFLAGS}
+       ${GNOME_PLATFORM_CFLAGS}
+       ${GCR4_BASE_CFLAGS}
+       ${GCR4_CFLAGS}
+       ${GTK4_CFLAGS}
+       ${OAUTH2_JSON_GLIB_CFLAGS}
+       ${OAUTH2_WEBKITGTK4_CFLAGS}
+)
+
+target_include_directories(edataserverui${UI_VERSION} PUBLIC
+       ${CMAKE_BINARY_DIR}
+       ${CMAKE_BINARY_DIR}/src
+       ${CMAKE_SOURCE_DIR}/src
+       ${CMAKE_CURRENT_BINARY_DIR}
+       ${DATA_SERVER_INCLUDE_DIRS}
+       ${CAMEL_INCLUDE_DIRS}
+       ${CODE_COVERAGE_INCLUDE_DIRS}
+       ${GNOME_PLATFORM_INCLUDE_DIRS}
+       ${GCR4_BASE_INCLUDE_DIRS}
+       ${GCR4_INCLUDE_DIRS}
+       ${GTK4_INCLUDE_DIRS}
+       ${OAUTH2_JSON_GLIB_INCLUDE_DIRS}
+       ${OAUTH2_WEBKITGTK4_INCLUDE_DIRS}
+)
+
+target_link_libraries(edataserverui${UI_VERSION}
+       ${DEPENDENCIES}
+       ${DATA_SERVER_LDFLAGS}
+       ${CAMEL_LDFLAGS}
+       ${CODE_COVERAGE_LDFLAGS}
+       ${GNOME_PLATFORM_LDFLAGS}
+       ${GCR4_BASE_LDFLAGS}
+       ${GCR4_LDFLAGS}
+       ${GTK4_LDFLAGS}
+       ${OAUTH2_JSON_GLIB_LDFLAGS}
+       ${OAUTH2_WEBKITGTK4_LDFLAGS}
+)
+
+install(TARGETS edataserverui${UI_VERSION}
+       DESTINATION ${LIB_INSTALL_DIR}
+)
+
+install(FILES ${HEADERS}
+       DESTINATION ${privincludedir}/libedataserverui${UI_VERSION}
+)
+
+set(gir_sources ${SOURCES} ${HEADERS})
+set(gir_identifies_prefixes E)
+set(gir_includes GObject-2.0 Gio-2.0 Gtk-4.0 Soup-2.4)
+set(gir_cflags
+       -DLIBEDATASERVERUI_COMPILATION
+       -I${CMAKE_BINARY_DIR}/src/calendar
+       -I${CMAKE_SOURCE_DIR}/src/calendar
+)
+set(gir_libdirs
+       ${CMAKE_BINARY_DIR}/src/private
+       ${CMAKE_BINARY_DIR}/src/calendar/libecal
+       ${CMAKE_BINARY_DIR}/src/camel
+       ${CMAKE_BINARY_DIR}/src/libedataserver
+)
+set(gir_libs
+       camel
+       ecal
+       edataserver
+       edataserverui${UI_VERSION}
+)
+set(gir_deps
+       ${CMAKE_BINARY_DIR}/src/camel/Camel-${API_VERSION}.gir
+       ${CMAKE_BINARY_DIR}/src/libedataserver/EDataServer-${API_VERSION}.gir
+       ${CMAKE_BINARY_DIR}/src/calendar/libecal/ECal-${CAL_API_VERSION}.gir
+)
+
+gir_add_introspection_simple(
+       EDataServerUI${UI_VERSION}
+       libedataserverui${UI_VERSION}
+       ${UI_API_VERSION}
+       "libedataserverui${UI_VERSION}/libedataserverui.h"
+       gir_identifies_prefixes
+       gir_includes
+       gir_cflags
+       gir_libdirs
+       gir_libs
+       gir_deps
+       gir_sources
+)
+endif(ENABLE_GTK4)
diff --git a/src/libedataserverui/e-buffer-tagger.c b/src/libedataserverui/e-buffer-tagger.c
index 468c9d3c4..6a2a9870a 100644
--- a/src/libedataserverui/e-buffer-tagger.c
+++ b/src/libedataserverui/e-buffer-tagger.c
@@ -33,34 +33,42 @@ static void
 e_show_uri (GtkWindow *parent,
            const gchar *uri)
 {
+#if !GTK_CHECK_VERSION(4, 0, 0)
        GtkWidget *dialog;
        GdkScreen *screen = NULL;
-       gchar *scheme;
        GError *error = NULL;
-       guint32 timestamp;
        gboolean success;
+#endif
+       gchar *scheme, *schemed_uri = NULL;
+       guint32 timestamp;
 
        g_return_if_fail (uri != NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       timestamp = GDK_CURRENT_TIME;
+#else
        timestamp = gtk_get_current_event_time ();
 
        if (parent != NULL)
                screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+#endif
 
        scheme = g_uri_parse_scheme (uri);
 
        if (!scheme || !*scheme) {
-               gchar *schemed_uri;
-
                schemed_uri = g_strconcat ("http://";, uri, NULL);
-               success = gtk_show_uri (screen, schemed_uri, timestamp, &error);
-               g_free (schemed_uri);
-       } else {
-               success = gtk_show_uri (screen, uri, timestamp, &error);
+               uri = schemed_uri;
        }
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_show_uri (parent, uri, timestamp);
+#else
+       success = gtk_show_uri (screen, uri, timestamp, &error);
+#endif
 
+       g_free (schemed_uri);
        g_free (scheme);
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        if (success)
                return;
 
@@ -77,6 +85,7 @@ e_show_uri (GtkWindow *parent,
 
        gtk_widget_destroy (dialog);
        g_error_free (error);
+#endif
 }
 
 enum EBufferTaggerState {
@@ -88,8 +97,12 @@ enum EBufferTaggerState {
        E_BUFFER_TAGGER_STATE_CTRL_DOWN = 1 << 4  /* Ctrl key is down */
 };
 
-#define E_BUFFER_TAGGER_DATA_STATE "EBufferTagger::state"
-#define E_BUFFER_TAGGER_LINK_TAG   "EBufferTagger::link"
+#define E_BUFFER_TAGGER_DATA_STATE             "EBufferTagger::state"
+#define E_BUFFER_TAGGER_DATA_KEY_CONTROLLER    "EBufferTagger::key-controller"
+#define E_BUFFER_TAGGER_DATA_LEGACY_CONTROLLER "EBufferTagger::legacy-controller"
+#define E_BUFFER_TAGGER_DATA_MOTION_CONTROLLER "EBufferTagger::motion-controller"
+#define E_BUFFER_TAGGER_DATA_CURRENT_URI       "EBufferTagger::current-uri"
+#define E_BUFFER_TAGGER_LINK_TAG               "EBufferTagger::link"
 
 struct _MagicInsertMatch {
        const gchar *regex;
@@ -226,9 +239,25 @@ markup_text (GtkTextBuffer *buffer)
 
 static void
 get_pointer_position (GtkTextView *text_view,
-                      gint *x,
-                      gint *y)
+                     GdkEvent *event,
+                      gint *out_x,
+                      gint *out_y)
 {
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gdouble tmp_x = -1.0, tmp_y = -1.0;
+       if (!event || !gdk_event_get_position (event, &tmp_x, &tmp_y)) {
+               tmp_x = -1;
+               tmp_y = -1;
+       } else {
+               GtkWidget *window = gtk_widget_get_ancestor (GTK_WIDGET (text_view), GTK_TYPE_WINDOW);
+               if (window)
+                       gtk_widget_translate_coordinates (window, GTK_WIDGET (text_view), tmp_x, tmp_y, 
&tmp_x, &tmp_y);
+       }
+       if (out_x)
+               *out_x = tmp_x;
+       if (out_y)
+               *out_y = tmp_y;
+#else
        GdkWindow *window;
        GdkDisplay *display;
        GdkDeviceManager *device_manager;
@@ -239,7 +268,8 @@ get_pointer_position (GtkTextView *text_view,
        device_manager = gdk_display_get_device_manager (display);
        device = gdk_device_manager_get_client_pointer (device_manager);
 
-       gdk_window_get_device_position (window, device, x, y, NULL);
+       gdk_window_get_device_position (window, device, out_x, out_y, NULL);
+#endif
 }
 
 static guint32
@@ -295,7 +325,7 @@ get_tag_bounds (GtkTextIter *iter,
                *start = *iter;
                *end = *iter;
 
-               if (!gtk_text_iter_begins_tag (start, tag))
+               if (!gtk_text_iter_starts_tag (start, tag))
                        gtk_text_iter_backward_to_tag_toggle (start, tag);
 
                if (!gtk_text_iter_ends_tag (end, tag))
@@ -409,13 +439,76 @@ buffer_cursor_position (GtkTextBuffer *buffer,
        set_state (buffer, state);
 }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void
+textview_open_uri_cb (GSimpleAction *simple,
+                     GVariant *parameter,
+                     gpointer user_data)
+#else
+static void
+textview_open_uri_cb (GtkWidget *widget,
+                     gpointer user_data)
+#endif
+{
+#if GTK_CHECK_VERSION(4, 0, 0)
+       const gchar *uri = parameter ? g_variant_get_string (parameter, NULL) : NULL;
+#else
+       const gchar *uri = user_data;
+#endif
+
+       g_return_if_fail (uri != NULL);
+
+       e_show_uri (NULL, uri);
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void
+textview_copy_uri_cb (GSimpleAction *simple,
+                     GVariant *parameter,
+                     gpointer user_data)
+#else
+static void
+textview_copy_uri_cb (GtkWidget *widget,
+                     gpointer user_data)
+#endif
+{
+#if GTK_CHECK_VERSION(4, 0, 0)
+       GdkClipboard *clipboard;
+       GtkWidget *widget = user_data;
+       const gchar *uri = parameter ? g_variant_get_string (parameter, NULL) : NULL;
+#else
+       const gchar *uri = user_data;
+       GtkClipboard *clipboard;
+#endif
+
+       g_return_if_fail (uri != NULL);
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+       clipboard = gtk_widget_get_primary_clipboard (widget);
+       gdk_clipboard_set_text (clipboard, uri);
+
+       clipboard = gtk_widget_get_clipboard (widget);
+       gdk_clipboard_set_text (clipboard, uri);
+#else
+       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+       gtk_clipboard_set_text (clipboard, uri, -1);
+       gtk_clipboard_store (clipboard);
+
+       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+       gtk_clipboard_set_text (clipboard, uri, -1);
+       gtk_clipboard_store (clipboard);
+#endif
+}
+
 static void
 update_mouse_cursor (GtkTextView *text_view,
                      gint x,
                      gint y)
 {
+#if !GTK_CHECK_VERSION(4, 0, 0)
        static GdkCursor *hand_cursor = NULL;
        static GdkCursor *regular_cursor = NULL;
+#endif
        gboolean hovering = FALSE, hovering_over_link = FALSE, hovering_real;
        guint32 state;
        GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
@@ -423,10 +516,12 @@ update_mouse_cursor (GtkTextView *text_view,
        GtkTextTag *tag;
        GtkTextIter iter;
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        if (!hand_cursor) {
                hand_cursor = gdk_cursor_new (GDK_HAND2);
                regular_cursor = gdk_cursor_new (GDK_XTERM);
        }
+#endif
 
        g_return_if_fail (buffer != NULL);
 
@@ -439,6 +534,65 @@ update_mouse_cursor (GtkTextView *text_view,
        gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
        hovering_real = gtk_text_iter_has_tag (&iter, tag);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       if (hovering_real) {
+               gchar *url;
+
+               url = get_url_at_iter (buffer, &iter);
+
+               if (url && *url && g_strcmp0 (url, g_object_get_data (G_OBJECT (text_view), 
E_BUFFER_TAGGER_DATA_CURRENT_URI)) != 0) {
+                       GActionGroup *action_group;
+                       GMenu *menu, *section;
+                       GMenuItem *item;
+                       GVariant *variant;
+                       const GActionEntry entries[] = {
+                               { "copy-uri",   textview_copy_uri_cb, "s" },
+                               { "open-uri",   textview_open_uri_cb, "s" }
+                       };
+
+                       action_group = G_ACTION_GROUP (g_simple_action_group_new ());
+                       g_action_map_add_action_entries (G_ACTION_MAP (action_group), entries, G_N_ELEMENTS 
(entries), text_view);
+                       gtk_widget_insert_action_group (GTK_WIDGET (text_view), "e-buffer-tagger", 
action_group);
+                       g_object_unref (action_group);
+
+                       variant = g_variant_new_string (url);
+
+                       section = g_menu_new ();
+
+                       item = g_menu_item_new (_("Copy _Link Location"), "e-buffer-tagger.copy-uri");
+                       g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_TARGET, variant);
+                       g_menu_append_item (section, item);
+                       g_object_unref (item);
+
+                       item = g_menu_item_new (_("O_pen Link in Browser"), "e-buffer-tagger.open-uri");
+                       g_menu_item_set_attribute_value (item, G_MENU_ATTRIBUTE_TARGET, variant);
+                       g_menu_append_item (section, item);
+                       g_object_unref (item);
+
+                       menu = g_menu_new ();
+                       g_menu_append_section (menu, NULL, G_MENU_MODEL (section));
+                       g_object_unref (section);
+
+                       gtk_text_view_set_extra_menu (text_view, G_MENU_MODEL (menu));
+
+                       g_object_unref (menu);
+
+                       g_object_set_data_full (G_OBJECT (text_view), E_BUFFER_TAGGER_DATA_CURRENT_URI, url, 
g_free);
+                       url = NULL;
+               } else if (!url || !*url) {
+                       gtk_text_view_set_extra_menu (text_view, NULL);
+                       gtk_widget_insert_action_group (GTK_WIDGET (text_view), "e-buffer-tagger", NULL);
+                       g_object_set_data (G_OBJECT (text_view), E_BUFFER_TAGGER_DATA_CURRENT_URI, NULL);
+               }
+
+               g_free (url);
+       } else {
+               gtk_text_view_set_extra_menu (text_view, NULL);
+               gtk_widget_insert_action_group (GTK_WIDGET (text_view), "e-buffer-tagger", NULL);
+               g_object_set_data (G_OBJECT (text_view), E_BUFFER_TAGGER_DATA_CURRENT_URI, NULL);
+       }
+#endif
+
        hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING) != 0;
        if ((state & E_BUFFER_TAGGER_STATE_CTRL_DOWN) == 0) {
                hovering = FALSE;
@@ -449,13 +603,17 @@ update_mouse_cursor (GtkTextView *text_view,
        if (hovering != hovering_over_link) {
                update_state (buffer, E_BUFFER_TAGGER_STATE_IS_HOVERING, hovering);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+               if (hovering && gtk_widget_has_focus (GTK_WIDGET (text_view)))
+                       gtk_widget_set_cursor_from_name (GTK_WIDGET (text_view), "pointer");
+               else
+                       gtk_widget_set_cursor_from_name (GTK_WIDGET (text_view), NULL);
+#else
                if (hovering && gtk_widget_has_focus (GTK_WIDGET (text_view)))
                        gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), 
hand_cursor);
                else
                        gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), 
regular_cursor);
-
-               /* XXX Is this necessary?  Appears to be a no-op. */
-               get_pointer_position (text_view, NULL, NULL);
+#endif
        }
 
        hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP) != 0;
@@ -467,6 +625,7 @@ update_mouse_cursor (GtkTextView *text_view,
        }
 }
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
 static void
 textview_style_updated_cb (GtkWidget *textview,
                           gpointer user_data)
@@ -499,14 +658,19 @@ textview_style_updated_cb (GtkWidget *textview,
        state = state | GTK_STATE_FLAG_LINK;
 
        gtk_style_context_save (context);
+       gtk_style_context_set_state (context, state);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_style_context_get_color (context, &rgba);
+#else
        /* Remove the 'view' style, because it can "confuse" some themes */
        gtk_style_context_remove_class (context, GTK_STYLE_CLASS_VIEW);
-       gtk_style_context_set_state (context, state);
        gtk_style_context_get_color (context, state, &rgba);
+#endif
        gtk_style_context_restore (context);
 
        g_object_set (G_OBJECT (tag), "foreground-rgba", &rgba, NULL);
 }
+#endif
 
 static gboolean
 textview_query_tooltip (GtkTextView *text_view,
@@ -557,17 +721,30 @@ textview_query_tooltip (GtkTextView *text_view,
 }
 
 /* Links can be activated by pressing Enter. */
+#if GTK_CHECK_VERSION(4, 0, 0)
+static gboolean
+textview_key_press_event (GtkEventControllerKey *controller,
+                         guint keyval,
+                         guint keycode,
+                         GdkModifierType state,
+                         GtkWidget *text_view)
+#else
 static gboolean
 textview_key_press_event (GtkWidget *text_view,
                           GdkEventKey *event)
+#endif
 {
+#if !GTK_CHECK_VERSION(4, 0, 0)
+       guint keyval = event->keyval;
+       GdkModifierType state = event->state;
+#endif
        GtkTextIter iter;
        GtkTextBuffer *buffer;
 
-       if ((event->state & GDK_CONTROL_MASK) == 0)
+       if ((state & GDK_CONTROL_MASK) == 0)
                return FALSE;
 
-       switch (event->keyval) {
+       switch (keyval) {
        case GDK_KEY_Return:
        case GDK_KEY_KP_Enter:
                buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
@@ -585,7 +762,8 @@ textview_key_press_event (GtkWidget *text_view,
 
 static void
 update_ctrl_state (GtkTextView *textview,
-                   gboolean ctrl_is_down)
+                   gboolean ctrl_is_down,
+                  GdkEvent *event)
 {
        GtkTextBuffer *buffer;
        gint x, y;
@@ -596,7 +774,7 @@ update_ctrl_state (GtkTextView *textview,
                        update_state (buffer, E_BUFFER_TAGGER_STATE_CTRL_DOWN, ctrl_is_down != FALSE);
                }
 
-               get_pointer_position (textview, &x, &y);
+               get_pointer_position (textview, event, &x, &y);
                gtk_text_view_window_to_buffer_coords (textview, GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y);
                update_mouse_cursor (textview, x, y);
        }
@@ -611,29 +789,44 @@ textview_event_after (GtkTextView *textview,
        GtkTextBuffer *buffer;
        gint x, y;
        GdkModifierType mt = 0;
+       GdkEventType event_type;
        guint event_button = 0;
        gdouble event_x_win = 0;
        gdouble event_y_win = 0;
 
        g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
 
-       if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+       event_type = gdk_event_get_event_type (event);
+#else
+       event_type = event->type;
+#endif
+
+       if (event_type == GDK_KEY_PRESS || event_type == GDK_KEY_RELEASE) {
                guint event_keyval = 0;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+               event_keyval = gdk_key_event_get_keyval (event);
+#else
                gdk_event_get_keyval (event, &event_keyval);
+#endif
 
                switch (event_keyval) {
                        case GDK_KEY_Control_L:
                        case GDK_KEY_Control_R:
                                update_ctrl_state (
                                        textview,
-                                       event->type == GDK_KEY_PRESS);
+                                       event_type == GDK_KEY_PRESS,
+                                       event);
                                break;
                }
 
                return FALSE;
        }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       mt = gdk_event_get_modifier_state (event);
+#else
        if (!gdk_event_get_state (event, &mt)) {
                GdkWindow *window;
                GdkDisplay *display;
@@ -647,14 +840,24 @@ textview_event_after (GtkTextView *textview,
 
                gdk_window_get_device_position (window, device, NULL, NULL, &mt);
        }
+#endif
 
-       update_ctrl_state (textview, (mt & GDK_CONTROL_MASK) != 0);
+       update_ctrl_state (textview, (mt & GDK_CONTROL_MASK) != 0, event);
 
-       if (event->type != GDK_BUTTON_RELEASE)
+       if (event_type != GDK_BUTTON_RELEASE)
                return FALSE;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       event_button = gdk_button_event_get_button (event);
+       if (gdk_event_get_position (event, &event_x_win, &event_y_win)) {
+               GtkWidget *window = gtk_widget_get_ancestor (GTK_WIDGET (textview), GTK_TYPE_WINDOW);
+               if (window)
+                       gtk_widget_translate_coordinates (window, GTK_WIDGET (textview), event_y_win, 
event_y_win, &event_x_win, &event_y_win);
+       }
+#else
        gdk_event_get_button (event, &event_button);
        gdk_event_get_coords (event, &event_x_win, &event_y_win);
+#endif
 
        if (event_button != 1 || (mt & GDK_CONTROL_MASK) == 0)
                return FALSE;
@@ -679,10 +882,21 @@ textview_event_after (GtkTextView *textview,
        return FALSE;
 }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+static gboolean
+textview_motion_notify_event (GtkTextView *textview,
+                              gdouble event_x,
+                             gdouble event_y,
+                             GtkEventControllerMotion *controller)
+#else
 static gboolean
 textview_motion_notify_event (GtkTextView *textview,
                               GdkEventMotion *event)
+#endif
 {
+#if !GTK_CHECK_VERSION(4, 0, 0)
+       gint event_x = event->x, event_y = event->y;
+#endif
        gint x, y;
 
        g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
@@ -690,62 +904,14 @@ textview_motion_notify_event (GtkTextView *textview,
        gtk_text_view_window_to_buffer_coords (
                textview,
                GTK_TEXT_WINDOW_WIDGET,
-               event->x, event->y, &x, &y);
+               event_x, event_y, &x, &y);
 
        update_mouse_cursor (textview, x, y);
 
        return FALSE;
 }
 
-static gboolean
-textview_visibility_notify_event (GtkTextView *textview,
-                                  GdkEventVisibility *event)
-{
-       gint wx, wy, bx, by;
-
-       g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
-
-       get_pointer_position (textview, &wx, &wy);
-
-       gtk_text_view_window_to_buffer_coords (
-               textview,
-               GTK_TEXT_WINDOW_WIDGET,
-               wx, wy, &bx, &by);
-
-       update_mouse_cursor (textview, bx, by);
-
-       return FALSE;
-}
-
-static void
-textview_open_uri_cb (GtkWidget *widget,
-                     gpointer user_data)
-{
-       const gchar *uri = user_data;
-
-       g_return_if_fail (uri != NULL);
-
-       e_show_uri (NULL, uri);
-}
-
-static void
-textview_copy_uri_cb (GtkWidget *widget,
-                     gpointer user_data)
-{
-       GtkClipboard *clipboard;
-       const gchar *uri = user_data;
-
-       g_return_if_fail (uri != NULL);
-
-       clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
-       gtk_clipboard_set_text (clipboard, uri, -1);
-       gtk_clipboard_store (clipboard);
-
-       clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-       gtk_clipboard_set_text (clipboard, uri, -1);
-       gtk_clipboard_store (clipboard);
-}
-
+#if !GTK_CHECK_VERSION(4, 0, 0)
 static void
 textview_populate_popup_cb (GtkTextView *textview,
                            GtkWidget *widget,
@@ -824,6 +990,7 @@ textview_populate_popup_cb (GtkTextView *textview,
                g_free (uri);
        }
 }
+#endif
 
 void
 e_buffer_tagger_connect (GtkTextView *textview)
@@ -831,6 +998,10 @@ e_buffer_tagger_connect (GtkTextView *textview)
        GtkTextBuffer *buffer;
        GtkTextTagTable *tag_table;
        GtkTextTag *tag;
+#if GTK_CHECK_VERSION(4, 0, 0)
+       GtkEventController *controller;
+       GtkWidget *widget;
+#endif
 
        init_magic_links ();
 
@@ -864,12 +1035,37 @@ e_buffer_tagger_connect (GtkTextView *textview)
 
        gtk_widget_set_has_tooltip (GTK_WIDGET (textview), TRUE);
 
-       g_signal_connect (
-               textview, "style-updated",
-               G_CALLBACK (textview_style_updated_cb), NULL);
        g_signal_connect (
                textview, "query-tooltip",
                G_CALLBACK (textview_query_tooltip), NULL);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       widget = GTK_WIDGET (textview);
+
+       controller = gtk_event_controller_key_new ();
+       g_object_set_data_full (G_OBJECT (textview), E_BUFFER_TAGGER_DATA_KEY_CONTROLLER,
+               g_object_ref (controller), g_object_unref);
+       gtk_widget_add_controller (widget, controller);
+       g_signal_connect_object (controller, "key-pressed",
+               G_CALLBACK (textview_key_press_event), textview, 0);
+
+       controller = gtk_event_controller_legacy_new ();
+       g_object_set_data_full (G_OBJECT (textview), E_BUFFER_TAGGER_DATA_LEGACY_CONTROLLER,
+               g_object_ref (controller), g_object_unref);
+       gtk_widget_add_controller (widget, controller);
+       g_signal_connect_object (controller, "event",
+               G_CALLBACK (textview_event_after), textview, G_CONNECT_SWAPPED);
+
+       controller = gtk_event_controller_motion_new ();
+       g_object_set_data_full (G_OBJECT (textview), E_BUFFER_TAGGER_DATA_MOTION_CONTROLLER,
+               g_object_ref (controller), g_object_unref);
+       gtk_widget_add_controller (widget, controller);
+       g_signal_connect_object (controller, "motion",
+               G_CALLBACK (textview_motion_notify_event), textview, G_CONNECT_SWAPPED);
+
+#else
+       g_signal_connect (
+               textview, "style-updated",
+               G_CALLBACK (textview_style_updated_cb), NULL);
        g_signal_connect (
                textview, "key-press-event",
                G_CALLBACK (textview_key_press_event), NULL);
@@ -879,12 +1075,10 @@ e_buffer_tagger_connect (GtkTextView *textview)
        g_signal_connect (
                textview, "motion-notify-event",
                G_CALLBACK (textview_motion_notify_event), NULL);
-       g_signal_connect (
-               textview, "visibility-notify-event",
-               G_CALLBACK (textview_visibility_notify_event), NULL);
        g_signal_connect (
                textview, "populate-popup",
                G_CALLBACK (textview_populate_popup_cb), NULL);
+#endif
 }
 
 void
@@ -893,6 +1087,9 @@ e_buffer_tagger_disconnect (GtkTextView *textview)
        GtkTextBuffer *buffer;
        GtkTextTagTable *tag_table;
        GtkTextTag *tag;
+#if GTK_CHECK_VERSION(4, 0, 0)
+       GtkWidget *widget;
+#endif
 
        g_return_if_fail (textview != NULL);
        g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
@@ -914,13 +1111,34 @@ e_buffer_tagger_disconnect (GtkTextView *textview)
 
        gtk_widget_set_has_tooltip (GTK_WIDGET (textview), FALSE);
 
-       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_style_updated_cb), NULL);
        g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_query_tooltip), NULL);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       widget = GTK_WIDGET (textview);
+
+#define drop_controller(_name) { \
+       gpointer controller = g_object_get_data (G_OBJECT (textview), _name); \
+       if (controller) \
+               gtk_widget_remove_controller (widget, controller); \
+       g_object_set_data (G_OBJECT (textview), _name, NULL); \
+       }
+
+       drop_controller (E_BUFFER_TAGGER_DATA_KEY_CONTROLLER);
+       drop_controller (E_BUFFER_TAGGER_DATA_LEGACY_CONTROLLER);
+       drop_controller (E_BUFFER_TAGGER_DATA_MOTION_CONTROLLER);
+
+#undef drop_controller
+
+       g_object_set_data (G_OBJECT (textview), E_BUFFER_TAGGER_DATA_CURRENT_URI, NULL);
+
+       gtk_text_view_set_extra_menu (textview, NULL);
+       gtk_widget_insert_action_group (widget, "e-buffer-tagger", NULL);
+#else
+       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_style_updated_cb), NULL);
        g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_key_press_event), NULL);
        g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_event_after), NULL);
        g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_motion_notify_event), NULL);
-       g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_visibility_notify_event), NULL);
        g_signal_handlers_disconnect_by_func (textview, G_CALLBACK (textview_populate_popup_cb), NULL);
+#endif
 }
 
 void
diff --git a/src/libedataserverui/e-cell-renderer-color.c b/src/libedataserverui/e-cell-renderer-color.c
index 3dafaffaf..dc3d7d16b 100644
--- a/src/libedataserverui/e-cell-renderer-color.c
+++ b/src/libedataserverui/e-cell-renderer-color.c
@@ -18,10 +18,10 @@
 
 #include "evolution-data-server-config.h"
 
-#include "e-cell-renderer-color.h"
-
 #include <string.h>
-#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include "e-cell-renderer-color.h"
 
 enum {
        PROP_0,
@@ -38,6 +38,13 @@ G_DEFINE_TYPE_WITH_PRIVATE (
        GTK_TYPE_CELL_RENDERER)
 
 static void
+#if GTK_CHECK_VERSION(4, 0, 0)
+cell_renderer_color_get_aligned_area (GtkCellRenderer *cell,
+                                     GtkWidget *widget,
+                                     GtkCellRendererState flags,
+                                     const GdkRectangle *cell_area,
+                                     GdkRectangle *aligned_area)
+#else
 cell_renderer_color_get_size (GtkCellRenderer *cell,
                               GtkWidget *widget,
                               const GdkRectangle *cell_area,
@@ -45,16 +52,23 @@ cell_renderer_color_get_size (GtkCellRenderer *cell,
                               gint *y_offset,
                               gint *width,
                               gint *height)
+#endif
 {
        gint color_width = 16;
        gint color_height = 16;
        gint calc_width;
        gint calc_height;
+       gint xx, yy;
        gfloat xalign;
        gfloat yalign;
        guint xpad;
        guint ypad;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       if (!aligned_area)
+               return;
+#endif
+
        g_object_get (
                cell, "xalign", &xalign, "yalign", &yalign,
                "xpad", &xpad, "ypad", &ypad, NULL);
@@ -63,38 +77,55 @@ cell_renderer_color_get_size (GtkCellRenderer *cell,
        calc_height = (gint) ypad * 2 + color_height;
 
        if (cell_area && color_width > 0 && color_height > 0) {
-               if (x_offset) {
-                       *x_offset = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
-                                       (1.0 - xalign) : xalign) *
-                                       (cell_area->width - calc_width));
-                       *x_offset = MAX (*x_offset, 0);
-               }
-
-               if (y_offset) {
-                       *y_offset =(yalign *
-                               (cell_area->height - calc_height));
-                       *y_offset = MAX (*y_offset, 0);
-               }
+               xx = (((gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) ?
+                               (1.0 - xalign) : xalign) *
+                               (cell_area->width - calc_width));
+               xx = MAX (xx, 0);
+
+               yy = yalign * (cell_area->height - calc_height);
+               yy = MAX (yy, 0);
        } else {
-               if (x_offset) *x_offset = 0;
-               if (y_offset) *y_offset = 0;
+               xx = 0;
+               yy = 0;
        }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       aligned_area->x = xx;
+       aligned_area->y = yy;
+       aligned_area->width = calc_width;
+       aligned_area->height = calc_height;
+#else
+       if (x_offset)
+               *x_offset = xx;
+       if (y_offset)
+               *y_offset = yy;
        if (width)
                *width = calc_width;
-
        if (height)
                *height = calc_height;
+#endif
 }
 
 static void
+#if GTK_CHECK_VERSION(4, 0, 0)
+cell_renderer_color_snapshot (GtkCellRenderer *cell,
+                             GtkSnapshot *snapshot,
+                             GtkWidget *widget,
+                             const GdkRectangle *background_area,
+                             const GdkRectangle *cell_area,
+                             GtkCellRendererState flags)
+#else
 cell_renderer_color_render (GtkCellRenderer *cell,
                             cairo_t *cr,
                             GtkWidget *widget,
                             const GdkRectangle *background_area,
                             const GdkRectangle *cell_area,
                             GtkCellRendererState flags)
+#endif
 {
+#if GTK_CHECK_VERSION(4, 0, 0)
+       graphene_rect_t rect;
+#endif
        ECellRendererColorPrivate *priv;
        GdkRectangle pix_rect;
        GdkRectangle draw_rect;
@@ -103,10 +134,14 @@ cell_renderer_color_render (GtkCellRenderer *cell,
 
        priv = E_CELL_RENDERER_COLOR (cell)->priv;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       cell_renderer_color_get_aligned_area (cell, widget, 0, cell_area, &pix_rect);
+#else
        cell_renderer_color_get_size (
                cell, widget, cell_area,
                &pix_rect.x, &pix_rect.y,
                &pix_rect.width, &pix_rect.height);
+#endif
 
        g_object_get (cell, "xpad", &xpad, "ypad", &ypad, NULL);
 
@@ -118,11 +153,54 @@ cell_renderer_color_render (GtkCellRenderer *cell,
        if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect))
                return;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       rect.origin.x = pix_rect.x;
+       rect.origin.y = pix_rect.y;
+       rect.size.width = draw_rect.width;
+       rect.size.height = draw_rect.height;
+
+       gtk_snapshot_append_color (snapshot, &priv->rgba, &rect);
+#else
        gdk_cairo_set_source_rgba (cr, &priv->rgba);
        cairo_rectangle (cr, pix_rect.x, pix_rect.y, draw_rect.width, draw_rect.height);
 
        cairo_fill (cr);
+#endif
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void
+cell_renderer_color_get_preferred_width (GtkCellRenderer *cell,
+                                        GtkWidget *widget,
+                                        gint *minimum_size,
+                                        gint *natural_size)
+{
+       gint xpad = 0, ypad = 0;
+
+       gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+       if (minimum_size)
+               *minimum_size = 16 + xpad;
+       if (natural_size)
+               *natural_size = 16 + xpad;
+}
+
+static void
+cell_renderer_color_get_preferred_height (GtkCellRenderer *cell,
+                                         GtkWidget *widget,
+                                         gint *minimum_size,
+                                         gint *natural_size)
+{
+       gint xpad = 0, ypad = 0;
+
+       gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
+
+       if (minimum_size)
+               *minimum_size = 16 + ypad;
+       if (natural_size)
+               *natural_size = 16 + ypad;
 }
+#endif
 
 static void
 cell_renderer_color_set_property (GObject *object,
@@ -183,8 +261,15 @@ e_cell_renderer_color_class_init (ECellRendererColorClass *class)
        object_class->get_property = cell_renderer_color_get_property;
 
        cell_class = GTK_CELL_RENDERER_CLASS (class);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       cell_class->get_aligned_area = cell_renderer_color_get_aligned_area;
+       cell_class->snapshot = cell_renderer_color_snapshot;
+       cell_class->get_preferred_width = cell_renderer_color_get_preferred_width;
+       cell_class->get_preferred_height = cell_renderer_color_get_preferred_height;
+#else
        cell_class->get_size = cell_renderer_color_get_size;
        cell_class->render = cell_renderer_color_render;
+#endif
 
        g_object_class_install_property (
                object_class,
diff --git a/src/libedataserverui/e-credentials-prompter-impl-oauth2.c 
b/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
index 60e053027..57820e7b5 100644
--- a/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
+++ b/src/libedataserverui/e-credentials-prompter-impl-oauth2.c
@@ -24,12 +24,27 @@
 
 #include <libedataserver/libedataserver.h>
 
+#include "libedataserverui-private.h"
 #include "e-credentials-prompter.h"
 #include "e-credentials-prompter-impl-oauth2.h"
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#if GTK_CHECK_VERSION(4, 0, 0)
+#ifdef ENABLE_OAUTH2_WEBKITGTK4
+#define WITH_WEBKITGTK 1
+#else
+#undef WITH_WEBKITGTK
+#endif
+#else /* !gtk4 */
+#ifdef ENABLE_OAUTH2_WEBKITGTK3
+#define WITH_WEBKITGTK 1
+#else
+#undef WITH_WEBKITGTK
+#endif
+#endif /* !gtk4 */
+
+#ifdef WITH_WEBKITGTK
 #include <webkit2/webkit2.h>
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
 struct _ECredentialsPrompterImplOAuth2Private {
        GMutex property_lock;
@@ -45,7 +60,7 @@ struct _ECredentialsPrompterImplOAuth2Private {
        gboolean refresh_failed_with_transport_error;
 
        GtkDialog *dialog;
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        WebKitWebView *web_view;
 #endif
        GtkNotebook *notebook;
@@ -102,15 +117,15 @@ cpi_oauth2_show_error (ECredentialsPrompterImplOAuth2 *prompter_oauth2,
                       const gchar *title,
                       const gchar *body_text)
 {
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        gchar *html;
 
        g_return_if_fail (WEBKIT_IS_WEB_VIEW (prompter_oauth2->priv->web_view));
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
        g_return_if_fail (title != NULL);
        g_return_if_fail (body_text != NULL);
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        html = g_markup_printf_escaped (
                "<html>"
                "<head><title>%s</title></head>"
@@ -120,7 +135,7 @@ cpi_oauth2_show_error (ECredentialsPrompterImplOAuth2 *prompter_oauth2,
                body_text);
        webkit_web_view_load_html (prompter_oauth2->priv->web_view, html, "none-local://");
        g_free (html);
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
        gtk_label_set_text (prompter_oauth2->priv->error_text_label, body_text);
 }
@@ -264,7 +279,7 @@ cpi_oauth2_test_authorization_code (ECredentialsPrompterImplOAuth2 *prompter_oau
        }
 }
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
 
 static void
 cpi_oauth2_extract_authentication_code (ECredentialsPrompterImplOAuth2 *prompter_oauth2,
@@ -421,7 +436,7 @@ cpi_oauth2_notify_estimated_load_progress_cb (WebKitWebView *web_view,
        gtk_progress_bar_set_fraction (progress_bar, visible ? progress : 0.0);
 }
 
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
 static void
 credentials_prompter_impl_oauth2_get_prompt_strings (ESourceRegistry *registry,
@@ -581,7 +596,7 @@ credentials_prompter_impl_oauth2_get_prompt_strings (ESourceRegistry *registry,
        g_free (display_name);
 }
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
 
 static void
 credentials_prompter_impl_oauth2_set_proxy (WebKitWebContext *web_context,
@@ -706,7 +721,7 @@ credentials_prompter_impl_oauth2_set_proxy (WebKitWebContext *web_context,
 
        g_clear_object (&proxy_source);
 }
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
 static void
 cpi_oauth2_url_entry_icon_release_cb (GtkEntry *entry,
@@ -717,8 +732,15 @@ cpi_oauth2_url_entry_icon_release_cb (GtkEntry *entry,
        ECredentialsPrompterImplOAuth2 *prompter_oauth2 = user_data;
        gpointer toplevel;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       toplevel = GTK_WIDGET (entry);
+       while (toplevel && !GTK_IS_WINDOW (toplevel)) {
+               toplevel = gtk_widget_get_parent (toplevel);
+       }
+#else
        toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
        toplevel = gtk_widget_is_toplevel (toplevel) ? toplevel : NULL;
+#endif
 
        if (icon_position == GTK_ENTRY_ICON_SECONDARY) {
                GError *error = NULL;
@@ -730,6 +752,10 @@ cpi_oauth2_url_entry_icon_release_cb (GtkEntry *entry,
                if (cpi_oauth2_get_debug ())
                        e_util_debug_print ("OAuth2", "Opening URI in browser: '%s'\n", uri);
 
+               #if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_show_uri (toplevel ? GTK_WINDOW (toplevel) : NULL, uri, GDK_CURRENT_TIME);
+               gtk_notebook_set_current_page (prompter_oauth2->priv->notebook, 0);
+               #else
                if (!
                        #if GTK_CHECK_VERSION (3, 22, 0)
                        gtk_show_uri_on_window (toplevel ? GTK_WINDOW (toplevel) : NULL,
@@ -744,12 +770,58 @@ cpi_oauth2_url_entry_icon_release_cb (GtkEntry *entry,
                } else {
                        gtk_notebook_set_current_page (prompter_oauth2->priv->notebook, 0);
                }
+               #endif
 
                g_clear_error (&error);
                g_free (uri);
        }
 }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+static const gchar *
+gtk_entry_get_text (GtkEntry *entry)
+{
+       return gtk_entry_buffer_get_text (gtk_entry_get_buffer (entry));
+}
+
+static void
+gtk_entry_set_text (GtkEntry *entry,
+                   const gchar *text)
+{
+       gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry), text, -1);
+}
+
+static void
+gtk_box_pack_start (GtkBox *box,
+                   GtkWidget *child,
+                   gboolean expand,
+                   gboolean fill,
+                   guint padding)
+{
+       if (gtk_orientable_get_orientation (GTK_ORIENTABLE (box)) == GTK_ORIENTATION_VERTICAL) {
+               if (expand)
+                       gtk_widget_set_hexpand (child, TRUE);
+               if (fill)
+                       gtk_widget_set_halign (child, GTK_ALIGN_FILL);
+               if (padding) {
+                       gtk_widget_set_margin_start (child, padding + gtk_widget_get_margin_start (child));
+                       gtk_widget_set_margin_end (child, padding + gtk_widget_get_margin_end (child));
+               }
+       } else {
+               if (expand)
+                       gtk_widget_set_vexpand (child, TRUE);
+               if (fill)
+                       gtk_widget_set_valign (child, GTK_ALIGN_FILL);
+               if (padding) {
+                       gtk_widget_set_margin_top (child, padding + gtk_widget_get_margin_top (child));
+                       gtk_widget_set_margin_bottom (child, padding + gtk_widget_get_margin_bottom (child));
+               }
+       }
+
+       gtk_box_append (box, child);
+}
+#endif /* GTK_CHECK_VERSION(4, 0, 0) */
+
 static void
 cpi_oauth2_manual_continue_clicked_cb (GtkButton *button,
                                       gpointer user_data)
@@ -788,12 +860,12 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
        GtkGrid *grid;
        GtkWindow *dialog_parent;
        ECredentialsPrompter *prompter;
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        GtkScrolledWindow *scrolled_window;
        GtkWidget *progress_bar;
        WebKitSettings *webkit_settings;
        WebKitWebContext *web_context;
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
        gchar *title, *uri;
        GString *info_markup;
        gint row = 0;
@@ -825,15 +897,18 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
                _("_Cancel"), GTK_RESPONSE_CANCEL,
                NULL);
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 680);
 #endif
        gtk_widget_set_name (dialog, "oauth2-prompt");
 
        css_provider = gtk_css_provider_new ();
        gtk_css_provider_load_from_data (css_provider,
-               "#oauth2-prompt { -GtkDialog-action-area-border:0px; -GtkDialog-content-area-border:0px; }",
-               -1, &error);
+               "#oauth2-prompt { -GtkDialog-action-area-border:0px; -GtkDialog-content-area-border:0px; }", 
-1
+       #if !GTK_CHECK_VERSION(4, 0, 0)
+               , &error
+       #endif
+       );
        style_context = gtk_widget_get_style_context (dialog);
        if (error == NULL) {
                gtk_style_context_add_provider (
@@ -850,14 +925,18 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
        gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
        if (dialog_parent)
                gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
+       #if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
        gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
+       #endif
 
        content_area = gtk_dialog_get_content_area (prompter_oauth2->priv->dialog);
 
        /* Override GtkDialog defaults */
        gtk_box_set_spacing (GTK_BOX (content_area), 12);
+       #if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+       #endif
 
        grid = GTK_GRID (gtk_grid_new ());
        gtk_grid_set_column_spacing (grid, 12);
@@ -867,7 +946,6 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
 
        /* Info Label */
        widget = gtk_label_new (NULL);
-       gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
        gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
        g_object_set (
                G_OBJECT (widget),
@@ -878,6 +956,7 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
                "width-chars", 60,
                "max-width-chars", 80,
                "xalign", 0.0,
+               "wrap", TRUE,
                NULL);
 
        gtk_grid_attach (grid, widget, 0, row, 1, 1);
@@ -923,7 +1002,9 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
                NULL);
 
        style_context = gtk_widget_get_style_context (url_entry);
+       #if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LABEL);
+       #endif
        gtk_style_context_set_state (style_context, GTK_STATE_FLAG_INSENSITIVE);
 
        gtk_entry_set_icon_tooltip_text (GTK_ENTRY (url_entry), GTK_ENTRY_ICON_SECONDARY, _("Click here to 
open the URL"));
@@ -1046,7 +1127,7 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
 
        prompter_oauth2->priv->error_text_label = GTK_LABEL (widget);
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 1);
        g_object_set (
                G_OBJECT (vbox),
@@ -1131,20 +1212,22 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
        gtk_style_context_add_class (gtk_widget_get_style_context (progress_bar), GTK_STYLE_CLASS_OSD);
 
        gtk_box_pack_start (GTK_BOX (vbox), progress_bar, FALSE, FALSE, 0);
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
+       #if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_widget_show_all (GTK_WIDGET (grid));
+       #endif
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        /* Switch to the last page, to prefer the built-in browser */
        gtk_notebook_set_current_page (prompter_oauth2->priv->notebook, -1);
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
        uri = cpi_oauth2_create_auth_uri (prompter_oauth2->priv->service, prompter_oauth2->priv->cred_source);
        if (!uri) {
                success = FALSE;
        } else {
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
                WebKitWebView *web_view = prompter_oauth2->priv->web_view;
                gulong decide_policy_handler_id, load_finished_handler_id, progress_handler_id;
 
@@ -1156,20 +1239,20 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
                        G_CALLBACK (cpi_oauth2_notify_estimated_load_progress_cb), progress_bar);
 
                webkit_web_view_load_uri (web_view, uri);
-#else /* ENABLE_OAUTH2_WEBKITGTK */
+#else /* WITH_WEBKITGTK */
                gtk_entry_set_text (GTK_ENTRY (url_entry), uri);
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
 
-               success = gtk_dialog_run (prompter_oauth2->priv->dialog) == GTK_RESPONSE_OK;
+               success = _libedataserverui_run_dialog (prompter_oauth2->priv->dialog) == GTK_RESPONSE_OK;
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
                if (decide_policy_handler_id)
                        g_signal_handler_disconnect (web_view, decide_policy_handler_id);
                if (load_finished_handler_id)
                        g_signal_handler_disconnect (web_view, load_finished_handler_id);
                if (progress_handler_id)
                        g_signal_handler_disconnect (web_view, progress_handler_id);
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
        }
 
        g_free (uri);
@@ -1177,11 +1260,16 @@ e_credentials_prompter_impl_oauth2_show_dialog (ECredentialsPrompterImplOAuth2 *
        if (prompter_oauth2->priv->cancellable)
                g_cancellable_cancel (prompter_oauth2->priv->cancellable);
 
-#ifdef ENABLE_OAUTH2_WEBKITGTK
+#ifdef WITH_WEBKITGTK
        prompter_oauth2->priv->web_view = NULL;
-#endif /* ENABLE_OAUTH2_WEBKITGTK */
+#endif /* WITH_WEBKITGTK */
        prompter_oauth2->priv->dialog = NULL;
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_window_destroy (GTK_WINDOW (dialog));
+#else
        gtk_widget_destroy (dialog);
+#endif
 
        g_string_free (info_markup, TRUE);
        g_free (title);
diff --git a/src/libedataserverui/e-credentials-prompter-impl-password.c 
b/src/libedataserverui/e-credentials-prompter-impl-password.c
index c2409fa14..bda3bcd29 100644
--- a/src/libedataserverui/e-credentials-prompter-impl-password.c
+++ b/src/libedataserverui/e-credentials-prompter-impl-password.c
@@ -42,6 +42,7 @@ struct _ECredentialsPrompterImplPasswordPrivate {
 
 G_DEFINE_TYPE_WITH_PRIVATE (ECredentialsPrompterImplPassword, e_credentials_prompter_impl_password, 
E_TYPE_CREDENTIALS_PROMPTER_IMPL)
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
 static gboolean
 password_dialog_map_event_cb (GtkWidget *dialog,
                              GdkEvent *event,
@@ -51,6 +52,7 @@ password_dialog_map_event_cb (GtkWidget *dialog,
 
        return FALSE;
 }
+#endif
 
 static void
 credentials_prompter_impl_password_get_prompt_strings (ESourceRegistry *registry,
@@ -198,6 +200,22 @@ credentials_prompter_impl_password_get_prompt_strings (ESourceRegistry *registry
        g_free (host_name);
 }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+
+static const gchar *
+gtk_entry_get_text (GtkEntry *entry)
+{
+       return gtk_entry_buffer_get_text (gtk_entry_get_buffer (entry));
+}
+
+static void
+gtk_entry_set_text (GtkEntry *entry,
+                   const gchar *text)
+{
+       gtk_entry_buffer_set_text (gtk_entry_get_buffer (entry), text, -1);
+}
+#endif /* GTK_CHECK_VERSION(4, 0, 0) */
+
 static gboolean
 e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPassword *prompter_password)
 {
@@ -205,7 +223,11 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
        GtkGrid *grid;
        GtkEntry *username_entry = NULL;
        GtkEntry *password_entry;
+#if GTK_CHECK_VERSION(4, 0, 0)
+       GtkCheckButton *remember_check = NULL;
+#else
        GtkToggleButton *remember_toggle = NULL;
+#endif
        GtkWindow *dialog_parent;
        ECredentialsPrompter *prompter;
        gchar *title;
@@ -243,23 +265,44 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
        gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
        if (dialog_parent)
                gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
-       gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
-       gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
 
        content_area = gtk_dialog_get_content_area (prompter_password->priv->dialog);
+       g_object_set (G_OBJECT (content_area),
+               "margin-start", 12,
+               "margin-end", 12,
+               "margin-top", 12,
+               "margin-bottom", 12,
+               NULL);
+
+#if !GTK_CHECK_VERSION(4, 0, 0)
+       gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
+       gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+#endif
 
        /* Override GtkDialog defaults */
        gtk_box_set_spacing (GTK_BOX (content_area), 12);
-       gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
 
        grid = GTK_GRID (gtk_grid_new ());
        gtk_grid_set_column_spacing (grid, 12);
        gtk_grid_set_row_spacing (grid, 6);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       g_object_set (G_OBJECT (grid),
+               "halign", GTK_ALIGN_FILL,
+               "valign", GTK_ALIGN_FILL,
+               NULL);
+       gtk_box_append (GTK_BOX (content_area), GTK_WIDGET (grid));
+#else
        gtk_box_pack_start (GTK_BOX (content_area), GTK_WIDGET (grid), FALSE, TRUE, 0);
+#endif
 
        /* Password Image */
+#if GTK_CHECK_VERSION(4, 0, 0)
+       widget = gtk_image_new_from_icon_name ("dialog-password");
+       gtk_image_set_pixel_size (GTK_IMAGE (widget), 48);
+#else
        widget = gtk_image_new_from_icon_name ("dialog-password", GTK_ICON_SIZE_DIALOG);
+#endif
        g_object_set (
                G_OBJECT (widget),
                "halign", GTK_ALIGN_START,
@@ -271,7 +314,11 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
 
        /* Password Label */
        widget = gtk_label_new (NULL);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_label_set_wrap (GTK_LABEL (widget), TRUE);
+#else
        gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+#endif
        gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
        g_object_set (
                G_OBJECT (widget),
@@ -346,8 +393,13 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
        if (e_named_parameters_get (prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
                gtk_entry_set_text (password_entry, e_named_parameters_get 
(prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
 
-       g_signal_connect (dialog, "map-event", G_CALLBACK (password_dialog_map_event_cb),
-               (username_entry && g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (username_entry)), "") == 0) ? 
username_entry : password_entry);
+       widget = GTK_WIDGET ((username_entry && g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (username_entry)), 
"") == 0) ? username_entry : password_entry);
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+       g_signal_connect_swapped (dialog, "map", G_CALLBACK (gtk_widget_grab_focus), widget);
+#else
+       g_signal_connect (dialog, "map-event", G_CALLBACK (password_dialog_map_event_cb), widget);
+#endif
 
        gtk_grid_attach (grid, GTK_WIDGET (password_entry), 1, row, 1, 1);
        row++;
@@ -382,8 +434,13 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
        if (auth_extension && !is_scratch_source) {
                /* Remember password check */
                widget = gtk_check_button_new_with_mnemonic (_("_Add this password to your keyring"));
+#if GTK_CHECK_VERSION(4, 0, 0)
+               remember_check = GTK_CHECK_BUTTON (widget);
+               gtk_check_button_set_active (remember_check, e_source_authentication_get_remember_password 
(auth_extension));
+#else
                remember_toggle = GTK_TOGGLE_BUTTON (widget);
                gtk_toggle_button_set_active (remember_toggle, e_source_authentication_get_remember_password 
(auth_extension));
+#endif
                g_object_set (
                        G_OBJECT (widget),
                        "hexpand", TRUE,
@@ -395,9 +452,11 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
                gtk_grid_attach (grid, widget, 1, row, 1, 1);
        }
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_widget_show_all (GTK_WIDGET (grid));
+#endif
 
-       success = gtk_dialog_run (prompter_password->priv->dialog) == GTK_RESPONSE_OK;
+       success = _libedataserverui_run_dialog (prompter_password->priv->dialog) == GTK_RESPONSE_OK;
 
        if (success) {
                if (username_entry)
@@ -406,13 +465,24 @@ e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPasswo
                e_named_parameters_set (prompter_password->priv->credentials,
                        E_SOURCE_CREDENTIAL_PASSWORD, gtk_entry_get_text (password_entry));
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+               if (auth_extension && remember_check) {
+                       e_source_authentication_set_remember_password (auth_extension,
+                               gtk_check_button_get_active (remember_check));
+               }
+#else
                if (auth_extension && remember_toggle) {
                        e_source_authentication_set_remember_password (auth_extension,
                                gtk_toggle_button_get_active (remember_toggle));
                }
+#endif
        }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_window_destroy (GTK_WINDOW (dialog));
+#else
        gtk_widget_destroy (dialog);
+#endif
        prompter_password->priv->dialog = NULL;
 
        g_string_free (info_markup, TRUE);
diff --git a/src/libedataserverui/e-reminders-widget.c b/src/libedataserverui/e-reminders-widget.c
index 52634678c..09edc1359 100644
--- a/src/libedataserverui/e-reminders-widget.c
+++ b/src/libedataserverui/e-reminders-widget.c
@@ -1240,6 +1240,60 @@ reminders_widget_add_snooze_update_sensitize_cb (GtkSpinButton *spin,
                gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (reminders->priv->add_snooze_days_spin)) > 
0);
 }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+static void
+gtk_box_pack_start (GtkBox *box,
+                   GtkWidget *child,
+                   gboolean expand,
+                   gboolean fill,
+                   guint padding)
+{
+       if (gtk_orientable_get_orientation (GTK_ORIENTABLE (box)) == GTK_ORIENTATION_VERTICAL) {
+               if (expand)
+                       gtk_widget_set_hexpand (child, TRUE);
+               if (fill)
+                       gtk_widget_set_halign (child, GTK_ALIGN_FILL);
+               if (padding) {
+                       gtk_widget_set_margin_start (child, padding + gtk_widget_get_margin_start (child));
+                       gtk_widget_set_margin_end (child, padding + gtk_widget_get_margin_end (child));
+               }
+       } else {
+               if (expand)
+                       gtk_widget_set_vexpand (child, TRUE);
+               if (fill)
+                       gtk_widget_set_valign (child, GTK_ALIGN_FILL);
+               if (padding) {
+                       gtk_widget_set_margin_top (child, padding + gtk_widget_get_margin_top (child));
+                       gtk_widget_set_margin_bottom (child, padding + gtk_widget_get_margin_bottom (child));
+               }
+       }
+
+       gtk_box_append (box, child);
+}
+
+static void
+gtk_paned_pack1 (GtkPaned *paned,
+                GtkWidget *widget,
+                gboolean resize,
+                gboolean shrink)
+{
+       gtk_paned_set_start_child (paned, widget);
+       gtk_paned_set_resize_start_child (paned, resize);
+       gtk_paned_set_shrink_start_child (paned, shrink);
+}
+
+static void
+gtk_paned_pack2 (GtkPaned *paned,
+                GtkWidget *widget,
+                gboolean resize,
+                gboolean shrink)
+{
+       gtk_paned_set_end_child (paned, widget);
+       gtk_paned_set_resize_end_child (paned, resize);
+       gtk_paned_set_shrink_end_child (paned, shrink);
+}
+#endif /* GTK_CHECK_VERSION(4, 0, 0) */
+
 static void
 reminders_widget_snooze_add_custom (ERemindersWidget *reminders)
 {
@@ -1331,14 +1385,28 @@ reminders_widget_snooze_add_custom (ERemindersWidget *reminders)
                        "halign", GTK_ALIGN_CENTER,
                        NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_box_append (vbox, reminders->priv->add_snooze_add_button);
+#else
                gtk_box_pack_start (vbox, reminders->priv->add_snooze_add_button, FALSE, FALSE, 0);
-
                gtk_widget_show_all (GTK_WIDGET (vbox));
-
+#endif
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+               reminders->priv->add_snooze_popover = gtk_popover_new ();
+               g_object_set (G_OBJECT (vbox),
+                       "margin-start", 6,
+                       "margin-end", 6,
+                       "margin-top", 6,
+                       "margin-bottom", 6,
+                       NULL);
+               gtk_popover_set_child (GTK_POPOVER (reminders->priv->add_snooze_popover), GTK_WIDGET (vbox));
+#else
                reminders->priv->add_snooze_popover = gtk_popover_new (GTK_WIDGET (reminders));
-               gtk_popover_set_position (GTK_POPOVER (reminders->priv->add_snooze_popover), GTK_POS_BOTTOM);
                gtk_container_add (GTK_CONTAINER (reminders->priv->add_snooze_popover), GTK_WIDGET (vbox));
                gtk_container_set_border_width (GTK_CONTAINER (reminders->priv->add_snooze_popover), 6);
+#endif
+               gtk_popover_set_position (GTK_POPOVER (reminders->priv->add_snooze_popover), GTK_POS_BOTTOM);
 
                g_signal_connect (reminders->priv->add_snooze_add_button, "clicked",
                        G_CALLBACK (reminders_widget_add_snooze_add_button_clicked_cb), reminders);
@@ -1372,7 +1440,12 @@ reminders_widget_snooze_add_custom (ERemindersWidget *reminders)
        }
 
        gtk_widget_hide (reminders->priv->add_snooze_popover);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       if (gtk_widget_get_parent (GTK_WIDGET (reminders->priv->add_snooze_popover)) == NULL)
+               gtk_widget_set_parent (GTK_WIDGET (reminders->priv->add_snooze_popover), 
reminders->priv->snooze_combo);
+#else
        gtk_popover_set_relative_to (GTK_POPOVER (reminders->priv->add_snooze_popover), 
reminders->priv->snooze_combo);
+#endif
        gtk_widget_show (reminders->priv->add_snooze_popover);
 
        gtk_widget_grab_focus (reminders->priv->add_snooze_days_spin);
@@ -1556,7 +1629,11 @@ reminders_widget_constructed (GObject *object)
 
        gtk_grid_attach (GTK_GRID (reminders), GTK_WIDGET (reminders->priv->paned), 0, 0, 1, 1);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       scrolled_window = gtk_scrolled_window_new ();
+#else
        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+#endif
        g_object_set (G_OBJECT (scrolled_window),
                "halign", GTK_ALIGN_FILL,
                "hexpand", TRUE,
@@ -1564,7 +1641,11 @@ reminders_widget_constructed (GObject *object)
                "vexpand", TRUE,
                "hscrollbar-policy", GTK_POLICY_NEVER,
                "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+#if GTK_CHECK_VERSION(4, 0, 0)
+               "has-frame", TRUE,
+#else
                "shadow-type", GTK_SHADOW_IN,
+#endif
                NULL);
 
        gtk_paned_pack1 (reminders->priv->paned, scrolled_window, FALSE, FALSE);
@@ -1590,7 +1671,11 @@ reminders_widget_constructed (GObject *object)
                "hover-selection", FALSE,
                NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), GTK_WIDGET 
(reminders->priv->tree_view));
+#else
        gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (reminders->priv->tree_view));
+#endif
 
        e_binding_bind_property (reminders, "empty",
                scrolled_window, "sensitive",
@@ -1616,7 +1701,11 @@ reminders_widget_constructed (GObject *object)
        column = gtk_tree_view_get_column (reminders->priv->tree_view, 1);
        gtk_tree_view_column_set_expand (column, TRUE);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       scrolled_window = gtk_scrolled_window_new ();
+#else
        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+#endif
        g_object_set (G_OBJECT (scrolled_window),
                "halign", GTK_ALIGN_FILL,
                "hexpand", TRUE,
@@ -1624,7 +1713,11 @@ reminders_widget_constructed (GObject *object)
                "vexpand", TRUE,
                "hscrollbar-policy", GTK_POLICY_NEVER,
                "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+#if GTK_CHECK_VERSION(4, 0, 0)
+               "has-frame", TRUE,
+#else
                "shadow-type", GTK_SHADOW_IN,
+#endif
                NULL);
 
        gtk_paned_pack2 (reminders->priv->paned, scrolled_window, TRUE, FALSE);
@@ -1640,7 +1733,11 @@ reminders_widget_constructed (GObject *object)
                "wrap-mode", GTK_WRAP_WORD_CHAR,
                NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), GTK_WIDGET 
(reminders->priv->details_text_view));
+#else
        gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (reminders->priv->details_text_view));
+#endif
 
        e_buffer_tagger_connect (reminders->priv->details_text_view);
 
@@ -1652,7 +1749,7 @@ reminders_widget_constructed (GObject *object)
        reminders_widget_fill_snooze_combo (reminders,
                g_settings_get_int (reminders->priv->settings, "notify-last-snooze-minutes"));
 
-       box = GTK_BOX (gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL));
+       box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4));
        g_object_set (G_OBJECT (box),
                "halign", GTK_ALIGN_END,
                "hexpand", TRUE,
@@ -1669,12 +1766,11 @@ reminders_widget_constructed (GObject *object)
        gtk_box_pack_start (box, reminders->priv->dismiss_button, FALSE, FALSE, 0);
        gtk_box_pack_start (box, reminders->priv->dismiss_all_button, FALSE, FALSE, 0);
 
-       gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (box), reminders->priv->snooze_combo, TRUE);
-       gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (box), widget, TRUE);
-
        gtk_grid_attach (GTK_GRID (reminders), GTK_WIDGET (box), 0, 1, 1, 1);
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_widget_show_all (GTK_WIDGET (reminders));
+#endif
 
        selection = gtk_tree_view_get_selection (reminders->priv->tree_view);
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
@@ -1737,6 +1833,11 @@ reminders_widget_dispose (GObject *object)
        if (reminders->priv->settings)
                g_signal_handlers_disconnect_by_data (reminders->priv->settings, reminders);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       if (reminders->priv->add_snooze_popover)
+               gtk_widget_unparent (reminders->priv->add_snooze_popover);
+#endif
+
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_reminders_widget_parent_class)->dispose (object);
 }
@@ -1976,7 +2077,7 @@ reminders_widget_error_response_cb (GtkInfoBar *info_bar,
        g_return_if_fail (E_IS_REMINDERS_WIDGET (reminders));
 
        if (reminders->priv->info_bar == info_bar) {
-               gtk_widget_destroy (GTK_WIDGET (reminders->priv->info_bar));
+               g_object_unref (reminders->priv->info_bar);
                reminders->priv->info_bar = NULL;
        }
 }
@@ -2019,7 +2120,7 @@ e_reminders_widget_report_error (ERemindersWidget *reminders,
        }
 
        if (reminders->priv->info_bar) {
-               gtk_widget_destroy (GTK_WIDGET (reminders->priv->info_bar));
+               g_object_unref (reminders->priv->info_bar);
                reminders->priv->info_bar = NULL;
        }
 
@@ -2030,11 +2131,16 @@ e_reminders_widget_report_error (ERemindersWidget *reminders,
        label = GTK_LABEL (gtk_label_new (message));
        gtk_label_set_width_chars (label, 20);
        gtk_label_set_max_width_chars (label, 120);
-       gtk_label_set_line_wrap (label, TRUE);
        gtk_label_set_selectable (label, TRUE);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_label_set_wrap (label, TRUE);
+       gtk_info_bar_add_child (GTK_INFO_BAR (reminders->priv->info_bar), GTK_WIDGET (label));
+#else
+       gtk_label_set_line_wrap (label, TRUE);
        gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (reminders->priv->info_bar)), 
GTK_WIDGET (label));
        gtk_widget_show (GTK_WIDGET (label));
        gtk_widget_show (GTK_WIDGET (reminders->priv->info_bar));
+#endif
 
        g_signal_connect (reminders->priv->info_bar, "response", G_CALLBACK 
(reminders_widget_error_response_cb), reminders);
 
diff --git a/src/libedataserverui/e-trust-prompt.c b/src/libedataserverui/e-trust-prompt.c
index 4604c33ad..de368d44f 100644
--- a/src/libedataserverui/e-trust-prompt.c
+++ b/src/libedataserverui/e-trust-prompt.c
@@ -21,10 +21,15 @@
 
 #include <glib.h>
 #include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+#define GcrParsed void
+#else
 #define GCR_API_SUBJECT_TO_CHANGE
 #include <gcr/gcr.h>
 #undef GCR_API_SUBJECT_TO_CHANGE
+#endif
 
 #include "camel/camel.h"
 #include "libedataserver/libedataserver.h"
@@ -57,13 +62,27 @@ trust_prompt_add_info_line (GtkGrid *grid,
        pango_attr_list_insert (bold, attr);
 
        widget = gtk_label_new (label_text);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       g_object_set (
+               G_OBJECT (widget),
+               "halign", GTK_ALIGN_START,
+               "valign", GTK_ALIGN_START,
+               "xalign", 0.0,
+               "yalign", 0.0,
+               NULL);
+#else
        gtk_misc_set_padding (GTK_MISC (widget), 0, 0);
        gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+#endif
 
        gtk_grid_attach (grid, widget, 1, *at_row, 1, 1);
 
        widget = gtk_label_new (value_text);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_label_set_wrap (GTK_LABEL (widget), wrap);
+#else
        gtk_label_set_line_wrap (GTK_LABEL (widget), wrap);
+#endif
        g_object_set (
                G_OBJECT (widget),
                "hexpand", TRUE,
@@ -97,14 +116,16 @@ trust_prompt_show (GtkWindow *parent,
                   gpointer user_data)
 {
        ETrustPromptResponse response;
+#if !GTK_CHECK_VERSION(4, 0, 0)
        GcrCertificateWidget *certificate_widget;
        GcrCertificate *certificate;
        GckAttributes *attributes;
+       const guchar *data;
+       gsize length;
+#endif
        GtkWidget *dialog, *widget;
        GtkGrid *grid;
-       const guchar *data;
        gchar *bhost, *tmp;
-       gsize length;
        gint row = 0;
 
        _libedataserverui_init_icon_theme ();
@@ -119,7 +140,9 @@ trust_prompt_show (GtkWindow *parent,
 
        widget = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+#endif
 
        grid = g_object_new (
                GTK_TYPE_GRID,
@@ -134,10 +157,25 @@ trust_prompt_show (GtkWindow *parent,
                "valign", GTK_ALIGN_FILL,
                NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       g_object_set (G_OBJECT (grid),
+               "halign", GTK_ALIGN_FILL,
+               "valign", GTK_ALIGN_FILL,
+               "margin-start", 5,
+               "margin-end", 5,
+               "margin-top", 5,
+               "margin-bottom", 5,
+               NULL);
+       gtk_box_append (GTK_BOX (widget), GTK_WIDGET (grid));
+
+       widget = gtk_image_new_from_icon_name ("dialog-warning");
+       gtk_image_set_pixel_size (GTK_IMAGE (widget), 48);
+#else
        gtk_container_set_border_width (GTK_CONTAINER (grid), 5);
        gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
 
        widget = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_DIALOG);
+#endif
        g_object_set (
                G_OBJECT (widget),
                "vexpand", FALSE,
@@ -189,7 +227,11 @@ trust_prompt_show (GtkWindow *parent,
        g_free (bhost);
 
        widget = gtk_label_new (NULL);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_label_set_wrap (GTK_LABEL (widget), TRUE);
+#else
        gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+#endif
        gtk_label_set_markup (GTK_LABEL (widget), tmp);
        g_object_set (
                G_OBJECT (widget),
@@ -212,6 +254,7 @@ trust_prompt_show (GtkWindow *parent,
        if (error_text)
                trust_prompt_add_info_line (grid, _("Detailed error:"), error_text, FALSE, TRUE, FALSE, &row);
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        data = gcr_parsed_get_data (parsed, &length);
        attributes = gcr_parsed_get_attributes (parsed);
 
@@ -225,13 +268,16 @@ trust_prompt_show (GtkWindow *parent,
        gtk_widget_show (widget);
 
        g_clear_object (&certificate);
+#endif
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_widget_show_all (GTK_WIDGET (grid));
+#endif
 
        if (dialog_ready_cb)
                dialog_ready_cb (GTK_DIALOG (dialog), user_data);
 
-       switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
+       switch (_libedataserverui_run_dialog (GTK_DIALOG (dialog))) {
        case GTK_RESPONSE_REJECT:
                response = E_TRUST_PROMPT_RESPONSE_REJECT;
                break;
@@ -246,7 +292,11 @@ trust_prompt_show (GtkWindow *parent,
                break;
        }
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_window_destroy (GTK_WINDOW (dialog));
+#else
        gtk_widget_destroy (dialog);
+#endif
 
        return response;
 }
@@ -300,6 +350,7 @@ e_trust_prompt_describe_certificate_errors (GTlsCertificateFlags flags)
        return g_string_free (reason, FALSE);
 }
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
 static void
 trust_prompt_parser_parsed_cb (GcrParser *parser,
                               GcrParsed **out_parsed)
@@ -311,6 +362,7 @@ trust_prompt_parser_parsed_cb (GcrParser *parser,
 
        *out_parsed = gcr_parsed_ref (parsed);
 }
+#endif
 
 static ETrustPromptResponse
 e_trust_prompt_run_with_dialog_ready_callback (GtkWindow *parent,
@@ -324,7 +376,9 @@ e_trust_prompt_run_with_dialog_ready_callback (GtkWindow *parent,
                                               gpointer user_data)
 {
        ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+#if !GTK_CHECK_VERSION(4, 0, 0)
        GcrParser *parser;
+#endif
        GcrParsed *parsed = NULL;
        GError *local_error = NULL;
 
@@ -333,6 +387,7 @@ e_trust_prompt_run_with_dialog_ready_callback (GtkWindow *parent,
        g_return_val_if_fail (host != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
        g_return_val_if_fail (certificate_pem != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        /* Continue even if PKCS#11 module registration fails.
         * Certificate details won't display correctly but the
         * user can still respond to the prompt. */
@@ -358,13 +413,18 @@ e_trust_prompt_run_with_dialog_ready_callback (GtkWindow *parent,
                ((parsed == NULL) && (local_error != NULL)));
 
        if (parsed != NULL) {
+#else
+       {
+#endif
                gchar *reason;
 
                reason = e_trust_prompt_describe_certificate_errors (certificate_errors);
 
                response = trust_prompt_show (parent, source_extension, source_display_name, host, 
error_text, parsed, reason, dialog_ready_cb, user_data);
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
                gcr_parsed_unref (parsed);
+#endif
                g_free (reason);
        }
 
diff --git a/src/libedataserverui/e-webdav-discover-widget.c b/src/libedataserverui/e-webdav-discover-widget.c
index c1679630a..b7d294e0e 100644
--- a/src/libedataserverui/e-webdav-discover-widget.c
+++ b/src/libedataserverui/e-webdav-discover-widget.c
@@ -137,7 +137,14 @@ e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
        self = g_object_new (E_TYPE_WEBDAV_DISCOVER_CONTENT,
                "row-spacing", 4,
                "column-spacing", 4,
+#if GTK_CHECK_VERSION(4, 0, 0)
+               "margin-start", 4,
+               "margin-end", 4,
+               "margin-top", 4,
+               "margin-bottom", 4,
+#else
                "border-width", 4,
+#endif
                NULL);
        self->credentials_prompter = g_object_ref (credentials_prompter);
        self->source = source ? g_object_ref (source) : NULL;
@@ -165,11 +172,19 @@ e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
                "valign", GTK_ALIGN_FILL,
                NULL);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       scrolled_window = gtk_scrolled_window_new ();
+#else
        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+#endif
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), tree_view);
+#else
        gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
+#endif
        gtk_grid_attach (GTK_GRID (self), scrolled_window, 0, 0, 1, 1);
 
        self->sources_tree_view = GTK_TREE_VIEW (tree_view);
@@ -204,8 +219,13 @@ e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
                widget = gtk_label_new_with_mnemonic (_("_User mail:"));
                gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (self->email_addresses_combo));
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_box_append (GTK_BOX (box), widget);
+               gtk_box_append (GTK_BOX (box), GTK_WIDGET (self->email_addresses_combo));
+#else
                gtk_container_add (GTK_CONTAINER (box), widget);
                gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (self->email_addresses_combo));
+#endif
 
                g_object_set (G_OBJECT (widget),
                        "hexpand", FALSE,
@@ -231,7 +251,10 @@ e_webdav_discover_content_new (ECredentialsPrompter *credentials_prompter,
                gtk_grid_attach (GTK_GRID (self), box, 0, 1, 1, 1);
        }
 
+#if !GTK_CHECK_VERSION(4, 0, 0)
        gtk_widget_show_all (GTK_WIDGET (self));
+#endif
+
        return GTK_WIDGET (self);
 }
 
@@ -624,7 +647,11 @@ refresh_data_free (gpointer data)
 
                if (content) {
                        if (content->info_bar && gtk_info_bar_get_message_type (content->info_bar) == 
GTK_MESSAGE_INFO) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+                               gtk_widget_unparent (GTK_WIDGET (content->info_bar));
+#else
                                gtk_widget_destroy (GTK_WIDGET (content->info_bar));
+#endif
                                content->info_bar = NULL;
                        }
 
@@ -748,7 +775,14 @@ e_webdav_discover_content_refresh_done_cb (GObject *source_object,
                        GtkWindow *parent;
                        GtkWidget *widget;
 
+#if GTK_CHECK_VERSION(4, 0, 0)
+                       widget = GTK_WIDGET (rd->content);
+                       while (widget && !GTK_IS_WINDOW (widget)) {
+                               widget = gtk_widget_get_parent (widget);
+                       }
+#else
                        widget = gtk_widget_get_toplevel (GTK_WIDGET (rd->content));
+#endif
                        parent = widget ? GTK_WINDOW (widget) : NULL;
 
                        e_trust_prompt_run_for_source (parent, source, certificate_pem, certificate_errors,
@@ -897,13 +931,21 @@ e_webdav_discover_content_refresh (GtkWidget *content,
                gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (self->email_addresses_combo));
 
        if (self->info_bar)
+#if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_widget_unparent (GTK_WIDGET (self->info_bar));
+#else
                gtk_widget_destroy (GTK_WIDGET (self->info_bar));
+#endif
 
        self->info_bar = GTK_INFO_BAR (gtk_info_bar_new_with_buttons (_("Cancel"), GTK_RESPONSE_CANCEL, 
NULL));
        gtk_info_bar_set_message_type (self->info_bar, GTK_MESSAGE_INFO);
        gtk_info_bar_set_show_close_button (self->info_bar, FALSE);
        label = gtk_label_new (_("Searching server sources..."));
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_info_bar_add_child (self->info_bar, label);
+#else
        gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (self->info_bar)), label);
+#endif
        gtk_widget_show (label);
        gtk_widget_show (GTK_WIDGET (self->info_bar));
 
@@ -962,7 +1004,11 @@ e_webdav_discover_info_bar_error_response_cb (GtkInfoBar *info_bar,
 
        self = (EWebDAVDiscoverContent *)content;
        if (self->info_bar == info_bar) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_widget_unparent (GTK_WIDGET (self->info_bar));
+#else
                gtk_widget_destroy (GTK_WIDGET (self->info_bar));
+#endif
                self->info_bar = NULL;
        }
 }
@@ -989,7 +1035,11 @@ e_webdav_discover_content_show_error (GtkWidget *content,
 
        self = (EWebDAVDiscoverContent *)content;
        if (self->info_bar) {
+#if GTK_CHECK_VERSION(4, 0, 0)
+               gtk_widget_unparent (GTK_WIDGET (self->info_bar));
+#else
                gtk_widget_destroy (GTK_WIDGET (self->info_bar));
+#endif
                self->info_bar = NULL;
        }
 
@@ -1003,11 +1053,16 @@ e_webdav_discover_content_show_error (GtkWidget *content,
        label = gtk_label_new (error->message);
        gtk_label_set_width_chars (GTK_LABEL (label), 20);
        gtk_label_set_max_width_chars (GTK_LABEL (label), 120);
-       gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
        gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_label_set_wrap (GTK_LABEL (label), TRUE);
+       gtk_info_bar_add_child (self->info_bar, label);
+#else
+       gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
        gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (self->info_bar)), label);
        gtk_widget_show (label);
        gtk_widget_show (GTK_WIDGET (self->info_bar));
+#endif
 
        g_signal_connect (self->info_bar, "response", G_CALLBACK 
(e_webdav_discover_info_bar_error_response_cb), content);
 
@@ -1066,7 +1121,7 @@ e_webdav_discover_dialog_init (EWebDAVDiscoverDialog *self)
  * can be asked for currently selected source(s).
  *
  * Returns: (transfer full): a newly created #GtkDialog, which should be freed
- * with gtk_widget_destroy(), when no longer needed.
+ * with g_object_unref(), when no longer needed.
  *
  * Since: 3.18
  **/
@@ -1107,7 +1162,11 @@ e_webdav_discover_dialog_new (GtkWindow *parent,
                NULL);
 
        container = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+#if GTK_CHECK_VERSION(4, 0, 0)
+       gtk_box_append (GTK_BOX (container), widget);
+#else
        gtk_container_add (GTK_CONTAINER (container), widget);
+#endif
 
        selection = e_webdav_discover_content_get_tree_selection (widget);
        g_signal_connect (selection, "changed", G_CALLBACK (e_webdav_discover_content_selection_changed_cb), 
dialog);
diff --git a/src/libedataserverui/libedataserverui-private.c b/src/libedataserverui/libedataserverui-private.c
index 0c6615d91..7caedacd6 100644
--- a/src/libedataserverui/libedataserverui-private.c
+++ b/src/libedataserverui/libedataserverui-private.c
@@ -61,10 +61,113 @@ _libedataserverui_init_icon_theme (void)
 {
        static gboolean icons_added = FALSE;
 
+       #if GTK_CHECK_VERSION(4, 0, 0)
+       if (!icons_added) {
+               GdkDisplayManager *manager;
+
+               manager = gdk_display_manager_get ();
+               if (manager) {
+                       GSList *displays, *link;
+
+                       displays = gdk_display_manager_list_displays (manager);
+                       icons_added = displays != NULL;
+
+                       for (link = displays; link; link = g_slist_next (link)) {
+                               GdkDisplay *display = link->data;
+                               GtkIconTheme *icon_theme = gtk_icon_theme_get_for_display (display);
+
+                               if (icon_theme)
+                                       gtk_icon_theme_add_search_path (icon_theme, E_DATA_SERVER_ICONDIR);
+                       }
+
+                       g_slist_free (displays);
+               }
+       }
+       #else
        /* The screen can be NULL when building the documentation */
        if (!icons_added && gdk_screen_get_default ()) {
                icons_added = TRUE;
 
                gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), E_DATA_SERVER_ICONDIR);
        }
+       #endif
+}
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+typedef struct
+{
+       gint response_id;
+       GMainLoop *loop;
+} RunInfo;
+
+static void
+shutdown_loop (RunInfo *run_info)
+{
+       if (g_main_loop_is_running (run_info->loop))
+               g_main_loop_quit (run_info->loop);
+}
+
+static void
+unmap_cb (GtkDialog *dialog,
+         RunInfo *run_info)
+{
+       shutdown_loop (run_info);
+}
+
+static void
+response_cb (GtkDialog *dialog,
+            gint response_id,
+            RunInfo *run_info)
+{
+       run_info->response_id = response_id;
+       shutdown_loop (run_info);
+}
+
+static gboolean
+close_requested_cb (GtkDialog *dialog,
+                   RunInfo *run_info)
+{
+       shutdown_loop (run_info);
+       return GDK_EVENT_STOP;
+}
+#endif /* GTK_CHECK_VERSION(4, 0, 0) */
+
+/*
+ * _libedataserverui_run_dialog:
+ * @dialog: a #GtkDialog
+ *
+ * Shows the @dialog and doesn't return until it's closed.
+ *
+ * Returns: the response ID the @dialog was closed with
+ *
+ * Since: 3.46
+ */
+gint
+_libedataserverui_run_dialog (GtkDialog *dialog)
+{
+       #if GTK_CHECK_VERSION(4, 0, 0)
+       RunInfo run_info;
+       gulong close_request_id, reponse_id, unmap_id;
+
+       close_request_id = g_signal_connect (dialog, "close-request", G_CALLBACK (close_requested_cb), 
&run_info);
+       reponse_id = g_signal_connect (dialog, "response", G_CALLBACK (response_cb), &run_info);
+       unmap_id = g_signal_connect (dialog, "unmap", G_CALLBACK (unmap_cb), &run_info);
+
+       run_info.response_id = GTK_RESPONSE_NONE;
+       run_info.loop = g_main_loop_new (NULL, FALSE);
+
+       if (!gtk_widget_get_visible (GTK_WIDGET (dialog)))
+               gtk_window_present (GTK_WINDOW (dialog));
+
+       g_main_loop_run (run_info.loop);
+       g_clear_pointer (&run_info.loop, g_main_loop_unref);
+
+       g_signal_handler_disconnect (dialog, close_request_id);
+       g_signal_handler_disconnect (dialog, reponse_id);
+       g_signal_handler_disconnect (dialog, unmap_id);
+
+       return run_info.response_id;
+       #else
+       return gtk_dialog_run (dialog);
+       #endif
 }
diff --git a/src/libedataserverui/libedataserverui-private.h b/src/libedataserverui/libedataserverui-private.h
index 6c3e18389..3a669b215 100644
--- a/src/libedataserverui/libedataserverui-private.h
+++ b/src/libedataserverui/libedataserverui-private.h
@@ -22,8 +22,11 @@
 
 G_BEGIN_DECLS
 
+struct _GtkDialog;
+
 void           _libedataserverui_load_modules          (void);
 void           _libedataserverui_init_icon_theme       (void);
+gint           _libedataserverui_run_dialog            (struct _GtkDialog *dialog);
 
 G_END_DECLS
 
diff --git a/src/libedataserverui/libedataserverui.pc.in b/src/libedataserverui/libedataserverui.pc.in
index f631fe1ec..bf5b8ac47 100644
--- a/src/libedataserverui/libedataserverui.pc.in
+++ b/src/libedataserverui/libedataserverui.pc.in
@@ -8,13 +8,13 @@ datadir=@SHARE_INSTALL_PREFIX@
 privlibdir=@privlibdir@
 privincludedir=@privincludedir@
 
-credentialmoduledir=@credentialmoduledir@
-uimoduledir=@uimoduledir@
+credentialmoduledir=@credentialmoduledir@@UI_VERSION@
+uimoduledir=@uimoduledir@@UI_VERSION@
 
-Name: libedataserverui
+Name: libedataserverui@UI_VERSION@
 Description: UI utility library for Evolution Data Server
 Version: @PROJECT_VERSION@
-Requires: gio-2.0 gmodule-2.0 libsecret-1 libxml-2.0 libsoup-2.4 gtk+-3.0 libedataserver-@API_VERSION@ 
libecal-@CAL_API_VERSION@
+Requires: gio-2.0 gmodule-2.0 libsecret-1 libxml-2.0 libsoup-2.4 @GTK_DEPENDENCY@ 
libedataserver-@API_VERSION@ libecal-@CAL_API_VERSION@
 Requires.private: camel-@API_VERSION@
-Libs: -L${libdir} -ledataserver-@API_VERSION@ -ledataserverui-@API_VERSION@
+Libs: -L${libdir} -ledataserver-@API_VERSION@ -ledataserverui@UI_VERSION@-@UI_API_VERSION@
 Cflags: -I${privincludedir}
diff --git a/src/vala/CMakeLists.txt b/src/vala/CMakeLists.txt
index caf940374..d0f80daea 100644
--- a/src/vala/CMakeLists.txt
+++ b/src/vala/CMakeLists.txt
@@ -327,7 +327,7 @@ _build_vala_files(libedata-cal-${CAL_API_VERSION}
 # libedataserverui
 # ***********************************
 
-if(HAVE_GTK)
+if(ENABLE_GTK)
        set(gir_fullname ${CMAKE_BINARY_DIR}/src/libedataserverui/EDataServerUI-${API_VERSION}.gir)
        gir_girfilename_to_target(gir_deps EDataServerUI-${API_VERSION}.gir)
 
@@ -356,7 +356,38 @@ if(HAVE_GTK)
                vala_deps
                extra_vapigen_files
        )
-endif(HAVE_GTK)
+endif(ENABLE_GTK)
+
+if(ENABLE_GTK4)
+       set(gir_fullname 
${CMAKE_BINARY_DIR}/src/libedataserverui/EDataServerUI4-${LIBEDATASERVERUI4_API_VERSION}.gir)
+       gir_girfilename_to_target(gir_deps EDataServerUI4-${LIBEDATASERVERUI4_API_VERSION}.gir)
+
+       set(gir_dirs
+               ${CMAKE_BINARY_DIR}/src/camel
+               ${CMAKE_BINARY_DIR}/src/libedataserver
+               ${CMAKE_BINARY_DIR}/src/calendar/libecal
+       )
+
+       set(vala_deps
+               *libedataserver-${API_VERSION}
+               *libecal-${CAL_API_VERSION}
+               gio-2.0
+               gtk4
+               libsoup-2.4
+               libxml-2.0
+               posix
+       )
+
+       set(extra_vapigen_files)
+
+       _build_vala_files(libedataserverui4-${LIBEDATASERVERUI4_API_VERSION}
+               gir_fullname
+               gir_deps
+               gir_dirs
+               vala_deps
+               extra_vapigen_files
+       )
+endif(ENABLE_GTK4)
 
 # ***********************************
 # Install all VAPI files


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]