From c3df9503faeb30706042d9add6b9db39b38ec164 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Wed, 3 Oct 2007 12:53:12 +0100 Subject: [PATCH] improve new disobedience search code. --choosealpha is dead now Organization: Straylight/Edgeware From: Richard Kettlewell --- disobedience/Makefile.am | 2 +- disobedience/choose.c | 122 ++++++++++++++---------------------- disobedience/disobedience.c | 7 +-- disobedience/disobedience.h | 9 +++ disobedience/progress.c | 84 +++++++++++++++++++++++++ disobedience/properties.c | 28 ++++----- 6 files changed, 154 insertions(+), 98 deletions(-) create mode 100644 disobedience/progress.c diff --git a/disobedience/Makefile.am b/disobedience/Makefile.am index 9897426..d8137cd 100644 --- a/disobedience/Makefile.am +++ b/disobedience/Makefile.am @@ -25,7 +25,7 @@ AM_CFLAGS=$(GLIB_CFLAGS) $(GTK_CFLAGS) disobedience_SOURCES=disobedience.h disobedience.c client.c queue.c \ choose.c misc.c style.h control.c properties.c menu.c \ - log.c \ + log.c progress.c \ ../lib/memgc.c disobedience_LDADD=../lib/libdisorder.a $(LIBPCRE) $(LIBGC) $(LIBGCRYPT) disobedience_LDFLAGS=$(GTK_LIBS) diff --git a/disobedience/choose.c b/disobedience/choose.c index 931b1b7..8e43d17 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -124,12 +124,14 @@ static GtkWidget *dir_menu; /**< @brief directory popup menu */ static struct choosenode *last_click; /**< @brief last clicked node for selection */ static int files_visible; /**< @brief total files visible */ static int files_selected; /**< @brief total files selected */ +static int gets_in_flight; /**< @brief total gets in flight */ static int search_in_flight; /**< @brief a search is underway */ static int search_obsolete; /**< @brief the current search is void */ static char **searchresults; /**< @brief search results */ static int nsearchresults; /**< @brief number of results */ static int nsearchvisible; /**< @brief number of search results visible */ static struct hash *searchhash; /**< @brief hash of search results */ +struct progress_window *spw; /**< @brief progress window */ /* Forward Declarations */ @@ -141,7 +143,6 @@ static struct choosenode *newnode(struct choosenode *parent, unsigned flags, void (*fill)(struct choosenode *)); static void fill_root_node(struct choosenode *cn); -static void fill_letter_node(struct choosenode *cn); static void fill_directory_node(struct choosenode *cn); static void got_files(void *v, int nvec, char **vec); static void got_resolved_file(void *v, const char *track); @@ -279,39 +280,28 @@ static void filled(struct choosenode *cn) { D(("filled %s %d/%d", cn->path, nsearchvisible, nsearchresults)); expand_from(cn); } + if(gets_in_flight == 0 && nsearchvisible < nsearchresults) + expand_from(root); } /** @brief Fill the root */ static void fill_root_node(struct choosenode *cn) { - int ch; - char *name; struct callbackdata *cbd; D(("fill_root_node")); clear_children(cn); - if(choosealpha) { - if(!cn->children.nvec) { /* Only need to do this once */ - for(ch = 'A'; ch <= 'Z'; ++ch) { - byte_xasprintf(&name, "%c", ch); - newnode(cn, "", name, name, CN_EXPANDABLE, fill_letter_node); - } - newnode(cn, "", "*", "~", CN_EXPANDABLE, fill_letter_node); - } - updated_node(cn, 1); - filled(cn); - } else { - /* More de-duping possible here */ - if(cn->flags & CN_GETTING_ANY) - return; - gtk_label_set_text(GTK_LABEL(report_label), "getting files"); - cbd = xmalloc(sizeof *cbd); - cbd->u.choosenode = cn; - disorder_eclient_dirs(client, got_dirs, "", 0, cbd); - cbd = xmalloc(sizeof *cbd); - cbd->u.choosenode = cn; - disorder_eclient_files(client, got_files, "", 0, cbd); - cn->flags |= CN_GETTING_FILES|CN_GETTING_DIRS; - } + /* More de-duping possible here */ + if(cn->flags & CN_GETTING_ANY) + return; + gtk_label_set_text(GTK_LABEL(report_label), "getting files"); + cbd = xmalloc(sizeof *cbd); + cbd->u.choosenode = cn; + disorder_eclient_dirs(client, got_dirs, "", 0, cbd); + cbd = xmalloc(sizeof *cbd); + cbd->u.choosenode = cn; + disorder_eclient_files(client, got_files, "", 0, cbd); + cn->flags |= CN_GETTING_FILES|CN_GETTING_DIRS; + gets_in_flight += 2; } /** @brief Delete all the widgets owned by @p cn */ @@ -360,38 +350,6 @@ static void clear_children(struct choosenode *cn) { cn->children.nvec = 0; } -/** @brief Fill a letter node */ -static void fill_letter_node(struct choosenode *cn) { - const char *regexp; - struct callbackdata *cbd; - - D(("fill_letter_node %s", cn->display)); - if(cn->flags & CN_GETTING_ANY) - return; - switch(cn->display[0]) { - default: - byte_xasprintf((char **)®exp, "^(the )?%c", tolower(cn->display[0])); - break; - case 'T': - regexp = "^(?!the [^t])t"; - break; - case '*': - regexp = "^[^a-z]"; - break; - } - /* TODO: caching */ - /* TODO: de-dupe against fill_directory_node */ - gtk_label_set_text(GTK_LABEL(report_label), "getting files"); - clear_children(cn); - cbd = xmalloc(sizeof *cbd); - cbd->u.choosenode = cn; - disorder_eclient_dirs(client, got_dirs, "", regexp, cbd); - cbd = xmalloc(sizeof *cbd); - cbd->u.choosenode = cn; - disorder_eclient_files(client, got_files, "", regexp, cbd); - cn->flags |= CN_GETTING_FILES|CN_GETTING_DIRS; -} - /** @brief Called with a list of files just below some node */ static void got_files(void *v, int nvec, char **vec) { struct callbackdata *cbd = v; @@ -402,10 +360,13 @@ static void got_files(void *v, int nvec, char **vec) { /* Complicated by the need to resolve aliases. We can save a bit of effort * by re-using cbd though. */ cn->flags &= ~CN_GETTING_FILES; + --gets_in_flight; if((cn->pending = nvec)) { cn->flags |= CN_RESOLVING_FILES; - for(n = 0; n < nvec; ++n) + for(n = 0; n < nvec; ++n) { disorder_eclient_resolve(client, got_resolved_file, vec[n], cbd); + ++gets_in_flight; + } } /* If there are no files and the directories are all read by now, we're * done */ @@ -424,6 +385,7 @@ static void got_resolved_file(void *v, const char *track) { trackname_transform("track", track, "display"), trackname_transform("track", track, "sort"), 0/*flags*/, 0/*fill*/); + --gets_in_flight; /* Only bother updating when we've got the lot */ if(--cn->pending == 0) { cn->flags &= ~CN_RESOLVING_FILES; @@ -447,6 +409,7 @@ static void got_dirs(void *v, int nvec, char **vec) { * Really we want a variant of files/dirs that produces both the * raw filename and the transformed name for a chosen context. */ + --gets_in_flight; for(n = 0; n < nvec; ++n) newnode(cn, vec[n], trackname_transform("dir", vec[n], "display"), @@ -464,7 +427,6 @@ static void fill_directory_node(struct choosenode *cn) { D(("fill_directory_node %s", cn->path)); /* TODO: caching */ - /* TODO: de-dupe against fill_letter_node */ if(cn->flags & CN_GETTING_ANY) return; assert(report_label != 0); @@ -477,6 +439,7 @@ static void fill_directory_node(struct choosenode *cn) { cbd->u.choosenode = cn; disorder_eclient_files(client, got_files, cn->path, 0, cbd); cn->flags |= CN_GETTING_FILES|CN_GETTING_DIRS; + gets_in_flight += 2; } /** @brief Expand a node */ @@ -511,23 +474,13 @@ static void expand_node(struct choosenode *cn, int contingent) { */ static void expand_from(struct choosenode *cn) { int n; - const size_t pathlen = strlen(cn->path); if(nsearchvisible == nsearchresults) /* We're done */ return; /* Are any of the search tracks at/below this point? */ - if(cn != root) { - for(n = 0; n < nsearchresults; ++n) - if(strlen(searchresults[n]) >= pathlen - && !strncmp(cn->path, searchresults[n], pathlen) - && (searchresults[n][pathlen] == 0 - || searchresults[n][pathlen] == '/')) - break; - if(n >= nsearchresults) - /* This is neither a search result nor an ancestor directory of one */ - return; - } + if(!(cn == root || hash_find(searchhash, cn->path))) + return; D(("expand_from %d/%d visible %s", nsearchvisible, nsearchresults, cn->path)); if(cn->flags & CN_EXPANDABLE) { @@ -535,15 +488,17 @@ static void expand_from(struct choosenode *cn) { /* This node is marked as expanded already. children.nvec might be 0, * indicating that expansion is still underway. We should get another * callback when it is expanded. */ - for(n = 0; n < cn->children.nvec; ++n) + for(n = 0; n < cn->children.nvec && gets_in_flight < 10; ++n) expand_from(cn->children.vec[n]); else { /* This node is not expanded yet */ expand_node(cn, 1); } - } else + } else { /* This is an actual search result */ ++nsearchvisible; + progress_window_progress(spw, nsearchvisible, nsearchresults); + } } /** @brief Contract all contingently expanded nodes below @p cn */ @@ -614,6 +569,7 @@ static int is_search_result(const char *track) { static void search_completed(void attribute((unused)) *v, int nvec, char **vec) { int n; + char *s; search_in_flight = 0; /* Contract any choosenodes that were only expanded to show search @@ -631,10 +587,26 @@ static void search_completed(void attribute((unused)) *v, if(nvec) { /* Create a new search hash for fast identification of results */ searchhash = hash_new(1); - for(n = 0; n < nvec; ++n) + for(n = 0; n < nvec; ++n) { + /* The filename itself lives in the hash */ hash_add(searchhash, vec[n], "", HASH_INSERT_OR_REPLACE); + /* So do its ancestor directories */ + for(s = vec[n] + 1; *s; ++s) { + if(*s == '/') { + *s = 0; + hash_add(searchhash, vec[n], "", HASH_INSERT_OR_REPLACE); + *s = '/'; + } + } + } /* We don't yet know that the results are visible */ nsearchvisible = 0; + if(spw) { + progress_window_progress(spw, 0, 0); + spw = 0; + } + if(nsearchresults > 50) + spw = progress_window_new("Fetching search results"); /* Initiate expansion */ expand_from(root); } else { diff --git a/disobedience/disobedience.c b/disobedience/disobedience.c index 2b21b61..2a39a15 100644 --- a/disobedience/disobedience.c +++ b/disobedience/disobedience.c @@ -69,9 +69,6 @@ int volume_r; double goesupto = 10; /* volume upper bound */ -/** @brief Break up choose tab by initial letter */ -int choosealpha; - /** @brief True if a NOP is in flight */ static int nop_in_flight; @@ -320,7 +317,6 @@ static const struct option options[] = { { "version", no_argument, 0, 'V' }, { "config", required_argument, 0, 'c' }, { "tufnel", no_argument, 0, 't' }, - { "choosealpha", no_argument, 0, 'C' }, { "debug", no_argument, 0, 'd' }, { 0, 0, 0, 0 } }; @@ -364,14 +360,13 @@ int main(int argc, char **argv) { if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale"); gtk_init(&argc, &argv); gtk_rc_parse_string(style); - while((n = getopt_long(argc, argv, "hVc:dtH", options, 0)) >= 0) { + while((n = getopt_long(argc, argv, "hVc:dtHC", options, 0)) >= 0) { switch(n) { case 'h': help(); case 'V': version(); case 'c': configfile = optarg; break; case 'd': debugging = 1; break; case 't': goesupto = 11; break; - case 'C': choosealpha = 1; break; /* not well tested any more */ default: fatal(0, "invalid option"); } } diff --git a/disobedience/disobedience.h b/disobedience/disobedience.h index 17fade0..d6644a1 100644 --- a/disobedience/disobedience.h +++ b/disobedience/disobedience.h @@ -58,6 +58,7 @@ struct queuelike; struct choosenode; +struct progress_window; /** @brief Callback data structure * @@ -130,6 +131,14 @@ GdkPixbuf *find_image(const char *name); void popup_error(const char *msg); /* Pop up an error message */ +struct progress_window *progress_window_new(const char *title); +/* Pop up a progress window */ + +void progress_window_progress(struct progress_window *pw, + int progress, + int limit); +/* Report current progress */ + void register_monitor(monitor_callback *callback, void *u, unsigned long mask); diff --git a/disobedience/progress.c b/disobedience/progress.c new file mode 100644 index 0000000..8efeb8f --- /dev/null +++ b/disobedience/progress.c @@ -0,0 +1,84 @@ +/* + * This file is part of DisOrder. + * Copyright (C) 2006, 2007 Richard Kettlewell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file disobedience/progress.c + * @brief Progress bar support + */ + +#include "disobedience.h" + +/** @brief State for progress windows */ +struct progress_window { + /** @brief The window */ + GtkWidget *window; + /** @brief The bar */ + GtkWidget *bar; +}; + +/** @brief Create a progress window */ +struct progress_window *progress_window_new(const char *title) { + struct progress_window *pw = xmalloc(sizeof *pw); + + pw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(pw->window, "destroy", + G_CALLBACK(gtk_widget_destroyed), &pw->window); + gtk_window_set_default_size(GTK_WINDOW(pw->window), 360, -1); + gtk_window_set_title(GTK_WINDOW(pw->window), title); + pw->bar = gtk_progress_bar_new(); + gtk_container_add(GTK_CONTAINER(pw->window), pw->bar); + gtk_widget_show_all(pw->window); + return pw; +} + +/** @brief Report current progress + * The window is automatically destroyed if @p progress >= @p limit. + * To cancel a window just call with both set to 0. + */ +void progress_window_progress(struct progress_window *pw, + int progress, + int limit) { + if(!pw) + return; + /* Maybe the user closed the window */ + if(!pw->window) + return; + /* Clamp insane or inconvenient values */ + if(limit <= 0) + progress = limit = 1; + if(progress < 0) + progress = 0; + /* Maybe we're done */ + if(progress >= limit) { + gtk_widget_destroy(pw->window); + pw->window = pw->bar = 0; + return; + } + /* Display current progress */ + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pw->bar), + (double)progress / limit); +} + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/disobedience/properties.c b/disobedience/properties.c index e9f61a2..3c25292 100644 --- a/disobedience/properties.c +++ b/disobedience/properties.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2006 Richard Kettlewell + * Copyright (C) 2006, 2007 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -156,7 +156,7 @@ static int prefs_total; /* Total prefs */ static struct prefdata *prefdatas; /* Current prefdatas */ static GtkWidget *properties_window; static GtkWidget *properties_table; -static GtkWidget *progress_window, *progress_bar; +static struct progress_window *pw; static void propagate_clicked(GtkButton attribute((unused)) *button, gpointer userdata) { @@ -286,22 +286,19 @@ void properties(int ntracks, const char **tracks) { gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(GTK_WIDGET(properties_table)->parent->parent), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + /* Zot any pre-existing progress window just in case */ + if(pw) + progress_window_progress(pw, 0, 0); /* Pop up a progress bar while we're waiting */ - progress_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - g_signal_connect(progress_window, "destroy", - G_CALLBACK(gtk_widget_destroyed), &progress_window); - gtk_window_set_default_size(GTK_WINDOW(progress_window), 360, -1); - gtk_window_set_title(GTK_WINDOW(progress_window), - "Fetching Track Properties"); - progress_bar = gtk_progress_bar_new(); - gtk_container_add(GTK_CONTAINER(progress_window), progress_bar); - gtk_widget_show_all(progress_window); + pw = progress_window_new("Fetching Track Properties"); } /* Everything is filled in now */ static void prefdata_alldone(void) { - if(progress_window) - gtk_widget_destroy(progress_window); + if(pw) { + progress_window_progress(pw, 0, 0); + pw = 0; + } /* Default size may be too small */ gtk_window_set_default_size(GTK_WINDOW(properties_window), 480, 512); /* TODO: relate default size to required size more closely */ @@ -464,9 +461,8 @@ static void prefdata_completed_common(struct prefdata *f, GTK_EXPAND|GTK_FILL/*xoptions*/, 0/*yoptions*/, 1, 1); --prefs_unfilled; - if(prefs_total && progress_window) - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), - 1.0 - (double)prefs_unfilled / prefs_total); + if(prefs_total) + progress_window_progress(pw, prefs_total - prefs_unfilled, prefs_total); if(!prefs_unfilled) prefdata_alldone(); } -- [mdw]