[evolution-data-server/wip/mcrha/gtk4] Initial setup - does not build
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/wip/mcrha/gtk4] Initial setup - does not build
- Date: Fri, 15 Oct 2021 08:09:25 +0000 (UTC)
commit fa073f345e1bc92f1ec9387a47f61fd85ece9031
Author: Milan Crha <mcrha redhat com>
Date: Fri Oct 8 12:48:59 2021 +0200
Initial setup - does not build
CMakeLists.txt | 35 +-
src/CMakeLists.txt | 5 +-
src/libedataserverui/CMakeLists.txt | 168 ++++-
src/libedataserverui/e-buffer-tagger4.c | 946 ++++++++++++++++++++++++++++
src/libedataserverui/libedataserverui.pc.in | 10 +-
5 files changed, 1146 insertions(+), 18 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6358035bf..0ea56b2e4 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)
@@ -92,13 +97,16 @@ set(glib_encoded_version GLIB_VERSION_2_46)
set(gdk_minimum_version 3.16)
set(gdk_encoded_version GDK_VERSION_3_16)
+# 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)
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)
@@ -123,6 +130,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
@@ -388,6 +396,14 @@ 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
# **************************************************************
@@ -399,9 +415,16 @@ if(ENABLE_OAUTH2)
json-glib-1.0>=${json_glib_minimum_version}
)
- pkg_check_modules_for_option(ENABLE_OAUTH2 "OAuth2 authentication support" OAUTH2_WEBKIT2GTK
- webkit2gtk-4.0>=${webkit2gtk_minimum_version}
- )
+ if(ENABLE_GTK)
+ pkg_check_modules_for_option(ENABLE_OAUTH2 "OAuth2 authentication support" OAUTH2_WEBKIT2GTK
+ webkit2gtk-4.0>=${webkit2gtk_minimum_version}
+ )
+ endif(ENABLE_GTK)
+ if(ENABLE_GTK4)
+ #pkg_check_modules_for_option(ENABLE_OAUTH2 "OAuth2 authentication support" OAUTH2_WEBKIT2GTK4
+ # webkit2gtk-4.0>=${webkit2gtk4_minimum_version}
+ #)
+ 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" "")
@@ -447,7 +470,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/src/CMakeLists.txt b/src/CMakeLists.txt
index 50ab1cdc9..f7d24f098 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(camel)
add_subdirectory(libedataserver)
+add_subdirectory(libedataserverui)
add_subdirectory(libebackend)
add_subdirectory(addressbook)
add_subdirectory(calendar)
@@ -8,10 +9,6 @@ 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 63f594fbe..79ecd73f0 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
@@ -53,6 +57,8 @@ target_compile_definitions(edataserverui PRIVATE
-DLIBEDATASERVERUI_COMPILATION
-DE_DATA_SERVER_PREFIX=\"${CMAKE_INSTALL_PREFIX}\"
-DE_DATA_SERVER_UIMODULEDIR=\"${uimoduledir}\"
+ -DGDK_VERSION_MAX_ALLOWED=${gdk_encoded_version}
+ -DGDK_VERSION_MIN_REQUIRED=${gdk_encoded_version}
)
target_compile_options(edataserverui PUBLIC
@@ -133,7 +139,7 @@ set(gir_deps
gir_add_introspection_simple(
EDataServerUI
libedataserverui
- ${API_VERSION}
+ ${UI_API_VERSION}
"libedataserverui/libedataserverui.h"
gir_identifies_prefixes
gir_includes
@@ -143,3 +149,159 @@ 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 libedataserverui4-${UI_API_VERSION}.pc)
+
+set(SOURCES
+ #e-buffer-tagger4.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 PROPERTIES
+ VERSION "${LIBEDATASERVERUI4_CURRENT}.${LIBEDATASERVERUI4_REVISION}.${LIBEDATASERVERUI4_AGE}"
+ SOVERSION ${LIBEDATASERVERUI_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}\"
+ -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_WEBKIT2GTK4_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_WEBKIT2GTK4_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_WEBKIT2GTK4_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 Gtk4 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-tagger4.c b/src/libedataserverui/e-buffer-tagger4.c
new file mode 100644
index 000000000..468c9d3c4
--- /dev/null
+++ b/src/libedataserverui/e-buffer-tagger4.c
@@ -0,0 +1,946 @@
+/*
+ * e-buffer-tagger.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "evolution-data-server-config.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <glib/gi18n-lib.h>
+#include <regex.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "e-buffer-tagger.h"
+
+static void
+e_show_uri (GtkWindow *parent,
+ const gchar *uri)
+{
+ GtkWidget *dialog;
+ GdkScreen *screen = NULL;
+ gchar *scheme;
+ GError *error = NULL;
+ guint32 timestamp;
+ gboolean success;
+
+ g_return_if_fail (uri != NULL);
+
+ timestamp = gtk_get_current_event_time ();
+
+ if (parent != NULL)
+ screen = gtk_widget_get_screen (GTK_WIDGET (parent));
+
+ 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);
+ }
+
+ g_free (scheme);
+
+ if (success)
+ return;
+
+ dialog = gtk_message_dialog_new_with_markup (
+ parent, GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "<big><b>%s</b></big>",
+ _("Could not open the link."));
+
+ gtk_message_dialog_format_secondary_text (
+ GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+}
+
+enum EBufferTaggerState {
+ E_BUFFER_TAGGER_STATE_NONE = 0,
+ E_BUFFER_TAGGER_STATE_INSDEL = 1 << 0, /* set when was called insert or delete of a text */
+ E_BUFFER_TAGGER_STATE_CHANGED = 1 << 1, /* remark of the buffer is scheduled */
+ E_BUFFER_TAGGER_STATE_IS_HOVERING = 1 << 2, /* mouse is over the link */
+ E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP = 1 << 3, /* mouse is over the link and the tooltip can be
shown */
+ 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"
+
+struct _MagicInsertMatch {
+ const gchar *regex;
+ regex_t *preg;
+ const gchar *prefix;
+};
+
+typedef struct _MagicInsertMatch MagicInsertMatch;
+
+static MagicInsertMatch mim[] = {
+ /* prefixed expressions */
+ {
"(news|telnet|nntp|file|http|ftp|sftp|https|webcal)://([-a-z0-9]+(:[-a-z0-9]+)?@)?[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-a-z0-9_$.+!*(),;:@%&=?/~#']*[^]'.}>\\)
\n\r\t,?!;:\"]?)?", NULL, NULL },
+ {
"(sip|h323|callto|tel):([-_a-z0-9.'\\+]+(:[0-9]{1,5})?(/[-_a-z0-9.']+)?)(@([-_a-z0-9.%=?]+|([0-9]{1,3}.){3}[0-9]{1,3})?)?(:[0-9]{1,5})?",
NULL, NULL },
+ { "mailto:[-_a-z0-9.'\\+]+@[-_a-z0-9.%=?]+", NULL, NULL },
+ /* not prefixed expression */
+ { "www\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]'.}>\\)
\n\r\t,?!;:\"]?)?", NULL, "http://" },
+ { "ftp\\.[-a-z0-9.]+[-a-z0-9](:[0-9]*)?(([.])?/[-A-Za-z0-9_$.+!*(),;:@%&=?/~#]*[^]'.}>\\)
\n\r\t,?!;:\"]?)?", NULL, "ftp://" },
+ { "[-_a-z0-9.'\\+]+@[-_a-z0-9.%=?]+", NULL, "mailto:" }
+};
+
+static void
+init_magic_links (void)
+{
+ static gboolean done = FALSE;
+ gint i;
+
+ if (done)
+ return;
+
+ done = TRUE;
+
+ for (i = 0; i < G_N_ELEMENTS (mim); i++) {
+ mim[i].preg = g_new0 (regex_t, 1);
+ if (regcomp (mim[i].preg, mim[i].regex, REG_EXTENDED | REG_ICASE)) {
+ /* error */
+ g_free (mim[i].preg);
+ mim[i].preg = 0;
+ }
+ }
+}
+
+static void
+markup_text (GtkTextBuffer *buffer)
+{
+ GtkTextIter start, end;
+ gchar *text;
+ gint i;
+ regmatch_t pmatch[2];
+ gboolean any;
+ const gchar *str;
+ gint offset = 0;
+
+ g_return_if_fail (buffer != NULL);
+
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+ gtk_text_buffer_remove_tag_by_name (buffer, E_BUFFER_TAGGER_LINK_TAG, &start, &end);
+ text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+ str = text;
+ any = TRUE;
+ while (any) {
+ any = FALSE;
+ for (i = 0; i < G_N_ELEMENTS (mim); i++) {
+ if (mim[i].preg && !regexec (mim[i].preg, str, 2, pmatch, 0)) {
+ gint char_so, char_eo, rm_eo;
+
+ /* Stop on the angle brackets, which cannot be part of the URL (see RFC 3986
Appendix C) */
+ for (rm_eo = pmatch[0].rm_eo - 1; rm_eo > pmatch[0].rm_so; rm_eo--) {
+ if (str[rm_eo] == '<' || str[rm_eo] == '>') {
+ pmatch[0].rm_eo = rm_eo;
+ break;
+ }
+ }
+
+ rm_eo = pmatch[0].rm_eo;
+
+ /* URLs are extremely unlikely to end with any
+ * punctuation, so strip any trailing
+ * punctuation off. Also strip off any closing
+ * double-quotes. */
+ while (rm_eo > pmatch[0].rm_so && strchr (",.:;?!-|}])\">", str[rm_eo - 1])) {
+ gchar open_bracket = 0, close_bracket = str[rm_eo - 1];
+
+ if (close_bracket == ')')
+ open_bracket = '(';
+ else if (close_bracket == '}')
+ open_bracket = '{';
+ else if (close_bracket == ']')
+ open_bracket = '[';
+ else if (close_bracket == '>')
+ open_bracket = '<';
+
+ if (open_bracket != 0) {
+ const gchar *ptr, *endptr;
+ gint n_opened = 0, n_closed = 0;
+
+ endptr = str + rm_eo;
+
+ for (ptr = str + pmatch[0].rm_so; ptr < endptr; ptr++) {
+ if (*ptr == open_bracket)
+ n_opened++;
+ else if (*ptr == close_bracket)
+ n_closed++;
+ }
+
+ /* The closing bracket can match one inside the URL,
+ thus keep it there. */
+ if (n_opened > 0 && n_opened - n_closed >= 0)
+ break;
+ }
+
+ rm_eo--;
+ pmatch[0].rm_eo--;
+ }
+
+ char_so = g_utf8_pointer_to_offset (str, str + pmatch[0].rm_so);
+ char_eo = g_utf8_pointer_to_offset (str, str + pmatch[0].rm_eo);
+
+ gtk_text_buffer_get_iter_at_offset (buffer, &start, offset + char_so);
+ gtk_text_buffer_get_iter_at_offset (buffer, &end, offset + char_eo);
+ gtk_text_buffer_apply_tag_by_name (buffer, E_BUFFER_TAGGER_LINK_TAG, &start,
&end);
+
+ any = TRUE;
+ str += pmatch[0].rm_eo;
+ offset += char_eo;
+ break;
+ }
+ }
+ }
+
+ g_free (text);
+}
+
+static void
+get_pointer_position (GtkTextView *text_view,
+ gint *x,
+ gint *y)
+{
+ GdkWindow *window;
+ GdkDisplay *display;
+ GdkDeviceManager *device_manager;
+ GdkDevice *device;
+
+ window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_WIDGET);
+ display = gdk_window_get_display (window);
+ 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);
+}
+
+static guint32
+get_state (GtkTextBuffer *buffer)
+{
+ g_return_val_if_fail (buffer != NULL, E_BUFFER_TAGGER_STATE_NONE);
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), E_BUFFER_TAGGER_STATE_NONE);
+
+ return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (buffer), E_BUFFER_TAGGER_DATA_STATE));
+}
+
+static void
+set_state (GtkTextBuffer *buffer,
+ guint32 state)
+{
+ g_object_set_data (G_OBJECT (buffer), E_BUFFER_TAGGER_DATA_STATE, GINT_TO_POINTER (state));
+}
+
+static void
+update_state (GtkTextBuffer *buffer,
+ guint32 value,
+ gboolean do_set)
+{
+ guint32 state;
+
+ g_return_if_fail (buffer != NULL);
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+
+ state = get_state (buffer);
+
+ if (do_set)
+ state = state | value;
+ else
+ state = state & (~value);
+
+ set_state (buffer, state);
+}
+
+static gboolean
+get_tag_bounds (GtkTextIter *iter,
+ GtkTextTag *tag,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ gboolean res = FALSE;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (tag != NULL, FALSE);
+ g_return_val_if_fail (start != NULL, FALSE);
+ g_return_val_if_fail (end != NULL, FALSE);
+
+ if (gtk_text_iter_has_tag (iter, tag)) {
+ *start = *iter;
+ *end = *iter;
+
+ if (!gtk_text_iter_begins_tag (start, tag))
+ gtk_text_iter_backward_to_tag_toggle (start, tag);
+
+ if (!gtk_text_iter_ends_tag (end, tag))
+ gtk_text_iter_forward_to_tag_toggle (end, tag);
+
+ res = TRUE;
+ }
+
+ return res;
+}
+
+static gchar *
+get_url_at_iter (GtkTextBuffer *buffer,
+ GtkTextIter *iter)
+{
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+ GtkTextIter start, end;
+ gchar *url = NULL;
+
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+ g_return_val_if_fail (tag != NULL, NULL);
+
+ if (get_tag_bounds (iter, tag, &start, &end))
+ url = gtk_text_iter_get_text (&start, &end);
+
+ return url;
+}
+
+static gboolean
+invoke_link_if_present (GtkTextBuffer *buffer,
+ GtkTextIter *iter)
+{
+ gboolean res;
+ gchar *url;
+
+ g_return_val_if_fail (buffer != NULL, FALSE);
+
+ url = get_url_at_iter (buffer, iter);
+
+ res = url && *url;
+ if (res)
+ e_show_uri (NULL, url);
+
+ g_free (url);
+
+ return res;
+}
+
+static void
+remove_tag_if_present (GtkTextBuffer *buffer,
+ GtkTextIter *where)
+{
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+ GtkTextIter start, end;
+
+ g_return_if_fail (buffer != NULL);
+ g_return_if_fail (where != NULL);
+
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+ g_return_if_fail (tag != NULL);
+
+ if (get_tag_bounds (where, tag, &start, &end))
+ gtk_text_buffer_remove_tag (buffer, tag, &start, &end);
+}
+
+static void
+buffer_insert_text (GtkTextBuffer *buffer,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ gpointer user_data)
+{
+ update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL, TRUE);
+ remove_tag_if_present (buffer, location);
+}
+
+static void
+buffer_delete_range (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end,
+ gpointer user_data)
+{
+ update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL, TRUE);
+ remove_tag_if_present (buffer, start);
+ remove_tag_if_present (buffer, end);
+}
+
+static void
+buffer_cursor_position (GtkTextBuffer *buffer,
+ gpointer user_data)
+{
+ guint32 state;
+
+ state = get_state (buffer);
+ if (state & E_BUFFER_TAGGER_STATE_INSDEL) {
+ state = (state & (~E_BUFFER_TAGGER_STATE_INSDEL)) | E_BUFFER_TAGGER_STATE_CHANGED;
+ } else {
+ if (state & E_BUFFER_TAGGER_STATE_CHANGED) {
+ markup_text (buffer);
+ }
+
+ state = state & (~ (E_BUFFER_TAGGER_STATE_CHANGED | E_BUFFER_TAGGER_STATE_INSDEL));
+ }
+
+ set_state (buffer, state);
+}
+
+static void
+update_mouse_cursor (GtkTextView *text_view,
+ gint x,
+ gint y)
+{
+ static GdkCursor *hand_cursor = NULL;
+ static GdkCursor *regular_cursor = NULL;
+ gboolean hovering = FALSE, hovering_over_link = FALSE, hovering_real;
+ guint32 state;
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer (text_view);
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+ GtkTextIter iter;
+
+ if (!hand_cursor) {
+ hand_cursor = gdk_cursor_new (GDK_HAND2);
+ regular_cursor = gdk_cursor_new (GDK_XTERM);
+ }
+
+ g_return_if_fail (buffer != NULL);
+
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+ g_return_if_fail (tag != NULL);
+
+ state = get_state (buffer);
+
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+ hovering_real = gtk_text_iter_has_tag (&iter, tag);
+
+ hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING) != 0;
+ if ((state & E_BUFFER_TAGGER_STATE_CTRL_DOWN) == 0) {
+ hovering = FALSE;
+ } else {
+ hovering = hovering_real;
+ }
+
+ if (hovering != hovering_over_link) {
+ update_state (buffer, E_BUFFER_TAGGER_STATE_IS_HOVERING, hovering);
+
+ 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);
+ }
+
+ hovering_over_link = (state & E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP) != 0;
+
+ if (hovering_real != hovering_over_link) {
+ update_state (buffer, E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP, hovering_real);
+
+ gtk_widget_trigger_tooltip_query (GTK_WIDGET (text_view));
+ }
+}
+
+static void
+textview_style_updated_cb (GtkWidget *textview,
+ gpointer user_data)
+{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkTextBuffer *buffer;
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+ GdkRGBA rgba;
+
+ g_return_if_fail (GTK_IS_WIDGET (textview));
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+ if (!tag)
+ return;
+
+ context = gtk_widget_get_style_context (textview);
+
+ rgba.red = 0;
+ rgba.green = 0;
+ rgba.blue = 1;
+ rgba.alpha = 1;
+
+ state = gtk_style_context_get_state (context);
+ state = state & (~(GTK_STATE_FLAG_VISITED | GTK_STATE_FLAG_LINK));
+ state = state | GTK_STATE_FLAG_LINK;
+
+ gtk_style_context_save (context);
+ /* 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);
+ gtk_style_context_restore (context);
+
+ g_object_set (G_OBJECT (tag), "foreground-rgba", &rgba, NULL);
+}
+
+static gboolean
+textview_query_tooltip (GtkTextView *text_view,
+ gint x,
+ gint y,
+ gboolean keyboard_mode,
+ GtkTooltip *tooltip,
+ gpointer user_data)
+{
+ GtkTextBuffer *buffer;
+ guint32 state;
+ gboolean res = FALSE;
+
+ if (keyboard_mode)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+ g_return_val_if_fail (buffer != NULL, FALSE);
+
+ state = get_state (buffer);
+
+ if ((state & E_BUFFER_TAGGER_STATE_IS_HOVERING_TOOLTIP) != 0) {
+ gchar *url;
+ GtkTextIter iter;
+
+ gtk_text_view_window_to_buffer_coords (
+ text_view,
+ GTK_TEXT_WINDOW_WIDGET,
+ x, y, &x, &y);
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+ url = get_url_at_iter (buffer, &iter);
+ res = url && *url;
+
+ if (res) {
+ gchar *str;
+
+ /* To Translators: The text is concatenated to a form: "Ctrl-click to open a link
http://www.example.com" */
+ str = g_strconcat (_("Ctrl-click to open a link"), " ", url, NULL);
+ gtk_tooltip_set_text (tooltip, str);
+ g_free (str);
+ }
+
+ g_free (url);
+ }
+
+ return res;
+}
+
+/* Links can be activated by pressing Enter. */
+static gboolean
+textview_key_press_event (GtkWidget *text_view,
+ GdkEventKey *event)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+
+ if ((event->state & GDK_CONTROL_MASK) == 0)
+ return FALSE;
+
+ switch (event->keyval) {
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
+ if (invoke_link_if_present (buffer, &iter))
+ return TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+update_ctrl_state (GtkTextView *textview,
+ gboolean ctrl_is_down)
+{
+ GtkTextBuffer *buffer;
+ gint x, y;
+
+ buffer = gtk_text_view_get_buffer (textview);
+ if (buffer) {
+ if (((get_state (buffer) & E_BUFFER_TAGGER_STATE_CTRL_DOWN) != 0) != (ctrl_is_down != FALSE))
{
+ update_state (buffer, E_BUFFER_TAGGER_STATE_CTRL_DOWN, ctrl_is_down != FALSE);
+ }
+
+ get_pointer_position (textview, &x, &y);
+ gtk_text_view_window_to_buffer_coords (textview, GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y);
+ update_mouse_cursor (textview, x, y);
+ }
+}
+
+/* Links can also be activated by clicking. */
+static gboolean
+textview_event_after (GtkTextView *textview,
+ GdkEvent *event)
+{
+ GtkTextIter start, end, iter;
+ GtkTextBuffer *buffer;
+ gint x, y;
+ GdkModifierType mt = 0;
+ 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) {
+ guint event_keyval = 0;
+
+ gdk_event_get_keyval (event, &event_keyval);
+
+ switch (event_keyval) {
+ case GDK_KEY_Control_L:
+ case GDK_KEY_Control_R:
+ update_ctrl_state (
+ textview,
+ event->type == GDK_KEY_PRESS);
+ break;
+ }
+
+ return FALSE;
+ }
+
+ if (!gdk_event_get_state (event, &mt)) {
+ GdkWindow *window;
+ GdkDisplay *display;
+ GdkDeviceManager *device_manager;
+ GdkDevice *device;
+
+ window = gtk_widget_get_parent_window (GTK_WIDGET (textview));
+ display = gdk_window_get_display (window);
+ device_manager = gdk_display_get_device_manager (display);
+ device = gdk_device_manager_get_client_pointer (device_manager);
+
+ gdk_window_get_device_position (window, device, NULL, NULL, &mt);
+ }
+
+ update_ctrl_state (textview, (mt & GDK_CONTROL_MASK) != 0);
+
+ if (event->type != GDK_BUTTON_RELEASE)
+ return FALSE;
+
+ gdk_event_get_button (event, &event_button);
+ gdk_event_get_coords (event, &event_x_win, &event_y_win);
+
+ if (event_button != 1 || (mt & GDK_CONTROL_MASK) == 0)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (textview);
+
+ /* we shouldn't follow a link if the user has selected something */
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+ return FALSE;
+
+ gtk_text_view_window_to_buffer_coords (
+ textview,
+ GTK_TEXT_WINDOW_WIDGET,
+ event_x_win, event_y_win, &x, &y);
+
+ gtk_text_view_get_iter_at_location (textview, &iter, x, y);
+
+ invoke_link_if_present (buffer, &iter);
+ update_mouse_cursor (textview, x, y);
+
+ return FALSE;
+}
+
+static gboolean
+textview_motion_notify_event (GtkTextView *textview,
+ GdkEventMotion *event)
+{
+ gint x, y;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (textview), FALSE);
+
+ gtk_text_view_window_to_buffer_coords (
+ textview,
+ GTK_TEXT_WINDOW_WIDGET,
+ 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);
+}
+
+static void
+textview_populate_popup_cb (GtkTextView *textview,
+ GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+ GdkDisplay *display;
+ gboolean iter_set = FALSE;
+ gchar *uri;
+
+ if (!GTK_IS_MENU (widget))
+ return;
+
+ buffer = gtk_text_view_get_buffer (textview);
+
+ display = gtk_widget_get_display (GTK_WIDGET (textview));
+
+ if (display && gtk_widget_get_window (GTK_WIDGET (textview))) {
+ GdkDeviceManager *device_manager;
+ GdkDevice *pointer;
+ gint px = 0, py = 0, xx = 0, yy = 0;
+
+ device_manager = gdk_display_get_device_manager (display);
+ pointer = gdk_device_manager_get_client_pointer (device_manager);
+
+ gdk_device_get_position (pointer, NULL, &px, &py);
+ gdk_window_get_origin (gtk_widget_get_window (GTK_WIDGET (textview)), &xx, &yy);
+
+ px -= xx;
+ py -= yy;
+
+ gtk_text_view_window_to_buffer_coords (
+ textview,
+ GTK_TEXT_WINDOW_WIDGET,
+ px, py, &xx, &yy);
+
+ iter_set = gtk_text_view_get_iter_at_location (textview, &iter, xx, yy);
+ }
+
+ if (!iter_set) {
+ GtkTextMark *mark;
+
+ mark = gtk_text_buffer_get_selection_bound (buffer);
+
+ if (!mark)
+ return;
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
+ }
+
+ uri = get_url_at_iter (buffer, &iter);
+
+ if (uri && *uri) {
+ GtkMenuShell *menu = GTK_MENU_SHELL (widget);
+ GtkWidget *item;
+
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_prepend (menu, item);
+
+ item = gtk_menu_item_new_with_mnemonic (_("Copy _Link Location"));
+ gtk_widget_show (item);
+ gtk_menu_shell_prepend (menu, item);
+
+ g_signal_connect_data (item, "activate",
+ G_CALLBACK (textview_copy_uri_cb), g_strdup (uri), (GClosureNotify) g_free, 0);
+
+ item = gtk_menu_item_new_with_mnemonic (_("O_pen Link in Browser"));
+ gtk_widget_show (item);
+ gtk_menu_shell_prepend (menu, item);
+
+ g_signal_connect_data (item, "activate",
+ G_CALLBACK (textview_open_uri_cb), uri, (GClosureNotify) g_free, 0);
+ } else {
+ g_free (uri);
+ }
+}
+
+void
+e_buffer_tagger_connect (GtkTextView *textview)
+{
+ GtkTextBuffer *buffer;
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+
+ init_magic_links ();
+
+ g_return_if_fail (textview != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+ buffer = gtk_text_view_get_buffer (textview);
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+ /* if tag is there already, then it is connected, thus claim */
+ g_return_if_fail (tag == NULL);
+
+ gtk_text_buffer_create_tag (
+ buffer, E_BUFFER_TAGGER_LINK_TAG,
+ "foreground", "blue",
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+
+ set_state (buffer, E_BUFFER_TAGGER_STATE_NONE);
+
+ g_signal_connect (
+ buffer, "insert-text",
+ G_CALLBACK (buffer_insert_text), NULL);
+ g_signal_connect (
+ buffer, "delete-range",
+ G_CALLBACK (buffer_delete_range), NULL);
+ g_signal_connect (
+ buffer, "notify::cursor-position",
+ G_CALLBACK (buffer_cursor_position), NULL);
+
+ 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);
+ g_signal_connect (
+ textview, "key-press-event",
+ G_CALLBACK (textview_key_press_event), NULL);
+ g_signal_connect (
+ textview, "event-after",
+ G_CALLBACK (textview_event_after), NULL);
+ 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);
+}
+
+void
+e_buffer_tagger_disconnect (GtkTextView *textview)
+{
+ GtkTextBuffer *buffer;
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+
+ g_return_if_fail (textview != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+ buffer = gtk_text_view_get_buffer (textview);
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+ /* if tag is not there, then it is not connected, thus claim */
+ g_return_if_fail (tag != NULL);
+
+ gtk_text_tag_table_remove (tag_table, tag);
+
+ set_state (buffer, E_BUFFER_TAGGER_STATE_NONE);
+
+ g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_insert_text), NULL);
+ g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_delete_range), NULL);
+ g_signal_handlers_disconnect_by_func (buffer, G_CALLBACK (buffer_cursor_position), NULL);
+
+ 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);
+ 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);
+}
+
+void
+e_buffer_tagger_update_tags (GtkTextView *textview)
+{
+ GtkTextBuffer *buffer;
+ GtkTextTagTable *tag_table;
+ GtkTextTag *tag;
+
+ g_return_if_fail (textview != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (textview));
+
+ buffer = gtk_text_view_get_buffer (textview);
+ tag_table = gtk_text_buffer_get_tag_table (buffer);
+ tag = gtk_text_tag_table_lookup (tag_table, E_BUFFER_TAGGER_LINK_TAG);
+
+ /* if tag is not there, then it is not connected, thus claim */
+ g_return_if_fail (tag != NULL);
+
+ update_state (buffer, E_BUFFER_TAGGER_STATE_INSDEL | E_BUFFER_TAGGER_STATE_CHANGED, FALSE);
+
+ markup_text (buffer);
+}
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}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]