complete TreeView search code patch
- From: Kristian Rietveld <kristian planet nl>
- To: <jrb redhat com>
- Cc: GTK Development list <gtk-devel-list gnome org>
- Subject: complete TreeView search code patch
- Date: Sun, 12 Aug 2001 23:16:16 +0200 (CEST)
Hi,
Here's a new TreeView patch which adds the final feature (for now), custom
search functions. Application developers can use their own compare
functions now (for example, put a nice image in the TreeView, associate
text with it and hack a compare function that compares the typed text
with the associated text of the image).
The keybindings haven't been changed, just give the TreeView focus and
start typing, the first match will be selected. Use the up/down arrow keys
to browse through all matching items.
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.116
diff -u -r1.116 gtktreeview.c
--- gtk+/gtk/gtktreeview.c 2001/07/31 19:55:02 1.116
+++ gtk+/gtk/gtktreeview.c 2001/08/12 20:22:02
@@ -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)
@@ -348,7 +351,39 @@
GtkTreePath *path,
gboolean clear_and_select);
+/* interactive search */
+static void gtk_tree_view_interactive_search_dialog_destroy (GtkWidget *search_dialog,
+ GtkTreeView *tree_view);
+static void gtk_tree_view_interactive_search_position_func (GtkTreeView *tree_view,
+ GtkWidget *search_dialog);
+static gboolean gtk_tree_view_interactive_search_delete_event (GtkWidget *widget,
+ GdkEventAny *event,
+ GtkTreeView *tree_view);
+static gboolean gtk_tree_view_interactive_search_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkTreeView *tree_view);
+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_compare_func (GtkTreeModel *model,
+ gint column,
+ gchar *key,
+ GtkTreeIter *iter);
+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 };
@@ -833,6 +868,9 @@
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;
+ tree_view->priv->search_dialog_position_func = gtk_tree_view_interactive_search_position_func;
+ tree_view->priv->search_compare_func = gtk_tree_view_interactive_search_compare_func;
}
@@ -956,7 +994,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));
@@ -2783,6 +2821,14 @@
}
return TRUE;
}
+
+ if (event->keyval >= GDK_A && event->keyval <= GDK_z
+ && event->state != GDK_CONTROL_MASK)
+ {
+ gtk_tree_view_interactive_search (tree_view, event->keyval);
+ return TRUE;
+ }
+
return (* GTK_WIDGET_CLASS (parent_class)->key_press_event) (widget, event);
}
@@ -2839,6 +2885,7 @@
gtk_tree_view_leave_notify (GtkWidget *widget,
GdkEventCrossing *event)
{
+ GtkWidget *search_dialog;
GtkTreeView *tree_view;
g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
@@ -2852,6 +2899,12 @@
NULL);
ensure_unprelighted (tree_view);
+
+ /* destroy interactive search dialog */
+ search_dialog = gtk_object_get_data (GTK_OBJECT (widget), "search-dialog");
+ if (search_dialog)
+ gtk_tree_view_interactive_search_dialog_destroy (search_dialog,
+ tree_view);
return TRUE;
}
@@ -2888,6 +2941,7 @@
gtk_widget_queue_draw (widget);
GTK_TREE_VIEW (widget)->priv->in_extended_selection = FALSE;
GTK_TREE_VIEW (widget)->priv->in_free_motion = FALSE;
+
return FALSE;
}
@@ -8390,4 +8444,488 @@
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;
+}
+
+/**
+ * gtk_tree_view_interactive_search_get_compare_func:
+ * @tree_view: A #GtkTreeView
+ *
+ * Returns the compare function currently in use.
+ *
+ * Return value: the currently used compare function for the search code.
+ */
+GtkTreeViewSearchCompareFunc
+gtk_tree_view_interactive_search_get_compare_func (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ return tree_view->priv->search_compare_func;
+}
+
+/**
+ * gtk_tree_view_interactive_search_set_compare_func:
+ * @tree_view: A #GtkTreeView
+ * @compare_func: the compare function to use during the search
+ *
+ * Sets the compare function to use to search the TreeView. See
+ * gtktreeview.c:gtk_tree_view_interactive_search_compare_func() for an
+ * example of such a compare function.
+ */
+void
+gtk_tree_view_interactive_search_set_compare_func (GtkTreeView *tree_view,
+ GtkTreeViewSearchCompareFunc compare_func)
+{
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (compare_func != NULL);
+
+ tree_view->priv->search_compare_func = compare_func;
+}
+
+/**
+ * gtk_tree_view_interactive_search_get_dialog_position_func:
+ * @tree_view: A #GtkTreeView
+ *
+ * Returns the position function currently in use.
+ *
+ * Return value: the currently used position function for the search dialog.
+ */
+GtkTreeViewSearchDialogPositionFunc
+gtk_tree_view_interactive_search_get_dialog_position_func (GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (GTK_IS_TREE_VIEW (tree_view), NULL);
+
+ return tree_view->priv->search_dialog_position_func;
+}
+
+/**
+ * gtk_tree_view_interactive_search_set_dialog_position_func:
+ * @tree_view: A #GtkTreeView
+ * @position_func: the position function to use for the search dialog
+ *
+ * Sets the position function to use for positioning the search dialog. See
+ * gtktreeview.c:gtk_tree_view_interactive_search_position_func() for an
+ * example of such a position function.
+ */
+void
+gtk_tree_view_interactive_search_set_dialog_position_func (GtkTreeView *tree_view,
+ GtkTreeViewSearchDialogPositionFunc position_func)
+{
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (position_func != NULL);
+
+ tree_view->priv->search_dialog_position_func = position_func;
+}
+
+/**
+ * gtk_tree_view_interactive_search_dialog_reposition:
+ * @tree_view: A #GtkTreeView
+ *
+ * Repositions the search dialog. Call this function after you set the
+ * position function with gtk_tree_view_interactive_search_set_dialog_position_func(),
+ * so the changes take effect.
+ */
+void
+gtk_tree_view_interactive_search_dialog_reposition (GtkTreeView *tree_view)
+{
+ GtkWidget *window;
+
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+
+ window = gtk_object_get_data (GTK_OBJECT (tree_view), "search-dialog");
+
+ tree_view->priv->search_dialog_position_func (tree_view, window);
+}
+
+static void
+gtk_tree_view_interactive_search_dialog_destroy (GtkWidget *search_dialog,
+ GtkTreeView *tree_view)
+{
+ /* remove data from tree_view */
+ gtk_object_remove_data (GTK_OBJECT (tree_view), "search-dialog");
+
+ gtk_widget_destroy (search_dialog);
+}
+
+static void
+gtk_tree_view_interactive_search_position_func (GtkTreeView *tree_view,
+ GtkWidget *search_dialog)
+{
+ gint ox, oy;
+ gint width, height;
+ gint pwidth, pheight;
+ GdkWindow *parent_window;
+
+ /* put window in the 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 (search_dialog->window, NULL, NULL, &width, &height,
+ NULL);
+ gdk_window_move (search_dialog->window, ox + pwidth - width, oy + pheight);
+}
+
+static void
+gtk_tree_view_interactive_search (GtkTreeView *tree_view,
+ gchar first_char)
+{
+ gchar hack[2] = {first_char, 0};
+ GtkWidget *window;
+ GtkWidget *entry;
+
+ 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
+ (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (gtk_tree_view_interactive_search_delete_event),
+ tree_view);
+ 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);
+
+ /* someone changed the behaviour of the GtkEntry .... */
+ gtk_entry_select_region (GTK_ENTRY (entry), 0, 0);
+ gtk_editable_set_position (GTK_EDITABLE (entry), 2);
+
+ /* position window */
+ tree_view->priv->search_dialog_position_func (tree_view, window);
+
+ gtk_object_set_data (GTK_OBJECT (window), "text",
+ gtk_entry_get_text (GTK_ENTRY (entry)));
+ gtk_object_set_data (GTK_OBJECT (tree_view), "search-dialog", window);
+
+ /* search first matching iter */
+ gtk_tree_view_interactive_search_init (entry, tree_view);
+}
+
+static gboolean
+gtk_tree_view_interactive_search_delete_event (GtkWidget *widget,
+ GdkEventAny *event,
+ GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ gtk_tree_view_interactive_search_dialog_destroy (widget, tree_view);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_view_interactive_search_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkTreeView *tree_view)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+ gtk_tree_view_interactive_search_dialog_destroy (widget, tree_view);
+
+ 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_tree_view_interactive_search_dialog_destroy (widget, tree_view);
+ 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_compare_func (GtkTreeModel *model,
+ gint column,
+ gchar *key,
+ GtkTreeIter *iter)
+{
+ gint len;
+ gchar *tmp;
+ GValue value = {0,};
+
+ len = strlen (key);
+
+ gtk_tree_model_get_value (model, iter, column, &value);
+ tmp = g_strdup (g_value_get_string (&value));
+ g_value_unset (&value);
+
+ if (!strncmp (key, tmp, len))
+ /* found a match, return true */
+ return TRUE;
+
+ /* not a match, return false */
+ return FALSE;
+}
+
+static gboolean
+gtk_tree_view_interactive_search_search_iter (GtkTreeModel *model,
+ GtkTreeSelection *selection,
+ GtkTreeIter *iter,
+ gchar *text,
+ gint *count,
+ gint n)
+{
+ GtkTreeView *tree_view = gtk_tree_selection_get_tree_view (selection);
+ GtkTreeViewColumn *column =
+ gtk_tree_view_get_column (tree_view, tree_view->priv->search_column);
+
+ if (tree_view->priv->search_compare_func (model, tree_view->priv->search_column,
+ text, iter))
+ {
+ (*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;
+ }
+ }
+
+ 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))
+ {
+ if (tree_view->priv->search_compare_func (model, tree_view->priv->search_column,
+ text, iter))
+ {
+ (*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;
+ }
+ }
+
+ 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/08/12 20:22:05
@@ -19,6 +19,7 @@
#ifndef __GTK_TREE_VIEW_H__
#define __GTK_TREE_VIEW_H__
+#include <gtk/gtkwidget.h>
#include <gtk/gtkcontainer.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreeviewcolumn.h>
@@ -119,6 +120,12 @@
GtkTreePath *path,
GtkTreeViewDropPosition *pos,
gpointer user_data);
+typedef void (*GtkTreeViewSearchDialogPositionFunc) (GtkTreeView *tree_view,
+ GtkWidget *search_dialog);
+typedef gboolean (*GtkTreeViewSearchCompareFunc) (GtkTreeModel *model,
+ gint column,
+ gchar *key,
+ GtkTreeIter *iter);
/* Creators */
@@ -274,6 +281,18 @@
GtkTreeViewDropPosition *pos);
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);
+GtkTreeViewSearchCompareFunc gtk_tree_view_interactive_search_get_compare_func (GtkTreeView *tree_view);
+void gtk_tree_view_interactive_search_set_compare_func (GtkTreeView *tree_view,
+ GtkTreeViewSearchCompareFunc compare_func);
+GtkTreeViewSearchDialogPositionFunc gtk_tree_view_interactive_search_get_dialog_position_func (GtkTreeView *tree_view);
+void gtk_tree_view_interactive_search_set_dialog_position_func (GtkTreeView *tree_view,
+ GtkTreeViewSearchDialogPositionFunc position_func);
+void gtk_tree_view_interactive_search_dialog_reposition (GtkTreeView *tree_view);
/* This function should really never be used. It is just for use by ATK.
Index: gtk+/gtk/gtktreeprivate.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktreeprivate.h,v
retrieving revision 1.27
diff -u -r1.27 gtktreeprivate.h
--- gtk+/gtk/gtktreeprivate.h 2001/07/22 20:09:00 1.27
+++ gtk+/gtk/gtktreeprivate.h 2001/08/12 20:22:06
@@ -171,6 +171,11 @@
guint in_extended_selection : 1;
guint in_free_motion : 1;
+
+ /* interactive search */
+ gint search_column;
+ GtkTreeViewSearchDialogPositionFunc search_dialog_position_func;
+ GtkTreeViewSearchCompareFunc search_compare_func;
};
#ifdef __GNUC__
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]