+ airuse = calculate_airuse(dive);
+ if (!airuse)
+ return 0;
+ if (!dive->duration.seconds)
+ return 0;
+
+ /* find and eliminate long surface intervals */
+ duration = dive->duration.seconds;
+ for (i = 0; i < dive->samples; i++) {
+ if (dive->sample[i].depth.mm < 100) { /* less than 10cm */
+ int end = i + 1;
+ while (end < dive->samples && dive->sample[end].depth.mm < 100)
+ end++;
+ /* we only want the actual surface time during a dive */
+ if (end < dive->samples) {
+ end--;
+ duration -= dive->sample[end].time.seconds -
+ dive->sample[i].time.seconds;
+ i = end + 1;
+ }
+ }
+ }
+ /* Mean pressure in atm: 1 atm per 10m */
+ pressure = 1 + (dive->meandepth.mm / 10000.0);
+ sac = airuse / pressure * 60 / duration;
+
+ /* milliliters per minute.. */
+ return sac * 1000;
+}
+
+void update_cylinder_related_info(struct dive *dive)
+{
+ if (dive != NULL) {
+ dive->sac = calculate_sac(dive);
+ dive->otu = calculate_otu(dive);
+ }
+}
+
+static void get_string(char **str, const char *s)
+{
+ int len;
+ char *n;
+
+ if (!s)
+ s = "";
+ len = strlen(s);
+ if (len > 60)
+ len = 60;
+ n = malloc(len+1);
+ memcpy(n, s, len);
+ n[len] = 0;
+ *str = n;
+}
+
+static void get_location(struct dive *dive, char **str)
+{
+ get_string(str, dive->location);
+}
+
+static void get_cylinder(struct dive *dive, char **str)
+{
+ get_string(str, dive->cylinder[0].type.description);
+}
+
+static void get_suit(struct dive *dive, char **str)
+{
+ get_string(str, dive->suit);
+}
+
+/*
+ * Set up anything that could have changed due to editing
+ * of dive information; we need to do this for both models,
+ * so we simply call set_one_dive again with the non-current model
+ */
+/* forward declaration for recursion */
+static gboolean set_one_dive(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+
+static void fill_one_dive(struct dive *dive,
+ GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ char *location, *cylinder, *suit;
+ GtkTreeStore *othermodel;
+
+ get_cylinder(dive, &cylinder);
+ get_location(dive, &location);
+ get_suit(dive, &suit);
+
+ gtk_tree_store_set(GTK_TREE_STORE(model), iter,
+ DIVE_NR, dive->number,
+ DIVE_LOCATION, location,
+ DIVE_CYLINDER, cylinder,
+ DIVE_RATING, dive->rating,
+ DIVE_SAC, dive->sac,
+ DIVE_OTU, dive->otu,
+ DIVE_TOTALWEIGHT, total_weight(dive),
+ DIVE_SUIT, suit,
+ -1);
+
+ free(location);
+ free(cylinder);
+ free(suit);
+
+ if (model == GTK_TREE_MODEL(dive_list.treemodel))
+ othermodel = dive_list.listmodel;
+ else
+ othermodel = dive_list.treemodel;
+ if (othermodel != dive_list.model)
+ /* recursive call */
+ gtk_tree_model_foreach(GTK_TREE_MODEL(othermodel), set_one_dive, dive);
+}
+
+static gboolean set_one_dive(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int idx;
+ struct dive *dive;
+
+ /* Get the dive number */
+ gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
+ if (idx < 0)
+ return FALSE;
+ dive = get_dive(idx);
+ if (!dive)
+ return TRUE;
+ if (data && dive != data)
+ return FALSE;
+
+ fill_one_dive(dive, model, iter);
+ return dive == data;
+}
+
+void flush_divelist(struct dive *dive)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
+
+ gtk_tree_model_foreach(model, set_one_dive, dive);
+}
+
+void set_divelist_font(const char *font)
+{
+ PangoFontDescription *font_desc = pango_font_description_from_string(font);
+ gtk_widget_modify_font(dive_list.tree_view, font_desc);
+ pango_font_description_free(font_desc);
+}
+
+void update_dive_list_units(void)
+{
+ const char *unit;
+ GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
+
+ (void) get_depth_units(0, NULL, &unit);
+ gtk_tree_view_column_set_title(dive_list.depth, unit);
+
+ (void) get_temp_units(0, &unit);
+ gtk_tree_view_column_set_title(dive_list.temperature, unit);
+
+ (void) get_weight_units(0, NULL, &unit);
+ gtk_tree_view_column_set_title(dive_list.totalweight, unit);
+
+ gtk_tree_model_foreach(model, set_one_dive, NULL);
+}
+
+void update_dive_list_col_visibility(void)
+{
+ gtk_tree_view_column_set_visible(dive_list.cylinder, visible_cols.cylinder);
+ gtk_tree_view_column_set_visible(dive_list.temperature, visible_cols.temperature);
+ gtk_tree_view_column_set_visible(dive_list.totalweight, visible_cols.totalweight);
+ gtk_tree_view_column_set_visible(dive_list.suit, visible_cols.suit);
+ gtk_tree_view_column_set_visible(dive_list.nitrox, visible_cols.nitrox);
+ gtk_tree_view_column_set_visible(dive_list.sac, visible_cols.sac);
+ gtk_tree_view_column_set_visible(dive_list.otu, visible_cols.otu);
+ return;
+}
+
+/* random heuristic - not diving in three days implies new dive trip */
+#define TRIP_THRESHOLD 3600*24*3
+static int new_group(struct dive *dive, struct dive **last_dive, time_t *tm_date)
+{
+ if (!last_dive)
+ return TRUE;
+ if (*last_dive) {
+ struct dive *ldive = *last_dive;
+ if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) {
+ *last_dive = dive;
+ return FALSE;
+ }
+ }
+ *last_dive = dive;
+ if (tm_date) {
+ struct tm *tm1 = gmtime(&dive->when);
+ tm1->tm_sec = 0;
+ tm1->tm_min = 0;
+ tm1->tm_hour = 0;
+ *tm_date = mktime(tm1);
+ }
+ return TRUE;
+}
+
+static void fill_dive_list(void)
+{
+ int i, group_size;
+ GtkTreeIter iter, parent_iter;
+ GtkTreeStore *liststore, *treestore;
+ struct dive *last_dive = NULL;
+ struct dive *last_trip_dive = NULL;
+ const char *last_location = NULL;
+ time_t dive_date;
+
+ treestore = GTK_TREE_STORE(dive_list.treemodel);
+ liststore = GTK_TREE_STORE(dive_list.listmodel);
+
+ i = dive_table.nr;
+ while (--i >= 0) {