X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=divelist.c;h=112307a1c28fbc89d7d96f8899b99f0767d7fdfc;hb=822b6409d752133090df24f5ca38f69656ff82b7;hp=b70b415cae4a064ff52c47162fca0387ab087a09;hpb=27a505e579057596ab10f7381c471b870ce86f87;p=ext%2Fsubsurface.git diff --git a/divelist.c b/divelist.c index b70b415..112307a 100644 --- a/divelist.c +++ b/divelist.c @@ -79,7 +79,73 @@ static void dump_model(GtkListStore *store) #endif static GList *selected_dives; +static int *selectiontracker; +/* 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; + 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; + 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; @@ -92,9 +158,12 @@ 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 */ + 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 */ @@ -102,33 +171,23 @@ 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); + /* 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 */ + * The dives that have been selected are processed */ 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; } @@ -775,8 +834,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; + + /* 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); + 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; } } @@ -818,14 +883,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; } @@ -896,23 +967,104 @@ 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 + 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. */ + 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)); - 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); + } } } @@ -984,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);