[PATCH] gtk-gui.c: Move the download dialog related code to a new file

Lubomir I. Ivanov neolit123 at gmail.com
Mon Jan 14 07:14:23 PST 2013


From: "Lubomir I. Ivanov" <neolit123 at gmail.com>

A new file download-dialog.c now contains all code related
to the download dialog, which was previously defined in gtk-gui.c.

Also, a new file callbacks-gtk.h now has two macros
OPTIONCALLBACK, UNITCALLBACK shared only between
download-dialog.c and gtk-gui.c.

Signed-off-by: Lubomir I. Ivanov <neolit123 at gmail.com>
---
 Makefile          |   7 +-
 callbacks-gtk.h   |  17 ++
 display-gtk.h     |   9 +-
 download-dialog.c | 474 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk-gui.c         | 483 +-----------------------------------------------------
 5 files changed, 505 insertions(+), 485 deletions(-)
 create mode 100644 callbacks-gtk.h
 create mode 100644 download-dialog.c

diff --git a/Makefile b/Makefile
index 7c1c89d..aade639 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 0000000..017a8c0
--- /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 60e0c40..e083a3e 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 0000000..73ed4ba
--- /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 4dcffbd..4dbd367 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)
-- 
1.7.11.msysgit.0



More information about the subsurface mailing list