diff --git a/Makefile b/Makefile index 7c1c89d76..aade639f2 100644 --- a/Makefile +++ b/Makefile @@ -132,7 +132,7 @@ MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/subsurface.m OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \ parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \ - gtk-gui.o statistics.o file.o cochran.o device.o $(OSSUPPORT).o $(RESFILE) + gtk-gui.o statistics.o file.o cochran.o device.o download-dialog.o $(OSSUPPORT).o $(RESFILE) $(NAME): $(OBJS) $(MSGOBJS) $(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS) @@ -250,12 +250,15 @@ deco.o: deco.c dive.h planner.o: planner.c dive.h divelist.h display-gtk.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c planner.c +download-dialog.o: download-dialog.c dive.h divelist.h display-gtk.h callbacks-gtk.h + $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) -c download-dialog.c + libdivecomputer.o: libdivecomputer.c dive.h display.h display-gtk.h libdivecomputer.h device.h $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \ $(LIBDIVECOMPUTERCFLAGS) \ -c libdivecomputer.c -gtk-gui.o: gtk-gui.c dive.h display.h divelist.h display-gtk.h libdivecomputer.h device.h Makefile +gtk-gui.o: gtk-gui.c dive.h display.h divelist.h display-gtk.h libdivecomputer.h device.h callbacks-gtk.h Makefile $(CC) $(CFLAGS) $(GTK2CFLAGS) $(GLIB2CFLAGS) $(GCONF2CFLAGS) $(XML2CFLAGS) \ $(LIBDIVECOMPUTERCFLAGS) \ -DVERSION_STRING='"v$(VERSION)"' \ diff --git a/callbacks-gtk.h b/callbacks-gtk.h new file mode 100644 index 000000000..017a8c0ae --- /dev/null +++ b/callbacks-gtk.h @@ -0,0 +1,17 @@ +#define UNITCALLBACK(name, type, value) \ +static void name(GtkWidget *w, gpointer data) \ +{ \ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ + prefs.units.type = value; \ + update_screen(); \ +} + +#define OPTIONCALLBACK(name, option) \ +static void name(GtkWidget *w, gpointer data) \ +{ \ + GtkWidget **entry = data; \ + option = GTK_TOGGLE_BUTTON(w)->active; \ + update_screen(); \ + if (entry) \ + gtk_widget_set_sensitive(*entry, option);\ +} diff --git a/display-gtk.h b/display-gtk.h index 60e0c40f8..e083a3e96 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -32,19 +32,24 @@ extern void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar, extern void quit(GtkWidget *w, gpointer data); extern gboolean on_delete(GtkWidget* w, gpointer data); -extern int is_default_dive_computer_device(const char *name); - extern const char *divelist_font; extern void set_divelist_font(const char *); extern void import_files(GtkWidget *, gpointer); +extern void update_screen(void); extern void download_dialog(GtkWidget *, gpointer); +extern int is_default_dive_computer_device(const char *); +extern int is_default_dive_computer(const char *, const char *); extern void add_dive_cb(GtkWidget *, gpointer); extern void report_error(GError* error); extern int process_ui_events(void); extern void update_progressbar(progressbar_t *progress, double value); extern void update_progressbar_text(progressbar_t *progress, const char *text); +extern const char *default_dive_computer_vendor; +extern const char *default_dive_computer_product; +extern const char *default_dive_computer_device; + // info.c enum { MATCH_EXACT, 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 +#include +#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); +} diff --git a/gtk-gui.c b/gtk-gui.c index 4dcffbddc..4dbd36799 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -18,6 +18,7 @@ #include "divelist.h" #include "display.h" #include "display-gtk.h" +#include "callbacks-gtk.h" #include "uemis.h" #include "device.h" @@ -38,11 +39,6 @@ const char *default_filename; char *nicknamestring; static GtkWidget *dive_profile; -static const char *default_dive_computer_vendor; -static const char *default_dive_computer_product; -static const char *default_dive_computer_device; -static gboolean force_download; -static gboolean prefer_downloaded; GtkActionGroup *action_group; @@ -51,47 +47,6 @@ struct units *get_units() return &prefs.units; } -static 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); -} - void repaint_dive(void) { update_dive(current_dive); @@ -457,21 +412,13 @@ static void create_radio(GtkWidget *vbox, const char *w_name, ...) va_end(args); } -static void update_screen() +void update_screen() { update_dive_list_units(); repaint_dive(); update_dive_list_col_visibility(); } -#define UNITCALLBACK(name, type, value) \ -static void name(GtkWidget *w, gpointer data) \ -{ \ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ - prefs.units.type = value; \ - update_screen(); \ -} - UNITCALLBACK(set_meter, length, METERS) UNITCALLBACK(set_feet, length, FEET) UNITCALLBACK(set_bar, pressure, BAR) @@ -483,16 +430,6 @@ UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT) UNITCALLBACK(set_kg, weight, KG) UNITCALLBACK(set_lbs, weight, LBS) -#define OPTIONCALLBACK(name, option) \ -static void name(GtkWidget *w, gpointer data) \ -{ \ - GtkWidget **entry = data; \ - option = GTK_TOGGLE_BUTTON(w)->active; \ - update_screen(); \ - if (entry) \ - gtk_widget_set_sensitive(*entry, option);\ -} - OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu) OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns) OPTIONCALLBACK(sac_toggle, prefs.visible_cols.sac) @@ -507,8 +444,6 @@ OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe) OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling) OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling) OPTIONCALLBACK(calc_ceiling_3m_toggle, prefs.calc_ceiling_3m_incr) -OPTIONCALLBACK(force_toggle, force_download) -OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded) static gboolean gflow_edit(GtkWidget *w, GdkEvent *event, gpointer _data) { @@ -1718,265 +1653,6 @@ int process_ui_events(void) return ret; } -struct vendor { - const char *vendor; - struct product *productlist; - struct vendor *next; -}; - -struct product { - const char *product; - dc_descriptor_t *descriptor; - struct product *next; -}; - -struct vendor *dc_list; - -struct mydescriptor { - const char *vendor; - const char *product; - dc_family_t type; - unsigned int model; -}; - -/* 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; -} - -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); -} - -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); -} - -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 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); -} - static void do_import_file(gpointer data, gpointer user_data) { GError *error = NULL; @@ -2041,161 +1717,6 @@ void import_files(GtkWidget *w, gpointer data) restore_tree_state(); } -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; -} - -/* 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); -} - void set_filename(const char *filename, gboolean force) { if (!force && existing_filename)