summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--callbacks-gtk.h17
-rw-r--r--display-gtk.h9
-rw-r--r--download-dialog.c474
-rw-r--r--gtk-gui.c483
5 files changed, 505 insertions, 485 deletions
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 <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);
+}
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)