diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2012-11-19 14:11:08 -0800 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2012-11-20 11:42:01 -0800 |
commit | a8d413551e3b6c7c2ab9d092b67e0976550e2115 (patch) | |
tree | a305ddc5fb8ee66a6c6ca44f4ccc929991493252 | |
parent | d1571ead2df6276ff8db06bd54d39b873b4ab9c9 (diff) | |
download | subsurface-a8d413551e3b6c7c2ab9d092b67e0976550e2115.tar.gz |
Allow the user to cancel a dive computer download
The code pretended to support this for libdivecomputer based downloads,
but it had never been hooked up when the native Uemis downloader was
implemented. When I finally decided to close that feature gap I realized
that the original code was, shall we say, "aspirational" or "completely
bogus" and therefore never worked.
So instead of just hooking up the code for the Uemis downloader I instead
implemented this correctly for the first time for both libdivecomputer and
the native Uemis downloader.
In order not to have to mess with multithreaded Gtk development I simply
opted for a helper function that fires on a 100ms timeout and have it end
the dialog without a response. This way we can run the dialog while
waiting for the download to finish, still update the progress bar and
respond in a useful manner to the user clicking cancel.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r-- | display-gtk.h | 2 | ||||
-rw-r--r-- | gtk-gui.c | 5 | ||||
-rw-r--r-- | libdivecomputer.c | 42 | ||||
-rw-r--r-- | libdivecomputer.h | 1 | ||||
-rw-r--r-- | uemis-downloader.c | 65 |
5 files changed, 97 insertions, 18 deletions
diff --git a/display-gtk.h b/display-gtk.h index 4f156c7f7..e1fd4f5d9 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -114,6 +114,6 @@ extern GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, cons data_func_t data_func, unsigned int flags); GError *uemis_download(const char *path, char **divenr, char **xml_buffer, - progressbar_t *progress, gboolean force_download); + progressbar_t *progress, GtkDialog *dialog, gboolean force_download); #endif @@ -1678,7 +1678,7 @@ static GError *setup_uemis_import(device_data_t *data) GError *error = NULL; char *buf = NULL; - error = uemis_download(data->devname, &uemis_max_dive_data, &buf, &data->progress, data->force_download); + error = uemis_download(data->devname, &uemis_max_dive_data, &buf, &data->progress, data->dialog, data->force_download); if (buf && strlen(buf) > 1) { #if UEMIS_DEBUG > 3 fprintf(debugfile, "xml buffer \"%s\"\n\n", buf); @@ -1742,7 +1742,7 @@ void download_dialog(GtkWidget *w, gpointer data) GTK_WINDOW(main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL); g_signal_connect(dialog, "delete-event", G_CALLBACK(download_dialog_delete), NULL); @@ -1819,6 +1819,7 @@ repeat: while (*(--ne) == ' ' || *ne == '\t') *ne = '\0'; devicedata.devname = ns; + devicedata.dialog = GTK_DIALOG(dialog); devicedata.force_download = force_download; force_download = FALSE; /* when retrying we don't want to restart */ info = import_dive_computer(&devicedata, GTK_DIALOG(dialog)); diff --git a/libdivecomputer.c b/libdivecomputer.c index 076126dfb..d4ffcc174 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -423,21 +423,55 @@ static void *pthread_wrapper(void *_data) return (void *)err_string; } +/* this simply ends the dialog without a response and asks not to be fired again + * as we set this function up in every loop while uemis_download is waiting for + * the download to finish */ +static gboolean timeout_func(gpointer _data) +{ + GtkDialog *dialog = _data; + if (!import_thread_cancelled) + gtk_dialog_response(dialog, GTK_RESPONSE_NONE); + return FALSE; +} + GError *do_import(device_data_t *data) { pthread_t pthread; void *retval; + GtkDialog *dialog = data->dialog; /* I'm sure there is some better interface for waiting on a thread in a UI main loop */ import_thread_done = 0; progress_bar_text = ""; progress_bar_fraction = 0.0; pthread_create(&pthread, NULL, pthread_wrapper, data); + /* loop here until the import is done or was cancelled by the user; + * in order to get control back from gtk we register a timeout function + * that ends the dialog with no response every 100ms; we then update the + * progressbar and setup the timeout again - unless of course the user + * pressed cancel, in which case we just wait for the download thread + * to react to that and exit */ while (!import_thread_done) { - import_thread_cancelled = process_ui_events(); - update_progressbar(&data->progress, progress_bar_fraction); - update_progressbar_text(&data->progress, progress_bar_text); - usleep(100000); + if (!import_thread_cancelled) { + int result; + g_timeout_add(100, timeout_func, dialog); + update_progressbar(&data->progress, progress_bar_fraction); + update_progressbar_text(&data->progress, progress_bar_text); + result = gtk_dialog_run(dialog); + switch (result) { + case GTK_RESPONSE_CANCEL: + import_thread_cancelled = TRUE; + progress_bar_text = "Cancelled..."; + break; + default: + /* nothing */ + break; + } + } else { + update_progressbar(&data->progress, progress_bar_fraction); + update_progressbar_text(&data->progress, progress_bar_text); + usleep(100000); + } } if (pthread_join(pthread, &retval) < 0) retval = _("Odd pthread error return"); diff --git a/libdivecomputer.h b/libdivecomputer.h index 7497abf11..2121c27bc 100644 --- a/libdivecomputer.h +++ b/libdivecomputer.h @@ -18,6 +18,7 @@ typedef struct device_data_t { progressbar_t progress; int preexisting; gboolean force_download; + GtkDialog *dialog; } device_data_t; extern GError *do_import(device_data_t *data); diff --git a/uemis-downloader.c b/uemis-downloader.c index fbab3dcef..ac45940b5 100644 --- a/uemis-downloader.c +++ b/uemis-downloader.c @@ -510,6 +510,8 @@ static gboolean uemis_get_answer(const char *path, char *request, int n_param_in mbuf = NULL; mbuf_size = 0; while (searching || assembling_mbuf) { + if (import_thread_cancelled) + return FALSE; progress_bar_fraction = filenr / 4000.0; snprintf(fl, 13, "ANS%d.TXT", filenr - 1); ans_path = g_build_filename(path, "ANS", fl, NULL); @@ -519,7 +521,7 @@ static gboolean uemis_get_answer(const char *path, char *request, int n_param_in #if UEMIS_DEBUG > 3 tmp[100]='\0'; fprintf(debugfile, "::t %s \"%s\"\n", ans_path, tmp); -#elsif UEMIS_DEBUG > 1 +#elif UEMIS_DEBUG > 1 char pbuf[4]; pbuf[0] = tmp[0]; pbuf[1] = tmp[1]; @@ -816,6 +818,9 @@ static char *do_uemis_download(struct argument_block *args) uemis_info("Start download"); if (! uemis_get_answer(mountpath, "processSync", 0, 2, &result)) goto bail; + /* before starting the long download, check if user pressed cancel */ + if (import_thread_cancelled) + goto bail; param_buff[1] = "notempty"; /* if we have an empty divelist then the user will almost * certainly want to start downloading from the first dive on @@ -841,6 +846,9 @@ static char *do_uemis_download(struct argument_block *args) /* if we got an error, deal with it */ if (!success) break; + /* if the user clicked cancel, exit gracefully but ignore everything read */ + if (import_thread_cancelled) + goto bail; /* also, if we got nothing back, we should stop trying */ if (!param_buff[3]) break; @@ -871,22 +879,24 @@ static char *do_uemis_download(struct argument_block *args) buffer_insert(xml_buffer, &xml_buffer_size, next_detail); free(next_detail); } - if (!success) + if (!success || import_thread_cancelled) break; } - if (! uemis_get_answer(mountpath, "terminateSync", 0, 3, &result)) - goto bail; +bail: + (void) uemis_get_answer(mountpath, "terminateSync", 0, 3, &result); if (! strcmp(param_buff[0], "error")) { if (! strcmp(param_buff[2],"Out of Memory")) result = _(ERR_FS_FULL); else result = param_buff[2]; } - buffer_add(xml_buffer, &xml_buffer_size, "</list></dives>"); + if (import_thread_cancelled) + xml_buffer[0] = 0; + else + buffer_add(xml_buffer, &xml_buffer_size, "</list></dives>"); #if UEMIS_DEBUG > 5 fprintf(debugfile, "XML buffer \"%s\"", *xml_buffer); #endif -bail: free(deviceid); return result; } @@ -899,8 +909,19 @@ static void *pthread_wrapper(void *_data) return (void *)err_string; } +/* this simply ends the dialog without a response and asks not to be fired again + * as we set this function up in every loop while uemis_download is waiting for + * the download to finish */ +static gboolean timeout_func(gpointer _data) +{ + GtkDialog *dialog = _data; + if (!import_thread_cancelled) + gtk_dialog_response(dialog, GTK_RESPONSE_NONE); + return FALSE; +} + GError *uemis_download(const char *mountpath, char **max_dive_data, char **xml_buffer, progressbar_t *progress, - gboolean force_download) + GtkDialog *dialog, gboolean force_download) { pthread_t pthread; void *retval; @@ -911,11 +932,33 @@ GError *uemis_download(const char *mountpath, char **max_dive_data, char **xml_b progress_bar_text = ""; progress_bar_fraction = 0.0; pthread_create(&pthread, NULL, pthread_wrapper, &args); + /* loop here until the import is done or was cancelled by the user; + * in order to get control back from gtk we register a timeout function + * that ends the dialog with no response every 100ms; we then update the + * progressbar and setup the timeout again - unless of course the user + * pressed cancel, in which case we just wait for the download thread + * to react to that and exit */ while (!import_thread_done) { - import_thread_cancelled = process_ui_events(); - update_progressbar(args.progress, progress_bar_fraction); - update_progressbar_text(args.progress, progress_bar_text); - usleep(100000); + if (!import_thread_cancelled) { + int result; + g_timeout_add(100, timeout_func, dialog); + update_progressbar(args.progress, progress_bar_fraction); + update_progressbar_text(args.progress, progress_bar_text); + result = gtk_dialog_run(dialog); + switch (result) { + case GTK_RESPONSE_CANCEL: + import_thread_cancelled = TRUE; + progress_bar_text = "Cancelled..."; + break; + default: + /* nothing */ + break; + } + } else { + update_progressbar(args.progress, progress_bar_fraction); + update_progressbar_text(args.progress, progress_bar_text); + usleep(100000); + } } if (pthread_join(pthread, &retval) < 0) return error("Pthread return with error"); |