[balsa/gtk3] Refactor HTML code to work with async search
- From: Peter Bloomfield <PeterB src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa/gtk3] Refactor HTML code to work with async search
- Date: Sat, 30 Mar 2013 19:17:27 +0000 (UTC)
commit 359a302736753b2642a0f70dd2f0fa4934240729
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date: Sat Mar 30 15:16:10 2013 -0400
Refactor HTML code to work with async search
* libbalsa/html.c (lbh_web_process_crashed_cb): spew message
only when debugging;
(lbh_search_failed_to_find_text_cb), (lbh_search_found_text_cb),
(lbh_search_init), (lbh_search_continue),
(libbalsa_html_search): work with asynchronous search;
(libbalsa_html_get_selection_bounds): is now gboolean, return
FALSE in Webkit2 version.
* libbalsa/html.h: new protoypes.
* src/balsa-message.c (bm_find_cb), (bm_find_entry_changed_cb),
(bm_find_again), (balsa_message_destroy): work with asynchronous
search.
* src/balsa-message.h: add gpointer member to store HTML search
info.
ChangeLog | 19 ++++
libbalsa/html.c | 240 ++++++++++++++++++++++++++++++++-------------------
libbalsa/html.h | 16 +++-
src/balsa-message.c | 89 +++++++++++++++-----
src/balsa-message.h | 3 +
5 files changed, 253 insertions(+), 114 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 43eb98c..d6e53db 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2013-03-30 Peter Bloomfield
+
+ Refactor HTML search code to work with asynchronous search in
+ Webkit2.
+
+ * libbalsa/html.c (lbh_web_process_crashed_cb): spew message
+ only when debugging;
+ (lbh_search_failed_to_find_text_cb), (lbh_search_found_text_cb),
+ (lbh_search_init), (lbh_search_continue),
+ (libbalsa_html_search): work with asynchronous search;
+ (libbalsa_html_get_selection_bounds): is now gboolean, return
+ FALSE in Webkit2 version.
+ * libbalsa/html.h: new protoypes.
+ * src/balsa-message.c (bm_find_cb), (bm_find_entry_changed_cb),
+ (bm_find_again), (balsa_message_destroy): work with asynchronous
+ search.
+ * src/balsa-message.h: add gpointer member to store HTML search
+ info.
+
2013-03-28 Peter Bloomfield
* libbalsa/html.c (lbh_navigation_policy_decision),
diff --git a/libbalsa/html.c b/libbalsa/html.c
index f4086d6..87e1cb9 100644
--- a/libbalsa/html.c
+++ b/libbalsa/html.c
@@ -417,7 +417,7 @@ static gboolean
lbh_web_process_crashed_cb(WebKitWebView * web_view,
gpointer data)
{
- g_print("%s\n", __func__);
+ d(g_print("%s\n", __func__));
return FALSE;
}
@@ -599,114 +599,161 @@ libbalsa_html_can_search(GtkWidget * widget)
}
/*
- * JavaScript-based helpers for text search
+ * Search for the text; if text is empty, call the callback with TRUE
+ * (for consistency with GtkTextIter methods).
*/
-static JSValueRef
-lbh_js_run_script(JSContextRef ctx,
- const gchar * script)
-{
- JSStringRef str;
- JSValueRef value;
- str = JSStringCreateWithUTF8CString(script);
- value = JSEvaluateScript(ctx, str, NULL, NULL, 0, NULL);
- JSStringRelease(str);
+typedef struct {
+ LibBalsaHtmlSearchCallback search_cb;
+ gpointer cb_data;
+ gchar * text;
+} LibBalsaHtmlSearchInfo;
- return value;
+#define LIBBALSA_HTML_SEARCH_INFO "LibBalsaHtmlSearchInfo"
+
+static void
+lbh_search_failed_to_find_text_cb(WebKitFindController * controller,
+ gpointer data)
+{
+ LibBalsaHtmlSearchInfo *info = data;
+
+ (*info->search_cb)(info->text, FALSE, info->cb_data);
}
-static gint
-lbh_js_object_get_property(JSContextRef ctx,
- JSObjectRef object,
- const gchar * property_name)
+static void
+lbh_search_found_text_cb(WebKitFindController *controller,
+ guint match_count,
+ gpointer data)
{
- JSStringRef str = JSStringCreateWithUTF8CString(property_name);
- JSValueRef value = JSObjectGetProperty(ctx, object, str, NULL);
- JSStringRelease(str);
+ LibBalsaHtmlSearchInfo *info = data;
- return (gint) JSValueToNumber(ctx, value, NULL);
+ (*info->search_cb)(info->text, TRUE, info->cb_data);
}
-/*
- * Search for the text; if text is empty, return TRUE (for consistency
- * with GtkTextIter methods).
- */
-gboolean
-libbalsa_html_search_text(GtkWidget * widget,
- const gchar * text,
- gboolean find_forward,
- gboolean wrap)
+static void
+lbh_search_init(WebKitFindController * controller,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap,
+ LibBalsaHtmlSearchCallback search_cb,
+ gpointer cb_data)
{
- WebKitWebView *web_view;
- WebKitFindController *find_controller;
- guint find_options;
+ LibBalsaHtmlSearchInfo *info;
+ guint32 find_options;
- if (!lbh_get_web_view(widget, &web_view))
- return FALSE;
+ info = g_object_get_data(G_OBJECT(controller),
+ LIBBALSA_HTML_SEARCH_INFO);
if (!*text) {
- JSGlobalContextRef ctx;
- gchar script[] = "window.getSelection().removeAllRanges()";
-
- ctx = webkit_web_view_get_javascript_global_context(web_view);
- lbh_js_run_script(ctx, script);
+ webkit_find_controller_search_finish(controller);
+ (*search_cb)(text, TRUE, cb_data);
+ if (info) {
+ g_free(info->text);
+ info->text = NULL;
+ }
+ return;
+ }
- return TRUE;
+ if (!info) {
+ info = g_new(LibBalsaHtmlSearchInfo, 1);
+ info->text = NULL;
+ g_object_set_data_full(G_OBJECT(controller),
+ LIBBALSA_HTML_SEARCH_INFO, info, g_free);
+ g_signal_connect(controller, "failed-to-find-text",
+ G_CALLBACK(lbh_search_failed_to_find_text_cb),
+ info);
+ g_signal_connect(controller, "found-text",
+ G_CALLBACK(lbh_search_found_text_cb),
+ info);
}
+ g_free(info->text);
+ info->text = g_strdup(text);
+ info->search_cb = search_cb;
+ info->cb_data = cb_data;
+
find_options = WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
if (!find_forward)
find_options |= WEBKIT_FIND_OPTIONS_BACKWARDS;
if (wrap)
find_options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND;
- find_controller = webkit_web_view_get_find_controller(web_view);
- webkit_find_controller_search(find_controller, text, find_options,
+ webkit_find_controller_search(controller, text, find_options,
G_MAXUINT);
+}
- return FALSE;
+static void
+lbh_search_continue(WebKitFindController * controller,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap)
+{
+ guint32 find_options;
+
+ find_options = webkit_find_controller_get_options(controller);
+
+ if (!find_forward)
+ find_options |= WEBKIT_FIND_OPTIONS_BACKWARDS;
+ else
+ find_options &= ~WEBKIT_FIND_OPTIONS_BACKWARDS;
+
+ if (wrap)
+ find_options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+ else
+ find_options &= ~WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+
+ if (find_options != webkit_find_controller_get_options(controller)) {
+ /* No setter for find-options, so we start a new search */
+ webkit_find_controller_search(controller, text, find_options,
+ G_MAXUINT);
+ } else {
+ /* OK to use next/previous methods */
+ if (find_forward)
+ webkit_find_controller_search_next(controller);
+ else
+ webkit_find_controller_search_previous(controller);
+ }
}
-/*
- * Get the rectangle containing the currently selected text, for
- * scrolling.
- */
void
-libbalsa_html_get_selection_bounds(GtkWidget * widget,
- GdkRectangle * selection_bounds)
+libbalsa_html_search(GtkWidget * widget,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap,
+ LibBalsaHtmlSearchCallback search_cb,
+ gpointer cb_data)
{
WebKitWebView *web_view;
- JSGlobalContextRef ctx;
- gchar script[] =
- "window.getSelection().getRangeAt(0).getBoundingClientRect()";
- JSValueRef value;
+ WebKitFindController *controller;
+ LibBalsaHtmlSearchInfo *info;
- if (!lbh_get_web_view(widget, &web_view))
+ if (!lbh_get_web_view(widget, &web_view)) {
return;
+ }
- ctx = webkit_web_view_get_javascript_global_context(web_view);
- value = lbh_js_run_script(ctx, script);
-
- if (value && JSValueIsObject(ctx, value)) {
- JSObjectRef object = JSValueToObject(ctx, value, NULL);
- gint x, y;
-
- x = lbh_js_object_get_property(ctx, object, "left");
- y = lbh_js_object_get_property(ctx, object, "top");
-
- gtk_widget_translate_coordinates(GTK_WIDGET(web_view), widget,
- x, y,
- &selection_bounds->x,
- &selection_bounds->y);
+ controller = webkit_web_view_get_find_controller(web_view);
- selection_bounds->width =
- lbh_js_object_get_property(ctx, object, "width");
- selection_bounds->height =
- lbh_js_object_get_property(ctx, object, "height");
+ if (!(info = g_object_get_data(G_OBJECT(controller),
+ LIBBALSA_HTML_SEARCH_INFO)) ||
+ (!info->text || strcmp(text, info->text))) {
+ lbh_search_init(controller, text, find_forward, wrap,
+ search_cb, cb_data);
+ } else {
+ lbh_search_continue(controller, text, find_forward, wrap);
}
}
/*
+ * We do not need selection bounds.
+ */
+gboolean
+libbalsa_html_get_selection_bounds(GtkWidget * widget,
+ GdkRectangle * selection_bounds)
+{
+ return FALSE;
+}
+
+/*
* Get the WebKitWebView widget from the container; we need to connect
* to its "populate-popup" signal.
*/
@@ -1231,35 +1278,42 @@ lbh_js_object_get_property(JSContextRef ctx,
* Search for the text; if text is empty, return TRUE (for consistency
* with GtkTextIter methods).
*/
-gboolean
-libbalsa_html_search_text(GtkWidget * widget,
- const gchar * text,
- gboolean find_forward,
- gboolean wrap)
+void
+libbalsa_html_search(GtkWidget * widget,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap,
+ LibBalsaHtmlSearchCallback search_cb,
+ gpointer cb_data)
{
WebKitWebView *web_view;
+ gboolean retval;
if (!lbh_get_web_view(widget, &web_view))
- return FALSE;
+ return;
if (!*text) {
gchar script[] = "window.getSelection().removeAllRanges()";
lbh_js_run_script(lbh_js_get_global_context(web_view), script);
- return TRUE;
+ (*search_cb)(text, TRUE, cb_data);
+ return;
}
- return webkit_web_view_search_text(web_view, text,
- FALSE, /* case-insensitive */
- find_forward, wrap);
+ retval = webkit_web_view_search_text(web_view, text,
+ FALSE, /* case-insensitive */
+ find_forward, wrap);
+ (*search_cb)(text, retval, cb_data);
+
+ return;
}
/*
* Get the rectangle containing the currently selected text, for
* scrolling.
*/
-void
+gboolean
libbalsa_html_get_selection_bounds(GtkWidget * widget,
GdkRectangle * selection_bounds)
{
@@ -1270,7 +1324,7 @@ libbalsa_html_get_selection_bounds(GtkWidget * widget,
JSValueRef value;
if (!lbh_get_web_view(widget, &web_view))
- return;
+ return FALSE;
ctx = lbh_js_get_global_context(web_view);
value = lbh_js_run_script(ctx, script);
@@ -1291,7 +1345,11 @@ libbalsa_html_get_selection_bounds(GtkWidget * widget,
lbh_js_object_get_property(ctx, object, "width");
selection_bounds->height =
lbh_js_object_get_property(ctx, object, "height");
+
+ return TRUE;
}
+
+ return FALSE;
}
/*
@@ -1820,17 +1878,21 @@ libbalsa_html_can_search(GtkWidget * widget)
return FALSE;
}
-gboolean
-libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
- gboolean find_forward, gboolean wrap)
+void
+libbalsa_html_search(GtkWidget * widget,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap,
+ LibBalsaHtmlSearchCallback search_cb,
+ gpointer cb_data)
{
- return FALSE;
}
-void
+gboolean
libbalsa_html_get_selection_bounds(GtkWidget * widget,
GdkRectangle * selection_bounds)
{
+ return FALSE;
}
/*
diff --git a/libbalsa/html.h b/libbalsa/html.h
index 0f578b7..03be566 100644
--- a/libbalsa/html.h
+++ b/libbalsa/html.h
@@ -59,11 +59,19 @@ void libbalsa_html_copy(GtkWidget * widget);
guint libbalsa_html_filter(LibBalsaHTMLType html_type, gchar ** text,
guint len);
+typedef void (*LibBalsaHtmlSearchCallback)(const gchar * text,
+ gboolean found,
+ gpointer data);
gboolean libbalsa_html_can_search(GtkWidget * widget);
-gboolean libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
- gboolean find_forward, gboolean wrap);
-void libbalsa_html_get_selection_bounds(GtkWidget * widget,
- GdkRectangle * selection_bounds);
+void libbalsa_html_search(GtkWidget * widget,
+ const gchar * text,
+ gboolean find_forward,
+ gboolean wrap,
+ LibBalsaHtmlSearchCallback search_cb,
+ gpointer cb_data);
+gboolean libbalsa_html_get_selection_bounds(GtkWidget * widget,
+ GdkRectangle *
+ selection_bounds);
GtkWidget *libbalsa_html_popup_menu_widget(GtkWidget * widget);
GtkWidget *libbalsa_html_get_view_widget(GtkWidget * widget);
diff --git a/src/balsa-message.c b/src/balsa-message.c
index df2ccd4..0392487 100644
--- a/src/balsa-message.c
+++ b/src/balsa-message.c
@@ -441,6 +441,44 @@ bm_find_scroll_to_selection(BalsaMessage * bm,
bm_find_scroll_to_rectangle(bm, GTK_WIDGET(text_view), &begin_location);
}
+#ifdef HAVE_HTML_WIDGET
+typedef struct {
+ BalsaMessage *bm;
+ GtkWidget *widget;
+ gboolean continuing;
+ gboolean wrapping;
+} BalsaMessageFindInfo;
+#define BALSA_MESSAGE_FIND_INFO "BalsaMessageFindInfo"
+
+static void
+bm_find_cb(const gchar * text, gboolean found, gpointer data)
+{
+ BalsaMessageFindInfo *info = data;
+
+ if (!found && info->continuing) {
+ info->wrapping = TRUE;
+ libbalsa_html_search(info->widget, text, info->bm->find_forward,
+ TRUE, bm_find_cb, info);
+ return;
+ }
+
+ if (found && *text) {
+ GdkRectangle selection_bounds;
+ if (libbalsa_html_get_selection_bounds(info->widget,
+ &selection_bounds))
+ bm_find_scroll_to_rectangle(info->bm, info->widget,
+ &selection_bounds);
+ }
+
+ if (info->wrapping) {
+ info->wrapping = FALSE;
+ bm_find_set_status(info->bm, BM_FIND_STATUS_WRAPPED);
+ } else
+ bm_find_set_status(info->bm, found ? BM_FIND_STATUS_FOUND :
+ BM_FIND_STATUS_NOT_FOUND);
+}
+#endif /* HAVE_HTML_WIDGET */
+
static void
bm_find_entry_changed_cb(GtkEditable * editable, gpointer data)
{
@@ -480,23 +518,26 @@ bm_find_entry_changed_cb(GtkEditable * editable, gpointer data)
&match_begin, &match_end);
bm->find_iter = match_begin;
}
+
+ bm_find_set_status(bm, found ? BM_FIND_STATUS_FOUND :
+ BM_FIND_STATUS_NOT_FOUND);
#ifdef HAVE_HTML_WIDGET
} else if (libbalsa_html_can_search(widget)) {
- found = libbalsa_html_search_text(widget, text,
- bm->find_forward, TRUE);
- if (found && *text) {
- GdkRectangle selection_bounds;
-
- libbalsa_html_get_selection_bounds(widget,
- &selection_bounds);
- bm_find_scroll_to_rectangle(bm, widget, &selection_bounds);
+ BalsaMessageFindInfo *info;
+
+ if (!(info = bm->html_find_info)) {
+ bm->html_find_info = info = g_new(BalsaMessageFindInfo, 1);
+ info->bm = bm;
}
+ info->widget = widget;
+ info->continuing = FALSE;
+ info->wrapping = FALSE;
+
+ libbalsa_html_search(widget, text, bm->find_forward, TRUE,
+ bm_find_cb, info);
#endif /* HAVE_HTML_WIDGET */
} else
g_assert_not_reached();
-
- bm_find_set_status(bm, found ?
- BM_FIND_STATUS_FOUND : BM_FIND_STATUS_NOT_FOUND);
}
static void
@@ -506,6 +547,8 @@ bm_find_again(BalsaMessage * bm, gboolean find_forward)
GtkWidget *widget = bm->current_part->mime_widget->widget;
gboolean found;
+ bm->find_forward = find_forward;
+
if (GTK_IS_TEXT_VIEW(widget)) {
GtkTextView *text_view = GTK_TEXT_VIEW(widget);
GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view);
@@ -535,22 +578,19 @@ bm_find_again(BalsaMessage * bm, gboolean find_forward)
bm_find_scroll_to_selection(bm, text_view,
&match_begin, &match_end);
bm->find_iter = match_begin;
+
+ bm_find_set_status(bm, found ?
+ BM_FIND_STATUS_FOUND : BM_FIND_STATUS_WRAPPED);
#ifdef HAVE_HTML_WIDGET
} else if (libbalsa_html_can_search(widget)) {
- GdkRectangle selection_bounds;
+ BalsaMessageFindInfo *info = bm->html_find_info;
- found = libbalsa_html_search_text(widget, text, find_forward, FALSE);
- if (!found)
- libbalsa_html_search_text(widget, text, find_forward, TRUE);
- libbalsa_html_get_selection_bounds(widget, &selection_bounds);
- bm_find_scroll_to_rectangle(bm, widget, &selection_bounds);
+ info->continuing = TRUE;
+ libbalsa_html_search(widget, text, find_forward, FALSE,
+ bm_find_cb, info);
#endif /* HAVE_HTML_WIDGET */
} else
g_assert_not_reached();
-
- bm_find_set_status(bm, found ?
- BM_FIND_STATUS_FOUND : BM_FIND_STATUS_WRAPPED);
- bm->find_forward = find_forward;
}
static void
@@ -804,6 +844,13 @@ balsa_message_destroy(GObject * object)
bm->parts_popup = NULL;
}
+#ifdef HAVE_HTML_WIDGET
+ if (bm->html_find_info) {
+ g_free(bm->html_find_info);
+ bm->html_find_info = NULL;
+ }
+#endif /* HAVE_HTML_WIDGET */
+
if (G_OBJECT_CLASS(parent_class)->dispose)
(*G_OBJECT_CLASS(parent_class)->dispose) (object);
}
diff --git a/src/balsa-message.h b/src/balsa-message.h
index 1596629..8d530c2 100644
--- a/src/balsa-message.h
+++ b/src/balsa-message.h
@@ -96,6 +96,9 @@ struct _BalsaMessage {
/* Tab position for headers */
gint tab_position;
+#ifdef HAVE_HTML_WIDGET
+ gpointer html_find_info;
+#endif /* HAVE_HTML_WIDGET */
};
struct _BalsaMessageClass {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]