[gtksourceview/wip/search: 5/36] Synchronous forward and backward search



commit b4136a0fa4af8959c8a7407623a96a3a8cae88c1
Author: Sébastien Wilmet <swilmet gnome org>
Date:   Tue Jun 25 14:58:04 2013 +0200

    Synchronous forward and backward search

 gtksourceview/gtksourcebuffer.c |   50 +++++++++++
 gtksourceview/gtksourcebuffer.h |   10 ++
 gtksourceview/gtksourcesearch.c |  185 +++++++++++++++++++++++++++++++++++++--
 gtksourceview/gtksourcesearch.h |   13 +++
 tests/test-search-ui.c          |   66 ++++++++++++++
 tests/test-search-ui.ui         |   40 +++++++++
 6 files changed, 357 insertions(+), 7 deletions(-)
---
diff --git a/gtksourceview/gtksourcebuffer.c b/gtksourceview/gtksourcebuffer.c
index 8b2532b..2037c69 100644
--- a/gtksourceview/gtksourcebuffer.c
+++ b/gtksourceview/gtksourcebuffer.c
@@ -2679,3 +2679,53 @@ gtk_source_buffer_get_search_occurrences_count (GtkSourceBuffer *buffer)
 
        return _gtk_source_search_get_occurrences_count (buffer->priv->search);
 }
+
+/**
+ * gtk_source_buffer_forward_search:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: start of search.
+ * @match_start: return location for start of match, or %NULL.
+ * @match_end: return location for end of match, or %NULL.
+ *
+ * Synchronous forward search.
+ *
+ * Returns: whether a match was found.
+ */
+gboolean
+gtk_source_buffer_forward_search (GtkSourceBuffer   *buffer,
+                                 const GtkTextIter *iter,
+                                 GtkTextIter       *match_start,
+                                 GtkTextIter       *match_end)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), FALSE);
+
+       return _gtk_source_search_forward (buffer->priv->search,
+                                          iter,
+                                          match_start,
+                                          match_end);
+}
+
+/**
+ * gtk_source_buffer_backward_search:
+ * @buffer: a #GtkSourceBuffer.
+ * @iter: start of search.
+ * @match_start: return location for start of match, or %NULL.
+ * @match_end: return location for end of match, or %NULL.
+ *
+ * Synchronous backward search.
+ *
+ * Returns: whether a match was found.
+ */
+gboolean
+gtk_source_buffer_backward_search (GtkSourceBuffer   *buffer,
+                                  const GtkTextIter *iter,
+                                  GtkTextIter       *match_start,
+                                  GtkTextIter       *match_end)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), FALSE);
+
+       return _gtk_source_search_backward (buffer->priv->search,
+                                           iter,
+                                           match_start,
+                                           match_end);
+}
diff --git a/gtksourceview/gtksourcebuffer.h b/gtksourceview/gtksourcebuffer.h
index fab8416..ea079ad 100644
--- a/gtksourceview/gtksourcebuffer.h
+++ b/gtksourceview/gtksourcebuffer.h
@@ -205,6 +205,16 @@ GtkSourceSearchFlags        gtk_source_buffer_get_search_flags     (GtkSourceBuffer      
  *buffe
 guint                   gtk_source_buffer_get_search_occurrences_count
                                                                (GtkSourceBuffer        *buffer);
 
+gboolean                gtk_source_buffer_forward_search       (GtkSourceBuffer        *buffer,
+                                                                const GtkTextIter      *iter,
+                                                                GtkTextIter            *match_start,
+                                                                GtkTextIter            *match_end);
+
+gboolean                gtk_source_buffer_backward_search      (GtkSourceBuffer        *buffer,
+                                                                const GtkTextIter      *iter,
+                                                                GtkTextIter            *match_start,
+                                                                GtkTextIter            *match_end);
+
 /* private */
 void                    _gtk_source_buffer_update_highlight    (GtkSourceBuffer        *buffer,
                                                                 const GtkTextIter      *start,
diff --git a/gtksourceview/gtksourcesearch.c b/gtksourceview/gtksourcesearch.c
index 8130250..a5696f7 100644
--- a/gtksourceview/gtksourcesearch.c
+++ b/gtksourceview/gtksourcesearch.c
@@ -148,11 +148,11 @@ dispose_has_run (GtkSourceSearch *search)
 }
 
 static gboolean
-forward_search (GtkSourceSearch   *search,
-               const GtkTextIter *iter,
-               GtkTextIter       *match_start,
-               GtkTextIter       *match_end,
-               const GtkTextIter *limit)
+basic_forward_search (GtkSourceSearch   *search,
+                     const GtkTextIter *iter,
+                     GtkTextIter       *match_start,
+                     GtkTextIter       *match_end,
+                     const GtkTextIter *limit)
 {
        GtkTextIter begin_search = *iter;
 
@@ -180,6 +180,39 @@ forward_search (GtkSourceSearch   *search,
        }
 }
 
+static gboolean
+basic_backward_search (GtkSourceSearch   *search,
+                      const GtkTextIter *iter,
+                      GtkTextIter       *match_start,
+                      GtkTextIter       *match_end,
+                      const GtkTextIter *limit)
+{
+       GtkTextIter begin_search = *iter;
+
+       while (TRUE)
+       {
+               gboolean found = gtk_text_iter_backward_search (&begin_search,
+                                                               search->priv->text,
+                                                               search->priv->flags,
+                                                               match_start,
+                                                               match_end,
+                                                               limit);
+
+               if (!found || !search->priv->at_word_boundaries)
+               {
+                       return found;
+               }
+
+               if (gtk_text_iter_starts_word (match_start) &&
+                   gtk_text_iter_ends_word (match_end))
+               {
+                       return TRUE;
+               }
+
+               begin_search = *match_start;
+       }
+}
+
 static void
 sync_found_tag (GtkSourceSearch *search)
 {
@@ -523,7 +556,7 @@ remove_occurrences_in_range (GtkSourceSearch *search,
        iter = *start;
 
        /* TODO optimization: search with forward_to_tag_toggle() */
-       while (forward_search (search, &iter, &match_start, &match_end, end))
+       while (basic_forward_search (search, &iter, &match_start, &match_end, end))
        {
                if (search->priv->scan_region == NULL)
                {
@@ -611,7 +644,7 @@ scan_subregion (GtkSourceSearch *search,
                GtkTextIter match_start;
                GtkTextIter match_end;
 
-               found = forward_search (search, &iter, &match_start, &match_end, limit);
+               found = basic_forward_search (search, &iter, &match_start, &match_end, limit);
 
                if (found)
                {
@@ -737,6 +770,122 @@ install_idle_scan (GtkSourceSearch *search)
        }
 }
 
+static gboolean
+smart_forward_search (GtkSourceSearch   *search,
+                     const GtkTextIter *start_at,
+                     GtkTextIter       *match_start,
+                     GtkTextIter       *match_end)
+{
+       GtkTextIter iter = *start_at;
+       GtkTextIter limit;
+       GtkTextRegion *region = NULL;
+
+       if (gtk_text_iter_is_end (start_at) ||
+           search->priv->text == NULL)
+       {
+               return FALSE;
+       }
+
+       if (search->priv->found_tag == NULL)
+       {
+               init_found_tag (search);
+       }
+
+       if (!gtk_text_iter_has_tag (&iter, search->priv->found_tag) ||
+           gtk_text_iter_ends_tag (&iter, search->priv->found_tag))
+       {
+               gtk_text_iter_forward_to_tag_toggle (&iter, search->priv->found_tag);
+       }
+
+       limit = iter;
+       gtk_text_iter_forward_to_tag_toggle (&limit, search->priv->found_tag);
+
+       if (search->priv->scan_region != NULL)
+       {
+               region = gtk_text_region_intersect (search->priv->scan_region, start_at, &limit);
+       }
+
+       if (is_text_region_empty (region))
+       {
+               gboolean found = basic_forward_search (search, &iter, match_start, match_end, &limit);
+
+               if (region != NULL)
+               {
+                       gtk_text_region_destroy (region, TRUE);
+               }
+
+               if (found)
+               {
+                       return TRUE;
+               }
+
+               return smart_forward_search (search, &limit, match_start, match_end);
+       }
+
+       scan_all_region (search, region);
+       gtk_text_region_destroy (region, TRUE);
+
+       return smart_forward_search (search, start_at, match_start, match_end);
+}
+
+static gboolean
+smart_backward_search (GtkSourceSearch   *search,
+                      const GtkTextIter *start_at,
+                      GtkTextIter       *match_start,
+                      GtkTextIter       *match_end)
+{
+       GtkTextIter iter = *start_at;
+       GtkTextIter limit;
+       GtkTextRegion *region = NULL;
+
+       if (gtk_text_iter_is_start (start_at) ||
+           search->priv->text == NULL)
+       {
+               return FALSE;
+       }
+
+       if (search->priv->found_tag == NULL)
+       {
+               init_found_tag (search);
+       }
+
+       if (!gtk_text_iter_has_tag (&iter, search->priv->found_tag) ||
+           gtk_text_iter_begins_tag (&iter, search->priv->found_tag))
+       {
+               gtk_text_iter_backward_to_tag_toggle (&iter, search->priv->found_tag);
+       }
+
+       limit = iter;
+       gtk_text_iter_backward_to_tag_toggle (&limit, search->priv->found_tag);
+
+       if (search->priv->scan_region != NULL)
+       {
+               region = gtk_text_region_intersect (search->priv->scan_region, &limit, start_at);
+       }
+
+       if (is_text_region_empty (region))
+       {
+               gboolean found = basic_backward_search (search, &iter, match_start, match_end, &limit);
+
+               if (region != NULL)
+               {
+                       gtk_text_region_destroy (region, TRUE);
+               }
+
+               if (found)
+               {
+                       return TRUE;
+               }
+
+               return smart_backward_search (search, &limit, match_start, match_end);
+       }
+
+       scan_all_region (search, region);
+       gtk_text_region_destroy (region, TRUE);
+
+       return smart_backward_search (search, start_at, match_start, match_end);
+}
+
 static void
 add_subregion_to_scan (GtkSourceSearch   *search,
                       const GtkTextIter *subregion_start,
@@ -1115,3 +1264,25 @@ _gtk_source_search_get_occurrences_count (GtkSourceSearch *search)
 
        return search->priv->occurrences_count;
 }
+
+gboolean
+_gtk_source_search_forward (GtkSourceSearch   *search,
+                           const GtkTextIter *iter,
+                           GtkTextIter       *match_start,
+                           GtkTextIter       *match_end)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_SEARCH (search), FALSE);
+
+       return smart_forward_search (search, iter, match_start, match_end);
+}
+
+gboolean
+_gtk_source_search_backward (GtkSourceSearch   *search,
+                            const GtkTextIter *iter,
+                            GtkTextIter       *match_start,
+                            GtkTextIter       *match_end)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_SEARCH (search), FALSE);
+
+       return smart_backward_search (search, iter, match_start, match_end);
+}
diff --git a/gtksourceview/gtksourcesearch.h b/gtksourceview/gtksourcesearch.h
index 6fe023b..ce9a143 100644
--- a/gtksourceview/gtksourcesearch.h
+++ b/gtksourceview/gtksourcesearch.h
@@ -77,8 +77,21 @@ void                 _gtk_source_search_update_highlight             (GtkSourceSearch      
  *search,
                                                                         const GtkTextIter      *end,
                                                                         gboolean                synchronous);
 
+G_GNUC_INTERNAL
 guint                  _gtk_source_search_get_occurrences_count        (GtkSourceSearch        *search);
 
+G_GNUC_INTERNAL
+gboolean               _gtk_source_search_forward                      (GtkSourceSearch        *search,
+                                                                        const GtkTextIter      *iter,
+                                                                        GtkTextIter            *match_start,
+                                                                        GtkTextIter            *match_end);
+
+G_GNUC_INTERNAL
+gboolean               _gtk_source_search_backward                     (GtkSourceSearch        *search,
+                                                                        const GtkTextIter      *iter,
+                                                                        GtkTextIter            *match_start,
+                                                                        GtkTextIter            *match_end);
+
 G_END_DECLS
 
 #endif /* __GTK_SOURCE_SEARCH_H__ */
diff --git a/tests/test-search-ui.c b/tests/test-search-ui.c
index b907e84..8a4e7b0 100644
--- a/tests/test-search-ui.c
+++ b/tests/test-search-ui.c
@@ -106,6 +106,58 @@ on_at_word_boundaries_toggled_cb (GtkToggleButton *button,
 }
 
 static void
+select_search_occurrence (GtkTextView       *view,
+                         GtkTextBuffer     *buffer,
+                         const GtkTextIter *match_start,
+                         const GtkTextIter *match_end)
+{
+       gtk_text_buffer_select_range (buffer, match_start, match_end);
+
+       gtk_text_view_scroll_mark_onscreen (view, gtk_text_buffer_get_insert (buffer));
+}
+
+static void
+on_button_previous_clicked_cb (GtkButton   *button,
+                              GtkTextView *view)
+{
+       GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
+       GtkTextIter iter;
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+
+       gtk_text_buffer_get_selection_bounds (buffer, &iter, NULL);
+
+       if (gtk_source_buffer_backward_search (GTK_SOURCE_BUFFER (buffer),
+                                              &iter,
+                                              &match_start,
+                                              &match_end))
+       {
+               select_search_occurrence (view, buffer, &match_start, &match_end);
+       }
+}
+
+static void
+on_button_next_clicked_cb (GtkButton   *button,
+                          GtkTextView *view)
+{
+       GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
+       GtkTextIter iter;
+       GtkTextIter match_start;
+       GtkTextIter match_end;
+
+       gtk_text_buffer_get_selection_bounds (buffer, NULL, &iter);
+
+       if (gtk_source_buffer_forward_search (GTK_SOURCE_BUFFER (buffer),
+                                             &iter,
+                                             &match_start,
+                                             &match_end))
+       {
+               select_search_occurrence (view, buffer, &match_start, &match_end);
+       }
+}
+
+
+static void
 create_window (void)
 {
        GtkBuilder *builder;
@@ -117,6 +169,8 @@ create_window (void)
        GtkLabel *label_occurrences_count;
        GtkCheckButton *match_case;
        GtkCheckButton *at_word_boundaries;
+       GtkButton *button_previous;
+       GtkButton *button_next;
        PangoFontDescription *font;
 
        builder = gtk_builder_new ();
@@ -136,6 +190,8 @@ create_window (void)
        label_occurrences_count = GTK_LABEL (gtk_builder_get_object (builder, "label_occurrences_count"));
        match_case = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "checkbutton_match_case"));
        at_word_boundaries = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, 
"checkbutton_at_word_boundaries"));
+       button_previous = GTK_BUTTON (gtk_builder_get_object (builder, "button_previous"));
+       button_next = GTK_BUTTON (gtk_builder_get_object (builder, "button_next"));
 
        font = pango_font_description_from_string ("Monospace 10");
        gtk_widget_override_font (GTK_WIDGET (source_view), font);
@@ -179,6 +235,16 @@ create_window (void)
                          G_CALLBACK (on_at_word_boundaries_toggled_cb),
                          source_buffer);
 
+       g_signal_connect (button_previous,
+                         "clicked",
+                         G_CALLBACK (on_button_previous_clicked_cb),
+                         source_view);
+
+       g_signal_connect (button_next,
+                         "clicked",
+                         G_CALLBACK (on_button_next_clicked_cb),
+                         source_view);
+
        g_object_unref (builder);
 }
 
diff --git a/tests/test-search-ui.ui b/tests/test-search-ui.ui
index 8bf9f72..5de2949 100644
--- a/tests/test-search-ui.ui
+++ b/tests/test-search-ui.ui
@@ -2,6 +2,18 @@
 <interface>
   <!-- interface-requires gtk+ 3.6 -->
   <!-- interface-requires gtksourceview 3.0 -->
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-go-up</property>
+    <property name="icon_size">1</property>
+  </object>
+  <object class="GtkImage" id="image2">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-go-down</property>
+    <property name="icon_size">1</property>
+  </object>
   <object class="GtkWindow" id="window">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -84,12 +96,40 @@
                 <property name="label">0 occurrences</property>
               </object>
               <packing>
+                <property name="left_attach">4</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button_previous">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">image1</property>
+              </object>
+              <packing>
                 <property name="left_attach">2</property>
                 <property name="top_attach">0</property>
                 <property name="width">1</property>
                 <property name="height">1</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkButton" id="button_next">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">image2</property>
+              </object>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="top_attach">0</property>
+                <property name="width">1</property>
+                <property name="height">1</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="left_attach">0</property>



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