mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 13:10:19 +00:00
Redo the delete dive code
The existing code (both my first single dive delete and then Lubomir's multi dive delete code) had way too many issues and was just painfully inefficient. This new code takes a radically different approach and mostly ignores the Gtk tree model (as that gets recreated after a delete, anyway) and instead is linear time on the number of dives in the list. It does do its best to maintain the existing selection and the expand state of tree model (the latter isn't possible if we have switched to the list model). Many thanks to "Lubomir I. Ivanov" <neolit123@gmail.com> for his work on this - this commit actually contains a few lines out of one of the patches that he wrote. Reported-by: "Lubomir I. Ivanov" <neolit123@gmail.com> Tested-by: "Lubomir I. Ivanov" <neolit123@gmail.com> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
bd3df859bc
commit
7f5ecb5bb7
2 changed files with 110 additions and 102 deletions
1
dive.h
1
dive.h
|
@ -246,6 +246,7 @@ typedef struct dive_trip {
|
||||||
timestamp_t when_from_file;
|
timestamp_t when_from_file;
|
||||||
char *location;
|
char *location;
|
||||||
char *notes;
|
char *notes;
|
||||||
|
int expanded:1, selected:1;
|
||||||
} dive_trip_t;
|
} dive_trip_t;
|
||||||
|
|
||||||
struct dive {
|
struct dive {
|
||||||
|
|
211
divelist.c
211
divelist.c
|
@ -142,28 +142,6 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GtkTreePath *path_match;
|
|
||||||
|
|
||||||
static gboolean match_dive(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
|
||||||
{
|
|
||||||
int idx;
|
|
||||||
struct dive *dive = data;
|
|
||||||
|
|
||||||
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
|
|
||||||
if (idx >= 0)
|
|
||||||
if (get_dive(idx)->when == dive->when) {
|
|
||||||
path_match = gtk_tree_path_copy(path);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GtkTreePath *get_path_from(struct dive *dive)
|
|
||||||
{
|
|
||||||
gtk_tree_model_foreach(TREEMODEL(dive_list), match_dive, dive);
|
|
||||||
return path_match;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dive *dive_from_path(GtkTreePath *path)
|
static struct dive *dive_from_path(GtkTreePath *path)
|
||||||
{
|
{
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
|
@ -1842,85 +1820,120 @@ void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath)
|
||||||
mark_divelist_changed(TRUE);
|
mark_divelist_changed(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delete a dive by passing a tree iterator */
|
/* this implements the mechanics of removing the dive from the table,
|
||||||
static void delete_single_dive(GtkTreeIter *iter)
|
* but doesn't deal with updating dive trips, etc */
|
||||||
|
static void delete_single_dive(int idx)
|
||||||
{
|
{
|
||||||
int i, idx;
|
int i;
|
||||||
struct dive *dive, *pdive, *ndive;
|
struct dive *dive = get_dive(idx);
|
||||||
GtkTreeIter parent;
|
if (!dive)
|
||||||
|
return; /* this should never happen */
|
||||||
gtk_tree_model_get(MODEL(dive_list), iter, DIVE_INDEX, &idx, -1);
|
|
||||||
dive = get_dive(idx);
|
|
||||||
|
|
||||||
if (dive->divetrip) {
|
|
||||||
/* we could be displaying the list model, in which case we can't find out
|
|
||||||
* if this is part of a trip and the only dive in that trip from the model,
|
|
||||||
* so let's do this the manual way */
|
|
||||||
pdive = get_dive(idx - 1);
|
|
||||||
ndive = get_dive(idx + 1);
|
|
||||||
if (! (pdive && pdive->divetrip == dive->divetrip) &&
|
|
||||||
! (ndive && ndive->divetrip == dive->divetrip)) {
|
|
||||||
/* if this is the only dive in the trip, remove the trip - the
|
|
||||||
* dive list update below will deal with making sure the treemodel
|
|
||||||
* is correct */
|
|
||||||
GList *trip = find_matching_trip(dive->when);
|
|
||||||
delete_trip(trip);
|
|
||||||
free(dive->divetrip);
|
|
||||||
dive->divetrip = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simply remove the dive and recreate the divelist
|
|
||||||
* (we can't just manipulate the tree_view as the indices for dives change) */
|
|
||||||
for (i = idx; i < dive_table.nr - 1; i++)
|
for (i = idx; i < dive_table.nr - 1; i++)
|
||||||
dive_table.dives[i] = dive_table.dives[i+1];
|
dive_table.dives[i] = dive_table.dives[i+1];
|
||||||
dive_table.nr--;
|
dive_table.nr--;
|
||||||
if (dive->selected)
|
if (dive->selected)
|
||||||
amount_selected--;
|
amount_selected--;
|
||||||
|
|
||||||
/* once the dive is deleted, update the 'when' flag of the trip it was part of
|
|
||||||
* to the 'when' flag of the earliest dive left in the same trip */
|
|
||||||
if (dive->divetrip) {
|
|
||||||
gtk_tree_model_iter_parent(MODEL(dive_list), &parent, iter);
|
|
||||||
gtk_tree_store_remove(STORE(dive_list), iter);
|
|
||||||
update_trip_timestamp(&parent, dive->divetrip);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(dive);
|
free(dive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* workaround for not using gtk_tree_selection_get_selected_rows() or modifying the tree
|
/* remember expanded state */
|
||||||
* directly from the gtk_tree_selection_selected_foreach() callback function */
|
static void remember_tree_state()
|
||||||
struct tree_selected_st {
|
|
||||||
GtkTreeIter *list;
|
|
||||||
int total;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void tree_selected_foreach(GtkTreeModel *model, GtkTreePath *path,
|
|
||||||
GtkTreeIter *iter, gpointer userdata)
|
|
||||||
{
|
{
|
||||||
struct tree_selected_st *st = (struct tree_selected_st *)userdata;
|
GtkTreeIter iter;
|
||||||
|
if (!gtk_tree_model_get_iter_first(TREEMODEL(dive_list), &iter))
|
||||||
|
return;
|
||||||
|
do {
|
||||||
|
int idx;
|
||||||
|
timestamp_t when;
|
||||||
|
GtkTreePath *path;
|
||||||
|
|
||||||
st->total++;
|
gtk_tree_model_get(TREEMODEL(dive_list), &iter,
|
||||||
st->list = (GtkTreeIter *)realloc(st->list, sizeof(GtkTreeIter) * st->total);
|
DIVE_INDEX, &idx, DIVE_DATE, &when, -1);
|
||||||
memcpy(&st->list[st->total - 1], iter, sizeof(GtkTreeIter));
|
if (idx >= 0)
|
||||||
|
continue;
|
||||||
|
path = gtk_tree_model_get_path(TREEMODEL(dive_list), &iter);
|
||||||
|
if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), path))
|
||||||
|
DIVE_TRIP(find_trip(when))->expanded = TRUE;
|
||||||
|
} while (gtk_tree_model_iter_next(TREEMODEL(dive_list), &iter));
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean restore_node_state(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
timestamp_t when;
|
||||||
|
struct dive *dive;
|
||||||
|
GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view);
|
||||||
|
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
|
||||||
|
|
||||||
|
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1);
|
||||||
|
if (idx < 0) {
|
||||||
|
if (DIVE_TRIP(find_trip(when))->expanded)
|
||||||
|
gtk_tree_view_expand_row(tree_view, path, FALSE);
|
||||||
|
if (DIVE_TRIP(find_trip(when))->selected)
|
||||||
|
gtk_tree_selection_select_iter(selection, iter);
|
||||||
|
} else {
|
||||||
|
dive = get_dive(idx);
|
||||||
|
if (dive->selected)
|
||||||
|
gtk_tree_selection_select_iter(selection, iter);
|
||||||
|
}
|
||||||
|
/* continue foreach */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* restore expanded and selected state */
|
||||||
|
static void restore_tree_state()
|
||||||
|
{
|
||||||
|
gtk_tree_model_foreach(MODEL(dive_list), restore_node_state, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* called when multiple dives are selected and one of these is right-clicked for delete */
|
/* called when multiple dives are selected and one of these is right-clicked for delete */
|
||||||
static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path)
|
static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view);
|
gboolean divetrip_needs_update = FALSE;
|
||||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
|
dive_trip_t *divetrip_to_update = NULL;
|
||||||
struct tree_selected_st st = {NULL, 0};
|
struct dive *dive;
|
||||||
|
|
||||||
gtk_tree_selection_selected_foreach(selection, tree_selected_foreach, &st);
|
remember_tree_state();
|
||||||
|
/* walk the dive list in chronological order */
|
||||||
for (i = 0; i < st.total; i++)
|
for (i = 0; i < dive_table.nr; i++) {
|
||||||
delete_single_dive(&st.list[i]);
|
dive = get_dive(i);
|
||||||
free(st.list);
|
if (!dive)
|
||||||
|
continue;
|
||||||
|
if (! dive->selected) {
|
||||||
|
if (divetrip_needs_update) {
|
||||||
|
divetrip_needs_update = FALSE;
|
||||||
|
/* this is the first unselected dive since we found the
|
||||||
|
* trip that needed to be updated; if we are still on the
|
||||||
|
* same trip, update the time, else delete the trip */
|
||||||
|
if (dive->divetrip == divetrip_to_update)
|
||||||
|
divetrip_to_update->when = dive->when;
|
||||||
|
else
|
||||||
|
delete_trip(find_trip(divetrip_to_update->when));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is a selected (i.e., to be deleted) dive; if we have a divetrip
|
||||||
|
* that needs to be updated, check if this dive is still in that trip;
|
||||||
|
* if not, delete the trip */
|
||||||
|
if (divetrip_needs_update && dive->divetrip != divetrip_to_update) {
|
||||||
|
delete_trip(find_trip(divetrip_to_update->when));
|
||||||
|
divetrip_needs_update = FALSE;
|
||||||
|
}
|
||||||
|
/* if this dive is part of a divetrip and is the first dive that
|
||||||
|
* determines the timestamp, then remember that divetrip for updating */
|
||||||
|
if (dive->divetrip && dive->when == dive->divetrip->when) {
|
||||||
|
divetrip_needs_update = TRUE;
|
||||||
|
divetrip_to_update = dive->divetrip;
|
||||||
|
}
|
||||||
|
/* now remove the dive from the table and free it. also move the iterator back,
|
||||||
|
* so that we don't skip a dive */
|
||||||
|
delete_single_dive(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
dive_list_update_dives();
|
dive_list_update_dives();
|
||||||
|
restore_tree_state();
|
||||||
mark_divelist_changed(TRUE);
|
mark_divelist_changed(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1928,32 +1941,26 @@ static void delete_selected_dives_cb(GtkWidget *menuitem, GtkTreePath *path)
|
||||||
* or as part of a trip */
|
* or as part of a trip */
|
||||||
static void delete_dive_cb(GtkWidget *menuitem, GtkTreePath *path)
|
static void delete_dive_cb(GtkWidget *menuitem, GtkTreePath *path)
|
||||||
{
|
{
|
||||||
GtkTreeIter iter;
|
int idx;
|
||||||
int i, idx;
|
|
||||||
struct dive *dive;
|
struct dive *dive;
|
||||||
GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view);
|
GtkTreeIter iter;
|
||||||
GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view);
|
|
||||||
|
|
||||||
gtk_tree_model_get_iter(MODEL(dive_list), &iter, path);
|
|
||||||
delete_single_dive(&iter);
|
|
||||||
|
|
||||||
|
remember_tree_state();
|
||||||
|
if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path))
|
||||||
|
return;
|
||||||
gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1);
|
gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1);
|
||||||
dive = get_dive(idx);
|
dive = get_dive(idx);
|
||||||
|
/* do we need to update the trip this dive is part of? */
|
||||||
/* now make sure the correct dive list is displayed, the same
|
if (dive->divetrip && dive->when == dive->divetrip->when) {
|
||||||
* dives stay selected and if necessary their trips are
|
struct dive *next_dive = get_dive(idx + 1);
|
||||||
* expanded. If no selected dives are left then fill_dive_list()
|
if (!next_dive || next_dive->divetrip != dive->divetrip)
|
||||||
* (which gets called from dive_list_update_dives()) will once
|
delete_trip(find_trip(dive->when));
|
||||||
* again select the last dive in the dive list */
|
else
|
||||||
|
next_dive->divetrip->when = next_dive->when;
|
||||||
|
}
|
||||||
|
delete_single_dive(idx);
|
||||||
dive_list_update_dives();
|
dive_list_update_dives();
|
||||||
for_each_dive(i, dive) {
|
restore_tree_state();
|
||||||
if (dive->selected) {
|
|
||||||
GtkTreePath *path = get_path_from(dive);
|
|
||||||
if (MODEL(dive_list) == TREEMODEL(dive_list))
|
|
||||||
gtk_tree_view_expand_to_path(tree_view, path);
|
|
||||||
gtk_tree_selection_select_path(selection, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mark_divelist_changed(TRUE);
|
mark_divelist_changed(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue