Interactive search code for TreeView: new patch



Hi,

Here's a new patch which adds the interactive search stuff to the
TreeView. Much has changed, a short summary:

- The search dialog is a GTK_WINDOW_POPUP now.
- I found out that the keybinding C-f was already used by the TreeView, so
I removed the C-f keybinding for the search dialog. If you select the
TreeView and start typing, the search dialog will pop up.
- the TreeView will scroll to the selected item now
- If there are more matching iters, you can cycle through them with the
arrow keys
- To get rid of the search dialog you can press Escape/Tab/Enter or just
click somewhere.
- It's now possible to set the search column. See
gtk_tree_view_interactive_search_set_search_column () and
gtk_tree_view_interactive_search_get_search_column (). Both functions
have got doc comments.
`
Other ideas, which are not implemented yet:
- make the position of the search dialog configurable?
- add code which enables people to use their own search functions.


That's all for now. Please mail your suggestions/comments/etc.


regards,


	Kris

-- 
Odi et amo. Quare id faciam, fortasse requiris?
	Nescio, sed fieri sentio et excrucior.

-Catullus (Carmen 85)
Index: gtk+/gtk/gtktreeview.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktreeview.c,v
retrieving revision 1.110
diff -u -r1.110 gtktreeview.c
--- gtk+/gtk/gtktreeview.c	2001/06/30 21:15:27	1.110
+++ gtk+/gtk/gtktreeview.c	2001/07/08 18:00:04
@@ -32,7 +32,10 @@
 #include "gtkarrow.h"
 #include "gtkintl.h"
 #include "gtkbindings.h"
+#include "gtkcontainer.h"
+#include "gtkentry.h"
 
+#include <string.h>
 #include <gdk/gdkkeysyms.h>
 
 #if defined (GDK_WINDOWING_X11)
@@ -344,7 +347,28 @@
 					   GtkTreePath     *path,
 					   gboolean         clear_and_select);
 
+/* interactive search */
+static gboolean gtk_tree_view_interactive_search_button_press_event (GtkWidget        *widget,
+								     GdkEventButton   *event,
+								     gpointer          data);
+static gboolean gtk_tree_view_interactive_search_key_press_event    (GtkWidget        *entry,
+								     GdkEventKey      *event,
+								     GtkTreeView      *tree_view);
+static void gtk_tree_view_interactive_search_search_move            (GtkWidget        *window,
+								     GtkTreeView      *tree_view,
+								     gboolean          up);
+static gboolean gtk_tree_view_interactive_search_search_iter        (GtkTreeModel     *model,
+								     GtkTreeSelection *selection,
+								     GtkTreeIter      *iter,
+								     gchar            *text,
+								     gint             *count,
+								     gint              n);
+static void gtk_tree_view_interactive_search_init                   (GtkWidget        *entry,
+								     GtkTreeView      *tree_view);
+static void gtk_tree_view_interactive_search                        (GtkTreeView      *tree_view,
+								     gchar             first_char);
 
+
 static GtkContainerClass *parent_class = NULL;
 static guint tree_view_signals[LAST_SIGNAL] = { 0 };
 
@@ -835,6 +859,7 @@
   gtk_tree_view_set_adjustments (tree_view, NULL, NULL);
   tree_view->priv->selection = _gtk_tree_selection_new_with_tree_view (tree_view);
   _gtk_tree_view_update_size (tree_view);
+  tree_view->priv->search_column = 0;
 }
 
 
@@ -958,7 +983,7 @@
       tree_view->priv->model = NULL;
     }
 
-  if (tree_view->priv->columns != NULL)
+ if (tree_view->priv->columns != NULL)
     {
       for (list = tree_view->priv->columns; list; list = list->next)
 	g_object_unref (G_OBJECT (list->data));
@@ -2777,6 +2802,13 @@
 	}
       return TRUE;
     }
+
+  if (event->keyval >= GDK_A && event->keyval <= GDK_z)
+    {
+      gtk_tree_view_interactive_search (tree_view, event->keyval);
+      return TRUE;
+    }
+
   return (* GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event);
 }
 
@@ -8214,4 +8246,357 @@
   tree_view->priv->destroy_count_func = func;
   tree_view->priv->destroy_count_data = data;
   tree_view->priv->destroy_count_destroy = destroy;
+}
+
+
+/* interactive search 
+ */
+
+/**
+ * gtk_tree_view_interactive_search_get_search_column:
+ * @tree_view: A #GtkTreeView
+ *
+ * Gets the column searched on by the interactive search code.
+ *
+ * Return value: the column the interactive search code searches in.
+ */
+gint
+gtk_tree_view_interactive_search_get_search_column (GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), 0);
+  
+  return (tree_view->priv->search_column);
+}
+
+/**
+ * gtk_tree_view_interactive_search_set_search_column:
+ * @tree_view: A #GtkTreeView
+ * @column: the column to search in
+ *
+ * Sets @column as the column where the interactive search code should search in.
+ */
+void
+gtk_tree_view_interactive_search_set_search_column (GtkTreeView *tree_view,
+						    gint         column)
+{
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  g_return_if_fail (column >= 0);
+
+  tree_view->priv->search_column = column;
+}
+
+static void
+gtk_tree_view_interactive_search (GtkTreeView *tree_view,
+				  gchar        first_char)
+{
+  gint ox, oy;
+  gint width, height;
+  gint pwidth, pheight;
+  gchar hack[2] = {first_char, 0};
+  GtkWidget *window;
+  GtkWidget *entry;
+  GdkWindow *parent_window;
+  
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+  /* set up window */
+  window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_window_set_title (GTK_WINDOW (window), "search dialog");
+  gtk_container_set_border_width (GTK_CONTAINER (window), 3);
+  gtk_window_set_modal (GTK_WINDOW (window), TRUE);
+  gtk_signal_connect_object (GTK_OBJECT (window), "delete_event",
+                             GTK_SIGNAL_FUNC (gtk_widget_destroy),
+                             GTK_OBJECT (window));
+  gtk_signal_connect 
+    (GTK_OBJECT (window), "key_press_event",
+     GTK_SIGNAL_FUNC (gtk_tree_view_interactive_search_key_press_event),
+     tree_view);
+  gtk_signal_connect
+    (GTK_OBJECT (window), "button_press_event",
+     GTK_SIGNAL_FUNC (gtk_tree_view_interactive_search_button_press_event),
+     tree_view);
+
+  /* add entry */
+  entry = gtk_entry_new ();
+  gtk_entry_append_text (GTK_ENTRY (entry), hack);
+  gtk_editable_set_position (GTK_EDITABLE (entry), 2);
+  gtk_signal_connect
+    (GTK_OBJECT (entry), "changed",
+     GTK_SIGNAL_FUNC (gtk_tree_view_interactive_search_init),
+     tree_view);
+  gtk_container_add (GTK_CONTAINER (window), entry);
+
+  /* done, show it */
+  gtk_widget_show_all (window);
+  gtk_widget_grab_focus (entry);
+
+  /* put window in the right position (lower right corner) */
+  parent_window = gtk_widget_get_parent_window (GTK_WIDGET (tree_view));
+  gdk_window_get_origin (parent_window, &ox, &oy);
+  gdk_window_get_geometry (parent_window, NULL, NULL, &pwidth, &pheight, NULL);
+  gdk_window_get_geometry (window->window, NULL, NULL, &width, &height, NULL);
+  gdk_window_move (window->window, ox + pwidth - width, oy + pheight);
+
+  gtk_object_set_data (GTK_OBJECT (window), "text",
+                       gtk_entry_get_text (GTK_ENTRY (entry)));
+}
+
+static gboolean
+gtk_tree_view_interactive_search_button_press_event (GtkWidget *widget,
+                                                     GdkEventButton *event,
+                                                     gpointer data)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+  gtk_widget_destroy (widget);
+  
+  return TRUE;
+}
+
+static gboolean
+gtk_tree_view_interactive_search_key_press_event (GtkWidget *widget,
+                                                  GdkEventKey *event,
+                                                  GtkTreeView *tree_view)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+  g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), FALSE);
+  
+  /* close window */
+  if (event->keyval == GDK_Escape || event->keyval == GDK_Return
+      || event->keyval == GDK_Tab)
+    {
+      gtk_widget_destroy (widget);
+      return TRUE;
+    }
+
+  /* select previous matching iter */
+  if (event->keyval == GDK_Up)
+    {
+      gtk_tree_view_interactive_search_search_move (widget,
+                                                    tree_view,
+                                                    TRUE);
+      return TRUE;
+    }
+
+  /* select next matching iter */
+  if (event->keyval == GDK_Down)
+    {
+      gtk_tree_view_interactive_search_search_move (widget,
+                                                    tree_view,
+                                                    FALSE);
+      return TRUE;
+    }
+  return FALSE;
+}
+
+static void
+gtk_tree_view_interactive_search_search_move (GtkWidget        *window,
+                                              GtkTreeView      *tree_view,
+                                              gboolean          up)
+{
+  gboolean ret;
+  gint *selected_iter;
+  gint len;
+  gint count = 0;
+  gchar *text;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  GtkTreeSelection *selection;
+
+  text = gtk_object_get_data (GTK_OBJECT (window), "text");
+  selected_iter = gtk_object_get_data (GTK_OBJECT (window), "selected-iter");
+
+  g_return_if_fail (text != NULL);
+
+  if (!selected_iter || (up && *selected_iter == 1))
+    return;
+
+  len = strlen (text);
+  
+  if (len < 1)
+    return;
+
+  model = gtk_tree_view_get_model (tree_view);
+  selection = gtk_tree_view_get_selection (tree_view);
+  
+  /* search */
+  gtk_tree_selection_unselect_all (selection);
+  gtk_tree_model_get_iter_root (model, &iter);
+  
+  ret = gtk_tree_view_interactive_search_search_iter
+    (model,
+     selection,
+     &iter,
+     text,
+     &count,
+     up?((*selected_iter) - 1):((*selected_iter + 1)));
+  
+  if (ret)
+    /* found */
+    *selected_iter += up?(-1):(1);
+  else
+    {
+      /* return to old iter */
+      count = 0;
+      gtk_tree_model_get_iter_root (model, &iter);
+      gtk_tree_view_interactive_search_search_iter (model,
+                                                    selection,
+                                                    &iter,
+                                                    text,
+                                                    &count,
+                                                    *selected_iter);
+
+      /* beep? */
+    }
+}
+
+static gboolean
+gtk_tree_view_interactive_search_search_iter (GtkTreeModel     *model,
+                                              GtkTreeSelection *selection,
+                                              GtkTreeIter      *iter,
+                                              gchar            *text,
+                                              gint             *count,
+                                              gint              n)
+{
+  gint len = strlen (text);
+  gchar *tmp;
+  GValue value = {0,};
+  GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
+  GtkTreeViewColumn *column =
+    gtk_tree_view_get_column (tree_view, tree_view->priv->search_column);
+  
+  gtk_tree_model_get_value (model, iter, tree_view->priv->search_column, &value);
+  tmp = g_strdup (g_value_get_string (&value));
+  if (!strncmp (text, tmp, len))
+    {
+      (*count)++;
+      
+      if (*count == n)
+        {
+          GtkTreePath *path;
+          
+          gtk_tree_selection_select_iter (selection, iter);
+          
+          path = gtk_tree_model_get_path (model, iter);
+	  gtk_tree_view_scroll_to_cell (tree_view,
+					path, column, 0.5, 0.5);
+          gtk_tree_path_free (path);
+          
+          return TRUE;
+        }
+    }
+  g_value_unset (&value);
+  
+  if (gtk_tree_model_iter_has_child (model, iter))
+    {
+      gboolean ret;
+      GtkTreeIter child;
+      
+      gtk_tree_model_iter_children (model, &child, iter);
+      ret = gtk_tree_view_interactive_search_search_iter (model,
+                                                          selection,
+                                                          &child,
+                                                          text,
+                                                          count,
+                                                          n);
+      
+      if (ret)
+        return TRUE; /* iter found and selected */
+    }
+  
+  while (gtk_tree_model_iter_next (model, iter))
+    {
+      gtk_tree_model_get_value (model, iter, tree_view->priv->search_column, &value);
+      tmp = g_value_get_string (&value);
+      if (!strncmp (text, tmp, len))
+        {
+          (*count)++;
+          if (*count == n)
+            {
+              GtkTreePath *path;
+              
+              gtk_tree_selection_select_iter (selection, iter);
+              
+              path = gtk_tree_model_get_path (model, iter);
+              gtk_tree_view_scroll_to_cell (tree_view,
+                                            path, column, 0.5, 0.5);
+              gtk_tree_path_free (path);
+              
+              return TRUE;
+            }
+        }
+      g_value_unset (&value);
+      
+      if (gtk_tree_model_iter_has_child (model, iter))
+        {
+          gboolean ret;
+          GtkTreeIter child;
+          
+          gtk_tree_model_iter_children (model, &child, iter);
+          ret = gtk_tree_view_interactive_search_search_iter (model,
+                                                              selection,
+                                                              &child,
+                                                              text,
+                                                              count,
+                                                              n);
+
+          if (ret)
+            return TRUE; /* iter found and selected */
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+gtk_tree_view_interactive_search_init (GtkWidget   *entry,
+                                       GtkTreeView *tree_view)
+{
+  gint ret;
+  gint *selected_iter;
+  gint len;
+  gint count = 0;
+  gchar *text;
+  GtkWidget *window;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  GtkTreeSelection *selection;
+  
+  g_return_if_fail (GTK_IS_ENTRY (entry));
+  g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+  
+  window = gtk_widget_get_parent (entry);
+  text = gtk_entry_get_text (GTK_ENTRY (entry));
+  len = strlen (text);
+  model = gtk_tree_view_get_model (tree_view);
+  selection = gtk_tree_view_get_selection (tree_view);
+
+  /* search */
+  gtk_tree_selection_unselect_all (selection);
+  selected_iter = gtk_object_get_data (GTK_OBJECT (window), "selected-iter");
+  if (selected_iter)
+    g_free (selected_iter);
+  gtk_object_remove_data (GTK_OBJECT (window), "selected-iter");
+
+  if (len < 1)
+    return;
+  
+  gtk_tree_model_get_iter_root (model, &iter);
+  
+  ret = gtk_tree_view_interactive_search_search_iter (model,
+                                                      selection,
+                                                      &iter,
+                                                      text,
+                                                      &count,
+                                                      1);
+
+  if (ret) 
+    {
+      selected_iter = g_malloc (sizeof (int));
+      *selected_iter = 1;
+      gtk_object_set_data (GTK_OBJECT (window), "selected-iter",
+                           selected_iter);
+    }
+  
+  return;
 }
Index: gtk+/gtk/gtktreeview.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktreeview.h,v
retrieving revision 1.33
diff -u -r1.33 gtktreeview.h
--- gtk+/gtk/gtktreeview.h	2001/06/29 03:11:01	1.33
+++ gtk+/gtk/gtktreeview.h	2001/07/08 18:00:05
@@ -275,6 +275,11 @@
 GdkPixmap             *gtk_tree_view_create_row_drag_icon          (GtkTreeView               *tree_view,
 								    GtkTreePath               *path);
 
+/* Interactive search */
+gint                   gtk_tree_view_interactive_search_get_search_column (GtkTreeView        *tree_view);
+void                   gtk_tree_view_interactive_search_set_search_column (GtkTreeView        *tree_view,
+									   gint                column);
+
 
 /* This function should really never be used.  It is just for use by ATK.
  */


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