diff options
Diffstat (limited to 'divelist.c')
-rw-r--r-- | divelist.c | 248 |
1 files changed, 226 insertions, 22 deletions
diff --git a/divelist.c b/divelist.c index c52a94191..aedcdf03e 100644 --- a/divelist.c +++ b/divelist.c @@ -65,6 +65,9 @@ enum { DIVELIST_COLUMNS }; +static void turn_dive_into_trip(GtkTreePath *path); +static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path); + #ifdef DEBUG_MODEL static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) @@ -136,6 +139,39 @@ 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) +{ + GtkTreeIter iter; + int idx; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + return get_dive(idx); + +} + /* 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) @@ -927,6 +963,49 @@ static struct dive *create_and_hookup_trip_from_dive(struct dive *dive) return dive_trip; } +/* check that a dive should be in a trip starting at 'when' + * first the regular check (dive is before the trip start, but within the + * threshold) + * then for dives that are after the trip start we walk back to the dive + * that starts at when and check on the way that there is no ungrouped + * dive and no break beyond the 3 day threshold between dives that + * haven't already been assigned to this trip */ +static gboolean dive_can_be_in_trip(int idx, struct dive *dive_trip) +{ + struct dive *dive, *pdive; + int i = idx; + time_t when = dive_trip->when; + + dive = get_dive(idx); + /* if the dive is before the trip start but within the threshold + * then just accept it, otherwise reject it */ + if (dive->when < when) { + if (DIVE_FITS_TRIP(dive, dive_trip)) + return TRUE; + else + return FALSE; + } + + while (--i >= 0) { + pdive = get_dive(i); + /* an ungrouped dive cannot be in the middle of a trip + * also, if there are two consecutive dives that are too far apart + * that aren't both already labeled as 'in trip' (which shows that + * this was manually done so we shouldn't override that) */ + if ( UNGROUPED_DIVE(pdive) || + (! (DIVE_IN_TRIP(pdive) && DIVE_IN_TRIP(dive)) && + dive->when - pdive->when > TRIP_THRESHOLD)) { + return FALSE; + } + if (pdive->when == when) + /* done - we have reached the first dive in the trip */ + return TRUE; + dive = pdive; + } + /* we should never get here */ + return TRUE; +} + static void fill_dive_list(void) { int i; @@ -965,17 +1044,27 @@ static void fill_dive_list(void) /* 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 = create_and_hookup_trip_from_dive(dive); - dive_trip->tripflag = IN_TRIP; /* this marks an autogen trip */ - trip = FIND_TRIP(dive_trip->when); + } else if (autogroup && DIVE_NEEDS_TRIP(dive)){ + /* if we already have existing trips there are up to two trips that this + * could potentially be part of. Let's try the one we are on, first */ + if (! (dive_trip && dive_can_be_in_trip(i, dive_trip))) { + /* there could be a better trip in our list already */ + trip = find_matching_trip(dive->when); + if (! (trip && dive_can_be_in_trip(i, DIVE_TRIP(trip)))) { + /* seems like neither of these trips work, so create + * a new one; all fields default to 0 and get filled + * in further down */ + parent_ptr = NULL; + dive_trip = create_and_hookup_trip_from_dive(dive); + dive_trip->tripflag = IN_TRIP; + trip = FIND_TRIP(dive_trip->when); + } + if (trip) + dive_trip = DIVE_TRIP(trip); } } else if (DIVE_IN_TRIP(dive)) { - trip = find_matching_trip(dive->when); - dive_trip = DIVE_TRIP(trip); + trip = find_matching_trip(dive->when); + dive_trip = DIVE_TRIP(trip); } else { /* dive is not in a trip and we aren't autogrouping */ dive_trip = NULL; @@ -1188,13 +1277,7 @@ void edit_selected_dives_cb(GtkWidget *menuitem, gpointer data) void edit_dive_from_path_cb(GtkWidget *menuitem, GtkTreePath *path) { - GtkTreeIter iter; - int idx; - struct dive *dive; - - gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); - gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); - dive = get_dive(idx); + struct dive *dive = dive_from_path(path); edit_multi_dive_info(dive); } @@ -1310,6 +1393,48 @@ static GtkTreeIter *move_dive_between_trips(GtkTreeIter *dive_iter, GtkTreeIter return new_iter; } +/* this gets called when we are on a top level dive and we know that the previous + * top level node is a trip; if multiple consecutive dives are selected, they are + * all merged into the previous trip*/ +static void merge_dive_into_trip_above_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + int idx; + GtkTreeIter dive_iter, trip_iter, prev_iter; + GtkTreePath *trip_path; + struct dive *dive, *prev_dive; + + /* get the path and iter for the trip and the last dive of that trip */ + trip_path = gtk_tree_path_copy(path); + (void)gtk_tree_path_prev(trip_path); + gtk_tree_model_get_iter(MODEL(dive_list), &trip_iter, trip_path); + gtk_tree_model_get_iter(MODEL(dive_list), &dive_iter, path); + gtk_tree_model_iter_nth_child(MODEL(dive_list), &prev_iter, &trip_iter, + gtk_tree_model_iter_n_children(MODEL(dive_list), &trip_iter) - 1); + gtk_tree_model_get(MODEL(dive_list), &dive_iter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + gtk_tree_model_get(MODEL(dive_list), &prev_iter, DIVE_INDEX, &idx, -1); + prev_dive = get_dive(idx); + /* add the dive to the trip */ + for (;;) { + dive->divetrip = prev_dive->divetrip; + dive->tripflag = IN_TRIP; + (void)move_dive_between_trips(&dive_iter, NULL, &trip_iter, NULL, TRUE); + prev_dive = dive; + /* by merging the dive into the trip above the path now points to the next + top level entry. If that iter exists, it's also a dive and both this dive + and that next dive are selected, continue merging dives into the trip */ + if (!gtk_tree_model_get_iter(MODEL(dive_list), &dive_iter, path)) + break; + gtk_tree_model_get(MODEL(dive_list), &dive_iter, DIVE_INDEX, &idx, -1); + if (idx < 0) + break; + dive = get_dive(idx); + if (!dive->selected || !prev_dive->selected) + break; + } + mark_divelist_changed(TRUE); +} + static void turn_dive_into_trip(GtkTreePath *path) { GtkTreeIter iter, *newiter, newparent; @@ -1390,10 +1515,25 @@ static void insert_trip_before(GtkTreePath *path) static void insert_trip_before_cb(GtkWidget *menuitem, GtkTreePath *path) { /* is this splitting a trip or turning a dive into a trip? */ - if (gtk_tree_path_get_depth(path) == 2) + if (gtk_tree_path_get_depth(path) == 2) { insert_trip_before(path); - else + } else { /* this is a top level dive */ + struct dive *dive, *next_dive; + GtkTreePath *next_path; + + dive = dive_from_path(path); turn_dive_into_trip(path); + /* if the dive was selected and the next dive was selected, too, + * then all of them should be part of the new trip */ + if (dive->selected) { + next_path = gtk_tree_path_copy(path); + gtk_tree_path_next(next_path); + next_dive = dive_from_path(next_path); + if (next_dive && next_dive->selected) + merge_dive_into_trip_above_cb(menuitem, next_path); + } + } + mark_divelist_changed(TRUE); } static void remove_from_trip(GtkTreePath *path) @@ -1492,6 +1632,7 @@ static void remove_from_trip_cb(GtkWidget *menuitem, GtkTreePath *path) /* just remove the dive the mouse pointer is on */ remove_from_trip(path); } + mark_divelist_changed(TRUE); } void remove_trip(GtkTreePath *trippath, gboolean force_no_trip) @@ -1540,6 +1681,7 @@ void remove_trip(GtkTreePath *trippath, gboolean force_no_trip) void remove_trip_cb(GtkWidget *menuitem, GtkTreePath *trippath) { remove_trip(trippath, TRUE); + mark_divelist_changed(TRUE); } void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath) @@ -1570,6 +1712,55 @@ void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath) free(DIVE_TRIP(trip)); delete_trip(trip); gtk_tree_store_remove(STORE(dive_list), &thistripiter); + mark_divelist_changed(TRUE); +} + +/* this gets called with path pointing to a dive, either in the top level + * or as part of a trip */ +static void delete_dive_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + GtkTreeIter iter; + int idx, i; + struct dive *dive, *pdive, *ndive; + GtkTreeView *tree_view = GTK_TREE_VIEW(dive_list.tree_view); + GtkTreeSelection *selection = gtk_tree_view_get_selection(tree_view); + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + 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); + } + } + /* 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++) + dive_table.dives[i] = dive_table.dives[i+1]; + dive_table.nr--; + free(dive); + dive_list_update_dives(); + /* now make sure the same dives stay selected and if necessary their trips are expanded */ + for_each_dive(i, dive) { + 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); } static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button, GdkEventButton *event) @@ -1622,6 +1813,9 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int g_signal_connect(menuitem, "activate", G_CALLBACK(remove_trip_cb), path); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); } else { + menuitem = gtk_menu_item_new_with_label("Delete Dive"); + g_signal_connect(menuitem, "activate", G_CALLBACK(delete_dive_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); dive = get_dive(idx); /* if we right click on selected dive(s), edit those */ if (dive->selected) { @@ -1638,14 +1832,24 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int } /* only offer trip editing options when we are displaying the tree model */ if (dive_list.model == dive_list.treemodel) { - int depth; - int *indices = gtk_tree_path_get_indices_with_depth(path, &depth); - + int depth = gtk_tree_path_get_depth(path); + int *indices = gtk_tree_path_get_indices(path); + /* top level dive or child dive that is not the first child */ if (depth == 1 || indices[1] > 0) { - menuitem = gtk_menu_item_new_with_label("Add new trip above"); + menuitem = gtk_menu_item_new_with_label("Create new trip above"); g_signal_connect(menuitem, "activate", G_CALLBACK(insert_trip_before_cb), path); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); } + prevpath = gtk_tree_path_copy(path); + /* top level dive with a trip right before it */ + if (depth == 1 && + gtk_tree_path_prev(prevpath) && + gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath) && + gtk_tree_model_iter_n_children(model, &previter)) { + menuitem = gtk_menu_item_new_with_label("Add to trip above"); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_dive_into_trip_above_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } if (DIVE_IN_TRIP(dive)) { if (dive->selected && amount_selected > 1) menuitem = gtk_menu_item_new_with_label("Remove selected dives from trip"); |