X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=divelist.c;h=ea59b08758528b7aac5acf121902285111653db1;hb=5ba89c13ac09ee7649abf2d0c4514f9a83bb6c8b;hp=2054ca017ca0b41b8d6642e22ec09aa2e223f687;hpb=81fddfa67e779c8d378eee4c57fd9f201e15f96f;p=ext%2Fsubsurface.git diff --git a/divelist.c b/divelist.c index 2054ca0..ea59b08 100644 --- a/divelist.c +++ b/divelist.c @@ -24,7 +24,7 @@ struct DiveList { GtkWidget *tree_view; GtkWidget *container_widget; - GtkListStore *model; + GtkTreeStore *model, *listmodel, *treemodel; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu; int changed; @@ -52,12 +52,62 @@ 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; + + 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 int *selectiontracker; -static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) +/* 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; - GValue value = {0, }; GtkTreePath *path; int nr_selected = gtk_tree_selection_count_selected_rows(selection); @@ -67,6 +117,7 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) 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 */ @@ -75,9 +126,11 @@ 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_value(model, &iter, DIVE_INDEX, &value); - selected_dive = g_value_get_int(&value); + 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); + selectiontracker[0] = selected_dive; repaint_dive(); } return; @@ -87,8 +140,11 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) * 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, model); + process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model)); repaint_dive(); return; } @@ -109,13 +165,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 < 0) { + *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 +185,43 @@ 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); + 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); + 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); } @@ -151,34 +231,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 < 0) { + *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); } @@ -189,10 +272,14 @@ static void duration_data_func(GtkTreeViewColumn *col, gpointer data) { unsigned int sec; + int 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 < 0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); g_object_set(renderer, "text", buffer, NULL); } @@ -203,13 +290,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 >= 0 && value) { double deg; switch (output_units.temperature) { case CELSIUS: @@ -227,6 +314,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 < 0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", nr); + g_object_set(renderer, "text", buffer, NULL); +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -304,12 +408,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); - dive = get_dive(index); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + *buffer = '\0'; + goto exit; + } + dive = get_dive(idx); get_dive_gas(dive, &o2, &he, &o2low); o2 = (o2 + 5) / 10; he = (he + 5) / 10; @@ -324,7 +432,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 +443,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 < 0 || !value) { + *buffer = '\0'; + goto exit; } sac = value / 1000.0; @@ -358,7 +466,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 +477,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 < 0 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", value); g_object_set(renderer, "text", buffer, NULL); } @@ -503,18 +609,26 @@ 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); - 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, @@ -522,6 +636,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, @@ -529,12 +650,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 < 0) + return FALSE; + dive = get_dive(idx); if (!dive) return TRUE; if (data && dive != data) @@ -582,27 +705,84 @@ 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) +{ + 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; - GtkTreeIter iter; - GtkListStore *store; + int i, j; + GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, }; + GtkTreeStore *liststore, *treestore; + struct dive *last_dive = NULL; + time_t dive_date; - store = GTK_LIST_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) { 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, + -1); + } + } update_cylinder_related_info(dive); - gtk_list_store_append(store, &iter); - gtk_list_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, 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); @@ -611,14 +791,21 @@ 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)); + *selectiontracker = selected_dive; } } 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.treemodel)); + gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel)); fill_dive_list(); repaint_dive(); } @@ -630,7 +817,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,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; - GtkListStore *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; } @@ -676,22 +869,181 @@ 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(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)); +} + +void add_dive_cb(GtkWidget *menuitem, gpointer data) +{ + struct dive *dive; + + dive = alloc_dive(); + if (add_new_dive(dive)) { + record_dive(dive); + report_dives(TRUE); return; - gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); - edit_dive_info(get_dive(index)); + } + free(dive); +} + +static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button) +{ + GtkWidget *menu, *menuitem; + + 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); + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, + button, gtk_get_current_event_time()); +} + +static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) +{ + popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0); +} + +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), GTK_TREE_MODEL(dive_list.model), 3); + return TRUE; + } + return FALSE; +} + +/* we need to have a temporary copy of the selected dives while + switching model as the selection_cb function keeps getting 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. + * 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; + +} + +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. + + 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) { + 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); + } + } else { + if (order != sortorder[colid]) { + update_column_and_order(colid); + } + } } GtkWidget *dive_list_create(void) { GtkTreeSelection *selection; - dive_list.model = gtk_list_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 */ @@ -705,6 +1057,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); @@ -733,8 +1086,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(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),