summaryrefslogtreecommitdiffstats
path: root/divelist.c
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2012-08-21 22:04:24 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2012-08-27 14:32:24 -0700
commite315abf645bbca8eb3cee7d6688db8b088c14cba (patch)
tree19e7c1a2d9c4ee78c19b085a0869eb2a1f93668a /divelist.c
parent5726a50d89d1f76b6bc2cfb96568d41d27f2b63e (diff)
downloadsubsurface-e315abf645bbca8eb3cee7d6688db8b088c14cba.tar.gz
First cut of explicit trip tracking
This code establishes the explicit trip data structures and loads and saves them in the XML data. No attempts are made to edit / modify the trips, yet. Loading XML files without trip data creates the trips based on timing as before. Saving out the same, unmodified data will create 'trip' entries in the XML file with a 'number' that reflects the number of dives in that trip. The trip tag also stores the beginning time of the first dive in the trip and the location of the trip (which we display in the summary entries in the UI). The logic allows for dives that aren't part of a dive trip. All other dives simply belong to the "previous" dive trip - i.e. the dive trip with the latest start time that is earlier or equal to the start time of this dive. This logic significantly simplifies the tracking of trips compared to other approaches that I have tried. The automatic grouping into trips now is an option that defaults to off (as it makes changes to the XML file - and people who don't want this feature shouldn't have trips added to their XML files that they then need to manually remove). For now you have to select this option, then exit the program and start it again. Still to do is to trigger the trip generation at run time. We also need a way to mark dives as not part of trips and to allow options to combine trips, split trips, edit trip location data, etc. The code has only had some limited testing when opening multiple files. The code is known to fail if a location name contains unquoted special characters like an "'". This commit also fixes a visual inconsistency in the preferences dialog where the font selector button didn't have a frame around it that told you what this option was about. Inspired-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'divelist.c')
-rw-r--r--divelist.c178
1 files changed, 104 insertions, 74 deletions
diff --git a/divelist.c b/divelist.c
index ef34b065c..a3bb1fb5e 100644
--- a/divelist.c
+++ b/divelist.c
@@ -31,6 +31,10 @@ struct DiveList {
};
static struct DiveList dive_list;
+GList *dive_trip_list;
+gboolean autogroup = FALSE;
+
+const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP" };
/*
* The dive list has the dive data in both string format (for showing)
@@ -54,19 +58,22 @@ enum {
DIVELIST_COLUMNS
};
-/* magic numbers that indicate (as negative values) model entries that
- * are summary entries for a divetrip */
-#define NEW_TRIP 1
-
#ifdef DEBUG_MODEL
static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
char *location;
- int idx, nr, rating, depth;
+ int idx, nr, duration;
+ struct dive *dive;
+
+ gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1);
+ printf("entry #%d : nr %d duration %d location %s ", idx, nr, duration, location);
+ dive = get_dive(idx);
+ if (dive)
+ printf("tripflag %d\n", dive->tripflag);
+ else
+ printf("without matching dive\n");
- 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;
@@ -327,16 +334,14 @@ static void date_data_func(GtkTreeViewColumn *col,
when = val;
tm = gmtime(&when);
- switch(idx) {
- case -NEW_TRIP:
+ if (idx < 0) {
snprintf(buffer, sizeof(buffer),
"Trip %s, %s %d, %d (%d dive%s)",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
nr, nr > 1 ? "s" : "");
- break;
- default:
+ } else {
snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %02d:%02d",
weekday(tm->tm_wday),
@@ -877,75 +882,101 @@ void update_dive_list_col_visibility(void)
return;
}
-/* random heuristic - not diving in three days implies new dive trip */
-#define TRIP_THRESHOLD 3600*24*3
-static int new_group(struct dive *dive, struct dive **last_dive, time_t *tm_date)
-{
- if (!last_dive)
- return TRUE;
- if (*last_dive) {
- struct dive *ldive = *last_dive;
- if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) {
- *last_dive = dive;
- return FALSE;
- }
- }
- *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, group_size;
- GtkTreeIter iter, parent_iter;
+ int i;
+ GtkTreeIter iter, parent_iter, *parent_ptr = NULL;
GtkTreeStore *liststore, *treestore;
- struct dive *last_dive = NULL;
- struct dive *last_trip_dive = NULL;
- const char *last_location = NULL;
- time_t dive_date;
+ struct dive *last_trip = NULL;
+ GList *trip;
+ struct dive *dive_trip = NULL;
+
+ /* if we have pre-existing trips, start on the last one */
+ trip = g_list_last(dive_trip_list);
+ if (trip)
+ dive_trip = DIVE_TRIP(trip);
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];
-
- if (new_group(dive, &last_dive, &dive_date))
- {
- /* make sure we display the first date of the trip in previous summary */
- if (last_trip_dive)
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_NR, group_size,
- DIVE_DATE, last_trip_dive->when,
- DIVE_LOCATION, last_location,
- -1);
-
- gtk_tree_store_append(treestore, &parent_iter, NULL);
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_INDEX, -NEW_TRIP,
- DIVE_NR, 1,
- DIVE_TEMPERATURE, 0,
- DIVE_SAC, 0,
+ struct dive *dive = get_dive(i);
+
+ /* make sure we display the first date of the trip in previous summary */
+ if (dive_trip && parent_ptr) {
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_NR, dive_trip->number,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
-1);
-
- group_size = 0;
- /* This might be NULL */
- last_location = dive->location;
}
- group_size++;
- last_trip_dive = dive;
- if (dive->location)
- last_location = dive->location;
+ /* tripflag defines how dives are handled;
+ * TF_NONE "not handled yet" - create time based group if autogroup == TRUE
+ * NO_TRIP "set as no group" - simply leave at top level
+ * IN_TRIP "use the trip with the largest trip time (when) that is <= this dive"
+ */
+ if (UNGROUPED_DIVE(dive)) {
+ /* 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 = alloc_dive();
+ dive_trip_list = INSERT_TRIP(dive_trip, dive_trip_list);
+ trip = FIND_TRIP(dive_trip, dive_trip_list);
+ }
+ } else { /* either the dive has a trip or we aren't creating trips */
+ if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) {
+ GList *last_trip = trip;
+ trip = PREV_TRIP(trip, dive_trip_list);
+ if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) {
+ /* we could get here if there are no trips in the XML file
+ * and we aren't creating trips, either.
+ * Otherwise we need to create a new trip */
+ if (autogroup) {
+ dive_trip = alloc_dive();
+ dive_trip_list = INSERT_TRIP(dive_trip, dive_trip_list);
+ trip = FIND_TRIP(dive_trip, dive_trip_list);
+ } else {
+ /* let's go back to the last valid trip */
+ trip = last_trip;
+ }
+ } else {
+ dive_trip = trip->data;
+ dive_trip->number = 0;
+ }
+ }
+ }
+ /* update dive_trip to include this dive, increase number of dives in
+ the trip and update location if necessary */
+ if (dive_trip) {
+ dive->tripflag = IN_TRIP;
+ dive_trip->number++;
+ dive_trip->when = dive->when;
+ if (!dive_trip->location && dive->location)
+ dive_trip->location = dive->location;
+ if (dive_trip != last_trip) {
+ last_trip = dive_trip;
+ /* create trip entry */
+ gtk_tree_store_append(treestore, &parent_iter, NULL);
+ parent_ptr = &parent_iter;
+ /* a duration of 0 (and negative index) identifies a group */
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_INDEX, -1,
+ DIVE_NR, dive_trip->number,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
+ DIVE_DURATION, 0,
+ -1);
+ }
+ }
+
+ /* store dive */
update_cylinder_related_info(dive);
- gtk_tree_store_append(treestore, &iter, &parent_iter);
+ gtk_tree_store_append(treestore, &iter, parent_ptr);
gtk_tree_store_set(treestore, &iter,
DIVE_INDEX, i,
DIVE_NR, dive->number,
@@ -974,13 +1005,12 @@ static void fill_dive_list(void)
}
/* make sure we display the first date of the trip in previous summary */
- if (last_trip_dive)
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_NR, group_size,
- DIVE_DATE, last_trip_dive->when,
- DIVE_LOCATION, last_location,
+ if (parent_ptr && dive_trip)
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_NR, dive_trip->number,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
-1);
-
update_dive_list_units();
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) {
GtkTreeSelection *selection;