[balsa] Initial support for webkit search-text



commit d4dc82623a32c521c01604f3d0486c1c4e7cd043
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Thu Dec 3 19:08:37 2009 -0500

    Initial support for webkit search-text

 ChangeLog           |   17 ++++++
 libbalsa/html.c     |  142 ++++++++++++++++++++++++++++++++-------------------
 libbalsa/html.h     |    2 +
 src/balsa-message.c |   83 +++++++++++++++++++++--------
 4 files changed, 169 insertions(+), 75 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 86177ad..1011ee8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2009-12-03  Peter Bloomfield
+
+	* src/balsa-message.c (bm_find_set_status),
+	(bm_find_scroll_to_rectangle), (bm_find_scroll_to_selection),
+	(bm_find_entry_changed_cb), (bm_find_again): implement
+	search-text for HTML parts; desensitize find-next and
+	find-previous when find-entry is empty.
+
+2009-12-03  Peter Bloomfield
+
+	* libbalsa/html.c (lbh_navigation_policy_decision_requested_cb),
+	(libbalsa_html_new), (libbalsa_html_search_text),
+	(libbalsa_html_get_selection_bounds),
+	(libbalsa_html_can_search): implement search-text for webkit
+	backend, but only if we can scroll to the text.
+	* libbalsa/html.h: implement libbalsa_html_search_text.
+
 2009-11-16  Peter Bloomfield
 
 	* src/balsa-message.c (balsa_message_find_in_message): grab
diff --git a/libbalsa/html.c b/libbalsa/html.c
index c8e6c28..ecbe65b 100644
--- a/libbalsa/html.c
+++ b/libbalsa/html.c
@@ -65,18 +65,14 @@
  * "request-url" signal; WebKit does not have one.  We turn off its
  * "auto-load-images" setting, which seems to mean that <img src="...">
  * tags are ignored.  However, that is apparently intended to save
- * bandwidth rather than to protect privacy.  The server in a <frame
- * src="..."> is known to be contacted; whether "src=" attributes on
+ * bandwidth rather than to protect privacy.  The WebView attempts to
+ * contact the server named in <frame src="...">, but we detect it
+ * because of the new frame, and block it; whether "src=" attributes on
  * other tags are followed is unknown.
  *
  * We do disable scripts, so the server in a <script src="..."> should
  * not be contacted.
  *
- * The action taken on a <frame src="..."> tag seems buggy: it results
- * in a "navigation-requested" signal, which is otherwise emitted when
- * the user clicks on a link.  Consequently, we pass it to the Gnome
- * browser without verifying that the user wants it.
- *
  * https://bugs.webkit.org/show_bug.cgi?id=17147 discusses these issues.
  */
 
@@ -84,10 +80,13 @@
 
 typedef struct {
     WebKitWebView        *web_view;
+    WebKitWebFrame       *frame;
     LibBalsaHtmlCallback  hover_cb;
     LibBalsaHtmlCallback  clicked_cb;
+#if !WEBKIT_CHECK_VERSION(1, 12, 0)
     GtkAdjustment        *hadj;
     GtkAdjustment        *vadj;
+#endif                          /* !WEBKIT_CHECK_VERSION(1, 12, 0) */
 } LibBalsaWebKitInfo;
 
 static void
@@ -101,6 +100,7 @@ lbh_hovering_over_link_cb(GtkWidget   * web_view,
     (*info->hover_cb)(uri);
 }
 
+#if !WEBKIT_CHECK_VERSION(1, 12, 0)
 static void
 lbh_size_request_cb(GtkWidget      * widget,
                     GtkRequisition * requisition,
@@ -116,6 +116,7 @@ lbh_size_request_cb(GtkWidget      * widget,
     if (upper > requisition->height)
         requisition->height = upper;
 }
+#endif                          /* !WEBKIT_CHECK_VERSION(1, 12, 0) */
 
 static gboolean
 lbh_navigation_policy_decision_requested_cb(WebKitWebView             * web_view,
@@ -130,15 +131,24 @@ lbh_navigation_policy_decision_requested_cb(WebKitWebView             * web_view
 
     g_object_get(action, "reason", &reason, NULL);
 
+    /* First request is for "about:blank"; we must allow it, but first
+     * we store the main frame, so we can detect a new frame later. */
+    if (!info->frame)
+        info->frame = frame;
+    else if (web_view == info->web_view
+             && frame != info->frame
+             && reason == WEBKIT_WEB_NAVIGATION_REASON_OTHER) {
+        g_message("%s new frame ignored:\n URI=\"%s\"", __func__, 
+                  webkit_network_request_get_uri(request));
+        webkit_web_policy_decision_ignore(decision);
+        return TRUE;
+    }
+
     if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED ||
         (web_view != info->web_view
          && reason == WEBKIT_WEB_NAVIGATION_REASON_OTHER)) {
-        const gchar *uri = webkit_network_request_get_uri(request);
-
-        (*info->clicked_cb) (uri);
-
+        (*info->clicked_cb) (webkit_network_request_get_uri(request));
         webkit_web_policy_decision_ignore(decision);
-
         return TRUE;
     }
 
@@ -222,6 +232,7 @@ libbalsa_html_new(const gchar        * text,
 
     info = g_new(LibBalsaWebKitInfo, 1);
     info->web_view = web_view = WEBKIT_WEB_VIEW(widget);
+    info->frame = NULL;
     g_object_weak_ref(G_OBJECT(web_view), (GWeakNotify) g_free, info);
 
     g_object_set(webkit_web_view_get_settings(web_view),
@@ -230,6 +241,7 @@ libbalsa_html_new(const gchar        * text,
                  "enable-plugins",   FALSE,
                  NULL);
 
+#if !WEBKIT_CHECK_VERSION(1, 12, 0)
     info->hadj =
         GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
     info->vadj =
@@ -240,6 +252,7 @@ libbalsa_html_new(const gchar        * text,
     g_signal_connect(web_view, "size-request",
                      G_CALLBACK(lbh_size_request_cb), info);
 
+#endif                          /* !WEBKIT_CHECK_VERSION(1, 12, 0) */
     info->hover_cb = hover_cb;
     g_signal_connect(web_view, "hovering-over-link",
                      G_CALLBACK(lbh_hovering_over_link_cb), info);
@@ -325,6 +338,8 @@ libbalsa_html_copy(GtkWidget * widget)
     webkit_web_view_copy_clipboard(WEBKIT_WEB_VIEW(widget));
 }
 
+#define WEBKIT_WEB_VIEW_CAN_MANAGE_SELECTION FALSE
+#if WEBKIT_WEB_VIEW_CAN_MANAGE_SELECTION
 /*
  * Does the widget support searching text?
  */
@@ -335,17 +350,55 @@ libbalsa_html_can_search(GtkWidget * widget)
 }
 
 /*
- * Search for the text.
+ * 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)
 {
+    if (!*text) {
+        webkit_web_view_clear_selection(WEBKIT_WEB_VIEW(widget));
+        return TRUE;
+    }
+
     return webkit_web_view_search_text(WEBKIT_WEB_VIEW(widget), text,
-                                       FALSE,   /* case-insensitive */
+                                       FALSE,    /* case-insensitive */
                                        find_forward, wrap);
 }
 
+/*
+ * Get the rectangle containing the currently selected text, for
+ * scrolling.
+ */
+void
+libbalsa_html_get_selection_bounds(GtkWidget    * widget,
+                                   GdkRectangle * selection_bounds)
+{
+    webkit_web_view_get_selection_bounds(WEBKIT_WEB_VIEW(widget),
+                                         selection_bounds);
+}
+#else                           /* WEBKIT_WEB_VIEW_CAN_MANAGE_SELECTION */
+gboolean
+libbalsa_html_can_search(GtkWidget * widget)
+{
+    return FALSE;
+}
+
+gboolean
+libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
+                          gboolean find_forward, gboolean wrap)
+{
+    return FALSE;
+}
+
+void
+libbalsa_html_get_selection_bounds(GtkWidget    * widget,
+                                   GdkRectangle * selection_bounds)
+{
+}
+#endif                          /* WEBKIT_WEB_VIEW_CAN_MANAGE_SELECTION */
+
 # else                          /* defined(HAVE_WEBKIT) */
 
 /* Common code for both GtkHtml widgets. */
@@ -601,25 +654,6 @@ libbalsa_html_copy(GtkWidget * widget)
     gtk_html_copy(GTK_HTML(widget));
 }
 
-/*
- * GtkHTML doesn't support searching text.
- */
-gboolean
-libbalsa_html_can_search(GtkWidget * widget)
-{
-    return FALSE;
-}
-
-/*
- * GtkHTML doesn't support searching text.
- */
-gboolean
-libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
-                          gboolean find_forward, gboolean wrap)
-{
-    return FALSE;
-}
-
 # else				/* HAVE_GTKHTML3 */
 
 /* Code for GtkHtml-2 */
@@ -755,25 +789,6 @@ libbalsa_html_copy(GtkWidget * widget)
 {
 }
 
-/*
- * HtmlView doesn't support searching text.
- */
-gboolean
-libbalsa_html_can_search(GtkWidget * widget)
-{
-    return FALSE;
-}
-
-/*
- * HtmlView doesn't support searching text.
- */
-gboolean
-libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
-                          gboolean find_forward, gboolean wrap)
-{
-    return FALSE;
-}
-
 # endif				/* HAVE_GTKHTML3 */
 
 /* Common code for both widgets. */
@@ -810,6 +825,29 @@ libbalsa_html_url_requested(GtkWidget * html, const gchar * url,
 
     return TRUE;
 }
+
+/*
+ * Neither widget supports searching text
+ */
+gboolean
+libbalsa_html_can_search(GtkWidget * widget)
+{
+    return FALSE;
+}
+
+gboolean
+libbalsa_html_search_text(GtkWidget * widget, const gchar * text,
+                          gboolean find_forward, gboolean wrap)
+{
+    return FALSE;
+}
+
+void
+libbalsa_html_get_selection_bounds(GtkWidget    * widget,
+                                   GdkRectangle * selection_bounds)
+{
+}
+
 # endif                         /* defined(HAVE_WEBKIT) */
 
 /* Filter text/enriched or text/richtext to text/html, if we have GMime
diff --git a/libbalsa/html.h b/libbalsa/html.h
index bd09382..23125ef 100644
--- a/libbalsa/html.h
+++ b/libbalsa/html.h
@@ -63,6 +63,8 @@ guint libbalsa_html_filter(LibBalsaHTMLType html_type, gchar ** text,
 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);
 
 # endif				/* HAVE_GTKHTML */
 
diff --git a/src/balsa-message.c b/src/balsa-message.c
index 85d1b73..705380c 100644
--- a/src/balsa-message.c
+++ b/src/balsa-message.c
@@ -401,7 +401,11 @@ bm_find_set_status(BalsaMessage * bm, BalsaMessageFindStatus status)
         case BM_FIND_STATUS_INIT:
             break;
         case BM_FIND_STATUS_FOUND:
-            sensitive = TRUE;
+            /* The widget returned "found"; if it really found a string,
+             * we sensitize the "next" and "previous" buttons, but if
+             * the find-entry was empty, we desensitize them. */
+            if (gtk_entry_get_text(GTK_ENTRY(bm->find_entry))[0])
+                sensitive = TRUE;
             break;
         case BM_FIND_STATUS_WRAPPED:
             text = _("Wrapped");
@@ -420,27 +424,46 @@ bm_find_set_status(BalsaMessage * bm, BalsaMessageFindStatus status)
 }
 
 static void
-bm_find_scroll_to_iter(BalsaMessage * bm,
-                       GtkTextView  * text_view,
-                       GtkTextIter  * iter)
+bm_find_scroll_to_rectangle(BalsaMessage * bm,
+                            GtkWidget    * widget,
+                            GdkRectangle * rectangle)
 {
-    GtkAdjustment *adj =
-        gtk_viewport_get_vadjustment(GTK_VIEWPORT(bm->cont_viewport));
-    GdkRectangle location;
-    gdouble y;
+    gint x, y;
+    GtkAdjustment *adj;
+
+    gtk_widget_translate_coordinates(widget, bm->bm_widget->widget,
+                                     rectangle->x, rectangle->y,
+                                     &x, &y);
 
-    gtk_text_view_get_iter_location(text_view, iter, &location);
+    adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(bm->cont_viewport));
+    gtk_adjustment_clamp_page(adj, x, x + rectangle->width);
+
+    adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(bm->cont_viewport));
+    gtk_adjustment_clamp_page(adj, y, y + rectangle->height);
+}
+
+static void
+bm_find_scroll_to_selection(BalsaMessage * bm,
+                            GtkTextView  * text_view,
+                            GtkTextIter  * begin_iter,
+                            GtkTextIter  * end_iter)
+{
+    GdkRectangle begin_location, end_location;
+
+    gtk_text_view_get_iter_location(text_view, begin_iter,
+                                    &begin_location);
+    gtk_text_view_get_iter_location(text_view, end_iter,
+                                    &end_location);
+    end_location.width = 0;
+    gdk_rectangle_union(&begin_location, &end_location, &begin_location);
     gtk_text_view_buffer_to_window_coords(text_view,
                                           GTK_TEXT_WINDOW_WIDGET,
-                                          location.x, location.y,
-                                          NULL, &location.y);
-    gtk_widget_translate_coordinates(GTK_WIDGET(text_view),
-                                     bm->bm_widget->widget,
-                                     location.x, location.y,
-                                     NULL, &location.y);
+                                          begin_location.x,
+                                          begin_location.y,
+                                          &begin_location.x,
+                                          &begin_location.y);
 
-    y = location.y;
-    gtk_adjustment_clamp_page(adj, y, y + location.height);
+    bm_find_scroll_to_rectangle(bm, GTK_WIDGET(text_view), &begin_location);
 }
 
 static void
@@ -478,12 +501,21 @@ bm_find_entry_changed_cb(GtkEditable * editable, gpointer data)
 
         if (found) {
             gtk_text_buffer_select_range(buffer, &match_begin, &match_end);
-            bm_find_scroll_to_iter(bm, text_view, &match_begin);
+            bm_find_scroll_to_selection(bm, text_view,
+                                        &match_begin, &match_end);
             bm->find_iter = match_begin;
         }
-    } else if (libbalsa_html_can_search(widget))
-        found = libbalsa_html_search_text(widget, text, bm->find_forward, TRUE);
-    else
+    } 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);
+        }
+    } else
         g_assert_not_reached();
 
     bm_find_set_status(bm, found ?
@@ -523,12 +555,17 @@ bm_find_again(BalsaMessage * bm, gboolean find_forward)
         }
 
         gtk_text_buffer_select_range(buffer, &match_begin, &match_end);
-        bm_find_scroll_to_iter(bm, text_view, &match_begin);
+        bm_find_scroll_to_selection(bm, text_view,
+                                    &match_begin, &match_end);
         bm->find_iter = match_begin;
     } else if (libbalsa_html_can_search(widget)) {
+        GdkRectangle selection_bounds;
+
         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);
     } else
         g_assert_not_reached();
 



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