+static GtkListStore *cylinder_model, *weightsystem_model;
+
+enum {
+ CYL_DESC,
+ CYL_SIZE,
+ CYL_WORKP,
+ CYL_STARTP,
+ CYL_ENDP,
+ CYL_O2,
+ CYL_HE,
+ CYL_COLUMNS
+};
+
+enum {
+ WS_DESC,
+ WS_WEIGHT,
+ WS_COLUMNS
+};
+
+struct equipment_list {
+ int max_index;
+ GtkListStore *model;
+ GtkWidget *edit, *add, *del;
+};
+
+static struct equipment_list cylinder_list, weightsystem_list;
+
+
+struct cylinder_widget {
+ int index, changed;
+ const char *name;
+ GtkWidget *hbox;
+ GtkComboBox *description;
+ GtkSpinButton *size, *pressure;
+ GtkWidget *start, *end, *pressure_button;
+ GtkWidget *o2, *he, *gasmix_button;
+};
+
+struct ws_widget {
+ int index, changed;
+ const char *name;
+ GtkWidget *hbox;
+ GtkComboBox *description;
+ GtkSpinButton *weight;
+};
+
+/* we want bar - so let's not use our unit functions */
+static int convert_pressure(int mbar, double *p)
+{
+ int decimals = 1;
+ double pressure;
+
+ if (output_units.pressure == PSI) {
+ pressure = mbar_to_PSI(mbar);
+ decimals = 0;
+ } else {
+ pressure = mbar / 1000.0;
+ }
+
+ *p = pressure;
+ return decimals;
+}
+
+static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
+{
+ int decimals = 1;
+ double volume, pressure;
+
+ volume = ml / 1000.0;
+ if (mbar) {
+ if (output_units.volume == CUFT) {
+ volume = ml_to_cuft(ml);
+ volume *= bar_to_atm(mbar / 1000.0);
+ }
+
+ if (output_units.pressure == PSI) {
+ pressure = mbar_to_PSI(mbar);
+ decimals = 0;
+ } else
+ pressure = mbar / 1000.0;
+ }
+ *v = volume;
+ *p = pressure;
+ return decimals;
+}
+
+static int convert_weight(int grams, double *m)
+{
+ int decimals = 1; /* not sure - do people do less than whole lbs/kg ? */
+ double weight;
+
+ if (output_units.weight == LBS)
+ weight = grams_to_lbs(grams);
+ else
+ weight = grams / 1000.0;
+ *m = weight;
+ return decimals;
+}
+
+static void set_cylinder_type_spinbuttons(struct cylinder_widget *cylinder, int ml, int mbar)
+{
+ double volume, pressure;
+
+ convert_volume_pressure(ml, mbar, &volume, &pressure);
+ gtk_spin_button_set_value(cylinder->size, volume);
+ gtk_spin_button_set_value(cylinder->pressure, pressure);
+}
+
+static void set_cylinder_pressure_spinbuttons(struct cylinder_widget *cylinder, cylinder_t *cyl)
+{
+ int set;
+ unsigned int start, end;
+ double pressure;
+
+ start = cyl->start.mbar;
+ end = cyl->end.mbar;
+ set = start || end;
+ if (!set) {
+ start = cyl->sample_start.mbar;
+ end = cyl->sample_end.mbar;
+ }
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cylinder->pressure_button), set);
+ gtk_widget_set_sensitive(cylinder->start, set);
+ gtk_widget_set_sensitive(cylinder->end, set);
+
+ convert_pressure(start, &pressure);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->start), pressure);
+ convert_pressure(end, &pressure);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->end), pressure);
+}
+
+static void set_weight_weight_spinbutton(struct ws_widget *ws_widget, int grams)
+{
+ double weight;
+
+ convert_weight(grams, &weight);
+ gtk_spin_button_set_value(ws_widget->weight, weight);
+}
+
+/*
+ * The gtk_tree_model_foreach() interface is bad. It could have
+ * returned whether the callback ever returned true
+ */
+static GtkTreeIter *found_match = NULL;
+static GtkTreeIter match_iter;
+
+static gboolean match_desc(GtkTreeModel *model, GtkTreePath *path,
+ GtkTreeIter *iter, gpointer data)
+{
+ int match;
+ gchar *name;
+ const char *desc = data;
+
+ gtk_tree_model_get(model, iter, 0, &name, -1);
+ match = !strcmp(desc, name);
+ g_free(name);
+ if (match) {
+ match_iter = *iter;
+ found_match = &match_iter;
+ }
+ return match;
+}
+
+static int get_active_item(GtkComboBox *combo_box, GtkTreeIter *iter, GtkListStore *model)
+{
+ char *desc;
+
+ if (gtk_combo_box_get_active_iter(combo_box, iter))
+ return TRUE;
+
+ desc = gtk_combo_box_get_active_text(combo_box);
+
+ found_match = NULL;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model), match_desc, (void *)desc);
+
+ g_free(desc);
+ if (!found_match)
+ return FALSE;
+
+ *iter = *found_match;
+ gtk_combo_box_set_active_iter(combo_box, iter);
+ return TRUE;
+}
+
+static void cylinder_cb(GtkComboBox *combo_box, gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
+ int ml, mbar;
+ struct cylinder_widget *cylinder = data;
+ cylinder_t *cyl = current_dive->cylinder + cylinder->index;
+
+ /* Did the user set it to some non-standard value? */
+ if (!get_active_item(combo_box, &iter, cylinder_model)) {
+ cylinder->changed = 1;
+ return;
+ }
+
+ /*
+ * We get "change" signal callbacks just because we set
+ * the description by hand. Whatever. So ignore them if
+ * they are no-ops.
+ */
+ if (!cylinder->changed && cyl->type.description) {
+ int same;
+ char *desc = gtk_combo_box_get_active_text(combo_box);
+
+ same = !strcmp(desc, cyl->type.description);
+ g_free(desc);
+ if (same)
+ return;
+ }
+ cylinder->changed = 1;
+
+ gtk_tree_model_get(model, &iter,
+ CYL_SIZE, &ml,
+ CYL_WORKP, &mbar,
+ -1);
+
+ set_cylinder_type_spinbuttons(cylinder, ml, mbar);
+}
+
+static void weight_cb(GtkComboBox *combo_box, gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
+ int weight;
+ struct ws_widget *ws_widget = data;
+ weightsystem_t *ws = current_dive->weightsystem + ws_widget->index;
+
+ /* Did the user set it to some non-standard value? */
+ if (!get_active_item(combo_box, &iter, weightsystem_model)) {
+ ws_widget->changed = 1;
+ return;
+ }
+
+ /*
+ * We get "change" signal callbacks just because we set
+ * the description by hand. Whatever. So ignore them if
+ * they are no-ops.
+ */
+ if (!ws_widget->changed && ws->description) {
+ int same;
+ char *desc = gtk_combo_box_get_active_text(combo_box);
+
+ same = !strcmp(desc, ws->description);
+ g_free(desc);
+ if (same)
+ return;
+ }
+ ws_widget->changed = 1;
+
+ gtk_tree_model_get(model, &iter,
+ WS_WEIGHT, &weight,
+ -1);
+
+ set_weight_weight_spinbutton(ws_widget, weight);
+}
+
+static GtkTreeIter *add_cylinder_type(const char *desc, int ml, int mbar, GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+
+ /* Don't even bother adding stuff without a size */
+ if (!ml)
+ return NULL;
+
+ found_match = NULL;
+ model = GTK_TREE_MODEL(cylinder_model);
+ gtk_tree_model_foreach(model, match_desc, (void *)desc);
+
+ if (!found_match) {
+ GtkListStore *store = GTK_LIST_STORE(model);
+
+ gtk_list_store_append(store, iter);
+ gtk_list_store_set(store, iter,
+ 0, desc,
+ 1, ml,
+ 2, mbar,
+ -1);
+ return iter;
+ }
+ return found_match;
+}
+
+static GtkTreeIter *add_weightsystem_type(const char *desc, int weight, GtkTreeIter *iter)
+{
+ GtkTreeModel *model;
+
+ found_match = NULL;
+ model = GTK_TREE_MODEL(weightsystem_model);
+ gtk_tree_model_foreach(model, match_desc, (void *)desc);
+
+ if (!found_match) {
+ GtkListStore *store = GTK_LIST_STORE(model);
+
+ gtk_list_store_append(store, iter);
+ gtk_list_store_set(store, iter,
+ 0, desc,
+ 1, weight,
+ -1);
+ return iter;
+ }
+ return found_match;
+}
+
+/*
+ * When adding a dive, we'll add all the pre-existing cylinder
+ * information from that dive to our cylinder model.
+ */
+void add_cylinder_description(cylinder_type_t *type)
+{
+ GtkTreeIter iter;
+ const char *desc;
+ unsigned int size, workp;
+
+ desc = type->description;
+ if (!desc)
+ return;
+ size = type->size.mliter;
+ workp = type->workingpressure.mbar;
+ add_cylinder_type(desc, size, workp, &iter);
+}
+
+static void add_cylinder(struct cylinder_widget *cylinder, const char *desc, int ml, int mbar)
+{
+ GtkTreeIter iter, *match;
+
+ cylinder->name = desc;
+ match = add_cylinder_type(desc, ml, mbar, &iter);
+ if (match)
+ gtk_combo_box_set_active_iter(cylinder->description, match);
+}
+
+void add_weightsystem_description(weightsystem_t *weightsystem)
+{
+ GtkTreeIter iter;
+ const char *desc;
+ unsigned int weight;
+
+ desc = weightsystem->description;
+ if (!desc)
+ return;
+ weight = weightsystem->weight.grams;
+ add_weightsystem_type(desc, weight, &iter);
+}
+
+static void add_weightsystem(struct ws_widget *ws_widget, const char *desc, int weight)
+{
+ GtkTreeIter iter, *match;
+
+ ws_widget->name = desc;
+ match = add_weightsystem_type(desc, weight, &iter);
+ if (match)
+ gtk_combo_box_set_active_iter(ws_widget->description, match);
+}
+
+static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
+{
+ const char *desc;
+ int ml, mbar;
+ int gasmix;
+ double o2, he;
+
+ /* Don't show uninitialized cylinder widgets */
+ if (!cylinder->description)
+ return;
+
+ desc = cyl->type.description;
+ if (!desc)
+ desc = "";
+ ml = cyl->type.size.mliter;
+ mbar = cyl->type.workingpressure.mbar;
+ add_cylinder(cylinder, desc, ml, mbar);
+
+ set_cylinder_type_spinbuttons(cylinder,
+ cyl->type.size.mliter, cyl->type.workingpressure.mbar);
+ set_cylinder_pressure_spinbuttons(cylinder, cyl);
+
+ gasmix = cyl->gasmix.o2.permille || cyl->gasmix.he.permille;
+ gtk_widget_set_sensitive(cylinder->o2, gasmix);
+ gtk_widget_set_sensitive(cylinder->he, gasmix);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cylinder->gasmix_button), gasmix);
+
+ o2 = cyl->gasmix.o2.permille / 10.0;
+ he = cyl->gasmix.he.permille / 10.0;
+ if (!o2)
+ o2 = 21.0;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->o2), o2);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->he), he);
+}
+
+static void show_weightsystem(weightsystem_t *ws, struct ws_widget *weightsystem_widget)
+{
+ const char *desc;
+ int grams;
+
+ /* Don't show uninitialized widgets */
+ if (!weightsystem_widget->description)
+ return;
+
+ desc = ws->description;
+ if (!desc)
+ desc = "";
+ grams = ws->weight.grams;
+ add_weightsystem(weightsystem_widget, desc, grams);
+
+ set_weight_weight_spinbutton(weightsystem_widget, ws->weight.grams);
+}
+
+int cylinder_none(void *_data)
+{
+ cylinder_t *cyl = _data;
+ return !cyl->type.size.mliter &&
+ !cyl->type.workingpressure.mbar &&
+ !cyl->type.description &&
+ !cyl->gasmix.o2.permille &&
+ !cyl->gasmix.he.permille &&
+ !cyl->sample_start.mbar &&
+ !cyl->sample_end.mbar &&
+ !cyl->start.mbar &&
+ !cyl->end.mbar;
+}
+
+int weightsystem_none(void *_data)
+{
+ weightsystem_t *ws = _data;
+ return !ws->weight.grams && !ws->description;
+}
+
+static void set_one_cylinder(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
+{
+ cylinder_t *cyl = _data;
+ unsigned int start, end;
+
+ start = cyl->start.mbar ? : cyl->sample_start.mbar;
+ end = cyl->end.mbar ? : cyl->sample_end.mbar;
+ gtk_list_store_set(model, iter,
+ CYL_DESC, cyl->type.description ? : "",
+ CYL_SIZE, cyl->type.size.mliter,
+ CYL_WORKP, cyl->type.workingpressure.mbar,
+ CYL_STARTP, start,
+ CYL_ENDP, end,
+ CYL_O2, cyl->gasmix.o2.permille,
+ CYL_HE, cyl->gasmix.he.permille,
+ -1);
+}
+
+static void set_one_weightsystem(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
+{
+ weightsystem_t *ws = _data;
+
+ gtk_list_store_set(model, iter,
+ WS_DESC, ws->description ? : "unspecified",
+ WS_WEIGHT, ws->weight.grams,
+ -1);
+}
+
+static void *cyl_ptr(struct dive *dive, int idx)
+{
+ if (idx < 0 || idx >= MAX_CYLINDERS)
+ return NULL;
+ return &dive->cylinder[idx];
+}
+
+static void *ws_ptr(struct dive *dive, int idx)
+{
+ if (idx < 0 || idx >= MAX_WEIGHTSYSTEMS)
+ return NULL;
+ return &dive->weightsystem[idx];
+}
+
+static void show_equipment(struct dive *dive, int max,
+ struct equipment_list *equipment_list,
+ void*(*ptr_function)(struct dive*, int),
+ int(*none_function)(void *),
+ void(*set_one_function)(int, void*, GtkListStore*, GtkTreeIter *))
+{
+ int i, used;
+ void *data;
+ GtkTreeIter iter;
+ GtkListStore *model = equipment_list->model;
+
+ gtk_list_store_clear(model);
+ used = max;
+ do {
+ data = ptr_function(dive, used-1);
+ if (!none_function(data))
+ break;
+ } while (--used);
+
+ equipment_list->max_index = used;
+
+ gtk_widget_set_sensitive(equipment_list->edit, 0);
+ gtk_widget_set_sensitive(equipment_list->del, 0);
+ gtk_widget_set_sensitive(equipment_list->add, used < max);
+
+ for (i = 0; i < used; i++) {
+ data = ptr_function(dive, i);
+ gtk_list_store_append(model, &iter);
+ set_one_function(i, data, model, &iter);
+ }
+}
+