From: Richard Kettlewell Date: Sat, 14 Jun 2008 19:23:05 +0000 (+0100) Subject: Typeahead find for Disobedience searching. This is implemented by X-Git-Tag: 4.1~15^2~13 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/a59663d4191472599e1e30f0faedba0355271a55 Typeahead find for Disobedience searching. This is implemented by hackily redirecting most keyboard events sent to the main view to the search entry. ^F focuses it, which is probably wrong given you can type into it anyway. ^G moves to the next match, which is why ^F focusing is wrong, because ^G breaks when you're focused on the search entry. The next/prev buttons (and ^G) now loop when you reach the end. --- diff --git a/disobedience/choose-search.c b/disobedience/choose-search.c index 82a0675..ebbedf0 100644 --- a/disobedience/choose-search.c +++ b/disobedience/choose-search.c @@ -28,7 +28,7 @@ int choose_auto_expanding; -static GtkWidget *choose_search_entry; +GtkWidget *choose_search_entry; static GtkWidget *choose_next; static GtkWidget *choose_prev; static GtkWidget *choose_clear; @@ -302,6 +302,10 @@ static void choose_search_completed(void attribute((unused)) *v, } else { gtk_widget_set_sensitive(choose_next, FALSE); gtk_widget_set_sensitive(choose_prev, FALSE); + choose_n_search_results = 0; + choose_search_results = 0; + choose_n_search_references = 0; + choose_search_references = 0; } event_raise("search-results-changed", 0); } @@ -430,8 +434,10 @@ static gboolean choose_get_visible_range(GtkTreeView *tree_view, return TRUE; } -static void choose_next_clicked(GtkButton attribute((unused)) *button, - gpointer attribute((unused)) userdata) { +void choose_next_clicked(GtkButton attribute((unused)) *button, + gpointer attribute((unused)) userdata) { + if(!choose_n_search_results) + return; /* Find the last visible row */ GtkTreePath *endpath; gboolean endvalid = choose_get_visible_range(GTK_TREE_VIEW(choose_view), @@ -453,11 +459,23 @@ static void choose_next_clicked(GtkButton attribute((unused)) *button, } gtk_tree_path_free(path); } + /* We didn't find one. Loop back to the first. */ + for(int n = 0; n < choose_n_search_references; ++n) { + GtkTreePath *path + = gtk_tree_row_reference_get_path(choose_search_references[n]); + if(!path) + continue; + choose_make_path_visible(path, 0.5); + gtk_tree_path_free(path); + return; + } } -static void choose_prev_clicked(GtkButton attribute((unused)) *button, - gpointer attribute((unused)) userdata) { +void choose_prev_clicked(GtkButton attribute((unused)) *button, + gpointer attribute((unused)) userdata) { /* TODO can we de-dupe with choose_next_clicked? Probably yes. */ + if(!choose_n_search_results) + return; /* Find the first visible row */ GtkTreePath *startpath; gboolean startvalid = choose_get_visible_range(GTK_TREE_VIEW(choose_view), @@ -479,6 +497,16 @@ static void choose_prev_clicked(GtkButton attribute((unused)) *button, } gtk_tree_path_free(path); } + /* We didn't find one. Loop down to the last. */ + for(int n = choose_n_search_references - 1; n >= 0; --n) { + GtkTreePath *path + = gtk_tree_row_reference_get_path(choose_search_references[n]); + if(!path) + continue; + choose_make_path_visible(path, 0.5); + gtk_tree_path_free(path); + return; + } } /** @brief Called when the cancel search button is clicked */ diff --git a/disobedience/choose.c b/disobedience/choose.c index 228194a..2ddf7f9 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -39,6 +39,7 @@ #include "disobedience.h" #include "choose.h" +#include /** @brief The current selection tree */ GtkTreeStore *choose_store; @@ -505,6 +506,46 @@ static void choose_refill(const char attribute((unused)) *event, //fprintf(stderr, "choose_list_in_flight -> %d+\n", choose_list_in_flight); } +/** @brief Called for key-*-event on the main view + * + * Switches focus to the + */ +static gboolean choose_key_event(GtkWidget attribute((unused)) *widget, + GdkEventKey *event, + gpointer attribute((unused)) user_data) { + /*fprintf(stderr, "choose_key_event type=%d state=%#x keyval=%#x\n", + event->type, event->state, event->keyval);*/ + switch(event->keyval) { + case GDK_Page_Up: + case GDK_Page_Down: + case GDK_Up: + case GDK_Down: + case GDK_Home: + case GDK_End: + return FALSE; /* We'll take these */ + case 'f': case 'F': + /* ^F is expected to start a search. We implement this by focusing the + * search entry box. */ + if((event->state & ~(GDK_LOCK_MASK|GDK_SHIFT_MASK)) == GDK_CONTROL_MASK + && event->type == GDK_KEY_PRESS) { + gtk_widget_grab_focus(user_data); + return FALSE; + } + break; + case 'g': case 'G': + /* ^G is expected to go the next match. We simulate a click on the 'next' + * button. */ + if((event->state & ~(GDK_LOCK_MASK|GDK_SHIFT_MASK)) == GDK_CONTROL_MASK + && event->type == GDK_KEY_PRESS) { + choose_next_clicked(0, 0); + return FALSE; + } + break; + } + gtk_widget_event(user_data, (GdkEvent *)event); + return TRUE; +} + /** @brief Create the choose tab */ GtkWidget *choose_widget(void) { /* Create the tree store. */ @@ -604,6 +645,13 @@ GtkWidget *choose_widget(void) { FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/); g_object_set_data(G_OBJECT(vbox), "type", (void *)&choose_tabtype); + + /* Redirect keyboard activity to the search widget */ + g_signal_connect(choose_view, "key-press-event", + G_CALLBACK(choose_key_event), choose_search_entry); + g_signal_connect(choose_view, "key-release-event", + G_CALLBACK(choose_key_event), choose_search_entry); + return vbox; } diff --git a/disobedience/choose.h b/disobedience/choose.h index cd82127..69460c4 100644 --- a/disobedience/choose.h +++ b/disobedience/choose.h @@ -54,6 +54,7 @@ extern GtkWidget *choose_view; extern GtkTreeSelection *choose_selection; extern const struct tabtype choose_tabtype; extern int choose_auto_expanding; +extern GtkWidget *choose_search_entry; struct choosedata *choose_iter_to_data(GtkTreeIter *iter); struct choosedata *choose_path_to_data(GtkTreePath *path); @@ -72,6 +73,10 @@ int choose_can_autocollapse(GtkTreeIter *iter); GtkWidget *choose_search_widget(void); int choose_is_search_result(const char *track); void choose_auto_collapse(void); +void choose_next_clicked(GtkButton *button, + gpointer userdata); +void choose_prev_clicked(GtkButton *button, + gpointer userdata); #endif /* CHOOSE_H */