From 1f3813eb3d0824c9eff4cf9b02e3d34c1c6db71c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 8 Aug 2012 09:35:38 -0700 Subject: Allow date based grouping This is the very first rough cut. It switches things over to a tree model so we can have date based summary nodes. It uses a DIVE_INDEX of -1 for summary nodes to easily tell them apart from actual dives. All the data functions are changed so the summary nodes only show the date they cover. The commit also adds a couple of debug functions to be able to easily peek into the model from the debugger. Lots of things left to do. There is no longer a first dive selected when starting subsurface. Sorting by columns other than date is messed up. We almost certainly want month and year summary entries as well. Signed-off-by: Dirk Hohndel --- divelist.c | 262 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 188 insertions(+), 74 deletions(-) diff --git a/divelist.c b/divelist.c index 21f343f7f..8899d7429 100644 --- a/divelist.c +++ b/divelist.c @@ -24,7 +24,7 @@ struct DiveList { GtkWidget *tree_view; GtkWidget *container_widget; - GtkListStore *model; + GtkTreeStore *model; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu; int changed; @@ -52,6 +52,26 @@ enum { DIVELIST_COLUMNS }; +#ifdef DEBUG_MODEL +static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + char *location; + int idx, nr, rating, depth; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_RATING, &rating, DIVE_DEPTH, &depth, DIVE_LOCATION, &location, -1); + printf("entry #%d : nr %d rating %d depth %d location %s \n", idx, nr, rating, depth, location); + free(location); + + return FALSE; +} + +static void dump_model(GtkListStore *store) +{ + gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL); +} +#endif + static GList *selected_dives; static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) @@ -77,6 +97,8 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) path = g_list_nth_data(selected_dives, 0); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value); + /* an index of -1 should mean select all dives from that day + * ===> still needs to be implemented */ selected_dive = g_value_get_int(&value); repaint_dive(); } @@ -109,13 +131,17 @@ static void star_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int nr_stars; + int nr_stars, idx; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_RATING, &nr_stars, -1); - if (nr_stars < 0 || nr_stars > 5) - nr_stars = 0; - snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1); + if (idx == -1) { + *buffer = '\0'; + } else { + if (nr_stars < 0 || nr_stars > 5) + nr_stars = 0; + snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); + } g_object_set(renderer, "text", buffer, NULL); } @@ -125,23 +151,30 @@ static void date_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int val; + int val, idx; struct tm *tm; time_t when; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_DATE, &val, -1); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1); /* 2038 problem */ when = val; tm = gmtime(&when); - snprintf(buffer, sizeof(buffer), - "%s, %s %d, %d %02d:%02d", - weekday(tm->tm_wday), - monthname(tm->tm_mon), - tm->tm_mday, tm->tm_year + 1900, - tm->tm_hour, tm->tm_min); + if (idx == -1) + snprintf(buffer, sizeof(buffer), + "%s, %s %d, %d", + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900); + else + snprintf(buffer, sizeof(buffer), + "%s, %s %d, %d %02d:%02d", + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900, + tm->tm_hour, tm->tm_min); g_object_set(renderer, "text", buffer, NULL); } @@ -151,34 +184,37 @@ static void depth_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int depth, integer, frac, len; + int depth, integer, frac, len, idx; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_DEPTH, &depth, -1); - - switch (output_units.length) { - case METERS: - /* To tenths of meters */ - depth = (depth + 49) / 100; - integer = depth / 10; - frac = depth % 10; - if (integer < 20) + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); + + if (idx == -1) { + *buffer = '\0'; + } else { + switch (output_units.length) { + case METERS: + /* To tenths of meters */ + depth = (depth + 49) / 100; + integer = depth / 10; + frac = depth % 10; + if (integer < 20) + break; + if (frac >= 5) + integer++; + frac = -1; break; - if (frac >= 5) - integer++; - frac = -1; - break; - case FEET: - integer = mm_to_feet(depth) + 0.5; - frac = -1; - break; - default: - return; + case FEET: + integer = mm_to_feet(depth) + 0.5; + frac = -1; + break; + default: + return; + } + len = snprintf(buffer, sizeof(buffer), "%d", integer); + if (frac >= 0) + len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); } - len = snprintf(buffer, sizeof(buffer), "%d", integer); - if (frac >= 0) - len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); - g_object_set(renderer, "text", buffer, NULL); } @@ -188,11 +224,14 @@ static void duration_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - unsigned int sec; + unsigned int sec, idx; char buffer[16]; - gtk_tree_model_get(model, iter, DIVE_DURATION, &sec, -1); - snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1); + if (idx == -1) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); g_object_set(renderer, "text", buffer, NULL); } @@ -203,13 +242,13 @@ static void temperature_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; char buffer[80]; - gtk_tree_model_get(model, iter, DIVE_TEMPERATURE, &value, -1); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1); *buffer = 0; - if (value) { + if (idx != -1 && value) { double deg; switch (output_units.temperature) { case CELSIUS: @@ -227,6 +266,23 @@ static void temperature_data_func(GtkTreeViewColumn *col, g_object_set(renderer, "text", buffer, NULL); } +static void nr_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int idx, nr; + char buffer[40]; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1); + if (idx == -1) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", nr); + g_object_set(renderer, "text", buffer, NULL); +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -309,6 +365,10 @@ static void nitrox_data_func(GtkTreeViewColumn *col, struct dive *dive; gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1); + if (index == -1) { + *buffer = '\0'; + goto exit; + } dive = get_dive(index); get_dive_gas(dive, &o2, &he, &o2low); o2 = (o2 + 5) / 10; @@ -324,7 +384,7 @@ static void nitrox_data_func(GtkTreeViewColumn *col, snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); else strcpy(buffer, "air"); - +exit: g_object_set(renderer, "text", buffer, NULL); } @@ -335,16 +395,16 @@ static void sac_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; const char *fmt; char buffer[16]; double sac; - gtk_tree_model_get(model, iter, DIVE_SAC, &value, -1); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1); - if (!value) { - g_object_set(renderer, "text", "", NULL); - return; + if (idx == -1 || !value) { + *buffer = '\0'; + goto exit; } sac = value / 1000.0; @@ -358,7 +418,7 @@ static void sac_data_func(GtkTreeViewColumn *col, break; } snprintf(buffer, sizeof(buffer), fmt, sac); - +exit: g_object_set(renderer, "text", buffer, NULL); } @@ -369,17 +429,15 @@ static void otu_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; char buffer[16]; - gtk_tree_model_get(model, iter, DIVE_OTU, &value, -1); - - if (!value) { - g_object_set(renderer, "text", "", NULL); - return; - } + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1); - snprintf(buffer, sizeof(buffer), "%d", value); + if (idx == -1 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", value); g_object_set(renderer, "text", buffer, NULL); } @@ -514,7 +572,7 @@ static void fill_one_dive(struct dive *dive, get_cylinder(dive, &cylinder); get_location(dive, &location); - gtk_list_store_set(GTK_LIST_STORE(model), iter, + gtk_tree_store_set(GTK_TREE_STORE(model), iter, DIVE_NR, dive->number, DIVE_LOCATION, location, DIVE_CYLINDER, cylinder, @@ -529,12 +587,14 @@ static gboolean set_one_dive(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - GValue value = {0, }; + int idx; struct dive *dive; /* Get the dive number */ - gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value); - dive = get_dive(g_value_get_int(&value)); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx == -1) + return TRUE; + dive = get_dive(idx); if (!dive) return TRUE; if (data && dive != data) @@ -582,27 +642,79 @@ void update_dive_list_col_visibility(void) return; } +static int new_day(struct dive *dive, struct dive **last_dive, time_t *tm_date) +{ + if (!last_dive) + return TRUE; + if (!*last_dive) { + *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; + } else { + struct dive *ldive = *last_dive; + struct tm tm1, tm2; + (void) gmtime_r(&dive->when, &tm1); + (void) gmtime_r(&ldive->when, &tm2); + if (tm1.tm_year != tm2.tm_year || + tm1.tm_mon != tm2.tm_mon || + tm1.tm_mday != tm2.tm_mday) { + *last_dive = dive; + if (tm_date) { + tm1.tm_sec = 0; + tm1.tm_min = 0; + tm1.tm_hour = 0; + *tm_date = mktime(&tm1); + } + return TRUE; + } + } + return FALSE; + +} + static void fill_dive_list(void) { int i; - GtkTreeIter iter; - GtkListStore *store; + GtkTreeIter iter, parent_iter, *parent = NULL; + GtkTreeStore *store; + struct dive *last_dive = NULL; + time_t dive_date; - store = GTK_LIST_STORE(dive_list.model); + store = GTK_TREE_STORE(dive_list.model); i = dive_table.nr; while (--i >= 0) { struct dive *dive = dive_table.dives[i]; + if (new_day(dive, &last_dive, &dive_date)) + { + gtk_tree_store_append(store, &parent_iter, NULL); + parent = &parent_iter; + gtk_tree_store_set(store, parent, + DIVE_INDEX, -1, + DIVE_NR, -1, + DIVE_DATE, dive_date, + DIVE_LOCATION, "", + DIVE_TEMPERATURE, 0, + DIVE_SAC, 0, + -1); + } update_cylinder_related_info(dive); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, + gtk_tree_store_append(store, &iter, parent); + gtk_tree_store_set(store, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, DIVE_DATE, dive->when, DIVE_DEPTH, dive->maxdepth, DIVE_DURATION, dive->duration.seconds, - DIVE_LOCATION, "location", + DIVE_LOCATION, dive->location, + DIVE_RATING, dive->rating, DIVE_TEMPERATURE, dive->watertemp.mkelvin, DIVE_SAC, 0, -1); @@ -618,7 +730,7 @@ static void fill_dive_list(void) void dive_list_update_dives(void) { - gtk_list_store_clear(GTK_LIST_STORE(dive_list.model)); + gtk_tree_store_clear(GTK_TREE_STORE(dive_list.model)); fill_dive_list(); repaint_dive(); } @@ -630,7 +742,7 @@ static struct divelist_column { unsigned int flags; int *visible; } dl_column[] = { - [DIVE_NR] = { "#", NULL, NULL, ALIGN_RIGHT | UNSORTABLE }, + [DIVE_NR] = { "#", nr_data_func, NULL, ALIGN_RIGHT | UNSORTABLE }, [DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT }, [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT }, [DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT }, @@ -653,7 +765,7 @@ static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_c unsigned int flags = col->flags; int *visible = col->visible; GtkWidget *tree_view = dl->tree_view; - GtkListStore *model = dl->model; + GtkTreeStore *model = dl->model; GtkTreeViewColumn *ret; if (visible && !*visible) @@ -684,7 +796,9 @@ static void row_activated_cb(GtkTreeView *tree_view, if (!gtk_tree_model_get_iter(model, &iter, path)) return; gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); - edit_dive_info(get_dive(index)); + /* an index of -1 is special for the "group by date" entries */ + if (index != -1) + edit_dive_info(get_dive(index)); } void add_dive_cb(GtkWidget *menuitem, gpointer data) @@ -734,7 +848,7 @@ GtkWidget *dive_list_create(void) { GtkTreeSelection *selection; - dive_list.model = gtk_list_store_new(DIVELIST_COLUMNS, + dive_list.model = gtk_tree_store_new(DIVELIST_COLUMNS, G_TYPE_INT, /* index */ G_TYPE_INT, /* nr */ G_TYPE_INT, /* Date */ -- cgit v1.2.3-70-g09d2 From 5f1e41de4376bef1968a42f48e3fb04bf10a52cb Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 13 Aug 2012 13:09:40 -0700 Subject: Improve tree model implementation We now support three hierarchy levels: day, month, and year. Each indicated by a negative DIVE_INDEX for -1 to -3. This allows a nice compact overview when doing date based sorting (the default). As indicated in the previous commit, things still go wrong with sorting by other columns as the entries are only sorted within each day, not globally across the whole dive list. Signed-off-by: Dirk Hohndel --- divelist.c | 152 +++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 88 insertions(+), 64 deletions(-) diff --git a/divelist.c b/divelist.c index 8899d7429..137b8a148 100644 --- a/divelist.c +++ b/divelist.c @@ -52,6 +52,12 @@ enum { DIVELIST_COLUMNS }; +/* magic numbers that indicate (as negative values) model entries that + * are summary entries for day / month / year */ +#define NEW_DAY 1 +#define NEW_MON 2 +#define NEW_YR 3 + #ifdef DEBUG_MODEL static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) @@ -77,7 +83,6 @@ static GList *selected_dives; static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) { GtkTreeIter iter; - GValue value = {0, }; GtkTreePath *path; int nr_selected = gtk_tree_selection_count_selected_rows(selection); @@ -96,10 +101,23 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) amount_selected = 1; path = g_list_nth_data(selected_dives, 0); if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value); - /* an index of -1 should mean select all dives from that day - * ===> still needs to be implemented */ - selected_dive = g_value_get_int(&value); + gtk_tree_model_get(model, &iter, DIVE_INDEX, &selected_dive, -1); + /* a negative index means we picked a summary entry; + expand that entry and use first real child instead */ + while (selected_dive < 0) { + GtkTreeIter parent; + GtkTreePath *tpath; + memcpy(&parent, &iter, sizeof(parent)); + tpath = gtk_tree_model_get_path(model, &parent); + if (!gtk_tree_model_iter_children(model, &iter, &parent)) + /* we should never have a parent without child */ + return; + if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath); + else + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + gtk_tree_model_get(model, &iter, DIVE_INDEX, &selected_dive, -1); + } repaint_dive(); } return; @@ -135,7 +153,7 @@ static void star_data_func(GtkTreeViewColumn *col, char buffer[40]; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1); - if (idx == -1) { + if (idx < 0) { *buffer = '\0'; } else { if (nr_stars < 0 || nr_stars > 5) @@ -162,19 +180,32 @@ static void date_data_func(GtkTreeViewColumn *col, when = val; tm = gmtime(&when); - if (idx == -1) + switch(idx) { + case -NEW_DAY: snprintf(buffer, sizeof(buffer), "%s, %s %d, %d", weekday(tm->tm_wday), monthname(tm->tm_mon), tm->tm_mday, tm->tm_year + 1900); - else + break; + case -NEW_MON: + snprintf(buffer, sizeof(buffer), + "%s %d", + monthname(tm->tm_mon), + tm->tm_year + 1900); + break; + case -NEW_YR: + snprintf(buffer, sizeof(buffer), + "%d", tm->tm_year + 1900); + break; + default: snprintf(buffer, sizeof(buffer), "%s, %s %d, %d %02d:%02d", weekday(tm->tm_wday), monthname(tm->tm_mon), tm->tm_mday, tm->tm_year + 1900, tm->tm_hour, tm->tm_min); + } g_object_set(renderer, "text", buffer, NULL); } @@ -189,7 +220,7 @@ static void depth_data_func(GtkTreeViewColumn *col, gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1); - if (idx == -1) { + if (idx < 0) { *buffer = '\0'; } else { switch (output_units.length) { @@ -224,11 +255,12 @@ static void duration_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - unsigned int sec, idx; + unsigned int sec; + int idx; char buffer[16]; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1); - if (idx == -1) + if (idx < 0) *buffer = '\0'; else snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); @@ -248,7 +280,7 @@ static void temperature_data_func(GtkTreeViewColumn *col, gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1); *buffer = 0; - if (idx != -1 && value) { + if (idx >= 0 && value) { double deg; switch (output_units.temperature) { case CELSIUS: @@ -276,7 +308,7 @@ static void nr_data_func(GtkTreeViewColumn *col, char buffer[40]; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1); - if (idx == -1) + if (idx < 0) *buffer = '\0'; else snprintf(buffer, sizeof(buffer), "%d", nr); @@ -360,16 +392,16 @@ static void nitrox_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int index, o2, he, o2low; + int idx, o2, he, o2low; char buffer[80]; struct dive *dive; - gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1); - if (index == -1) { + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { *buffer = '\0'; goto exit; } - dive = get_dive(index); + dive = get_dive(idx); get_dive_gas(dive, &o2, &he, &o2low); o2 = (o2 + 5) / 10; he = (he + 5) / 10; @@ -402,7 +434,7 @@ static void sac_data_func(GtkTreeViewColumn *col, gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1); - if (idx == -1 || !value) { + if (idx < 0 || !value) { *buffer = '\0'; goto exit; } @@ -434,7 +466,7 @@ static void otu_data_func(GtkTreeViewColumn *col, gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1); - if (idx == -1 || !value) + if (idx < 0 || !value) *buffer = '\0'; else snprintf(buffer, sizeof(buffer), "%d", value); @@ -592,8 +624,8 @@ static gboolean set_one_dive(GtkTreeModel *model, /* Get the dive number */ gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); - if (idx == -1) - return TRUE; + if (idx < 0) + return FALSE; dive = get_dive(idx); if (!dive) return TRUE; @@ -642,46 +674,36 @@ void update_dive_list_col_visibility(void) return; } -static int new_day(struct dive *dive, struct dive **last_dive, time_t *tm_date) +static int new_date(struct dive *dive, struct dive **last_dive, const int flag, time_t *tm_date) { if (!last_dive) return TRUE; - if (!*last_dive) { - *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; - } else { + if (*last_dive) { struct dive *ldive = *last_dive; struct tm tm1, tm2; (void) gmtime_r(&dive->when, &tm1); (void) gmtime_r(&ldive->when, &tm2); - if (tm1.tm_year != tm2.tm_year || - tm1.tm_mon != tm2.tm_mon || - tm1.tm_mday != tm2.tm_mday) { - *last_dive = dive; - if (tm_date) { - tm1.tm_sec = 0; - tm1.tm_min = 0; - tm1.tm_hour = 0; - *tm_date = mktime(&tm1); - } - return TRUE; - } + if (tm1.tm_year == tm2.tm_year && + (tm1.tm_mon == tm2.tm_mon || flag > NEW_MON) && + (tm1.tm_mday == tm2.tm_mday || flag > NEW_DAY)) + return FALSE; } - return FALSE; - + if (flag == NEW_DAY) + *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; - GtkTreeIter iter, parent_iter, *parent = NULL; + int i, j; + GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, }; GtkTreeStore *store; struct dive *last_dive = NULL; time_t dive_date; @@ -692,21 +714,23 @@ static void fill_dive_list(void) while (--i >= 0) { struct dive *dive = dive_table.dives[i]; - if (new_day(dive, &last_dive, &dive_date)) - { - gtk_tree_store_append(store, &parent_iter, NULL); - parent = &parent_iter; - gtk_tree_store_set(store, parent, - DIVE_INDEX, -1, - DIVE_NR, -1, - DIVE_DATE, dive_date, - DIVE_LOCATION, "", - DIVE_TEMPERATURE, 0, - DIVE_SAC, 0, - -1); + for (j = NEW_YR; j >= NEW_DAY; j--) { + if (new_date(dive, &last_dive, j, &dive_date)) + { + gtk_tree_store_append(store, &parent_iter[j], parents[j+1]); + parents[j] = &parent_iter[j]; + gtk_tree_store_set(store, parents[j], + DIVE_INDEX, -j, + DIVE_NR, -j, + DIVE_DATE, dive_date, + DIVE_LOCATION, "", + DIVE_TEMPERATURE, 0, + DIVE_SAC, 0, + -1); + } } update_cylinder_related_info(dive); - gtk_tree_store_append(store, &iter, parent); + gtk_tree_store_append(store, &iter, parents[NEW_DAY]); gtk_tree_store_set(store, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, @@ -796,8 +820,8 @@ static void row_activated_cb(GtkTreeView *tree_view, if (!gtk_tree_model_get_iter(model, &iter, path)) return; gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); - /* an index of -1 is special for the "group by date" entries */ - if (index != -1) + /* a negative index is special for the "group by date" entries */ + if (index >= 0) edit_dive_info(get_dive(index)); } -- cgit v1.2.3-70-g09d2 From 27a505e579057596ab10f7381c471b870ce86f87 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 13 Aug 2012 14:42:55 -0700 Subject: Create duplicate list model so sorting by columns works again One major downside of the switch to a tree model is that sorting by columns other than date was broken - it would sort the entries within each date which is not all that useful. After playing with some Gtk trickery that would allow us to filter out those rows it quickly became clear that the much easier solution is to simply maintain TWO models (and therefore two storages). This causes some overhead and requires some careful tracking of all changes, but it turned out to be rather straight forward to do. dive_list now has three model related members: model - current model displayed (which is one of the following two) treemodel - the tree model listmodel - the list model One side effect is that the callbacks no longer can pass the model around (as this could have changed since the callback was registered), but that seems only a minor drawback and was easily addressed. The implementation in this commit still has a couple of obvious flaws: when switching back from the list model to the tree model all the expansion state of the rows is lost and we end up with just a list of the different years visible. Also, selections aren't maintained when switching models. Signed-off-by: Dirk Hohndel --- divelist.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 30 deletions(-) diff --git a/divelist.c b/divelist.c index 137b8a148..b70b415ca 100644 --- a/divelist.c +++ b/divelist.c @@ -24,7 +24,7 @@ struct DiveList { GtkWidget *tree_view; GtkWidget *container_widget; - GtkTreeStore *model; + GtkTreeStore *model, *listmodel, *treemodel; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu; int changed; @@ -80,7 +80,7 @@ static void dump_model(GtkListStore *store) static GList *selected_dives; -static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) +static void selection_cb(GtkTreeSelection *selection, gpointer userdata) { GtkTreeIter iter; GtkTreePath *path; @@ -100,23 +100,23 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) /* just pick that dive as selected */ amount_selected = 1; path = g_list_nth_data(selected_dives, 0); - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get(model, &iter, DIVE_INDEX, &selected_dive, -1); + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) { + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); /* a negative index means we picked a summary entry; expand that entry and use first real child instead */ while (selected_dive < 0) { GtkTreeIter parent; GtkTreePath *tpath; memcpy(&parent, &iter, sizeof(parent)); - tpath = gtk_tree_model_get_path(model, &parent); - if (!gtk_tree_model_iter_children(model, &iter, &parent)) + tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(dive_list.model), &parent); + if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(dive_list.model), &iter, &parent)) /* we should never have a parent without child */ return; if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath); else gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); - gtk_tree_model_get(model, &iter, DIVE_INDEX, &selected_dive, -1); + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); } repaint_dive(); } @@ -128,7 +128,7 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) * I do however want to keep around which dives have * been selected */ amount_selected = g_list_length(selected_dives); - process_selected_dives(selected_dives, model); + process_selected_dives(selected_dives, GTK_TREE_MODEL(dive_list.model)); repaint_dive(); return; } @@ -593,13 +593,21 @@ static void get_cylinder(struct dive *dive, char **str) /* * Set up anything that could have changed due to editing - * of dive information + * 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; + GtkTreeStore *othermodel; get_cylinder(dive, &cylinder); get_location(dive, &location); @@ -612,6 +620,13 @@ static void fill_one_dive(struct dive *dive, DIVE_SAC, dive->sac, DIVE_OTU, dive->otu, -1); + 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, @@ -704,11 +719,12 @@ static void fill_dive_list(void) { int i, j; GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, }; - GtkTreeStore *store; + GtkTreeStore *liststore, *treestore; struct dive *last_dive = NULL; time_t dive_date; - store = GTK_TREE_STORE(dive_list.model); + treestore = GTK_TREE_STORE(dive_list.treemodel); + liststore = GTK_TREE_STORE(dive_list.listmodel); i = dive_table.nr; while (--i >= 0) { @@ -717,9 +733,9 @@ static void fill_dive_list(void) for (j = NEW_YR; j >= NEW_DAY; j--) { if (new_date(dive, &last_dive, j, &dive_date)) { - gtk_tree_store_append(store, &parent_iter[j], parents[j+1]); + gtk_tree_store_append(treestore, &parent_iter[j], parents[j+1]); parents[j] = &parent_iter[j]; - gtk_tree_store_set(store, parents[j], + gtk_tree_store_set(treestore, parents[j], DIVE_INDEX, -j, DIVE_NR, -j, DIVE_DATE, dive_date, @@ -730,8 +746,20 @@ static void fill_dive_list(void) } } update_cylinder_related_info(dive); - gtk_tree_store_append(store, &iter, parents[NEW_DAY]); - gtk_tree_store_set(store, &iter, + gtk_tree_store_append(treestore, &iter, parents[NEW_DAY]); + gtk_tree_store_set(treestore, &iter, + DIVE_INDEX, i, + DIVE_NR, dive->number, + DIVE_DATE, dive->when, + DIVE_DEPTH, dive->maxdepth, + DIVE_DURATION, dive->duration.seconds, + DIVE_LOCATION, dive->location, + DIVE_RATING, dive->rating, + DIVE_TEMPERATURE, dive->watertemp.mkelvin, + DIVE_SAC, 0, + -1); + gtk_tree_store_append(liststore, &iter, NULL); + gtk_tree_store_set(liststore, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, DIVE_DATE, dive->when, @@ -754,7 +782,8 @@ static void fill_dive_list(void) void dive_list_update_dives(void) { - gtk_tree_store_clear(GTK_TREE_STORE(dive_list.model)); + gtk_tree_store_clear(GTK_TREE_STORE(dive_list.treemodel)); + gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel)); fill_dive_list(); repaint_dive(); } @@ -812,14 +841,14 @@ static void realize_cb(GtkWidget *tree_view, gpointer userdata) static void row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + gpointer userdata) { int index; GtkTreeIter iter; - if (!gtk_tree_model_get_iter(model, &iter, path)) + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) return; - gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &index, -1); /* a negative index is special for the "group by date" entries */ if (index >= 0) edit_dive_info(get_dive(index)); @@ -844,7 +873,7 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int menu = gtk_menu_new(); menuitem = gtk_menu_item_new_with_label("Add dive"); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), model); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); gtk_widget_show_all(menu); @@ -852,27 +881,60 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button, gtk_get_current_event_time()); } -static void popup_menu_cb(GtkTreeView *tree_view, - GtkTreeModel *model) +static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) { - popup_divelist_menu(tree_view, model, 0); + popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0); } -static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, GtkTreeModel *model) +static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata) { /* Right-click? Bring up the menu */ if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - popup_divelist_menu(GTK_TREE_VIEW(treeview), model, 3); + popup_divelist_menu(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(dive_list.model), 3); return TRUE; } return FALSE; } +/* If the sort column is date (default), show the tree model. + For every other sort column only show the list model. + If the model changed, inform the new model of the chosen sort column. */ +static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) +{ + int colid; + GtkSortType order; + GtkTreeStore *currentmodel = dive_list.model; + + gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order); + if(colid == DIVE_DATE) + dive_list.model = dive_list.treemodel; + else + dive_list.model = dive_list.listmodel; + if (dive_list.model != currentmodel) { + gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model)); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, order); + } +} + GtkWidget *dive_list_create(void) { GtkTreeSelection *selection; - dive_list.model = gtk_tree_store_new(DIVELIST_COLUMNS, + dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS, + G_TYPE_INT, /* index */ + G_TYPE_INT, /* nr */ + G_TYPE_INT, /* Date */ + G_TYPE_INT, /* Star rating */ + G_TYPE_INT, /* Depth */ + G_TYPE_INT, /* Duration */ + G_TYPE_INT, /* Temperature */ + G_TYPE_STRING, /* Cylinder */ + G_TYPE_INT, /* Nitrox */ + G_TYPE_INT, /* SAC */ + G_TYPE_INT, /* OTU */ + G_TYPE_STRING /* Location */ + ); + dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, G_TYPE_INT, /* index */ G_TYPE_INT, /* nr */ G_TYPE_INT, /* Date */ @@ -886,6 +948,7 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* OTU */ G_TYPE_STRING /* Location */ ); + dive_list.model = dive_list.treemodel; dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model)); set_divelist_font(divelist_font); @@ -914,10 +977,12 @@ GtkWidget *dive_list_create(void) NULL); g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL); - g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), dive_list.model); - g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), dive_list.model); - g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), dive_list.model); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model); + g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL); + g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL); + g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), NULL); + g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget), -- cgit v1.2.3-70-g09d2 From dc9d0e23e5d158ea4775021b2f629e7f90b5377c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 13 Aug 2012 14:53:07 -0700 Subject: Maintain selected rows when switching between list model and tree model We keep track of the DIVE_INDEX of all selected dives and simply re-select those dives after changing model (date based sort or sort by other column). There are a few TODOs left. We lose the sort direction (ascending / descending) when switching models. We also don't correctly deal with the user selecting summary rows in the tree model. Signed-off-by: Dirk Hohndel --- display-gtk.h | 2 +- divelist.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- statistics.c | 11 ++++++++--- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index 4ce05468c..3043c873b 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -72,7 +72,7 @@ extern GtkWidget *dive_list_create(void); unsigned int amount_selected; -extern void process_selected_dives(GList *, GtkTreeModel *); +extern void process_selected_dives(GList *, int *, GtkTreeModel *); typedef void (*data_func_t)(GtkTreeViewColumn *col, GtkCellRenderer *renderer, diff --git a/divelist.c b/divelist.c index b70b415ca..4260651b7 100644 --- a/divelist.c +++ b/divelist.c @@ -79,6 +79,7 @@ static void dump_model(GtkListStore *store) #endif static GList *selected_dives; +static int *selectiontracker; static void selection_cb(GtkTreeSelection *selection, gpointer userdata) { @@ -92,6 +93,7 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) g_list_free (selected_dives); } selected_dives = gtk_tree_selection_get_selected_rows(selection, NULL); + selectiontracker = realloc(selectiontracker, nr_selected * sizeof(int)); switch (nr_selected) { case 0: /* keep showing the last selected dive */ @@ -118,6 +120,7 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); } + selectiontracker[0] = selected_dive; repaint_dive(); } return; @@ -127,8 +130,11 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) * is the most intuitive solution. * I do however want to keep around which dives have * been selected */ + /* TODO: + this also does not handle the case if a summary row is selected; + We should iterate and select all dives under that row */ amount_selected = g_list_length(selected_dives); - process_selected_dives(selected_dives, GTK_TREE_MODEL(dive_list.model)); + process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model)); repaint_dive(); return; } @@ -777,6 +783,8 @@ static void fill_dive_list(void) GtkTreeSelection *selection; selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); gtk_tree_selection_select_iter(selection, &iter); + selectiontracker = realloc(selectiontracker, sizeof(int)); + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, selectiontracker, -1); } } @@ -896,9 +904,37 @@ static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpoi return FALSE; } +/* we need to have a temporary copy of the selected dives while + switching model as the selection_cb function keeps getting called + by when gtk_tree_selection_select_path is called. */ +static int *oldselection; +static int old_nr_selected; + +/* Check if this dive was selected previously and select it again in the new model; + * This is used after we switch models to maintain consistent selections. + * We always return FALSE to iterate through all dives */ +static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + int i, idx; + GtkTreeSelection *selection = GTK_TREE_SELECTION(data); + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + for (i = 0; i < old_nr_selected; i++) + if (oldselection[i] == idx) { + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + gtk_tree_selection_select_path(selection, path); + + return FALSE; + } + return FALSE; + +} + /* If the sort column is date (default), show the tree model. For every other sort column only show the list model. - If the model changed, inform the new model of the chosen sort column. */ + If the model changed, inform the new model of the chosen sort column and make + sure the same dives are still selected. */ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) { int colid; @@ -911,8 +947,23 @@ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) else dive_list.model = dive_list.listmodel; if (dive_list.model != currentmodel) { + /* TODO + we should remember the sort order we had for each column */ + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + /* remember what is currently selected, switch models and reselect the selected rows */ + old_nr_selected = amount_selected; + oldselection = malloc(old_nr_selected * sizeof(int)); + memcpy(oldselection, selectiontracker, amount_selected * sizeof(int)); + gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model)); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, order); + + if (old_nr_selected) { + /* we need to select all the dives that were selected */ + /* this is fundamentally an n^2 algorithm as implemented - YUCK */ + gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), select_selected, selection); + } } } diff --git a/statistics.c b/statistics.c index 19105653c..adfc9c77a 100644 --- a/statistics.c +++ b/statistics.c @@ -142,10 +142,11 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive) } } -void process_selected_dives(GList *selected_dives, GtkTreeModel *model) +void process_selected_dives(GList *selected_dives, int *selectiontracker, GtkTreeModel *model) { struct dive *dp; unsigned int i; + int idx; GtkTreeIter iter; GtkTreePath *path; @@ -157,9 +158,13 @@ void process_selected_dives(GList *selected_dives, GtkTreeModel *model) path = g_list_nth_data(selected_dives, i); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get_value(model, &iter, 0, &value); - dp = get_dive(g_value_get_int(&value)); + idx = g_value_get_int(&value); + dp = get_dive(idx); + if (dp) { + selectiontracker[i] = idx; + process_dive(dp, &stats_selection); + } } - process_dive(dp, &stats_selection); } } -- cgit v1.2.3-70-g09d2 From 5ba89c13ac09ee7649abf2d0c4514f9a83bb6c8b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 13 Aug 2012 15:07:38 -0700 Subject: Apply sort functions to the correct model, don't select summary entries We only set up the column specific sort functions for the default (tree) model, which caused us to not sort correctly in the list model. This commit also somewhat cleans up the handling of selecting summary lines in the tree model, which includes the very first selection made at program start (which happens to be the very last dive). But it still doesn't work the way I expect it to work (i.e., the correct row is not highlighted). Fundamentally I would prefer clicks on the summary lines to instead select (or as ctrl-click, possibly deselect) all the dives under that summary entry. Still TODO. Signed-off-by: Dirk Hohndel --- divelist.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 27 deletions(-) diff --git a/divelist.c b/divelist.c index 4260651b7..ea59b0875 100644 --- a/divelist.c +++ b/divelist.c @@ -81,6 +81,30 @@ static void dump_model(GtkListStore *store) static GList *selected_dives; static int *selectiontracker; +/* if we are sorting by date and are using a tree model, we don't want the selection + to be a summary entry, but instead the first child below that entry. So we descend + down the tree until we find a leaf (entry with non-negative index) + */ +static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) +{ + GtkTreeIter parent; + GtkTreePath *tpath; + + while (*diveidx < 0) { + memcpy(&parent, iter, sizeof(parent)); + tpath = gtk_tree_model_get_path(model, &parent); + if (!gtk_tree_model_iter_children(model, iter, &parent)) + /* we should never have a parent without child */ + return; + /* clicking on a parent should toggle expand status */ + if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath); + else + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + gtk_tree_model_get(GTK_TREE_MODEL(model), iter, DIVE_INDEX, diveidx, -1); + } +} + static void selection_cb(GtkTreeSelection *selection, gpointer userdata) { GtkTreeIter iter; @@ -104,22 +128,8 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) path = g_list_nth_data(selected_dives, 0); if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) { gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); - /* a negative index means we picked a summary entry; - expand that entry and use first real child instead */ - while (selected_dive < 0) { - GtkTreeIter parent; - GtkTreePath *tpath; - memcpy(&parent, &iter, sizeof(parent)); - tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(dive_list.model), &parent); - if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(dive_list.model), &iter, &parent)) - /* we should never have a parent without child */ - return; - if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) - gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath); - else - gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); - gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); - } + /* make sure we're not on a summary entry */ + first_leaf (GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive); selectiontracker[0] = selected_dive; repaint_dive(); } @@ -781,10 +791,14 @@ static void fill_dive_list(void) update_dive_list_units(); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) { GtkTreeSelection *selection; + + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); + /* make sure it's an actual dive that is selected */ + first_leaf(GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); gtk_tree_selection_select_iter(selection, &iter); selectiontracker = realloc(selectiontracker, sizeof(int)); - gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, selectiontracker, -1); + *selectiontracker = selected_dive; } } @@ -826,14 +840,20 @@ static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_c unsigned int flags = col->flags; int *visible = col->visible; GtkWidget *tree_view = dl->tree_view; - GtkTreeStore *model = dl->model; + GtkTreeStore *treemodel = dl->treemodel; + GtkTreeStore *listmodel = dl->listmodel; GtkTreeViewColumn *ret; if (visible && !*visible) flags |= INVISIBLE; ret = tree_view_column(tree_view, index, title, data_func, flags); - if (sort_func) - gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), index, sort_func, NULL, NULL); + if (sort_func) { + /* the sort functions are needed in the corresponding models */ + if (index == DIVE_DATE) + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treemodel), index, sort_func, NULL, NULL); + else + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(listmodel), index, sort_func, NULL, NULL); + } return ret; } @@ -906,9 +926,14 @@ static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpoi /* we need to have a temporary copy of the selected dives while switching model as the selection_cb function keeps getting called - by when gtk_tree_selection_select_path is called. */ + when gtk_tree_selection_select_path is called. We also need to + keep copies of the sort order so we can restore that as well after + switching models. */ static int *oldselection; static int old_nr_selected; +static gboolean second_call = FALSE; +static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, }; +static int lastcol = DIVE_DATE; /* Check if this dive was selected previously and select it again in the new model; * This is used after we switch models to maintain consistent selections. @@ -931,39 +956,72 @@ static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path, } +static void update_column_and_order(int colid) +{ + /* Careful: the index into treecolumns is off by one as we don't have a + tree_view column for DIVE_INDEX */ + GtkTreeViewColumn **treecolumns = &dive_list.nr; + + /* this will trigger a second call into sort_column_change_cb, + so make sure we don't start an infinite recursion... */ + second_call = TRUE; + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, sortorder[colid]); + gtk_tree_view_column_set_sort_order(treecolumns[colid - 1], sortorder[colid]); + second_call = FALSE; +} + /* If the sort column is date (default), show the tree model. For every other sort column only show the list model. If the model changed, inform the new model of the chosen sort column and make - sure the same dives are still selected. */ + sure the same dives are still selected. + + The challenge with this function is that once we change the model + we also need to change the sort column again (as it was changed in + the other model) and that causes this function to be called + recursively - so we need to catch that. +*/ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) { int colid; GtkSortType order; GtkTreeStore *currentmodel = dive_list.model; + if (second_call) + return; + gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order); + if(colid == lastcol) { + /* we just changed sort order */ + sortorder[colid] = order; + return; + } else { + lastcol = colid; + } if(colid == DIVE_DATE) dive_list.model = dive_list.treemodel; else dive_list.model = dive_list.listmodel; if (dive_list.model != currentmodel) { - /* TODO - we should remember the sort order we had for each column */ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); /* remember what is currently selected, switch models and reselect the selected rows */ old_nr_selected = amount_selected; oldselection = malloc(old_nr_selected * sizeof(int)); - memcpy(oldselection, selectiontracker, amount_selected * sizeof(int)); - + if (amount_selected) + memcpy(oldselection, selectiontracker, amount_selected * sizeof(int)); gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model)); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, order); + + update_column_and_order(colid); if (old_nr_selected) { /* we need to select all the dives that were selected */ /* this is fundamentally an n^2 algorithm as implemented - YUCK */ gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), select_selected, selection); } + } else { + if (order != sortorder[colid]) { + update_column_and_order(colid); + } } } -- cgit v1.2.3-70-g09d2 From 822b6409d752133090df24f5ca38f69656ff82b7 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Mon, 13 Aug 2012 21:11:09 -0700 Subject: Fix selecting and unselecting summary items The dive list now seems to behave intuitively. In order to do this we had to intercept the select function in addition to having a selection-changed callback. That way we can simulate the multi-level selection and unselection that was missing. Signed-off-by: Dirk Hohndel --- divelist.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++-------------- statistics.c | 23 +++++++++++----- 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/divelist.c b/divelist.c index ea59b0875..112307a1c 100644 --- a/divelist.c +++ b/divelist.c @@ -81,10 +81,9 @@ static void dump_model(GtkListStore *store) static GList *selected_dives; static int *selectiontracker; -/* if we are sorting by date and are using a tree model, we don't want the selection - to be a summary entry, but instead the first child below that entry. So we descend - down the tree until we find a leaf (entry with non-negative index) - */ +/* when subsurface starts we want to have the last dive selected. So we simply + walk to the first leaf (and skip the summary entries - which have negative + DIVE_INDEX) */ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) { GtkTreeIter parent; @@ -96,15 +95,57 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) if (!gtk_tree_model_iter_children(model, iter, &parent)) /* we should never have a parent without child */ return; - /* clicking on a parent should toggle expand status */ - if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) - gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath); - else + if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); gtk_tree_model_get(GTK_TREE_MODEL(model), iter, DIVE_INDEX, diveidx, -1); } } +/* if we click on a summary dive, we actually want to select / unselect + all the dives "below" it */ +static void select_children(GtkTreeModel *model, GtkTreeSelection * selection, + GtkTreeIter *iter, gboolean was_selected) +{ + int i, nr_children; + GtkTreeIter parent; + GtkTreePath *tpath; + + memcpy(&parent, iter, sizeof(parent)); + + tpath = gtk_tree_model_get_path(model, &parent); + if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + + nr_children = gtk_tree_model_iter_n_children(model, &parent); + for (i = 0; i < nr_children; i++) { + gtk_tree_model_iter_nth_child(model, iter, &parent, i); + if (was_selected) + gtk_tree_selection_unselect_iter(selection, iter); + else + gtk_tree_selection_select_iter(selection, iter); + } +} + +/* this is called _before_ the selection is changed, for every single entry; + * we simply have it call down the tree to make sure that summary items toggle + * their children */ +gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model, + GtkTreePath *path, gboolean was_selected, gpointer userdata) +{ + GtkTreeIter iter; + int dive_idx; + + if (gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_model_get(model, &iter, DIVE_INDEX, &dive_idx, -1); + if (dive_idx < 0) { + select_children(model, selection, &iter, was_selected); + } + } + /* allow this selection to proceed */ + return TRUE; +} + +/* this is called when gtk thinks that the selection has changed */ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) { GtkTreeIter iter; @@ -120,7 +161,9 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) selectiontracker = realloc(selectiontracker, nr_selected * sizeof(int)); switch (nr_selected) { - case 0: /* keep showing the last selected dive */ + case 0: /* there is no clear way to figure out which dive to show */ + amount_selected = 0; + selected_dive = -1; return; case 1: /* just pick that dive as selected */ @@ -128,21 +171,21 @@ static void selection_cb(GtkTreeSelection *selection, gpointer userdata) path = g_list_nth_data(selected_dives, 0); if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) { gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); - /* make sure we're not on a summary entry */ - first_leaf (GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive); + /* due to the way this callback gets invoked it is possible that + in the process of unselecting a summary dive we get here with + just one summary dive selected - ignore that case */ + if (selected_dive < 0) { + amount_selected = 0; + return; + } selectiontracker[0] = selected_dive; repaint_dive(); } return; - default: /* multiple selections - what now? At this point I - * don't want to change the selected dive unless - * there is exactly one dive selected; not sure this + default: /* multiple selections - what now? + * We don't change the selected dive unless there is exactly one dive selected; not sure this * is the most intuitive solution. - * I do however want to keep around which dives have - * been selected */ - /* TODO: - this also does not handle the case if a summary row is selected; - We should iterate and select all dives under that row */ + * The dives that have been selected are processed */ amount_selected = g_list_length(selected_dives); process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model)); repaint_dive(); @@ -792,8 +835,8 @@ static void fill_dive_list(void) if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) { GtkTreeSelection *selection; + /* select the last dive (and make sure it's an actual dive that is selected) */ gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); - /* make sure it's an actual dive that is selected */ first_leaf(GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); gtk_tree_selection_select_iter(selection, &iter); @@ -1093,6 +1136,8 @@ GtkWidget *dive_list_create(void) g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + gtk_tree_selection_set_select_function(selection, modify_selection_cb, NULL, NULL); + dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); diff --git a/statistics.c b/statistics.c index adfc9c77a..3a260066a 100644 --- a/statistics.c +++ b/statistics.c @@ -142,30 +142,39 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive) } } +/* make sure we skip the selected summary entries */ void process_selected_dives(GList *selected_dives, int *selectiontracker, GtkTreeModel *model) { struct dive *dp; - unsigned int i; + unsigned int i, j; int idx; GtkTreeIter iter; GtkTreePath *path; memset(&stats_selection, 0, sizeof(stats_selection)); - stats_selection.selection_size = amount_selected; - for (i = 0; i < amount_selected; ++i) { + /* adjust amount_selected and remove negative index entries from list */ + for (i = 0, j = 0; j < amount_selected; ++i) { GValue value = {0, }; path = g_list_nth_data(selected_dives, i); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get_value(model, &iter, 0, &value); idx = g_value_get_int(&value); - dp = get_dive(idx); - if (dp) { - selectiontracker[i] = idx; - process_dive(dp, &stats_selection); + if (idx > 0) { + dp = get_dive(idx); + if (dp) { + selectiontracker[j] = idx; + process_dive(dp, &stats_selection); + j++; + continue; + } } } + /* we didn't process it, so shorten the list */ + amount_selected--; } + /* record the actual number of dives selected */ + stats_selection.selection_size = amount_selected; } static void set_label(GtkWidget *w, const char *fmt, ...) -- cgit v1.2.3-70-g09d2 From 621761233b2e1b139c07987b562ef2aa299ff35e Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 15 Aug 2012 15:21:34 -0700 Subject: Redo dive editing This commit addresses two issues: We now can add / edit / delete equipment from the edit dive dialog We now can edit multiple dives at once The latter feature has some interesting design constraints: It picks the 'selected_dive' as the one to start the edit from - so if this dive already has some information filled in, that information needs to be overwritten before it is stored in all of the dives. Similarly, only changes to the cylinders or weightsystems are recorded. Also, the notes field is not editable in the multi dive edit mode (as that didn't seem useful). The workflow seems to work best if using the multi-edit right after importing new dives from a dive computer. The user then can select all the new dives and only needs to edit things like location, divemaster, buddy, weights, etc. once. This commit will create some obvious conflicts with the commit that adds exposure protection tracking. It was implemented on top of the tree_view changes as it reuses some of the infrastructure for tracking the selected dives. Signed-off-by: Dirk Hohndel --- display-gtk.h | 5 +- dive.h | 7 ++- divelist.c | 13 +++++ equipment.c | 158 +++++++++++++++++++++++++++++++--------------------------- gtk-gui.c | 2 +- info.c | 109 ++++++++++++++++++++++++++++++---------- main.c | 2 +- 7 files changed, 191 insertions(+), 105 deletions(-) diff --git a/display-gtk.h b/display-gtk.h index 3043c873b..1cf150bb6 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -63,10 +63,11 @@ extern void update_progressbar_text(progressbar_t *progress, const char *text); extern GtkWidget *dive_profile_widget(void); extern GtkWidget *dive_info_frame(void); extern GtkWidget *extended_dive_info_widget(void); -extern GtkWidget *equipment_widget(void); +extern GtkWidget *equipment_widget(int w_idx); extern GtkWidget *single_stats_widget(void); extern GtkWidget *total_stats_widget(void); -extern GtkWidget *cylinder_list_widget(void); +extern GtkWidget *cylinder_list_widget(int w_idx); +extern GtkWidget *weightsystem_list_widget(int w_idx); extern GtkWidget *dive_list_create(void); diff --git a/dive.h b/dive.h index a25fafb39..de95d5e66 100644 --- a/dive.h +++ b/dive.h @@ -227,6 +227,8 @@ struct event { #define MAX_CYLINDERS (8) #define MAX_WEIGHTSYSTEMS (4) +#define W_IDX_PRIMARY 0 +#define W_IDX_SECONDARY 1 struct dive { int number; @@ -282,7 +284,7 @@ extern int selected_dive; static inline struct dive *get_dive(unsigned int nr) { - if (nr >= dive_table.nr) + if (nr >= dive_table.nr || nr < 0) return NULL; return dive_table.dives[nr]; } @@ -299,7 +301,7 @@ extern xmlDoc *test_xslt_transforms(xmlDoc *doc); extern void show_dive_info(struct dive *); -extern void show_dive_equipment(struct dive *); +extern void show_dive_equipment(struct dive *, int w_idx); extern void show_dive_stats(struct dive *); @@ -345,6 +347,7 @@ extern void evn_foreach(void (*callback)(const char *, int *, void *), void *dat extern int add_new_dive(struct dive *dive); extern int edit_dive_info(struct dive *dive); +extern int edit_multi_dive_info(int nr, int *indices); extern void dive_list_update_dives(void); extern void flush_divelist(struct dive *dive); diff --git a/divelist.c b/divelist.c index 112307a1c..a4221f7b9 100644 --- a/divelist.c +++ b/divelist.c @@ -938,14 +938,27 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data) free(dive); } +void edit_dive_cb(GtkWidget *menuitem, gpointer data) +{ + edit_multi_dive_info(amount_selected, selectiontracker); +} + static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button) { GtkWidget *menu, *menuitem; + char editlabel[] = "Edit dives"; menu = gtk_menu_new(); menuitem = gtk_menu_item_new_with_label("Add dive"); g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + if (amount_selected) { + if (amount_selected == 1) + editlabel[strlen(editlabel) - 1] = '\0'; + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_cb), model); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, diff --git a/equipment.c b/equipment.c index b74d85b7b..ccfeb1270 100644 --- a/equipment.c +++ b/equipment.c @@ -2,10 +2,10 @@ /* creates the UI for the equipment page - * controlled through the following interfaces: * - * void show_dive_equipment(struct dive *dive) + * void show_dive_equipment(struct dive *dive, int w_idx) * * called from gtk-ui: - * GtkWidget *equipment_widget(void) + * GtkWidget *equipment_widget(int w_idx) */ #include #include @@ -40,10 +40,11 @@ enum { struct equipment_list { int max_index; GtkListStore *model; + GtkTreeView *tree_view; GtkWidget *edit, *add, *del; }; -static struct equipment_list cylinder_list, weightsystem_list; +static struct equipment_list cylinder_list[2], weightsystem_list[2]; struct cylinder_widget { @@ -519,11 +520,11 @@ static void show_equipment(struct dive *dive, int max, } } -void show_dive_equipment(struct dive *dive) +void show_dive_equipment(struct dive *dive, int w_idx) { - show_equipment(dive, MAX_CYLINDERS, &cylinder_list, + show_equipment(dive, MAX_CYLINDERS, &cylinder_list[w_idx], &cyl_ptr, &cylinder_none, &set_one_cylinder); - show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list, + show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list[w_idx], &ws_ptr, &weightsystem_none, &set_one_weightsystem); } @@ -1061,11 +1062,12 @@ static int get_model_index(GtkListStore *model, GtkTreeIter *iter) return index; } -static void edit_cb(GtkButton *button, GtkTreeView *tree_view) +static void edit_cb(GtkButton *button, int w_idx) { int index; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; cylinder_t cyl; @@ -1083,11 +1085,12 @@ static void edit_cb(GtkButton *button, GtkTreeView *tree_view) repaint_dive(); } -static void add_cb(GtkButton *button, GtkTreeView *tree_view) +static void add_cb(GtkButton *button, int w_idx) { - int index = cylinder_list.max_index; + int index = cylinder_list[w_idx].max_index; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; cylinder_t cyl; @@ -1100,15 +1103,16 @@ static void add_cb(GtkButton *button, GtkTreeView *tree_view) selection = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_select_iter(selection, &iter); - cylinder_list.max_index++; - gtk_widget_set_sensitive(cylinder_list.add, cylinder_list.max_index < MAX_CYLINDERS); + cylinder_list[w_idx].max_index++; + gtk_widget_set_sensitive(cylinder_list[w_idx].add, cylinder_list[w_idx].max_index < MAX_CYLINDERS); } -static void del_cb(GtkButton *button, GtkTreeView *tree_view) +static void del_cb(GtkButton *button, int w_idx) { int index, nr; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; struct dive *dive; cylinder_t *cyl; @@ -1125,27 +1129,28 @@ static void del_cb(GtkButton *button, GtkTreeView *tree_view) if (!dive) return; cyl = dive->cylinder + index; - nr = cylinder_list.max_index - index - 1; + nr = cylinder_list[w_idx].max_index - index - 1; gtk_list_store_remove(model, &iter); - cylinder_list.max_index--; + cylinder_list[w_idx].max_index--; memmove(cyl, cyl+1, nr*sizeof(*cyl)); memset(cyl+nr, 0, sizeof(*cyl)); mark_divelist_changed(TRUE); flush_divelist(dive); - gtk_widget_set_sensitive(cylinder_list.edit, 0); - gtk_widget_set_sensitive(cylinder_list.del, 0); - gtk_widget_set_sensitive(cylinder_list.add, 1); + gtk_widget_set_sensitive(cylinder_list[w_idx].edit, 0); + gtk_widget_set_sensitive(cylinder_list[w_idx].del, 0); + gtk_widget_set_sensitive(cylinder_list[w_idx].add, 1); } -static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_edit_cb(GtkButton *button, int w_idx) { int index; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; weightsystem_t ws; @@ -1163,11 +1168,12 @@ static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view) repaint_dive(); } -static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_add_cb(GtkButton *button, int w_idx) { - int index = weightsystem_list.max_index; + int index = weightsystem_list[w_idx].max_index; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; weightsystem_t ws; @@ -1180,15 +1186,16 @@ static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view) selection = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_select_iter(selection, &iter); - weightsystem_list.max_index++; - gtk_widget_set_sensitive(weightsystem_list.add, weightsystem_list.max_index < MAX_WEIGHTSYSTEMS); + weightsystem_list[w_idx].max_index++; + gtk_widget_set_sensitive(weightsystem_list[w_idx].add, weightsystem_list[w_idx].max_index < MAX_WEIGHTSYSTEMS); } -static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_del_cb(GtkButton *button, int w_idx) { int index, nr; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; struct dive *dive; weightsystem_t *ws; @@ -1205,20 +1212,20 @@ static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view) if (!dive) return; ws = dive->weightsystem + index; - nr = weightsystem_list.max_index - index - 1; + nr = weightsystem_list[w_idx].max_index - index - 1; gtk_list_store_remove(model, &iter); - weightsystem_list.max_index--; + weightsystem_list[w_idx].max_index--; memmove(ws, ws+1, nr*sizeof(*ws)); memset(ws+nr, 0, sizeof(*ws)); mark_divelist_changed(TRUE); flush_divelist(dive); - gtk_widget_set_sensitive(weightsystem_list.edit, 0); - gtk_widget_set_sensitive(weightsystem_list.del, 0); - gtk_widget_set_sensitive(weightsystem_list.add, 1); + gtk_widget_set_sensitive(weightsystem_list[w_idx].edit, 0); + gtk_widget_set_sensitive(weightsystem_list[w_idx].del, 0); + gtk_widget_set_sensitive(weightsystem_list[w_idx].add, 1); } static GtkListStore *create_tank_size_model(void) @@ -1338,33 +1345,33 @@ static void selection_cb(GtkTreeSelection *selection, struct equipment_list *lis static void row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + int w_idx) { - edit_cb(NULL, tree_view); + edit_cb(NULL, w_idx); } static void ws_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + int w_idx) { - ws_edit_cb(NULL, tree_view); + ws_edit_cb(NULL, w_idx); } -GtkWidget *cylinder_list_widget(void) +GtkWidget *cylinder_list_widget(int w_idx) { - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; GtkWidget *tree_view; GtkTreeSelection *selection; tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_widget_set_can_focus(tree_view, FALSE); - g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), model); + g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), GINT_TO_POINTER(w_idx)); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list[w_idx]); g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, @@ -1380,9 +1387,9 @@ GtkWidget *cylinder_list_widget(void) return tree_view; } -GtkWidget *weightsystem_list_widget(void) +GtkWidget *weightsystem_list_widget(int w_idx) { - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; GtkWidget *tree_view; GtkTreeSelection *selection; @@ -1392,7 +1399,7 @@ GtkWidget *weightsystem_list_widget(void) selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list[w_idx]); g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, @@ -1405,7 +1412,7 @@ GtkWidget *weightsystem_list_widget(void) return tree_view; } -static GtkWidget *cylinder_list_create(void) +static GtkWidget *cylinder_list_create(int w_idx) { GtkListStore *model; @@ -1418,11 +1425,11 @@ static GtkWidget *cylinder_list_create(void) G_TYPE_INT, /* CYL_O2: permille */ G_TYPE_INT /* CYL_HE: permille */ ); - cylinder_list.model = model; - return cylinder_list_widget(); + cylinder_list[w_idx].model = model; + return cylinder_list_widget(w_idx); } -static GtkWidget *weightsystem_list_create(void) +static GtkWidget *weightsystem_list_create(int w_idx) { GtkListStore *model; @@ -1430,11 +1437,11 @@ static GtkWidget *weightsystem_list_create(void) G_TYPE_STRING, /* WS_DESC: utf8 */ G_TYPE_INT /* WS_WEIGHT: grams */ ); - weightsystem_list.model = model; - return weightsystem_list_widget(); + weightsystem_list[w_idx].model = model; + return weightsystem_list_widget(w_idx); } -GtkWidget *equipment_widget(void) +GtkWidget *equipment_widget(int w_idx) { GtkWidget *vbox, *hbox, *frame, *framebox, *tree_view; GtkWidget *add, *del, *edit; @@ -1442,14 +1449,17 @@ GtkWidget *equipment_widget(void) vbox = gtk_vbox_new(FALSE, 3); /* - * We create the cylinder size model at startup, since - * we're going to share it across all cylinders and all - * dives. So if you add a new cylinder type in one dive, - * it will show up when you edit the cylinder types for - * another dive. + * We create the cylinder size (and weightsystem) models + * at startup for the primary cylinder / weightsystem widget, + * since we're going to share it across all cylinders and all + * dives. So if you add a new cylinder type or weightsystem in + * one dive, it will show up when you edit the cylinder types + * or weightsystems for another dive. */ - cylinder_model = create_tank_size_model(); - tree_view = cylinder_list_create(); + if (w_idx == W_IDX_PRIMARY) + cylinder_model = create_tank_size_model(); + tree_view = cylinder_list_create(w_idx); + cylinder_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view); hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); @@ -1475,19 +1485,21 @@ GtkWidget *equipment_widget(void) gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0); - cylinder_list.edit = edit; - cylinder_list.add = add; - cylinder_list.del = del; + cylinder_list[w_idx].edit = edit; + cylinder_list[w_idx].add = add; + cylinder_list[w_idx].del = del; - g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), tree_view); - g_signal_connect(add, "clicked", G_CALLBACK(add_cb), tree_view); - g_signal_connect(del, "clicked", G_CALLBACK(del_cb), tree_view); + g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(add, "clicked", G_CALLBACK(add_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(del, "clicked", G_CALLBACK(del_cb), GINT_TO_POINTER(w_idx)); hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); - weightsystem_model = create_weightsystem_model(); - tree_view = weightsystem_list_create(); + if (w_idx == W_IDX_PRIMARY) + weightsystem_model = create_weightsystem_model(); + tree_view = weightsystem_list_create(w_idx); + weightsystem_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view); frame = gtk_frame_new("Weight"); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, FALSE, 3); @@ -1510,13 +1522,13 @@ GtkWidget *equipment_widget(void) gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0); - weightsystem_list.edit = edit; - weightsystem_list.add = add; - weightsystem_list.del = del; + weightsystem_list[w_idx].edit = edit; + weightsystem_list[w_idx].add = add; + weightsystem_list[w_idx].del = del; - g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), tree_view); - g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), tree_view); - g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), tree_view); + g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), GINT_TO_POINTER(w_idx)); return vbox; } diff --git a/gtk-gui.c b/gtk-gui.c index 45aa21263..f7d3c5008 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -794,7 +794,7 @@ void init_ui(int *argcp, char ***argvp) gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes")); /* Frame for dive equipment */ - nb_page = equipment_widget(); + nb_page = equipment_widget(W_IDX_PRIMARY); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment")); /* Frame for single dive statistics */ diff --git a/info.c b/info.c index 1847a49bf..842a15001 100644 --- a/info.c +++ b/info.c @@ -344,25 +344,27 @@ static void save_dive_info_changes(struct dive *dive, struct dive_info *info) changed =1; } - old_text = dive->notes; - dive->notes = get_text(info->notes); - if (text_changed(old_text,dive->notes)) - changed = 1; - if (old_text) - g_free(old_text); - + if (info->notes) { + old_text = dive->notes; + dive->notes = get_text(info->notes); + if (text_changed(old_text,dive->notes)) + changed = 1; + if (old_text) + g_free(old_text); + } if (changed) { mark_divelist_changed(TRUE); update_dive(dive); } } -static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info) +static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info, gboolean multi) { - GtkWidget *hbox, *label, *cylinder, *frame; - char buffer[80]; + GtkWidget *hbox, *label, *frame, *equipment; + char buffer[80] = "Edit multiple dives"; - divename(buffer, sizeof(buffer), dive); + if (!multi) + divename(buffer, sizeof(buffer), dive); label = gtk_label_new(buffer); gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); @@ -379,28 +381,57 @@ static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info info->rating = text_entry(hbox, "Rating", star_list, star_strings[dive->rating]); - info->notes = text_view(box, "Notes", READ_WRITE); - if (dive->notes && *dive->notes) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); - + /* only show notes if editing a single dive */ + if (multi) { + info->notes = NULL; + } else { + info->notes = text_view(box, "Notes", READ_WRITE); + if (dive->notes && *dive->notes) + gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); + } hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - frame = gtk_frame_new("Cylinder"); - cylinder = cylinder_list_widget(); - gtk_container_add(GTK_CONTAINER(frame), cylinder); + /* create a secondary Equipment widget */ + frame = gtk_frame_new("Equipment"); + equipment = equipment_widget(W_IDX_SECONDARY); + gtk_container_add(GTK_CONTAINER(frame), equipment); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); } -int edit_dive_info(struct dive *dive) +/* we use these to find out if we edited the cylinder or weightsystem entries */ +static cylinder_t remember_cyl[MAX_CYLINDERS]; +static weightsystem_t remember_ws[MAX_WEIGHTSYSTEMS]; + +void save_equipment_data(struct dive *dive) { - int success; + if (dive) { + memcpy(remember_cyl, dive->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS); + memcpy(remember_ws, dive->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS); + } +} + +void update_equipment_data(struct dive *dive, struct dive *master) +{ + if (dive == master) + return; + if (memcmp(remember_cyl, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS)) { + memcpy(dive->cylinder, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS); + } + if (memcmp(remember_ws, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS)) { + memcpy(dive->weightsystem, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS); + } +} + +int edit_multi_dive_info(int nr, int *indices) +{ + int success, i; GtkWidget *dialog, *vbox; struct dive_info info; + struct dive *dive; - if (!dive) + if (!nr) return 0; - dialog = gtk_dialog_new_with_buttons("Dive Info", GTK_WINDOW(main_window), GTK_DIALOG_DESTROY_WITH_PARENT, @@ -409,18 +440,44 @@ int edit_dive_info(struct dive *dive) NULL); vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - dive_info_widget(vbox, dive, &info); - + /* SCARY STUFF - IS THIS THE BEST WAY TO DO THIS??? + * + * current_dive is one of our selected dives - and that is + * the one that is used to pre-fill the edit widget. Its + * data is used as the starting point for all selected dives + * I think it would be better to somehow collect and combine + * info from all the selected dives */ + dive = current_dive; + dive_info_widget(vbox, dive, &info, (nr > 1)); + show_dive_equipment(dive, W_IDX_SECONDARY); + save_equipment_data(dive); gtk_widget_show_all(dialog); success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; if (success) - save_dive_info_changes(dive, &info); - + for (i = 0; i < nr; i++) { + /* copy all "info" fields */ + save_dive_info_changes(get_dive(indices[i]), &info); + /* copy the cylinders / weightsystems */ + update_equipment_data(get_dive(indices[i]), dive); + /* this is extremely inefficient... it loops through all + dives to find the right one - but we KNOW the index already */ + flush_divelist(get_dive(indices[i])); + } gtk_widget_destroy(dialog); return success; } +int edit_dive_info(struct dive *dive) +{ + int idx; + + if (!dive) + return 0; + idx = dive->number; + return edit_multi_dive_info(1, &idx); +} + static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...) { va_list ap; diff --git a/main.c b/main.c index 60f2902e6..8e579f88c 100644 --- a/main.c +++ b/main.c @@ -190,7 +190,7 @@ void update_dive(struct dive *new_dive) } if (new_dive) { show_dive_info(new_dive); - show_dive_equipment(new_dive); + show_dive_equipment(new_dive, W_IDX_PRIMARY); show_dive_stats(new_dive); } buffered_dive = new_dive; -- cgit v1.2.3-70-g09d2 From e6ecddfa3d17901847474b67b6121c0c8f56f078 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 16 Aug 2012 04:27:03 -0700 Subject: Switch from date based to dive trip based grouping Linus HATED the date based grouping - too much wasted space visually ("three levels of grouping are way too much") and asked for dive trip based grouping instead. This is a quick change to do just that, with an assumption that no dive in 3 days means it's a new trip. This also changes the summary entry to display a location for the trip, for now we pick the location of the (chronologically) first dive of the trip. Signed-off-by: Dirk Hohndel --- divelist.c | 76 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/divelist.c b/divelist.c index a4221f7b9..5b4ef0a10 100644 --- a/divelist.c +++ b/divelist.c @@ -53,10 +53,8 @@ enum { }; /* magic numbers that indicate (as negative values) model entries that - * are summary entries for day / month / year */ -#define NEW_DAY 1 -#define NEW_MON 2 -#define NEW_YR 3 + * are summary entries for a divetrip */ +#define NEW_TRIP 1 #ifdef DEBUG_MODEL static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, @@ -240,23 +238,13 @@ static void date_data_func(GtkTreeViewColumn *col, tm = gmtime(&when); switch(idx) { - case -NEW_DAY: + case -NEW_TRIP: snprintf(buffer, sizeof(buffer), - "%s, %s %d, %d", + "Trip %s, %s %d, %d", weekday(tm->tm_wday), monthname(tm->tm_mon), tm->tm_mday, tm->tm_year + 1900); break; - case -NEW_MON: - snprintf(buffer, sizeof(buffer), - "%s %d", - monthname(tm->tm_mon), - tm->tm_year + 1900); - break; - case -NEW_YR: - snprintf(buffer, sizeof(buffer), - "%d", tm->tm_year + 1900); - break; default: snprintf(buffer, sizeof(buffer), "%s, %s %d, %d %02d:%02d", @@ -748,22 +736,20 @@ void update_dive_list_col_visibility(void) return; } -static int new_date(struct dive *dive, struct dive **last_dive, const int flag, time_t *tm_date) +/* 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; - struct tm tm1, tm2; - (void) gmtime_r(&dive->when, &tm1); - (void) gmtime_r(&ldive->when, &tm2); - if (tm1.tm_year == tm2.tm_year && - (tm1.tm_mon == tm2.tm_mon || flag > NEW_MON) && - (tm1.tm_mday == tm2.tm_mday || flag > NEW_DAY)) + if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) { + *last_dive = dive; return FALSE; + } } - if (flag == NEW_DAY) - *last_dive = dive; + *last_dive = dive; if (tm_date) { struct tm *tm1 = gmtime(&dive->when); tm1->tm_sec = 0; @@ -776,10 +762,12 @@ static int new_date(struct dive *dive, struct dive **last_dive, const int flag, static void fill_dive_list(void) { - int i, j; - GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, }; + int i; + GtkTreeIter iter, parent_iter; GtkTreeStore *liststore, *treestore; struct dive *last_dive = NULL; + struct dive *first_trip_dive = NULL; + struct dive *last_trip_dive = NULL; time_t dive_date; treestore = GTK_TREE_STORE(dive_list.treemodel); @@ -789,23 +777,29 @@ static void fill_dive_list(void) while (--i >= 0) { struct dive *dive = dive_table.dives[i]; - for (j = NEW_YR; j >= NEW_DAY; j--) { - if (new_date(dive, &last_dive, j, &dive_date)) - { - gtk_tree_store_append(treestore, &parent_iter[j], parents[j+1]); - parents[j] = &parent_iter[j]; - gtk_tree_store_set(treestore, parents[j], - DIVE_INDEX, -j, - DIVE_NR, -j, - DIVE_DATE, dive_date, - DIVE_LOCATION, "", - DIVE_TEMPERATURE, 0, - DIVE_SAC, 0, + if (new_group(dive, &last_dive, &dive_date)) + { + /* make sure we display the first date of the trip in previous summary */ + if (first_trip_dive && last_trip_dive && last_trip_dive->when < first_trip_dive->when) + gtk_tree_store_set(treestore, &parent_iter, + DIVE_DATE, last_trip_dive->when, + DIVE_LOCATION, last_trip_dive->location, -1); - } + first_trip_dive = dive; + + gtk_tree_store_append(treestore, &parent_iter, NULL); + gtk_tree_store_set(treestore, &parent_iter, + DIVE_INDEX, -NEW_TRIP, + DIVE_NR, -NEW_TRIP, + DIVE_DATE, dive_date, + DIVE_LOCATION, dive->location, + DIVE_TEMPERATURE, 0, + DIVE_SAC, 0, + -1); } + last_trip_dive = dive; update_cylinder_related_info(dive); - gtk_tree_store_append(treestore, &iter, parents[NEW_DAY]); + gtk_tree_store_append(treestore, &iter, &parent_iter); gtk_tree_store_set(treestore, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, -- cgit v1.2.3-70-g09d2