X-Git-Url: http://git.tdb.fi/?p=ext%2Fsubsurface.git;a=blobdiff_plain;f=divelist.c;h=a773b60179674084a2dfe251e5537bf37285e64d;hp=a4221f7b95b6dd5b410fa42c46eeb62aab4b57d0;hb=HEAD;hpb=621761233b2e1b139c07987b562ef2aa299ff35e diff --git a/divelist.c b/divelist.c index a4221f7..a773b60 100644 --- a/divelist.c +++ b/divelist.c @@ -26,11 +26,15 @@ struct DiveList { GtkWidget *container_widget; GtkTreeStore *model, *listmodel, *treemodel; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; - GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu; + GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu; int changed; }; static struct DiveList dive_list; +GList *dive_trip_list; +gboolean autogroup = FALSE; + +const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP" }; /* * The dive list has the dive data in both string format (for showing) @@ -44,6 +48,8 @@ enum { DIVE_DEPTH, /* int: dive->maxdepth in mm */ DIVE_DURATION, /* int: in seconds */ DIVE_TEMPERATURE, /* int: in mkelvin */ + DIVE_TOTALWEIGHT, /* int: in grams */ + DIVE_SUIT, /* "wet, 3mm" */ DIVE_CYLINDER, DIVE_NITROX, /* int: dummy */ DIVE_SAC, /* int: in ml/min */ @@ -52,21 +58,22 @@ 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) { char *location; - int idx, nr, rating, depth; + int idx, nr, duration; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1); + printf("entry #%d : nr %d duration %d location %s ", idx, nr, duration, location); + dive = get_dive(idx); + if (dive) + printf("tripflag %d\n", dive->tripflag); + else + printf("without matching dive\n"); - 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; @@ -78,8 +85,20 @@ static void dump_model(GtkListStore *store) } #endif -static GList *selected_dives; -static int *selectiontracker; +#if DEBUG_SELECTION_TRACKING +void dump_selection(void) +{ + int i; + struct dive *dive; + + printf("currently selected are %d dives:", amount_selected); + for_each_dive(i, dive) { + if (dive->selected) + printf(" %d", i); + } + printf("\n"); +} +#endif /* 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 @@ -101,96 +120,172 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) } } -/* 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) +/* make sure that if we expand a summary row that is selected, the children show + up as selected, too */ +void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) { - int i, nr_children; - GtkTreeIter parent; - GtkTreePath *tpath; + GtkTreeIter child; + GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + if (!gtk_tree_model_iter_children(model, &child, iter)) + return; - memcpy(&parent, iter, sizeof(parent)); + do { + int idx; + struct dive *dive; - 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); + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); - 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); + if (dive->selected) + gtk_tree_selection_select_iter(selection, &child); else - gtk_tree_selection_select_iter(selection, iter); - } + gtk_tree_selection_unselect_iter(selection, &child); + } while (gtk_tree_model_iter_next(model, &child)); +} + +static int selected_children(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter child; + + if (!gtk_tree_model_iter_children(model, &child, iter)) + return FALSE; + + do { + int idx; + struct dive *dive; + + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + + if (dive->selected) + return TRUE; + } while (gtk_tree_model_iter_next(model, &child)); + return FALSE; +} + +/* Make sure that if we collapse a summary row with any selected children, the row + shows up as selected too */ +void row_collapsed_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) +{ + GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + if (selected_children(model, iter)) + 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 */ +static GList *selection_changed = NULL; + +/* + * This is called _before_ the selection is changed, for every single entry; + * + * We simply create a list of all changed entries, and make sure that the + * group entries go at the end of the list. + */ gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean was_selected, gpointer userdata) { - GtkTreeIter iter; - int dive_idx; + GtkTreeIter iter, *p; - 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 */ + if (!gtk_tree_model_get_iter(model, &iter, path)) + return TRUE; + + /* Add the group entries to the end */ + p = gtk_tree_iter_copy(&iter); + if (gtk_tree_model_iter_has_child(model, p)) + selection_changed = g_list_append(selection_changed, p); + else + selection_changed = g_list_prepend(selection_changed, p); return TRUE; } -/* this is called when gtk thinks that the selection has changed */ -static void selection_cb(GtkTreeSelection *selection, gpointer userdata) +static void select_dive(struct dive *dive, int selected) { - GtkTreeIter iter; - GtkTreePath *path; - - int nr_selected = gtk_tree_selection_count_selected_rows(selection); - - if (selected_dives) { - g_list_foreach (selected_dives, (GFunc) gtk_tree_path_free, NULL); - g_list_free (selected_dives); + if (dive->selected != selected) { + amount_selected += selected ? 1 : -1; + dive->selected = selected; } - selected_dives = gtk_tree_selection_get_selected_rows(selection, NULL); - selectiontracker = realloc(selectiontracker, nr_selected * sizeof(int)); +} - switch (nr_selected) { - 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 */ - amount_selected = 1; - 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); - /* 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(); - } +/* + * This gets called when a dive group has changed selection. + */ +static void select_dive_group(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter, int selected) +{ + int first = 1; + GtkTreeIter child; + + if (selected == selected_children(model, iter)) return; - 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. - * 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(); + + if (!gtk_tree_model_iter_children(model, &child, iter)) return; + + do { + int idx; + struct dive *dive; + + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + if (first && selected) + selected_dive = idx; + first = 0; + dive = get_dive(idx); + if (dive->selected == selected) + continue; + + select_dive(dive, selected); + if (selected) + gtk_tree_selection_select_iter(selection, &child); + else + gtk_tree_selection_unselect_iter(selection, &child); + } while (gtk_tree_model_iter_next(model, &child)); +} + +/* + * This gets called _after_ the selections have changed, for each entry that + * may have changed. Check if the gtk selection state matches our internal + * selection state to verify. + * + * The group entries are at the end, this guarantees that we have handled + * all the dives before we handle groups. + */ +static void check_selection_cb(GtkTreeIter *iter, GtkTreeSelection *selection) +{ + GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + struct dive *dive; + int idx, gtk_selected; + + gtk_tree_model_get(model, iter, + DIVE_INDEX, &idx, + -1); + dive = get_dive(idx); + gtk_selected = gtk_tree_selection_iter_is_selected(selection, iter); + if (idx < 0) + select_dive_group(model, selection, iter, gtk_selected); + else { + select_dive(dive, gtk_selected); + if (gtk_selected) + selected_dive = idx; } + gtk_tree_iter_free(iter); +} + +/* this is called when gtk thinks that the selection has changed */ +static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) +{ + GList *changed = selection_changed; + + selection_changed = NULL; + g_list_foreach(changed, (GFunc) check_selection_cb, selection); + g_list_free(changed); +#if DEBUG_SELECTION_TRACKING + dump_selection(); +#endif + + process_selected_dives(); + repaint_dive(); } const char *star_strings[] = { @@ -228,36 +323,25 @@ static void date_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int val, idx; + int val, idx, nr; struct tm *tm; time_t when; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, DIVE_NR, &nr, -1); /* 2038 problem */ when = val; tm = gmtime(&when); - switch(idx) { - case -NEW_DAY: + if (idx < 0) { snprintf(buffer, sizeof(buffer), - "%s, %s %d, %d", + "Trip %s, %s %d, %d (%d dive%s)", 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: + tm->tm_mday, tm->tm_year + 1900, + nr, nr > 1 ? "s" : ""); + } else { snprintf(buffer, sizeof(buffer), "%s, %s %d, %d %02d:%02d", weekday(tm->tm_wday), @@ -416,6 +500,38 @@ newmax: *o2low_p = mino2; } +static int total_weight(struct dive *dive) +{ + int i, total_grams = 0; + + if (dive) + for (i=0; i< MAX_WEIGHTSYSTEMS; i++) + total_grams += dive->weightsystem[i].weight.grams; + return total_grams; +} + +static void weight_data_func(GtkTreeViewColumn *col, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int indx, decimals; + double value; + char buffer[80]; + struct dive *dive; + + gtk_tree_model_get(model, iter, DIVE_INDEX, &indx, -1); + dive = get_dive(indx); + value = get_weight_units(total_weight(dive), &decimals, NULL); + if (value == 0.0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%.*f", decimals, value); + + g_object_set(renderer, "text", buffer, NULL); +} + static gint nitrox_sort_func(GtkTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b, @@ -650,6 +766,11 @@ 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, @@ -665,11 +786,12 @@ static void fill_one_dive(struct dive *dive, GtkTreeModel *model, GtkTreeIter *iter) { - char *location, *cylinder; + 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, @@ -678,7 +800,14 @@ static void fill_one_dive(struct dive *dive, 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 @@ -735,6 +864,9 @@ void update_dive_list_units(void) (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); } @@ -742,70 +874,110 @@ 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; } -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) { - 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)) - 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, j; - GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, }; + int i; + GtkTreeIter iter, parent_iter, *parent_ptr = NULL; GtkTreeStore *liststore, *treestore; - struct dive *last_dive = NULL; - time_t dive_date; + struct dive *last_trip = NULL; + GList *trip; + struct dive *dive_trip = NULL; + + /* if we have pre-existing trips, start on the last one */ + trip = g_list_last(dive_trip_list); treestore = GTK_TREE_STORE(dive_list.treemodel); liststore = GTK_TREE_STORE(dive_list.listmodel); i = dive_table.nr; 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, + struct dive *dive = get_dive(i); + + /* make sure we display the first date of the trip in previous summary */ + if (dive_trip && parent_ptr) { + gtk_tree_store_set(treestore, parent_ptr, + DIVE_NR, dive_trip->number, + DIVE_DATE, dive_trip->when, + DIVE_LOCATION, dive_trip->location, + -1); + } + /* the dive_trip info might have been killed by a previous UNGROUPED dive */ + if (trip) + dive_trip = DIVE_TRIP(trip); + /* tripflag defines how dives are handled; + * TF_NONE "not handled yet" - create time based group if autogroup == TRUE + * NO_TRIP "set as no group" - simply leave at top level + * IN_TRIP "use the trip with the largest trip time (when) that is <= this dive" + */ + if (UNGROUPED_DIVE(dive)) { + /* first dives that go to the top level */ + parent_ptr = NULL; + dive_trip = NULL; + } else if (autogroup && !DIVE_IN_TRIP(dive)) { + if ( ! dive_trip || ! DIVE_FITS_TRIP(dive, dive_trip)) { + /* allocate new trip - all fields default to 0 + and get filled in further down */ + dive_trip = alloc_dive(); + dive_trip_list = insert_trip(dive_trip, dive_trip_list); + trip = FIND_TRIP(dive_trip, dive_trip_list); + } + } else { /* either the dive has a trip or we aren't creating trips */ + if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) { + GList *last_trip = trip; + trip = PREV_TRIP(trip, dive_trip_list); + if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) { + /* we could get here if there are no trips in the XML file + * and we aren't creating trips, either. + * Otherwise we need to create a new trip */ + if (autogroup) { + dive_trip = alloc_dive(); + dive_trip_list = insert_trip(dive_trip, dive_trip_list); + trip = FIND_TRIP(dive_trip, dive_trip_list); + } else { + /* let's go back to the last valid trip */ + trip = last_trip; + } + } else { + dive_trip = trip->data; + dive_trip->number = 0; + } + } + } + /* update dive_trip to include this dive, increase number of dives in + the trip and update location if necessary */ + if (dive_trip) { + dive->tripflag = IN_TRIP; + dive_trip->number++; + dive_trip->when = dive->when; + if (!dive_trip->location && dive->location) + dive_trip->location = dive->location; + if (dive_trip != last_trip) { + last_trip = dive_trip; + /* create trip entry */ + gtk_tree_store_append(treestore, &parent_iter, NULL); + parent_ptr = &parent_iter; + /* a duration of 0 (and negative index) identifies a group */ + gtk_tree_store_set(treestore, parent_ptr, + DIVE_INDEX, -1, + DIVE_NR, dive_trip->number, + DIVE_DATE, dive_trip->when, + DIVE_LOCATION, dive_trip->location, + DIVE_DURATION, 0, -1); } } + + /* store dive */ update_cylinder_related_info(dive); - gtk_tree_store_append(treestore, &iter, parents[NEW_DAY]); + gtk_tree_store_append(treestore, &iter, parent_ptr); gtk_tree_store_set(treestore, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, @@ -827,10 +999,19 @@ static void fill_dive_list(void) DIVE_LOCATION, dive->location, DIVE_RATING, dive->rating, DIVE_TEMPERATURE, dive->watertemp.mkelvin, + DIVE_TOTALWEIGHT, 0, + DIVE_SUIT, dive->suit, DIVE_SAC, 0, -1); } + /* make sure we display the first date of the trip in previous summary */ + if (parent_ptr && dive_trip) + gtk_tree_store_set(treestore, parent_ptr, + DIVE_NR, dive_trip->number, + DIVE_DATE, dive_trip->when, + DIVE_LOCATION, dive_trip->location, + -1); update_dive_list_units(); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) { GtkTreeSelection *selection; @@ -840,8 +1021,6 @@ static void fill_dive_list(void) 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)); - *selectiontracker = selected_dive; } } @@ -866,6 +1045,8 @@ static struct divelist_column { [DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT }, [DIVE_DURATION] = { "min", duration_data_func, NULL, ALIGN_RIGHT }, [DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &visible_cols.temperature }, + [DIVE_TOTALWEIGHT] = { "lbs", weight_data_func, NULL, ALIGN_RIGHT, &visible_cols.totalweight }, + [DIVE_SUIT] = { "Suit", NULL, NULL, ALIGN_LEFT, &visible_cols.suit }, [DIVE_CYLINDER] = { "Cyl", NULL, NULL, 0, &visible_cols.cylinder }, [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox }, [DIVE_SAC] = { "SAC", sac_data_func, NULL, 0, &visible_cols.sac }, @@ -909,6 +1090,20 @@ static void realize_cb(GtkWidget *tree_view, gpointer userdata) gtk_widget_grab_focus(tree_view); } +/* + * Double-clicking on a group entry will expand a collapsed group + * and vice versa. + */ +static void collapse_expand(GtkTreeView *tree_view, GtkTreePath *path) +{ + if (!gtk_tree_view_row_expanded(tree_view, path)) + gtk_tree_view_expand_row(tree_view, path, FALSE); + else + gtk_tree_view_collapse_row(tree_view, path); + +} + +/* Double-click on a dive list */ static void row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, @@ -919,10 +1114,14 @@ static void row_activated_cb(GtkTreeView *tree_view, if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) return; + 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)); + if (index < 0) { + collapse_expand(tree_view, path); + return; + } + edit_dive_info(get_dive(index)); } void add_dive_cb(GtkWidget *menuitem, gpointer data) @@ -940,16 +1139,28 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data) void edit_dive_cb(GtkWidget *menuitem, gpointer data) { - edit_multi_dive_info(amount_selected, selectiontracker); + edit_multi_dive_info(NULL); +} + +static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) +{ + gtk_tree_view_expand_all(tree_view); +} + +static void collapse_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) +{ + gtk_tree_view_collapse_all(tree_view); } static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button) { - GtkWidget *menu, *menuitem; + GtkWidget *menu, *menuitem, *image; char editlabel[] = "Edit dives"; menu = gtk_menu_new(); - menuitem = gtk_menu_item_new_with_label("Add dive"); + menuitem = gtk_image_menu_item_new_with_label("Add dive"); + image = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); if (amount_selected) { @@ -959,6 +1170,12 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_cb), model); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); } + menuitem = gtk_menu_item_new_with_label("Expand all"); + g_signal_connect(menuitem, "activate", G_CALLBACK(expand_all_cb), tree_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + menuitem = gtk_menu_item_new_with_label("Collapse all"); + g_signal_connect(menuitem, "activate", G_CALLBACK(collapse_all_cb), tree_view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, @@ -985,8 +1202,6 @@ static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpoi 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; @@ -994,20 +1209,27 @@ 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. * We always return FALSE to iterate through all dives */ -static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path, +static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { - int i, idx; GtkTreeSelection *selection = GTK_TREE_SELECTION(data); + int idx, selected; + struct dive *dive; - 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; - } + gtk_tree_model_get(model, iter, + DIVE_INDEX, &idx, + -1); + if (idx < 0) { + GtkTreeIter child; + if (gtk_tree_model_iter_children(model, &child, iter)) + gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1); + } + dive = get_dive(idx); + selected = dive && dive->selected; + if (selected) { + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + gtk_tree_selection_select_path(selection, path); + } return FALSE; } @@ -1060,20 +1282,9 @@ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) if (dive_list.model != currentmodel) { 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)); - 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)); - 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); - } + gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), set_selected, selection); } else { if (order != sortorder[colid]) { update_column_and_order(colid); @@ -1093,6 +1304,8 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* Depth */ G_TYPE_INT, /* Duration */ G_TYPE_INT, /* Temperature */ + G_TYPE_INT, /* Total weight */ + G_TYPE_STRING, /* Suit */ G_TYPE_STRING, /* Cylinder */ G_TYPE_INT, /* Nitrox */ G_TYPE_INT, /* SAC */ @@ -1107,6 +1320,8 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* Depth */ G_TYPE_INT, /* Duration */ G_TYPE_INT, /* Temperature */ + G_TYPE_INT, /* Total weight */ + G_TYPE_STRING, /* Suit */ G_TYPE_STRING, /* Cylinder */ G_TYPE_INT, /* Nitrox */ G_TYPE_INT, /* SAC */ @@ -1128,6 +1343,8 @@ GtkWidget *dive_list_create(void) dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH); dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION); dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE); + dive_list.totalweight = divelist_column(&dive_list, dl_column + DIVE_TOTALWEIGHT); + dive_list.suit = divelist_column(&dive_list, dl_column + DIVE_SUIT); dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER); dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX); dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC); @@ -1143,9 +1360,11 @@ GtkWidget *dive_list_create(void) 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), NULL); + g_signal_connect(dive_list.tree_view, "row-expanded", G_CALLBACK(row_expanded_cb), NULL); + g_signal_connect(dive_list.tree_view, "row-collapsed", G_CALLBACK(row_collapsed_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(selection, "changed", G_CALLBACK(selection_cb), dive_list.model); 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);