diff options
Diffstat (limited to 'download-dialog.c')
-rw-r--r-- | download-dialog.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/download-dialog.c b/download-dialog.c new file mode 100644 index 000000000..73ed4bac9 --- /dev/null +++ b/download-dialog.c @@ -0,0 +1,474 @@ +#include <libintl.h> +#include <glib/gi18n.h> +#include "dive.h" +#include "divelist.h" +#include "display.h" +#include "display-gtk.h" +#include "callbacks-gtk.h" +#include "libdivecomputer.h" + +const char *default_dive_computer_vendor; +const char *default_dive_computer_product; +const char *default_dive_computer_device; + +static gboolean force_download; +static gboolean prefer_downloaded; + +OPTIONCALLBACK(force_toggle, force_download) +OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) + +struct product { + const char *product; + dc_descriptor_t *descriptor; + struct product *next; +}; + +struct vendor { + const char *vendor; + struct product *productlist; + struct vendor *next; +}; + +struct mydescriptor { + const char *vendor; + const char *product; + dc_family_t type; + unsigned int model; +}; + +struct vendor *dc_list; + +static void render_dc_vendor(GtkCellLayout *cell, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + const char *vendor; + + gtk_tree_model_get(model, iter, 0, &vendor, -1); + g_object_set(renderer, "text", vendor, NULL); +} + +static void render_dc_product(GtkCellLayout *cell, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + dc_descriptor_t *descriptor = NULL; + const char *product; + + gtk_tree_model_get(model, iter, 0, &descriptor, -1); + product = dc_descriptor_get_product(descriptor); + g_object_set(renderer, "text", product, NULL); +} + +int is_default_dive_computer(const char *vendor, const char *product) +{ + return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) && + default_dive_computer_product && !strcmp(product, default_dive_computer_product); +} + +int is_default_dive_computer_device(const char *name) +{ + return default_dive_computer_device && !strcmp(name, default_dive_computer_device); +} + +static void set_default_dive_computer(const char *vendor, const char *product) +{ + if (!vendor || !*vendor) + return; + if (!product || !*product) + return; + if (is_default_dive_computer(vendor, product)) + return; + if (default_dive_computer_vendor) + free((void *)default_dive_computer_vendor); + if (default_dive_computer_product) + free((void *)default_dive_computer_product); + default_dive_computer_vendor = strdup(vendor); + default_dive_computer_product = strdup(product); + subsurface_set_conf("dive_computer_vendor", vendor); + subsurface_set_conf("dive_computer_product", product); +} + +static void set_default_dive_computer_device(const char *name) +{ + if (!name || !*name) + return; + if (is_default_dive_computer_device(name)) + return; + if (default_dive_computer_device) + free((void *)default_dive_computer_device); + default_dive_computer_device = strdup(name); + subsurface_set_conf("dive_computer_device", name); +} + +static void dive_computer_selector_changed(GtkWidget *combo, gpointer data) +{ + GtkWidget *import, *button; + + import = gtk_widget_get_ancestor(combo, GTK_TYPE_DIALOG); + button = gtk_dialog_get_widget_for_response(GTK_DIALOG(import), GTK_RESPONSE_ACCEPT); + gtk_widget_set_sensitive(button, TRUE); +} + +static GtkListStore **product_model; +static void dive_computer_vendor_changed(GtkComboBox *vendorcombo, GtkComboBox *productcombo) +{ + int vendor = gtk_combo_box_get_active(vendorcombo); + gtk_combo_box_set_model(productcombo, GTK_TREE_MODEL(product_model[vendor + 1])); + gtk_combo_box_set_active(productcombo, -1); +} + +static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog) +{ + GError *error; + GtkWidget *vbox, *info, *container, *label, *button; + + /* HACK to simply include the Uemis Zurich in the list */ + if (! strcmp(data->vendor, "Uemis") && ! strcmp(data->product, "Zurich")) { + error = uemis_download(data->devname, &data->progress, data->dialog, data->force_download); + } else { + error = do_import(data); + } + if (!error) + return NULL; + + button = gtk_dialog_get_widget_for_response(dialog, GTK_RESPONSE_ACCEPT); + gtk_button_set_use_stock(GTK_BUTTON(button), 0); + gtk_button_set_label(GTK_BUTTON(button), _("Retry")); + + vbox = gtk_dialog_get_content_area(dialog); + + info = gtk_info_bar_new(); + container = gtk_info_bar_get_content_area(GTK_INFO_BAR(info)); + label = gtk_label_new(error->message); + gtk_container_add(GTK_CONTAINER(container), label); + gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0); + return info; +} + + +/* create a list of lists and keep the elements sorted */ +static void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor) +{ + struct vendor *dcl = dc_list; + struct vendor **dclp = &dc_list; + struct product *pl, **plp; + + if (!vendor || !product) + return; + while (dcl && strcmp(dcl->vendor, vendor) < 0) { + dclp = &dcl->next; + dcl = dcl->next; + } + if (!dcl || strcmp(dcl->vendor, vendor)) { + dcl = calloc(sizeof(struct vendor), 1); + dcl->next = *dclp; + *dclp = dcl; + dcl->vendor = strdup(vendor); + } + /* we now have a pointer to the requested vendor */ + plp = &dcl->productlist; + pl = *plp; + while (pl && strcmp(pl->product, product) < 0) { + plp = &pl->next; + pl = pl->next; + } + if (!pl || strcmp(pl->product, product)) { + pl = calloc(sizeof(struct product), 1); + pl->next = *plp; + *plp = pl; + pl->product = strdup(product); + } + /* one would assume that the vendor / product combinations are unique, + * but that is not the case. At the time of this writing, there are two + * flavors of the Oceanic OC1 - but looking at the code in libdivecomputer + * they are handled exactly the same, so we ignore this issue for now + * + if (pl->descriptor && memcmp(pl->descriptor, descriptor, sizeof(struct mydescriptor))) + printf("duplicate entry with different descriptor for %s - %s\n", vendor, product); + else + */ + pl->descriptor = descriptor; +} + +/* fill the vendors and create and fill the respective product stores; return the longest product name + * and also the indices of the default vendor / product */ +static int fill_computer_list(GtkListStore *vendorstore, GtkListStore ***productstore, int *vendor_index, int *product_index) +{ + int i, j, numvendor, width = 10; + GtkTreeIter iter; + dc_iterator_t *iterator = NULL; + dc_descriptor_t *descriptor = NULL; + struct mydescriptor *mydescriptor; + struct vendor *dcl; + struct product *pl; + GtkListStore **pstores; + + dc_descriptor_iterator(&iterator); + while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) { + const char *vendor = dc_descriptor_get_vendor(descriptor); + const char *product = dc_descriptor_get_product(descriptor); + add_dc(vendor, product, descriptor); + if (product && strlen(product) > width) + width = strlen(product); + } + dc_iterator_free(iterator); + /* and add the Uemis Zurich which we are handling internally + THIS IS A HACK as we magically have a data structure here that + happens to match a data structure that is internal to libdivecomputer; + this WILL BREAK if libdivecomputer changes the dc_descriptor struct... + eventually the UEMIS code needs to move into libdivecomputer, I guess */ + mydescriptor = malloc(sizeof(struct mydescriptor)); + mydescriptor->vendor = "Uemis"; + mydescriptor->product = "Zurich"; + mydescriptor->type = DC_FAMILY_NULL; + mydescriptor->model = 0; + add_dc("Uemis", "Zurich", (dc_descriptor_t *)mydescriptor); + dcl = dc_list; + numvendor = 0; + while (dcl) { + numvendor++; + dcl = dcl->next; + } + /* we need an extra vendor for the empty one */ + numvendor += 1; + dcl = dc_list; + i = 0; + *vendor_index = *product_index = -1; + if (*productstore) + free(*productstore); + pstores = *productstore = malloc(numvendor * sizeof(GtkListStore *)); + while (dcl) { + gtk_list_store_append(vendorstore, &iter); + gtk_list_store_set(vendorstore, &iter, + 0, dcl->vendor, + -1); + pl = dcl->productlist; + pstores[i + 1] = gtk_list_store_new(1, G_TYPE_POINTER); + j = 0; + while (pl) { + gtk_list_store_append(pstores[i + 1], &iter); + gtk_list_store_set(pstores[i + 1], &iter, + 0, pl->descriptor, + -1); + if (is_default_dive_computer(dcl->vendor, pl->product)) { + *vendor_index = i; + *product_index = j; + } + j++; + pl = pl->next; + } + i++; + dcl = dcl->next; + } + /* now add the empty product list in case no vendor is selected */ + pstores[0] = gtk_list_store_new(1, G_TYPE_POINTER); + + return width; +} + +static GtkComboBox *dive_computer_selector(GtkWidget *vbox) +{ + GtkWidget *hbox, *vendor_combo_box, *product_combo_box, *frame; + GtkListStore *vendor_model; + GtkCellRenderer *vendor_renderer, *product_renderer; + int vendor_default_index, product_default_index, width; + + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); + + vendor_model = gtk_list_store_new(1, G_TYPE_POINTER); + + width = fill_computer_list(vendor_model, &product_model, &vendor_default_index, &product_default_index); + + frame = gtk_frame_new(_("Dive computer vendor and product")); + gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3); + + hbox = gtk_hbox_new(FALSE, 6); + gtk_container_add(GTK_CONTAINER(frame), hbox); + + vendor_combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(vendor_model)); + product_combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(product_model[vendor_default_index + 1])); + + g_signal_connect(G_OBJECT(vendor_combo_box), "changed", G_CALLBACK(dive_computer_vendor_changed), product_combo_box); + g_signal_connect(G_OBJECT(product_combo_box), "changed", G_CALLBACK(dive_computer_selector_changed), NULL); + gtk_box_pack_start(GTK_BOX(hbox), vendor_combo_box, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(hbox), product_combo_box, FALSE, FALSE, 3); + + vendor_renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(vendor_combo_box), vendor_renderer, TRUE); + gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(vendor_combo_box), vendor_renderer, render_dc_vendor, NULL, NULL); + + product_renderer = gtk_cell_renderer_text_new(); + gtk_cell_renderer_set_fixed_size(product_renderer, 10 * width, -1); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(product_combo_box), product_renderer, TRUE); + gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(product_combo_box), product_renderer, render_dc_product, NULL, NULL); + + gtk_combo_box_set_active(GTK_COMBO_BOX(vendor_combo_box), vendor_default_index); + gtk_combo_box_set_active(GTK_COMBO_BOX(product_combo_box), product_default_index); + + return GTK_COMBO_BOX(product_combo_box); +} + +static GtkComboBox *dc_device_selector(GtkWidget *vbox) +{ + GtkWidget *hbox, *combo_box, *frame; + GtkListStore *model; + GtkCellRenderer *renderer; + int default_index; + + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); + + model = gtk_list_store_new(1, G_TYPE_STRING); + default_index = subsurface_fill_device_list(model); + + frame = gtk_frame_new(_("Device or mount point")); + gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3); + + combo_box = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0); + gtk_container_add(GTK_CONTAINER(frame), combo_box); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE); + + if (default_index != -1) + gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), default_index); + else + if (default_dive_computer_device) + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box))), + default_dive_computer_device); + + return GTK_COMBO_BOX(combo_box); +} + +/* this prevents clicking the [x] button, while the import thread is still running */ +static void download_dialog_delete(GtkWidget *w, gpointer data) +{ + /* a no-op */ +} + +void download_dialog(GtkWidget *w, gpointer data) +{ + int result; + char *devname, *ns, *ne; + GtkWidget *dialog, *button, *hbox, *vbox, *label, *info = NULL; + GtkComboBox *computer, *device; + GtkTreeIter iter; + device_data_t devicedata = { + .devname = NULL, + }; + + remember_tree_state(); + dialog = gtk_dialog_new_with_buttons(_("Download From Dive Computer"), + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + g_signal_connect(dialog, "delete-event", G_CALLBACK(download_dialog_delete), NULL); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + label = gtk_label_new(_(" Please select dive computer and device. ")); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); + computer = dive_computer_selector(vbox); + device = dc_device_selector(vbox); + hbox = gtk_hbox_new(FALSE, 6); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3); + devicedata.progress.bar = gtk_progress_bar_new(); + gtk_container_add(GTK_CONTAINER(hbox), devicedata.progress.bar); + + force_download = FALSE; + button = gtk_check_button_new_with_label(_("Force download of all dives")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(force_toggle), NULL); + + prefer_downloaded = FALSE; + button = gtk_check_button_new_with_label(_("Always prefer downloaded dive")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 6); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(prefer_dl_toggle), NULL); + + button = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + if (!gtk_combo_box_get_active_iter(computer, &iter)) + gtk_widget_set_sensitive(button, FALSE); + +repeat: + gtk_widget_show_all(dialog); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + dc_descriptor_t *descriptor; + GtkTreeModel *model; + + case GTK_RESPONSE_ACCEPT: + /* once the accept event is triggered the dialog becomes non-modal. + * lets re-set that */ + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + if (info) + gtk_widget_destroy(info); + const char *vendor, *product; + + if (!gtk_combo_box_get_active_iter(computer, &iter)) + break; + + model = gtk_combo_box_get_model(computer); + gtk_tree_model_get(model, &iter, + 0, &descriptor, + -1); + + vendor = dc_descriptor_get_vendor(descriptor); + product = dc_descriptor_get_product(descriptor); + + devicedata.descriptor = descriptor; + devicedata.vendor = vendor; + devicedata.product = product; + set_default_dive_computer(vendor, product); + + /* get the device name from the combo box entry and set as default */ + devname = strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(device))))); + set_default_dive_computer_device(devname); + /* clear leading and trailing white space from the device name and also + * everything after (and including) the first '(' char. */ + ns = devname; + while (*ns == ' ' || *ns == '\t') + ns++; + ne = ns; + while (*ne && *ne != '(') + ne++; + *ne = '\0'; + if (ne > ns) + 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)); + free((void *)devname); + if (info) + goto repeat; + report_dives(TRUE, prefer_downloaded); + break; + default: + /* it's possible that some dives were downloaded */ + report_dives(TRUE, prefer_downloaded); + break; + } + gtk_widget_destroy(dialog); + restore_tree_state(); +} + +void update_progressbar(progressbar_t *progress, double value) +{ + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->bar), value); +} + +void update_progressbar_text(progressbar_t *progress, const char *text) +{ + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text); +} |