#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;
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 */
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;
}
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;
}
}
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;
}
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);
+ }
}
}
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);