[libgda/LIBGDA_4.2] GdaBrowser: reworked search in text in separate widget



commit 4fa1bcd83ba0d9fa5ad15ecf54b36f726bbfc23f
Author: Vivien Malerba <malerba gnome-db org>
Date:   Thu Jun 16 21:01:39 2011 +0200

    GdaBrowser: reworked search in text in separate widget

 tools/browser/Makefile.am                     |    4 +-
 tools/browser/ldap-browser/class-properties.c |  138 +++++++----
 tools/browser/ldap-browser/entry-properties.c |  204 +---------------
 tools/browser/text-search.c                   |  311 +++++++++++++++++++++++++
 tools/browser/text-search.h                   |   52 ++++
 5 files changed, 464 insertions(+), 245 deletions(-)
---
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index 3712e8a..7042c58 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -84,7 +84,9 @@ libbrowser_la_SOURCES=\
 	mgr-favorites.h \
 	mgr-favorites.c \
 	browser-stock-icons.c \
-	browser-stock-icons.h
+	browser-stock-icons.h \
+	text-search.c \
+	text-search.h
 
 gda_browser_4_0_SOURCES=\
 	main.c
diff --git a/tools/browser/ldap-browser/class-properties.c b/tools/browser/ldap-browser/class-properties.c
index 40314fc..1be4004 100644
--- a/tools/browser/ldap-browser/class-properties.c
+++ b/tools/browser/ldap-browser/class-properties.c
@@ -22,16 +22,20 @@
 #include "class-properties.h"
 #include "marshal.h"
 #include <gdk/gdkkeysyms.h>
+#include "../text-search.h"
 
 struct _ClassPropertiesPrivate {
 	BrowserConnection *bcnc;
 
+	GtkTextView *view;
 	GtkTextBuffer *text;
 	gboolean hovering_over_link;
+
+	GtkWidget *text_search;
 };
 
 static void class_properties_class_init (ClassPropertiesClass *klass);
-static void class_properties_init       (ClassProperties *eprop, ClassPropertiesClass *klass);
+static void class_properties_init       (ClassProperties *cprop, ClassPropertiesClass *klass);
 static void class_properties_dispose   (GObject *object);
 
 static GObjectClass *parent_class = NULL;
@@ -69,24 +73,24 @@ class_properties_class_init (ClassPropertiesClass *klass)
 
 
 static void
-class_properties_init (ClassProperties *eprop, G_GNUC_UNUSED ClassPropertiesClass *klass)
+class_properties_init (ClassProperties *cprop, G_GNUC_UNUSED ClassPropertiesClass *klass)
 {
-	eprop->priv = g_new0 (ClassPropertiesPrivate, 1);
-	eprop->priv->hovering_over_link = FALSE;
+	cprop->priv = g_new0 (ClassPropertiesPrivate, 1);
+	cprop->priv->hovering_over_link = FALSE;
 }
 
 static void
 class_properties_dispose (GObject *object)
 {
-	ClassProperties *eprop = (ClassProperties *) object;
+	ClassProperties *cprop = (ClassProperties *) object;
 
 	/* free memory */
-	if (eprop->priv) {
-		if (eprop->priv->bcnc) {
-			g_object_unref (eprop->priv->bcnc);
+	if (cprop->priv) {
+		if (cprop->priv->bcnc) {
+			g_object_unref (cprop->priv->bcnc);
 		}
-		g_free (eprop->priv);
-		eprop->priv = NULL;
+		g_free (cprop->priv);
+		cprop->priv = NULL;
 	}
 
 	parent_class->dispose (object);
@@ -115,10 +119,12 @@ class_properties_get_type (void)
 	return type;
 }
 
-static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *eprop);
-static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop);
-static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *eprop);
-static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ClassProperties *eprop);
+static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *cprop);
+static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *cprop);
+static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *cprop);
+static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ClassProperties *cprop);
+
+static void show_search_bar (ClassProperties *cprop);
 
 /**
  * class_properties_new:
@@ -128,11 +134,11 @@ static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibilit
 GtkWidget *
 class_properties_new (BrowserConnection *bcnc)
 {
-	ClassProperties *eprop;
+	ClassProperties *cprop;
 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
 
-	eprop = CLASS_PROPERTIES (g_object_new (CLASS_PROPERTIES_TYPE, NULL));
-	eprop->priv->bcnc = g_object_ref (bcnc);
+	cprop = CLASS_PROPERTIES (g_object_new (CLASS_PROPERTIES_TYPE, NULL));
+	cprop->priv->bcnc = g_object_ref (bcnc);
 	
 	GtkWidget *sw;
         sw = gtk_scrolled_window_new (NULL, NULL);
@@ -140,7 +146,7 @@ class_properties_new (BrowserConnection *bcnc)
         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
                                         GTK_POLICY_AUTOMATIC,
                                         GTK_POLICY_AUTOMATIC);
-	gtk_box_pack_start (GTK_BOX (eprop), sw, TRUE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (cprop), sw, TRUE, TRUE, 0);
 
 	GtkWidget *textview;
 	textview = gtk_text_view_new ();
@@ -149,35 +155,36 @@ class_properties_new (BrowserConnection *bcnc)
         gtk_text_view_set_right_margin (GTK_TEXT_VIEW (textview), 5);
         gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), FALSE);
 	gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (textview), FALSE);
-        eprop->priv->text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+        cprop->priv->text = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+	cprop->priv->view = GTK_TEXT_VIEW (textview);
         gtk_widget_show_all (sw);
 
-        gtk_text_buffer_create_tag (eprop->priv->text, "section",
+        gtk_text_buffer_create_tag (cprop->priv->text, "section",
                                     "weight", PANGO_WEIGHT_BOLD,
                                     "foreground", "blue", NULL);
 
-        gtk_text_buffer_create_tag (eprop->priv->text, "error",
+        gtk_text_buffer_create_tag (cprop->priv->text, "error",
                                     "foreground", "red", NULL);
 
-        gtk_text_buffer_create_tag (eprop->priv->text, "data",
+        gtk_text_buffer_create_tag (cprop->priv->text, "data",
                                     "left-margin", 20, NULL);
 
-        gtk_text_buffer_create_tag (eprop->priv->text, "starter",
+        gtk_text_buffer_create_tag (cprop->priv->text, "starter",
                                     "indent", -10,
                                     "left-margin", 20, NULL);
 
 	g_signal_connect (textview, "key-press-event", 
-			  G_CALLBACK (key_press_event), eprop);
+			  G_CALLBACK (key_press_event), cprop);
 	g_signal_connect (textview, "event-after", 
-			  G_CALLBACK (event_after), eprop);
+			  G_CALLBACK (event_after), cprop);
 	g_signal_connect (textview, "motion-notify-event", 
-			  G_CALLBACK (motion_notify_event), eprop);
+			  G_CALLBACK (motion_notify_event), cprop);
 	g_signal_connect (textview, "visibility-notify-event", 
-			  G_CALLBACK (visibility_notify_event), eprop);
+			  G_CALLBACK (visibility_notify_event), cprop);
 
-	class_properties_set_class (eprop, NULL);
+	class_properties_set_class (cprop, NULL);
 
-	return (GtkWidget*) eprop;
+	return (GtkWidget*) cprop;
 }
 
 static GdkCursor *hand_cursor = NULL;
@@ -188,7 +195,7 @@ static GdkCursor *regular_cursor = NULL;
  * typically used by web browsers.
  */
 static void
-set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ClassProperties *eprop)
+set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ClassProperties *cprop)
 {
 	GSList *tags = NULL, *tagp = NULL;
 	GtkTextIter iter;
@@ -206,10 +213,10 @@ set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ClassProperti
 		}
 	}
 	
-	if (hovering != eprop->priv->hovering_over_link) {
-		eprop->priv->hovering_over_link = hovering;
+	if (hovering != cprop->priv->hovering_over_link) {
+		cprop->priv->hovering_over_link = hovering;
 		
-		if (eprop->priv->hovering_over_link) {
+		if (cprop->priv->hovering_over_link) {
 			if (! hand_cursor)
 				hand_cursor = gdk_cursor_new (GDK_HAND2);
 			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
@@ -235,7 +242,7 @@ set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ClassProperti
  */
 static gboolean
 visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility *event,
-			 ClassProperties *eprop)
+			 ClassProperties *cprop)
 {
 	gint wx, wy, bx, by;
 	
@@ -245,7 +252,7 @@ visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility
 					       GTK_TEXT_WINDOW_WIDGET,
 					       wx, wy, &bx, &by);
 	
-	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, eprop);
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, cprop);
 	
 	return FALSE;
 }
@@ -254,7 +261,7 @@ visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility
  * Update the cursor image if the pointer moved. 
  */
 static gboolean
-motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *eprop)
+motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassProperties *cprop)
 {
 	gint x, y;
 	
@@ -262,7 +269,7 @@ motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassPropertie
 					       GTK_TEXT_WINDOW_WIDGET,
 					       event->x, event->y, &x, &y);
 	
-	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, eprop);
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, cprop);
 
 	gdk_window_get_pointer (gtk_widget_get_window (text_view), NULL, NULL, NULL);
 
@@ -274,7 +281,7 @@ motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ClassPropertie
  * by the data attached to it.
  */
 static void
-follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ClassProperties *eprop)
+follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ClassProperties *cprop)
 {
 	GSList *tags = NULL, *tagp = NULL;
 	
@@ -285,7 +292,7 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ClassProp
 		
 		dn = g_object_get_data (G_OBJECT (tag), "class");
 		if (dn)
-			g_signal_emit (eprop, class_properties_signals [OPEN_CLASS], 0, dn);
+			g_signal_emit (cprop, class_properties_signals [OPEN_CLASS], 0, dn);
         }
 
 	if (tags) 
@@ -297,7 +304,7 @@ follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ClassProp
  * Links can also be activated by clicking.
  */
 static gboolean
-event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop)
+event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *cprop)
 {
 	GtkTextIter start, end, iter;
 	GtkTextBuffer *buffer;
@@ -325,7 +332,7 @@ event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop)
 	
 	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
 	
-	follow_if_link (text_view, &iter, eprop);
+	follow_if_link (text_view, &iter, cprop);
 	
 	return FALSE;
 }
@@ -334,7 +341,7 @@ event_after (GtkWidget *text_view, GdkEvent *ev, ClassProperties *eprop)
  * Links can be activated by pressing Enter.
  */
 static gboolean
-key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *eprop)
+key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *cprop)
 {
 	GtkTextIter iter;
 	GtkTextBuffer *buffer;
@@ -345,9 +352,19 @@ key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *epro
 		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));
-		follow_if_link (text_view, &iter, eprop);
+		follow_if_link (text_view, &iter, cprop);
 		break;
-		
+	case GDK_KEY_F:
+	case GDK_KEY_f:
+		if (event->state & GDK_CONTROL_MASK) {
+			show_search_bar (cprop);
+			return TRUE;
+		}
+		break;
+	case GDK_KEY_slash:
+		show_search_bar (cprop);
+		return TRUE;
+		break;		
 	default:
 		break;
 	}
@@ -356,23 +373,23 @@ key_press_event (GtkWidget *text_view, GdkEventKey *event, ClassProperties *epro
 
 /**
  * class_properties_set_class:
- * @eprop: a #ClassProperties widget
+ * @cprop: a #ClassProperties widget
  * @classname: a DN to display information for
  *
  * Adjusts the display to show @classname's properties
  */
 void
-class_properties_set_class (ClassProperties *eprop, const gchar *classname)
+class_properties_set_class (ClassProperties *cprop, const gchar *classname)
 {
-	g_return_if_fail (IS_CLASS_PROPERTIES (eprop));
+	g_return_if_fail (IS_CLASS_PROPERTIES (cprop));
 
 	GtkTextBuffer *tbuffer;
 	GtkTextIter start, end;
 	GdaLdapClass *lcl;
 	GtkTextIter current;
-	gint i;
+	guint i;
 
-	tbuffer = eprop->priv->text;
+	tbuffer = cprop->priv->text;
 	gtk_text_buffer_get_start_iter (tbuffer, &start);
         gtk_text_buffer_get_end_iter (tbuffer, &end);
         gtk_text_buffer_delete (tbuffer, &start, &end);
@@ -380,9 +397,9 @@ class_properties_set_class (ClassProperties *eprop, const gchar *classname)
 	if (!classname || !*classname)
 		return;
 
-	lcl = browser_connection_get_class_info (eprop->priv->bcnc, classname);
+	lcl = browser_connection_get_class_info (cprop->priv->bcnc, classname);
 	if (!lcl) {
-		browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eprop)),
+		browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) cprop)),
 				      "%s", _("Could not get information about LDAP class"));
 		return;
 	}
@@ -570,4 +587,23 @@ class_properties_set_class (ClassProperties *eprop, const gchar *classname)
 			gtk_text_buffer_insert (tbuffer, &current, "\n", -1);
 		}
 	}
+
+	if (cprop->priv->text_search && gtk_widget_get_visible (cprop->priv->text_search))
+		text_search_rerun (TEXT_SEARCH (cprop->priv->text_search));
+}
+
+static void
+show_search_bar (ClassProperties *cprop)
+{
+	if (! cprop->priv->text_search) {
+		cprop->priv->text_search = text_search_new (GTK_TEXT_VIEW (cprop->priv->view));
+		gtk_box_pack_start (GTK_BOX (cprop), cprop->priv->text_search, FALSE, FALSE, 0);
+		gtk_widget_show (cprop->priv->text_search);
+	}
+	else {
+		gtk_widget_show (cprop->priv->text_search);
+		text_search_rerun (TEXT_SEARCH (cprop->priv->text_search));
+	}
+
+	gtk_widget_grab_focus (cprop->priv->text_search);
 }
diff --git a/tools/browser/ldap-browser/entry-properties.c b/tools/browser/ldap-browser/entry-properties.c
index 5a6e929..44dac63 100644
--- a/tools/browser/ldap-browser/entry-properties.c
+++ b/tools/browser/ldap-browser/entry-properties.c
@@ -24,6 +24,7 @@
 #include "marshal.h"
 #include <time.h>
 #include <libgda-ui/libgda-ui.h>
+#include "../text-search.h"
 
 struct _EntryPropertiesPrivate {
 	BrowserConnection *bcnc;
@@ -32,11 +33,7 @@ struct _EntryPropertiesPrivate {
 	GtkTextBuffer *text;
 	gboolean hovering_over_link;
 
-	GtkWidget *search_bar;
-	GtkWidget *search_entry;
-	GtkToggleButton *search_sensitive;
-	GList     *search_marks;
-	GList     *current_mark; /* in @search_marks */
+	GtkWidget *text_search;
 
 	/* coordinates of mouse */
 	gint bx;
@@ -47,9 +44,6 @@ static void entry_properties_class_init (EntryPropertiesClass *klass);
 static void entry_properties_init       (EntryProperties *eprop, EntryPropertiesClass *klass);
 static void entry_properties_dispose   (GObject *object);
 
-static void search_text_changed_cb (GtkEntry *entry, EntryProperties *eprop);
-
-
 static GObjectClass *parent_class = NULL;
 
 /* signals */
@@ -112,9 +106,6 @@ entry_properties_dispose (GObject *object)
 			g_object_unref (eprop->priv->bcnc);
 		}
 
-		if (eprop->priv->search_marks)
-			g_list_free (eprop->priv->search_marks);
-
 		g_free (eprop->priv);
 		eprop->priv = NULL;
 	}
@@ -205,9 +196,6 @@ entry_properties_new (BrowserConnection *bcnc)
                                     "indent", -10,
                                     "left-margin", 20, NULL);
 
-	gtk_text_buffer_create_tag (eprop->priv->text, "search",
-                                    "background", "yellow", NULL);
-
 	g_signal_connect (textview, "key-press-event", 
 			  G_CALLBACK (key_press_event), eprop);
 	g_signal_connect (textview, "event-after", 
@@ -934,8 +922,8 @@ info_fetch_cb (BrowserConnection *bcnc, gpointer out_result, EntryProperties *ep
 		browser_show_message (GTK_WINDOW (gtk_widget_get_toplevel ((GtkWidget*) eprop)),
 				      "%s", _("Could not get information about LDAP entry"));
 
-	if (eprop->priv->search_bar && gtk_widget_get_visible (eprop->priv->search_bar))
-		search_text_changed_cb (GTK_ENTRY (eprop->priv->search_entry), eprop);
+	if (eprop->priv->text_search && gtk_widget_get_visible (eprop->priv->text_search))
+		text_search_rerun (TEXT_SEARCH (eprop->priv->text_search));
 
 	g_object_unref (eprop);
 }
@@ -971,188 +959,18 @@ entry_properties_set_dn (EntryProperties *eprop, const gchar *dn)
 	}
 }
 
-/*
- * Search capabilities
- */
-static void
-search_text_changed_cb (GtkEntry *entry, EntryProperties *eprop)
-{
-	GtkTextIter iter, siter, end;
-	GtkTextBuffer *buffer;
-	const gchar *search_text, *sptr;
-	gboolean sensitive;
-
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop->priv->view));
-	
-	/* clean all previous search result */
-	gtk_text_buffer_get_bounds (buffer, &iter, &end);
-	gtk_text_buffer_remove_tag_by_name (buffer, "search", &iter, &end);
-	eprop->priv->current_mark = NULL;
-	if (eprop->priv->search_marks) {
-		GList *list;
-		for (list = eprop->priv->search_marks; list; list = list->next)
-			gtk_text_buffer_delete_mark (buffer, GTK_TEXT_MARK (list->data));
-
-		g_list_free (eprop->priv->search_marks);
-		eprop->priv->search_marks = NULL;
-	}
-
-	gtk_text_buffer_get_start_iter (buffer, &iter);
-	search_text = gtk_entry_get_text (entry);
-
-	if (!search_text || !*search_text)
-		return;
-
-	sensitive = gtk_toggle_button_get_active (eprop->priv->search_sensitive);
-
-	while (1) {
-		gboolean high = TRUE;
-		siter = iter;
-		sptr = search_text;
-
-		/* search for @search_text starting from the @siter position */
-		while (1) {
-			gunichar c1, c2;
-			c1 = gtk_text_iter_get_char (&siter);
-			c2 = g_utf8_get_char (sptr);
-			if (!sensitive) {
-				c1 = g_unichar_tolower (c1);
-				c2 = g_unichar_tolower (c2);
-			}
-			if (c1 != c2) {
-				high = FALSE;
-				break;
-			}
-			
-			sptr = g_utf8_find_next_char (sptr, NULL);
-			if (!sptr || !*sptr)
-				break;
-
-			if (! gtk_text_iter_forward_char (&siter)) {
-				high = FALSE;
-				break;
-			}
-		}
-		if (high) {
-			if (gtk_text_iter_forward_char (&siter)) {
-				GtkTextMark *mark;
-				gtk_text_buffer_apply_tag_by_name (buffer, "search", &iter, &siter);
-				mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
-				eprop->priv->search_marks = g_list_prepend (eprop->priv->search_marks,
-									    mark);
-			}
-			iter = siter;
-		}
-		else {
-			if (! gtk_text_iter_forward_char (&iter))
-				break;
-		}
-	}
-
-	if (eprop->priv->search_marks) {
-		eprop->priv->search_marks = g_list_reverse (eprop->priv->search_marks);
-		eprop->priv->current_mark = eprop->priv->search_marks;
-		gtk_text_view_scroll_mark_onscreen (eprop->priv->view,
-						    GTK_TEXT_MARK (eprop->priv->current_mark->data));
-	}
-}
-
-static void
-sensitive_toggled_cb (G_GNUC_UNUSED GtkToggleButton *button, EntryProperties *eprop)
-{
-	search_text_changed_cb (GTK_ENTRY (eprop->priv->search_entry), eprop);
-}
-
-static void
-hide_search_bar (EntryProperties *eprop)
-{
-	GtkTextIter start, end;
-	GtkTextBuffer *buffer;
-
-	/* clean all previous search result */
-	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop->priv->view));
-	gtk_text_buffer_get_bounds (buffer, &start, &end);
-	gtk_text_buffer_remove_tag_by_name (buffer, "search", &start, &end);
-
-	if (eprop->priv->search_marks) {
-		GList *list;
-		for (list = eprop->priv->search_marks; list; list = list->next)
-			gtk_text_buffer_delete_mark (buffer, GTK_TEXT_MARK (list->data));
-
-		g_list_free (eprop->priv->search_marks);
-		eprop->priv->search_marks = NULL;
-	}
-	eprop->priv->current_mark = NULL;
-
-	gtk_widget_hide (eprop->priv->search_bar);
-}
-
-static void
-go_back_search_cb (G_GNUC_UNUSED GtkButton *button, EntryProperties *eprop)
-{
-	if (eprop->priv->current_mark && eprop->priv->current_mark->prev) {
-		eprop->priv->current_mark = eprop->priv->current_mark->prev;
-		gtk_text_view_scroll_mark_onscreen (eprop->priv->view,
-						    GTK_TEXT_MARK (eprop->priv->current_mark->data));
-	}
-}
-
-static void
-go_forward_search_cb (G_GNUC_UNUSED GtkButton *button, EntryProperties *eprop)
-{
-	if (eprop->priv->current_mark && eprop->priv->current_mark->next) {
-		eprop->priv->current_mark = eprop->priv->current_mark->next;
-		gtk_text_view_scroll_mark_onscreen (eprop->priv->view,
-						    GTK_TEXT_MARK (eprop->priv->current_mark->data));
-	}
-}
-
 static void
 show_search_bar (EntryProperties *eprop)
 {
-	if (! eprop->priv->search_bar) {
-		GtkWidget *hbox, *wid;
-		hbox = gtk_hbox_new (FALSE, 5);
-		eprop->priv->search_bar = hbox;
-
-		wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_CLOSE,
-						 _("Hide search toolbar"));
-		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
-		g_signal_connect_swapped (wid, "clicked",
-					  G_CALLBACK (hide_search_bar), eprop);
-
-		wid = gtk_label_new (_("Search:"));
-		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
-
-		wid = gtk_entry_new ();
-		gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, TRUE, 0);
-		eprop->priv->search_entry = wid;
-		g_signal_connect (wid, "changed",
-				  G_CALLBACK (search_text_changed_cb), eprop);
-
-		wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_GO_BACK, NULL);
-		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
-		g_signal_connect (wid, "clicked",
-				  G_CALLBACK (go_back_search_cb), eprop);
-
-		wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_GO_FORWARD, NULL);
-		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
-		g_signal_connect (wid, "clicked",
-				  G_CALLBACK (go_forward_search_cb), eprop);
-		
-		wid = gtk_check_button_new_with_label (_("Case sensitive"));
-		gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
-		eprop->priv->search_sensitive = GTK_TOGGLE_BUTTON (wid);
-		g_signal_connect (wid, "toggled",
-				  G_CALLBACK (sensitive_toggled_cb), eprop);
-
-		gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->search_bar, FALSE, FALSE, 0);
-		gtk_widget_show_all (eprop->priv->search_bar);
+	if (! eprop->priv->text_search) {
+		eprop->priv->text_search = text_search_new (eprop->priv->view);
+		gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->text_search, FALSE, FALSE, 0);
+		gtk_widget_show (eprop->priv->text_search);
 	}
 	else {
-		gtk_widget_show (eprop->priv->search_bar);
-		search_text_changed_cb (GTK_ENTRY (eprop->priv->search_entry), eprop);
+		gtk_widget_show (eprop->priv->text_search);
+		text_search_rerun (TEXT_SEARCH (eprop->priv->text_search));
 	}
 
-	gtk_widget_grab_focus (eprop->priv->search_entry);
+	gtk_widget_grab_focus (eprop->priv->text_search);
 }
diff --git a/tools/browser/text-search.c b/tools/browser/text-search.c
new file mode 100644
index 0000000..b8f150a
--- /dev/null
+++ b/tools/browser/text-search.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <glib/gi18n-lib.h>
+#include <string.h>
+#include "text-search.h"
+#include "support.h"
+
+struct _TextSearchPrivate {
+	GtkTextView *view;
+	GtkTextBuffer *text;
+        GtkWidget *search_entry;
+        GtkToggleButton *search_sensitive;
+        GList     *search_marks;
+        GList     *current_mark; /* in @search_marks */
+};
+
+static void text_search_class_init (TextSearchClass *klass);
+static void text_search_init       (TextSearch *tsearch, TextSearchClass *klass);
+static void text_search_dispose    (GObject *object);
+static void text_search_grab_focus (GtkWidget *widget);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * TextSearch class implementation
+ */
+
+static void
+text_search_class_init (TextSearchClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+	GTK_WIDGET_CLASS (klass)->grab_focus = text_search_grab_focus;
+	object_class->dispose = text_search_dispose;
+}
+
+static void
+text_search_init (TextSearch *tsearch, G_GNUC_UNUSED TextSearchClass *klass)
+{
+	tsearch->priv = g_new0 (TextSearchPrivate, 1);
+}
+
+static void
+text_search_dispose (GObject *object)
+{
+	TextSearch *tsearch = (TextSearch *) object;
+
+	/* free memory */
+	if (tsearch->priv) {
+		g_object_unref ((GObject*) tsearch->priv->view);
+		if (tsearch->priv->search_marks)
+                        g_list_free (tsearch->priv->search_marks);
+
+		g_free (tsearch->priv);
+		tsearch->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+static void
+text_search_grab_focus (GtkWidget *widget)
+{
+	gtk_widget_grab_focus (TEXT_SEARCH (widget)->priv->search_entry);
+}
+
+GType
+text_search_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (TextSearchClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) text_search_class_init,
+			NULL,
+			NULL,
+			sizeof (TextSearch),
+			0,
+			(GInstanceInitFunc) text_search_init,
+			0
+		};
+
+		type = g_type_register_static (GTK_TYPE_HBOX, "TextSearch", &info, 0);
+	}
+	return type;
+}
+
+static void
+search_text_changed_cb (GtkEntry *entry, TextSearch *tsearch)
+{
+	GtkTextIter iter, siter, end;
+	GtkTextBuffer *buffer;
+	const gchar *search_text, *sptr;
+	gboolean sensitive;
+
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tsearch->priv->view));
+	
+	/* clean all previous search result */
+	gtk_text_buffer_get_bounds (buffer, &iter, &end);
+	gtk_text_buffer_remove_tag_by_name (buffer, "search", &iter, &end);
+	tsearch->priv->current_mark = NULL;
+	if (tsearch->priv->search_marks) {
+		GList *list;
+		for (list = tsearch->priv->search_marks; list; list = list->next)
+			gtk_text_buffer_delete_mark (buffer, GTK_TEXT_MARK (list->data));
+
+		g_list_free (tsearch->priv->search_marks);
+		tsearch->priv->search_marks = NULL;
+	}
+
+	gtk_text_buffer_get_start_iter (buffer, &iter);
+	search_text = gtk_entry_get_text (entry);
+
+	if (!search_text || !*search_text)
+		return;
+
+	sensitive = gtk_toggle_button_get_active (tsearch->priv->search_sensitive);
+
+	while (1) {
+		gboolean high = TRUE;
+		siter = iter;
+		sptr = search_text;
+
+		/* search for @search_text starting from the @siter position */
+		while (1) {
+			gunichar c1, c2;
+			c1 = gtk_text_iter_get_char (&siter);
+			c2 = g_utf8_get_char (sptr);
+			if (!sensitive) {
+				c1 = g_unichar_tolower (c1);
+				c2 = g_unichar_tolower (c2);
+			}
+			if (c1 != c2) {
+				high = FALSE;
+				break;
+			}
+			
+			sptr = g_utf8_find_next_char (sptr, NULL);
+			if (!sptr || !*sptr)
+				break;
+
+			if (! gtk_text_iter_forward_char (&siter)) {
+				high = FALSE;
+				break;
+			}
+		}
+		if (high) {
+			if (gtk_text_iter_forward_char (&siter)) {
+				GtkTextMark *mark;
+				gtk_text_buffer_apply_tag_by_name (buffer, "search", &iter, &siter);
+				mark = gtk_text_buffer_create_mark (buffer, NULL, &iter, FALSE);
+				tsearch->priv->search_marks = g_list_prepend (tsearch->priv->search_marks,
+									    mark);
+			}
+			iter = siter;
+		}
+		else {
+			if (! gtk_text_iter_forward_char (&iter))
+				break;
+		}
+	}
+
+	if (tsearch->priv->search_marks) {
+		tsearch->priv->search_marks = g_list_reverse (tsearch->priv->search_marks);
+		tsearch->priv->current_mark = tsearch->priv->search_marks;
+		gtk_text_view_scroll_mark_onscreen (tsearch->priv->view,
+						    GTK_TEXT_MARK (tsearch->priv->current_mark->data));
+	}
+}
+
+static void
+sensitive_toggled_cb (G_GNUC_UNUSED GtkToggleButton *button, TextSearch *tsearch)
+{
+	search_text_changed_cb (GTK_ENTRY (tsearch->priv->search_entry), tsearch);
+}
+
+static void
+hide_search_bar (TextSearch *tsearch)
+{
+	GtkTextIter start, end;
+	GtkTextBuffer *buffer;
+
+	/* clean all previous search result */
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tsearch->priv->view));
+	gtk_text_buffer_get_bounds (buffer, &start, &end);
+	gtk_text_buffer_remove_tag_by_name (buffer, "search", &start, &end);
+
+	if (tsearch->priv->search_marks) {
+		GList *list;
+		for (list = tsearch->priv->search_marks; list; list = list->next)
+			gtk_text_buffer_delete_mark (buffer, GTK_TEXT_MARK (list->data));
+
+		g_list_free (tsearch->priv->search_marks);
+		tsearch->priv->search_marks = NULL;
+	}
+	tsearch->priv->current_mark = NULL;
+
+	gtk_widget_hide (GTK_WIDGET (tsearch));
+}
+
+static void
+go_back_search_cb (G_GNUC_UNUSED GtkButton *button, TextSearch *tsearch)
+{
+	if (tsearch->priv->current_mark && tsearch->priv->current_mark->prev) {
+		tsearch->priv->current_mark = tsearch->priv->current_mark->prev;
+		gtk_text_view_scroll_mark_onscreen (tsearch->priv->view,
+						    GTK_TEXT_MARK (tsearch->priv->current_mark->data));
+	}
+}
+
+static void
+go_forward_search_cb (G_GNUC_UNUSED GtkButton *button, TextSearch *tsearch)
+{
+	if (tsearch->priv->current_mark && tsearch->priv->current_mark->next) {
+		tsearch->priv->current_mark = tsearch->priv->current_mark->next;
+		gtk_text_view_scroll_mark_onscreen (tsearch->priv->view,
+						    GTK_TEXT_MARK (tsearch->priv->current_mark->data));
+	}
+}
+
+/**
+ * text_search_new:
+ *
+ * Returns: a new #GtkWidget
+ */
+GtkWidget *
+text_search_new (GtkTextView *view)
+{
+	g_return_val_if_fail (GTK_IS_TEXT_VIEW (view), NULL);
+
+	TextSearch *tsearch;
+	GtkWidget *wid;
+
+	tsearch = TEXT_SEARCH (g_object_new (TEXT_SEARCH_TYPE, "spacing", 5,
+					     "homogeneous", FALSE, NULL));
+	tsearch->priv->view = view;
+	g_object_ref ((GObject*) tsearch->priv->view);
+	tsearch->priv->text = gtk_text_view_get_buffer (view);
+
+	gtk_text_buffer_create_tag (tsearch->priv->text, "search",
+                                    "background", "yellow", NULL);
+
+	wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_CLOSE,
+					 _("Hide search toolbar"));
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, FALSE, FALSE, 0);
+	g_signal_connect_swapped (wid, "clicked",
+				  G_CALLBACK (hide_search_bar), tsearch);
+	
+	wid = gtk_label_new (_("Search:"));
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, FALSE, FALSE, 0);
+	
+	wid = gtk_entry_new ();
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, TRUE, TRUE, 0);
+	tsearch->priv->search_entry = wid;
+	gtk_container_set_focus_child (GTK_CONTAINER (tsearch), tsearch->priv->search_entry);
+	g_signal_connect (wid, "changed",
+			  G_CALLBACK (search_text_changed_cb), tsearch);
+	
+	wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_GO_BACK, NULL);
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, FALSE, FALSE, 0);
+	g_signal_connect (wid, "clicked",
+			  G_CALLBACK (go_back_search_cb), tsearch);
+	
+	wid = browser_make_small_button (FALSE, FALSE, NULL, GTK_STOCK_GO_FORWARD, NULL);
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, FALSE, FALSE, 0);
+	g_signal_connect (wid, "clicked",
+			  G_CALLBACK (go_forward_search_cb), tsearch);
+	
+	wid = gtk_check_button_new_with_label (_("Case sensitive"));
+	gtk_box_pack_start (GTK_BOX (tsearch), wid, FALSE, FALSE, 0);
+	tsearch->priv->search_sensitive = GTK_TOGGLE_BUTTON (wid);
+	g_signal_connect (wid, "toggled",
+			  G_CALLBACK (sensitive_toggled_cb), tsearch);
+
+	gtk_widget_show_all ((GtkWidget*) tsearch);
+	gtk_widget_hide ((GtkWidget*) tsearch);
+
+	return (GtkWidget*) tsearch;
+}
+
+/**
+ * text_search_rerun:
+ *
+ * To be executed when the #GtkTextView's contents has changed
+ */
+void
+text_search_rerun (TextSearch *tsearch)
+{
+	g_return_if_fail (IS_TEXT_SEARCH (tsearch));
+	search_text_changed_cb (GTK_ENTRY (tsearch->priv->search_entry), tsearch);
+}
diff --git a/tools/browser/text-search.h b/tools/browser/text-search.h
new file mode 100644
index 0000000..001e0c8
--- /dev/null
+++ b/tools/browser/text-search.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Vivien Malerba <malerba gnome-db org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __TEXT_EDITOR_H__
+#define __TEXT_EDITOR_H__
+
+#include <gtk/gtk.h>
+#include <libgda/libgda.h>
+
+G_BEGIN_DECLS
+
+#define TEXT_SEARCH_TYPE            (text_search_get_type())
+#define TEXT_SEARCH(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, TEXT_SEARCH_TYPE, TextSearch))
+#define TEXT_SEARCH_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, TEXT_SEARCH_TYPE, TextSearchClass))
+#define IS_TEXT_SEARCH(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, TEXT_SEARCH_TYPE))
+#define IS_TEXT_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEXT_SEARCH_TYPE))
+
+typedef struct _TextSearch        TextSearch;
+typedef struct _TextSearchClass   TextSearchClass;
+typedef struct _TextSearchPrivate TextSearchPrivate;
+
+struct _TextSearch {
+	GtkHBox            parent;
+	TextSearchPrivate *priv;
+};
+
+struct _TextSearchClass {
+	GtkVBoxClass         parent_class;
+};
+
+GType        text_search_get_type       (void) G_GNUC_CONST;
+GtkWidget   *text_search_new            (GtkTextView *view);
+void         text_search_rerun          (TextSearch *tsearch);
+
+G_END_DECLS
+
+#endif



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