[PATCH] Typeahead search in list view



Hi

I've created a patch to enable the typeahead search in the list view as
it is already implemented in the icon view. Hopefully going in until
Monday.

http://bugzilla.gnome.org/show_bug.cgi?id=85230

Regards,

Jürg
diff -ur nautilus/src/file-manager/fm-list-view.c nautilus-list-typeahead/src/file-manager/fm-list-view.c
--- nautilus/src/file-manager/fm-list-view.c	2004-01-10 20:45:12.000000000 +0100
+++ nautilus-list-typeahead/src/file-manager/fm-list-view.c	2004-01-10 20:50:03.000000000 +0100
@@ -54,6 +54,11 @@
 #include <libnautilus/nautilus-scroll-positionable.h>
 #include <libnautilus-private/nautilus-cell-renderer-pixbuf-emblem.h>
 
+typedef struct {
+	char *type_select_pattern;
+	guint64 last_typeselect_time;
+} TypeSelectState;
+
 struct FMListViewDetails {
 	GtkTreeView *tree_view;
 	FMListModel *model;
@@ -82,6 +87,9 @@
 
 	gboolean drag_started;
 	gboolean row_selected_on_button_down;
+
+	/* typeahead selection state */
+	TypeSelectState *type_select_state;
 };
 
 /*
@@ -91,6 +99,14 @@
  */
 #define LIST_VIEW_MINIMUM_ROW_HEIGHT	28
 
+enum {
+	NAUTILUS_TYPESELECT_FLUSH_DELAY = 1000000
+	/* After this time the current typeselect buffer will be
+	 * thrown away and the new pressed character will be made
+	 * the the start of a new pattern.
+	 */
+};
+
 static int                      click_policy_auto_value;
 static NautilusFileSortType	default_sort_order_auto_value;
 static gboolean			default_sort_reversed_auto_value;
@@ -571,6 +587,110 @@
 }
 
 static gboolean
+select_matching_name (FMListView *view,
+		      const char *match_name)
+{
+	GtkTreeIter iter;
+	GtkTreePath *path;
+	gboolean match_found;
+	GValue value = { 0 };
+	const gchar *file_name;
+	
+	match_found = FALSE;
+	
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view->details->model), &iter)) {
+		return FALSE;
+	}
+	
+	do {
+		gtk_tree_model_get_value (GTK_TREE_MODEL (view->details->model), &iter, FM_LIST_MODEL_NAME_COLUMN, &value);
+		file_name = g_value_get_string (&value);
+		match_found = (g_ascii_strncasecmp (match_name, file_name, MIN (strlen (match_name), strlen (file_name))) == 0);
+		g_value_unset (&value);
+
+		if (match_found) {
+			path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter);
+			gtk_tree_view_set_cursor (view->details->tree_view, path, NULL, FALSE);
+			gtk_tree_view_scroll_to_cell (view->details->tree_view, path, NULL, FALSE, 0, 0);
+			gtk_tree_path_free (path);
+			
+			return TRUE;
+		}
+	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (view->details->model), &iter));
+	
+	return FALSE;
+}
+
+void
+fm_list_view_flush_typeselect_state (FMListView *view)
+{
+	if (view->details->type_select_state == NULL) {
+		return;
+	}
+	
+	g_free (view->details->type_select_state->type_select_pattern);
+	g_free (view->details->type_select_state);
+	view->details->type_select_state = NULL;
+}
+
+static gboolean
+handle_typeahead (FMListView *view, const char *key_string)
+{
+	char *new_pattern;
+	gint64 now;
+	gint64 time_delta;
+	int key_string_length;
+	int index;
+
+	g_assert (key_string != NULL);
+	g_assert (strlen (key_string) < 5);
+
+	key_string_length = strlen (key_string);
+
+	if (key_string_length == 0) {
+		/* can be an empty string if the modifier was held down, etc. */
+		return FALSE;
+	}
+
+	/* only handle if printable keys typed */
+	for (index = 0; index < key_string_length; index++) {
+		if (!g_ascii_isprint (key_string[index])) {
+			return FALSE;
+		}
+	}
+
+	/* lazily allocate the typeahead state */
+	if (view->details->type_select_state == NULL) {
+		view->details->type_select_state = g_new0 (TypeSelectState, 1);
+	}
+
+	/* find out how long since last character was typed */
+	now = eel_get_system_time ();
+	time_delta = now - view->details->type_select_state->last_typeselect_time;
+	if (time_delta < 0 || time_delta > NAUTILUS_TYPESELECT_FLUSH_DELAY) {
+		/* the typeselect state is too old, start with a fresh one */
+		g_free (view->details->type_select_state->type_select_pattern);
+		view->details->type_select_state->type_select_pattern = NULL;
+	}
+
+	if (view->details->type_select_state->type_select_pattern != NULL) {
+		new_pattern = g_strconcat
+			(view->details->type_select_state->type_select_pattern,
+			 key_string, NULL);
+		g_free (view->details->type_select_state->type_select_pattern);
+	} else {
+		new_pattern = g_strdup (key_string);
+	}
+
+	view->details->type_select_state->type_select_pattern = new_pattern;
+	view->details->type_select_state->last_typeselect_time = now;
+
+	select_matching_name (view, new_pattern);
+
+	return TRUE;
+}
+
+static gboolean
 popup_menu_callback (GtkWidget *widget, gpointer callback_data)
 {
  	FMListView *view;
@@ -587,8 +707,12 @@
 {
 	FMDirectoryView *view;
 	GdkEventButton button_event = { 0 };
+	gboolean handled;
+	gboolean flush_typeahead;
 
 	view = FM_DIRECTORY_VIEW (callback_data);
+	handled = FALSE;
+	flush_typeahead = TRUE;
 	
 	switch (event->keyval) {
 	case GDK_F10:
@@ -598,23 +722,37 @@
 		break;
 	case GDK_space:
 		if (event->state & GDK_CONTROL_MASK) {
-			return FALSE;
+			handled = FALSE;
+			break;
 		}
 		if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (FM_LIST_VIEW (view)->details->tree_view))) {
-			return FALSE;
+			handled = FALSE;
+			break;
 		}
 		activate_selected_items (FM_LIST_VIEW (view));
-		return TRUE;
+		handled = TRUE;
+		break;
 	case GDK_Return:
 	case GDK_KP_Enter:
 		activate_selected_items (FM_LIST_VIEW (view));
-		return TRUE;
+		handled = TRUE;
+		break;
 
 	default:
+		/* Don't use Control or Alt keys for type-selecting, because they
+		 * might be used for menus.
+		 */
+		handled = (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) == 0 &&
+			handle_typeahead (FM_LIST_VIEW (view), event->string);
+		flush_typeahead = !handled;
 		break;
 	}
+	if (flush_typeahead) {
+		/* any non-ascii key will force the typeahead state to be forgotten */
+		fm_list_view_flush_typeselect_state (FM_LIST_VIEW (view));
+	}
 
-	return FALSE;
+	return handled;
 }
 
 static void
@@ -1437,6 +1575,8 @@
 	}	
 
 	gtk_target_list_unref (list_view->details->source_target_list);
+	
+	fm_list_view_flush_typeselect_state (list_view);
 
 	g_free (list_view->details);
 
@@ -1604,4 +1744,6 @@
 				 G_CALLBACK (list_view_get_first_visible_file_callback), list_view, 0);
 	g_signal_connect_object (list_view->details->positionable, "scroll_to_file",
 				 G_CALLBACK (list_view_scroll_to_file_callback), list_view, 0);
+	
+	list_view->details->type_select_state = NULL;
 }


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