+struct cylinder_widget {
+ int index, changed;
+ const char *name;
+ GtkWidget *hbox;
+ GtkComboBox *description;
+ GtkSpinButton *size, *pressure;
+ GtkWidget *o2, *gasmix_button;
+};
+
+static struct cylinder_widget gtk_cylinder[MAX_CYLINDERS];
+
+static void set_cylinder_spinbuttons(struct cylinder_widget *cylinder, int ml, int mbar)
+{
+ double volume, pressure;
+
+ volume = ml / 1000.0;
+ pressure = mbar / 1000.0;
+ if (mbar) {
+ if (output_units.volume == CUFT) {
+ volume /= 28.3168466; /* Liters to cuft */
+ volume *= pressure / 1.01325;
+ }
+ if (output_units.pressure == PSI) {
+ pressure *= 14.5037738; /* Bar to PSI */
+ }
+ }
+
+ gtk_spin_button_set_value(cylinder->size, volume);
+ gtk_spin_button_set_value(cylinder->pressure, pressure);
+}
+
+static void cylinder_cb(GtkComboBox *combo_box, gpointer data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
+ GValue value1 = {0, }, value2 = {0,};
+ struct cylinder_widget *cylinder = data;
+ cylinder_t *cyl = current_dive->cylinder + cylinder->index;
+
+ /* Did the user set it to some non-standard value? */
+ if (!gtk_combo_box_get_active_iter(combo_box, &iter)) {
+ 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_value(model, &iter, 1, &value1);
+ gtk_tree_model_get_value(model, &iter, 2, &value2);
+
+ set_cylinder_spinbuttons(cylinder, g_value_get_int(&value1), g_value_get_int(&value2));
+}
+
+/*
+ * The gtk_tree_model_foreach() interface is bad. It could have
+ * returned whether the callback ever returned true
+ */
+static int found_match = 0;
+
+static gboolean match_cylinder(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ const char *name;
+ struct cylinder_widget *cylinder = data;
+ GValue value = {0, };
+
+ gtk_tree_model_get_value(model, iter, 0, &value);
+ name = g_value_get_string(&value);
+ if (strcmp(cylinder->name, name))
+ return FALSE;
+ gtk_combo_box_set_active_iter(cylinder->description, iter);
+ found_match = 1;
+ return TRUE;
+}
+
+static void add_cylinder(struct cylinder_widget *cylinder, const char *desc, int ml, int mbar)
+{
+ GtkTreeModel *model;
+
+ found_match = 0;
+ model = gtk_combo_box_get_model(cylinder->description);
+ cylinder->name = desc;
+ gtk_tree_model_foreach(model, match_cylinder, cylinder);
+
+ if (!found_match) {
+ GtkListStore *store = GTK_LIST_STORE(model);
+ GtkTreeIter iter;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, desc,
+ 1, ml,
+ 2, mbar,
+ -1);
+ gtk_combo_box_set_active_iter(cylinder->description, &iter);
+ }
+}
+
+static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
+{
+ const char *desc;
+ int ml, mbar;
+ double o2;
+
+ /* 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_spinbuttons(cylinder, cyl->type.size.mliter, cyl->type.workingpressure.mbar);
+ o2 = cyl->gasmix.o2.permille / 10.0;
+ gtk_widget_set_sensitive(cylinder->o2, !!o2);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cylinder->gasmix_button), !!o2);
+ if (!o2)
+ o2 = 21.0;
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->o2), o2);
+}
+