X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/15a66a1380c40c0f711e88e5863f9bed1347d2df..2fadbbc60560fe8562d7b6e652f8788c1d084d5f:/disobedience/choose.c diff --git a/disobedience/choose.c b/disobedience/choose.c index 31535b3..781b415 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -35,7 +35,6 @@ * TODO: * - sweep up contracted nodes * - update when content may have changed (e.g. after a rescan) - * - searching! * - proper sorting */ @@ -51,6 +50,12 @@ GtkWidget *choose_view; /** @brief The selection tree's selection */ GtkTreeSelection *choose_selection; +/** @brief Count of file listing operations in flight */ +static int choose_list_in_flight; + +/** @brief Count of files inserted in current batch of listing operations */ +static int choose_inserted; + static char *choose_get_string(GtkTreeIter *iter, int column) { gchar *gs; gtk_tree_model_get(GTK_TREE_MODEL(choose_store), iter, @@ -123,6 +128,16 @@ static gboolean choose_set_state_callback(GtkTreeModel attribute((unused)) *mode LENGTH_COLUMN, length, STATE_COLUMN, queued(track), -1); + if(choose_is_search_result(track)) + gtk_tree_store_set(choose_store, it, + BG_COLUMN, SEARCH_RESULT_BG, + FG_COLUMN, SEARCH_RESULT_FG, + -1); + else + gtk_tree_store_set(choose_store, it, + BG_COLUMN, (char *)0, + FG_COLUMN, (char *)0, + -1); } return FALSE; /* continue walking */ } @@ -150,6 +165,7 @@ static void choose_set_state(const char attribute((unused)) *event, static void choose_populate(GtkTreeRowReference *parent_ref, int nvec, char **vec, int isfile) { + const char *type = isfile ? "track" : "dir"; /* Compute parent_* */ GtkTreeIter pit[1], *parent_it; GtkTreePath *parent_path; @@ -160,17 +176,17 @@ static void choose_populate(GtkTreeRowReference *parent_ref, pit, parent_path); assert(pitv); /*fprintf(stderr, "choose_populate %s: parent path is [%s]\n", - choose_type_map[type], + type, gtk_tree_path_to_string(parent_path));*/ } else { parent_path = 0; parent_it = 0; /*fprintf(stderr, "choose_populate %s: populating the root\n", - choose_type_map[type]);*/ + type);*/ } /* Remove unwanted nodes and find out which we must add */ - //fprintf(stderr, " trimming unwanted %s nodes\n", choose_type_map[type]); - char *found = xmalloc(nvec); + //fprintf(stderr, " trimming unwanted %s nodes\n", type); + struct tracksort_data *td = tracksort_init(nvec, vec, type); GtkTreeIter it[1]; gboolean itv = gtk_tree_model_iter_children(GTK_TREE_MODEL(choose_store), it, @@ -185,13 +201,13 @@ static void choose_populate(GtkTreeRowReference *parent_ref, keep = 0; } else if(choose_is_file(it) == isfile) { /* This is the type we care about */ - //fprintf(stderr, " %s is a %s\n", track, isfile ? "file" : "dir"); + //fprintf(stderr, " %s is a %s\n", track, type); int n; - for(n = 0; n < nvec && strcmp(vec[n], track); ++n) + for(n = 0; n < nvec && strcmp(td[n].track, track); ++n) ; if(n < nvec) { //fprintf(stderr, " ... and survives\n"); - found[n] = 1; + td[n].extra = td; keep = 1; } else { //fprintf(stderr, " ... and is to be removed\n"); @@ -209,21 +225,16 @@ static void choose_populate(GtkTreeRowReference *parent_ref, } /* Add nodes we don't have */ int inserted = 0; - //fprintf(stderr, " inserting new %s nodes\n", choose_type_map[type]); - const char *typename = isfile ? "track" : "dir"; + //fprintf(stderr, " inserting new %s nodes\n", type); for(int n = 0; n < nvec; ++n) { - if(!found[n]) { - //fprintf(stderr, " %s was not found\n", vec[n]); + if(!td[n].extra) { + //fprintf(stderr, " %s was not found\n", td[n].track); gtk_tree_store_append(choose_store, it, parent_it); gtk_tree_store_set(choose_store, it, - NAME_COLUMN, trackname_transform(typename, - vec[n], - "display"), + NAME_COLUMN, td[n].display, ISFILE_COLUMN, isfile, - TRACK_COLUMN, vec[n], - SORT_COLUMN, trackname_transform(typename, - vec[n], - "sort"), + TRACK_COLUMN, td[n].track, + SORT_COLUMN, td[n].sort, -1); /* Update length and state; we expect this to kick off length lookups * rather than necessarily get the right value the first time round. */ @@ -254,6 +265,18 @@ static void choose_populate(GtkTreeRowReference *parent_ref, gtk_tree_row_reference_free(parent_ref); gtk_tree_path_free(parent_path); } + /* We only notify others that we've inserted tracks when there are no more + * insertions pending, so that they don't have to keep track of how many + * requests they've made. */ + choose_inserted += inserted; + if(--choose_list_in_flight == 0) { + /* Notify interested parties that we inserted some tracks, AFTER making + * sure that the row is properly expanded */ + if(choose_inserted) { + event_raise("choose-inserted-tracks", parent_it); + choose_inserted = 0; + } + } } static void choose_dirs_completed(void *v, @@ -324,6 +347,7 @@ static void choose_row_expanded(GtkTreeView attribute((unused)) *treeview, gtk_tree_row_reference_new(GTK_TREE_MODEL(choose_store), path)); /* The row references are destroyed in the _completed handlers. */ + choose_list_in_flight += 2; } /** @brief Create the choose tab */ @@ -335,6 +359,8 @@ GtkWidget *choose_widget(void) { G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING); /* Create the view */ @@ -344,16 +370,18 @@ GtkWidget *choose_widget(void) { /* Create cell renderers and columns */ /* TODO use a table */ { - GtkCellRenderer *r = gtk_cell_renderer_text_new(); + GtkCellRenderer *r = gtk_cell_renderer_toggle_new(); GtkTreeViewColumn *c = gtk_tree_view_column_new_with_attributes - ("Track", + ("Queued", r, - "text", NAME_COLUMN, + "active", STATE_COLUMN, + "visible", ISFILE_COLUMN, (char *)0); gtk_tree_view_column_set_resizable(c, TRUE); gtk_tree_view_column_set_reorderable(c, TRUE); - g_object_set(c, "expand", TRUE, (char *)0); gtk_tree_view_append_column(GTK_TREE_VIEW(choose_view), c); + g_signal_connect(r, "toggled", + G_CALLBACK(choose_state_toggled), 0); } { GtkCellRenderer *r = gtk_cell_renderer_text_new(); @@ -368,18 +396,19 @@ GtkWidget *choose_widget(void) { gtk_tree_view_append_column(GTK_TREE_VIEW(choose_view), c); } { - GtkCellRenderer *r = gtk_cell_renderer_toggle_new(); + GtkCellRenderer *r = gtk_cell_renderer_text_new(); GtkTreeViewColumn *c = gtk_tree_view_column_new_with_attributes - ("Queued", + ("Track", r, - "active", STATE_COLUMN, - "visible", ISFILE_COLUMN, + "text", NAME_COLUMN, + "background", BG_COLUMN, + "foreground", FG_COLUMN, (char *)0); gtk_tree_view_column_set_resizable(c, TRUE); gtk_tree_view_column_set_reorderable(c, TRUE); + g_object_set(c, "expand", TRUE, (char *)0); gtk_tree_view_append_column(GTK_TREE_VIEW(choose_view), c); - g_signal_connect(r, "toggled", - G_CALLBACK(choose_state_toggled), 0); + gtk_tree_view_set_expander_column(GTK_TREE_VIEW(choose_view), c); } /* The selection should support multiple things being selected */ @@ -397,15 +426,25 @@ GtkWidget *choose_widget(void) { event_register("queue-list-changed", choose_set_state, 0); event_register("playing-track-changed", choose_set_state, 0); - + event_register("search-results-changed", choose_set_state, 0); + event_register("lookups-completed", choose_set_state, 0); + /* Fill the root */ disorder_eclient_files(client, choose_files_completed, "", NULL, NULL); disorder_eclient_dirs(client, choose_dirs_completed, "", NULL, NULL); - + /* Make the widget scrollable */ GtkWidget *scrolled = scroll_widget(choose_view); - g_object_set_data(G_OBJECT(scrolled), "type", (void *)&choose_tabtype); - return scrolled; + + /* Pack vertically with the search widget */ + GtkWidget *vbox = gtk_vbox_new(FALSE/*homogenous*/, 1/*spacing*/); + gtk_box_pack_start(GTK_BOX(vbox), scrolled, + TRUE/*expand*/, TRUE/*fill*/, 0/*padding*/); + gtk_box_pack_end(GTK_BOX(vbox), choose_search_widget(), + FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/); + + g_object_set_data(G_OBJECT(vbox), "type", (void *)&choose_tabtype); + return vbox; } /*