diff options
-rw-r--r-- | dive.h | 46 | ||||
-rw-r--r-- | divelist.c | 647 | ||||
-rw-r--r-- | divelist.h | 1 | ||||
-rw-r--r-- | dives/test21.xml | 2 | ||||
-rw-r--r-- | dives/test22.xml | 2 | ||||
-rw-r--r-- | dives/test23.xml | 9 | ||||
-rw-r--r-- | gtk-gui.c | 18 | ||||
-rw-r--r-- | info.c | 62 | ||||
-rw-r--r-- | parse-xml.c | 13 | ||||
-rw-r--r-- | save-xml.c | 4 |
10 files changed, 707 insertions, 97 deletions
@@ -241,6 +241,7 @@ extern const char *tripflag_names[NUM_TRIPFLAGS]; struct dive { int number; tripflag_t tripflag; + struct dive *divetrip; int selected; time_t when; char *location; @@ -270,49 +271,63 @@ extern gboolean autogroup; #define UNGROUPED_DIVE(_dive) ((_dive)->tripflag == NO_TRIP) #define DIVE_IN_TRIP(_dive) ((_dive)->tripflag == IN_TRIP) -#define NEXT_TRIP(_entry, _list) ((_entry) ? g_list_next(_entry) : (_list)) -#define PREV_TRIP(_entry, _list) ((_entry) ? g_list_previous(_entry) : g_list_last(_list)) +#define NEXT_TRIP(_entry) ((_entry) ? g_list_next(_entry) : (dive_trip_list)) +#define PREV_TRIP(_entry) ((_entry) ? g_list_previous(_entry) : g_list_last(dive_trip_list)) #define DIVE_TRIP(_trip) ((struct dive *)(_trip)->data) #define DIVE_FITS_TRIP(_dive, _dive_trip) ((_dive_trip)->when - TRIP_THRESHOLD <= (_dive)->when) +/* compare two dives by when they happened */ static inline int dive_date_cmp(gconstpointer _a, gconstpointer _b) { - return ((struct dive *)(_a))->when - ((struct dive *)(_b))->when; + return ((struct dive *)_a)->when - ((struct dive *)_b)->when; } -#define FIND_TRIP(_trip, _list) g_list_find_custom((_list), (_trip), dive_date_cmp) +/* returns 0 if the dive happened exactly at time */ +static inline int dive_when_find(gconstpointer _dive, gconstpointer _time) { + return ((struct dive *)_dive)->when != (time_t) _time; +} + +#define FIND_TRIP(_when) g_list_find_custom(dive_trip_list, (gconstpointer)(_when), dive_when_find) #ifdef DEBUG_TRIP static void dump_trip_list(void) { GList *p = NULL; int i=0; - while ((p = NEXT_TRIP(p, dive_trip_list))) { + while ((p = NEXT_TRIP(p))) { struct tm *tm = gmtime(&DIVE_TRIP(p)->when); - printf("trip %d to \"%s\" on %04u-%02u-%02u\n", ++i, DIVE_TRIP(p)->location, - tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday); + printf("trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u\n", ++i, DIVE_TRIP(p)->location, + tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } printf("-----\n"); } #endif -/* insert the trip into the list - but ensure you don't have two trips - * for the same date; but if you have, make sure you don't keep the - * one with less information */ -static inline GList *insert_trip(struct dive *_trip, GList *_list) +/* insert the trip into the dive_trip_list - but ensure you don't have + * two trips for the same date; but if you have, make sure you don't + * keep the one with less information */ +static void inline insert_trip(struct dive **trip) { - GList *result = FIND_TRIP(_trip, _list); + struct dive *dive_trip = *trip; + GList *result = FIND_TRIP(dive_trip->when); if (result) { if (! DIVE_TRIP(result)->location) - DIVE_TRIP(result)->location = _trip->location; + DIVE_TRIP(result)->location = dive_trip->location; + *trip = DIVE_TRIP(result); } else { - result = g_list_insert_sorted((_list), (_trip), dive_date_cmp); + dive_trip_list = g_list_insert_sorted(dive_trip_list, dive_trip, dive_date_cmp); } #ifdef DEBUG_TRIP dump_trip_list(); #endif - return result; } +static inline void delete_trip(GList *trip) +{ + dive_trip_list = g_list_delete_link(dive_trip_list, trip); +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} /* * We keep our internal data in well-specified units, but * the input and output may come in some random format. This @@ -421,6 +436,7 @@ extern void remember_event(const char *eventname); extern void evn_foreach(void (*callback)(const char *, int *, void *), void *data); extern int add_new_dive(struct dive *dive); +extern gboolean edit_trip(struct dive *trip); extern int edit_dive_info(struct dive *dive); extern int edit_multi_dive_info(struct dive *single_dive); extern void dive_list_update_dives(void); diff --git a/divelist.c b/divelist.c index 1e1d1c921..c52a94191 100644 --- a/divelist.c +++ b/divelist.c @@ -31,6 +31,13 @@ struct DiveList { }; static struct DiveList dive_list; +#define MODEL(_dl) GTK_TREE_MODEL((_dl).model) +#define TREEMODEL(_dl) GTK_TREE_MODEL((_dl).treemodel) +#define LISTMODEL(_dl) GTK_TREE_MODEL((_dl).listmodel) +#define STORE(_dl) GTK_TREE_STORE((_dl).model) +#define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel) +#define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel) + GList *dive_trip_list; gboolean autogroup = FALSE; @@ -65,9 +72,17 @@ static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, char *location; int idx, nr, duration; struct dive *dive; + time_t when; + struct tm *tm; - 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); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DATE, &when, + DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1); + tm = gmtime(&when); + printf("iter %x:%x entry #%d : nr %d @ %04d-%02d-%02d %02d:%02d:%02d duration %d location %s ", + iter->stamp, iter->user_data, idx, nr, + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + duration, location); dive = get_dive(idx); if (dive) printf("tripflag %d\n", dive->tripflag); @@ -82,6 +97,7 @@ static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path, static void dump_model(GtkListStore *store) { gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL); + printf("\n---\n\n"); } #endif @@ -116,7 +132,7 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) 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); + gtk_tree_model_get(model, iter, DIVE_INDEX, diveidx, -1); } } @@ -125,7 +141,7 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) { GtkTreeIter child; - GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeModel *model = MODEL(dive_list); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); if (!gtk_tree_model_iter_children(model, &child, iter)) @@ -169,7 +185,7 @@ static int selected_children(GtkTreeModel *model, GtkTreeIter *iter) shows up as selected too */ void row_collapsed_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data) { - GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeModel *model = MODEL(dive_list); GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); if (selected_children(model, iter)) @@ -253,7 +269,7 @@ static void select_dive_group(GtkTreeModel *model, GtkTreeSelection *selection, */ static void check_selection_cb(GtkTreeIter *iter, GtkTreeSelection *selection) { - GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeModel *model = MODEL(dive_list); struct dive *dive; int idx, gtk_selected; @@ -449,13 +465,20 @@ static void nr_data_func(GtkTreeViewColumn *col, { int idx, nr; char buffer[40]; + struct dive *dive; gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1); - if (idx < 0) + if (idx < 0) { *buffer = '\0'; - else - snprintf(buffer, sizeof(buffer), "%d", nr); - g_object_set(renderer, "text", buffer, NULL); + } else { + /* make dives that are not in trips stand out */ + dive = get_dive(idx); + if (!DIVE_IN_TRIP(dive)) + snprintf(buffer, sizeof(buffer), "<b>%d</b>", nr); + else + snprintf(buffer, sizeof(buffer), "%d", nr); + } + g_object_set(renderer, "markup", buffer, NULL); } /* @@ -787,7 +810,7 @@ static void fill_one_dive(struct dive *dive, GtkTreeIter *iter) { char *location, *cylinder, *suit; - GtkTreeStore *othermodel; + GtkTreeModel *othermodel; get_cylinder(dive, &cylinder); get_location(dive, &location); @@ -808,13 +831,13 @@ static void fill_one_dive(struct dive *dive, free(cylinder); free(suit); - if (model == GTK_TREE_MODEL(dive_list.treemodel)) - othermodel = dive_list.listmodel; + if (model == TREEMODEL(dive_list)) + othermodel = LISTMODEL(dive_list); else - othermodel = dive_list.treemodel; - if (othermodel != dive_list.model) + othermodel = TREEMODEL(dive_list); + if (othermodel != MODEL(dive_list)) /* recursive call */ - gtk_tree_model_foreach(GTK_TREE_MODEL(othermodel), set_one_dive, dive); + gtk_tree_model_foreach(othermodel, set_one_dive, dive); } static gboolean set_one_dive(GtkTreeModel *model, @@ -841,7 +864,7 @@ static gboolean set_one_dive(GtkTreeModel *model, void flush_divelist(struct dive *dive) { - GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeModel *model = MODEL(dive_list); gtk_tree_model_foreach(model, set_one_dive, dive); } @@ -856,7 +879,7 @@ void set_divelist_font(const char *font) void update_dive_list_units(void) { const char *unit; - GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model); + GtkTreeModel *model = MODEL(dive_list); (void) get_depth_units(0, NULL, &unit); gtk_tree_view_column_set_title(dive_list.depth, unit); @@ -882,6 +905,28 @@ void update_dive_list_col_visibility(void) return; } +static GList *find_matching_trip(time_t when) +{ + GList *trip = dive_trip_list; + if (!trip || DIVE_TRIP(trip)->when > when) + return NULL; + while (trip->next && DIVE_TRIP(trip->next)->when <= when) + trip = trip->next; + return trip; +} + +static struct dive *create_and_hookup_trip_from_dive(struct dive *dive) +{ + struct dive *dive_trip = alloc_dive(); + dive_trip->when = dive->when; + if (dive->location) + dive_trip->location = strdup(dive->location); + insert_trip(&dive_trip); + dive->divetrip = dive_trip; + dive->tripflag = IN_TRIP; + return dive_trip; +} + static void fill_dive_list(void) { int i; @@ -894,8 +939,8 @@ static void fill_dive_list(void) /* if we have pre-existing trips, start on the last one */ trip = g_list_last(dive_trip_list); - treestore = GTK_TREE_STORE(dive_list.treemodel); - liststore = GTK_TREE_STORE(dive_list.listmodel); + treestore = TREESTORE(dive_list); + liststore = LISTSTORE(dive_list); i = dive_table.nr; while (--i >= 0) { @@ -924,35 +969,25 @@ static void fill_dive_list(void) 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 = 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 (DIVE_IN_TRIP(dive)) { + 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; + parent_ptr = NULL; } - /* update dive_trip time and (if necessary) location */ + /* update dive as part of dive_trip and + * (if necessary) update dive_trip time and location */ if (dive_trip) { dive->tripflag = IN_TRIP; - dive_trip->when = dive->when; + dive->divetrip = dive_trip; + if (dive_trip->when > dive->when) + dive_trip->when = dive->when; if (!dive_trip->location && dive->location) dive_trip->location = dive->location; if (dive_trip != last_trip) { @@ -1007,12 +1042,12 @@ static void fill_dive_list(void) DIVE_LOCATION, dive_trip->location, -1); update_dive_list_units(); - if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) { + if (gtk_tree_model_get_iter_first(MODEL(dive_list), &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); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &selected_dive, -1); + first_leaf(MODEL(dive_list), &iter, &selected_dive); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); gtk_tree_selection_select_iter(selection, &iter); } @@ -1020,8 +1055,8 @@ static void fill_dive_list(void) void dive_list_update_dives(void) { - gtk_tree_store_clear(GTK_TREE_STORE(dive_list.treemodel)); - gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel)); + gtk_tree_store_clear(TREESTORE(dive_list)); + gtk_tree_store_clear(LISTSTORE(dive_list)); fill_dive_list(); repaint_dive(); } @@ -1106,10 +1141,10 @@ static void row_activated_cb(GtkTreeView *tree_view, int index; GtkTreeIter iter; - if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) + if (!gtk_tree_model_get_iter(MODEL(dive_list), &iter, path)) return; - gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &index, -1); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &index, -1); /* a negative index is special for the "group by date" entries */ if (index < 0) { collapse_expand(tree_view, path); @@ -1131,11 +1166,39 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data) free(dive); } -void edit_dive_cb(GtkWidget *menuitem, gpointer data) +void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + GtkTreeIter iter; + time_t when; + struct dive *dive_trip; + GList *trip; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_DATE, &when, -1); + trip = FIND_TRIP(when); + dive_trip = DIVE_TRIP(trip); + if (edit_trip(dive_trip)) + gtk_tree_store_set(STORE(dive_list), &iter, DIVE_LOCATION, dive_trip->location, -1); +} + +void edit_selected_dives_cb(GtkWidget *menuitem, gpointer data) { edit_multi_dive_info(NULL); } +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); + + edit_multi_dive_info(dive); +} + static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) { gtk_tree_view_expand_all(tree_view); @@ -1146,10 +1209,382 @@ static void collapse_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view) gtk_tree_view_collapse_all(tree_view); } -static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button) +/* copy the node and return the index */ +static int copy_tree_node(GtkTreeIter *a, GtkTreeIter *b) +{ + struct dive store_dive; + int totalweight, idx; + char *cylinder_text; + + gtk_tree_model_get(MODEL(dive_list), a, + DIVE_INDEX, &idx, + DIVE_NR, &store_dive.number, + DIVE_DATE, &store_dive.when, + DIVE_RATING, &store_dive.rating, + DIVE_DEPTH, &store_dive.maxdepth, + DIVE_DURATION, &store_dive.duration, + DIVE_TEMPERATURE, &store_dive.watertemp.mkelvin, + DIVE_TOTALWEIGHT, &totalweight, + DIVE_SUIT, &store_dive.suit, + DIVE_CYLINDER, &cylinder_text, + DIVE_SAC, &store_dive.sac, + DIVE_OTU, &store_dive.otu, + DIVE_LOCATION, &store_dive.location, + -1); + gtk_tree_store_set(STORE(dive_list), b, + DIVE_INDEX, idx, + DIVE_NR, store_dive.number, + DIVE_DATE, store_dive.when, + DIVE_RATING, store_dive.rating, + DIVE_DEPTH, store_dive.maxdepth, + DIVE_DURATION, store_dive.duration, + DIVE_TEMPERATURE, store_dive.watertemp.mkelvin, + DIVE_TOTALWEIGHT, totalweight, + DIVE_SUIT, store_dive.suit, + DIVE_CYLINDER, cylinder_text, + DIVE_SAC, store_dive.sac, + DIVE_OTU, store_dive.otu, + DIVE_LOCATION, store_dive.location, + -1); + return idx; +} + +/* to avoid complicated special cases based on ordering or number of children, + we always take the first and last child and pick the smaller time_t (which + works regardless of ordering and also with just one child) */ +static void update_trip_timestamp(GtkTreeIter *parent, struct dive *divetrip) +{ + GtkTreeIter first_child, last_child; + int nr; + time_t t1, t2, tnew; + + if (gtk_tree_store_iter_depth(STORE(dive_list), parent) != 0 || + gtk_tree_model_iter_n_children(MODEL(dive_list), parent) == 0) + return; + nr = gtk_tree_model_iter_n_children(MODEL(dive_list), parent); + gtk_tree_model_iter_nth_child(MODEL(dive_list), &first_child, parent, 0); + gtk_tree_model_get(MODEL(dive_list), &first_child, DIVE_DATE, &t1, -1); + gtk_tree_model_iter_nth_child(MODEL(dive_list), &last_child, parent, nr - 1); + gtk_tree_model_get(MODEL(dive_list), &last_child, DIVE_DATE, &t2, -1); + tnew = MIN(t1, t2); + gtk_tree_store_set(STORE(dive_list), parent, DIVE_DATE, tnew, -1); + if (divetrip) + divetrip->when = tnew; +} + +/* move dive_iter, which is a child of old_trip (could be NULL) to new_trip (could be NULL); + * either of the trips being NULL means that this was (or will be) a dive without a trip; + * update the dive trips (especially the starting times) accordingly + * maintain the selected status of the dive + * IMPORTANT - the move needs to keep the tree consistant - so no out of order moving... */ +static GtkTreeIter *move_dive_between_trips(GtkTreeIter *dive_iter, GtkTreeIter *old_trip, GtkTreeIter *new_trip, + GtkTreeIter *sibling, gboolean before) +{ + int idx; + time_t old_when, new_when; + struct dive *dive, *old_divetrip, *new_divetrip; + GtkTreeIter *new_iter = malloc(sizeof(GtkTreeIter)); + + if (before) + gtk_tree_store_insert_before(STORE(dive_list), new_iter, new_trip, sibling); + else + gtk_tree_store_insert_after(STORE(dive_list), new_iter, new_trip, sibling); + idx = copy_tree_node(dive_iter, new_iter); + gtk_tree_model_get(MODEL(dive_list), new_iter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + gtk_tree_store_remove(STORE(dive_list), dive_iter); + if (old_trip) { + gtk_tree_model_get(MODEL(dive_list), old_trip, DIVE_DATE, &old_when, -1); + old_divetrip = DIVE_TRIP(find_matching_trip(old_when)); + update_trip_timestamp(old_trip, old_divetrip); + } + if (new_trip) { + gtk_tree_model_get(MODEL(dive_list), new_trip, DIVE_DATE, &new_when, -1); + new_divetrip = dive->divetrip; + update_trip_timestamp(new_trip, new_divetrip); + } + if (dive->selected) { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + gtk_tree_selection_select_iter(selection, new_iter); + } + return new_iter; +} + +static void turn_dive_into_trip(GtkTreePath *path) +{ + GtkTreeIter iter, *newiter, newparent; + GtkTreePath *treepath; + time_t when; + char *location; + int idx; + struct dive *dive; + + /* this is a dive on the top level, insert trip AFTER it, populate its date / location, and + * then move the dive below that trip */ + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_store_insert_after(STORE(dive_list), &newparent, NULL, &iter); + gtk_tree_model_get(MODEL(dive_list), &iter, + DIVE_INDEX, &idx, DIVE_DATE, &when, DIVE_LOCATION, &location, -1); + gtk_tree_store_set(STORE(dive_list), &newparent, + DIVE_INDEX, -1, DIVE_DATE, when, DIVE_LOCATION, location, -1); + free(location); + newiter = move_dive_between_trips(&iter, NULL, &newparent, NULL, FALSE); + treepath = gtk_tree_model_get_path(MODEL(dive_list), newiter); + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), treepath); + dive = get_dive(idx); + /* we don't need the return value - everything is hooked up in the function */ + (void)create_and_hookup_trip_from_dive(dive); +} + +/* we know that path is pointing at a dive in a trip and are asked to split this trip into two */ +static void insert_trip_before(GtkTreePath *path) +{ + GtkTreeIter iter, prev_iter, parent, newparent, nextsibling; + GtkTreePath *treepath, *prev_path; + struct dive *dive, *prev_dive, *new_divetrip; + int idx, nr, i; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + prev_path = gtk_tree_path_copy(path); + if (!gtk_tree_path_prev(prev_path) || + !gtk_tree_model_iter_parent(MODEL(dive_list), &parent, &iter)) + return; + gtk_tree_model_get_iter(MODEL(dive_list), &prev_iter, prev_path); + gtk_tree_model_get(MODEL(dive_list), &prev_iter, DIVE_INDEX, &idx, -1); + prev_dive = get_dive(idx); + gtk_tree_store_insert_after(STORE(dive_list), &newparent, NULL, &parent); + copy_tree_node(&parent, &newparent); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + /* make sure that the time_t of the previous divetrip is correct before + * inserting a new one */ + if (dive->when < prev_dive->when) + if (prev_dive->divetrip && prev_dive->divetrip->when < prev_dive->when) + prev_dive->divetrip->when = prev_dive->when; + new_divetrip = create_and_hookup_trip_from_dive(dive); + + /* in order for the data structures to stay consistent we need to walk from + * the last child backwards to this one. The easiest way seems to be to do + * this with the nth iterator API */ + nr = gtk_tree_model_iter_n_children(MODEL(dive_list), &parent); + for (i = nr - 1; i >= 0; i--) { + gtk_tree_model_iter_nth_child(MODEL(dive_list), &nextsibling, &parent, i); + treepath = gtk_tree_model_get_path(MODEL(dive_list), &nextsibling); + gtk_tree_model_get(MODEL(dive_list), &nextsibling, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + dive->divetrip = new_divetrip; + if (dive->when < dive->divetrip->when) + dive->divetrip->when = dive->when; + (void) move_dive_between_trips(&nextsibling, &parent, &newparent, NULL, FALSE); + if (gtk_tree_path_compare(path, treepath) == 0) + /* we copied the dive we were called with; we are done */ + break; + } + treepath = gtk_tree_model_get_path(MODEL(dive_list), &newparent); + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), treepath); +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +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) + insert_trip_before(path); + else + turn_dive_into_trip(path); +} + +static void remove_from_trip(GtkTreePath *path) +{ + GtkTreeIter iter, nextiter, *newiter, parent; + GtkTreePath *nextpath; + struct dive *dive; + int idx; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + if (!gtk_tree_model_iter_parent(MODEL(dive_list), &parent, &iter)) + return; + /* if this isn't the last dive in a trip we simply split the trip + in two right after this dive */ + nextpath = gtk_tree_path_copy(path); + gtk_tree_path_next(nextpath); + if (gtk_tree_model_get_iter(MODEL(dive_list), &nextiter, nextpath)) + insert_trip_before(nextpath); + /* now move the dive to the top level, as sibling after its former parent */ + newiter = move_dive_between_trips(&iter, &parent, NULL, &parent, FALSE); + gtk_tree_model_get(MODEL(dive_list), newiter, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + if (dive->selected) { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + gtk_tree_selection_select_iter(selection, newiter); + } + /* if this was the last dive on the trip, remove the trip */ + if (! gtk_tree_model_iter_has_child(MODEL(dive_list), &parent)) { + gtk_tree_store_remove(STORE(dive_list), &parent); + delete_trip(FIND_TRIP(dive->divetrip->when)); + free(dive->divetrip); + } + /* mark the dive as intentionally at the top level */ + dive->tripflag = NO_TRIP; + dive->divetrip = NULL; +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +static void remove_rowref_from_trip(gpointer data, gpointer user_data) +{ + GtkTreeRowReference *rowref = data; + GtkTreePath *path = gtk_tree_row_reference_get_path(rowref); + if (path) + remove_from_trip(path); +} + +static gboolean add_rowref_if_selected(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + GList **rowref_list = data; + int idx; + struct dive *dive; + + gtk_tree_model_get(MODEL(dive_list), iter, DIVE_INDEX, &idx, -1); + if (idx >=0 ) { + dive = get_dive(idx); + if (dive->selected) { + /* we need to store the Row References as those + stay valid across modifications of the model */ + GtkTreeRowReference *rowref; + rowref = gtk_tree_row_reference_new(TREEMODEL(dive_list), path); + *rowref_list = g_list_append(*rowref_list, rowref); + } + } + return FALSE; /* continue foreach loop */ +} + +static void remove_from_trip_cb(GtkWidget *menuitem, GtkTreePath *path) +{ + GtkTreeIter iter, parent; + struct dive *dive; + int idx; + + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + if (!gtk_tree_model_iter_parent(MODEL(dive_list), &parent, &iter)) + return; + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); + if (idx < 0 ) + return; + dive = get_dive(idx); + if (dive->selected) { + /* remove all the selected dives + since removing the dives from trips changes the model we need to + take the extra step of acquiring rowrefs before actually moving dives */ + GList *rowref_list = NULL; + gtk_tree_model_foreach(MODEL(dive_list), add_rowref_if_selected, &rowref_list); + /* We need to walk that list backwards as otherwise + the newly insered trips below dives that were + removed also mess with the validity */ + rowref_list = g_list_reverse(rowref_list); + g_list_foreach(rowref_list, remove_rowref_from_trip, NULL); + + } else { + /* just remove the dive the mouse pointer is on */ + remove_from_trip(path); + } +} + +void remove_trip(GtkTreePath *trippath, gboolean force_no_trip) +{ + GtkTreeIter newiter, parent, child, *lastiter = &parent; + struct dive *dive, *dive_trip = NULL; + int idx; + GtkTreePath *childpath; + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + /* what a pain - we can't just move the nodes, we have to + * create new ones and delete the existing ones instead */ + gtk_tree_model_get_iter(MODEL(dive_list), &parent, trippath); + childpath = gtk_tree_path_copy(trippath); + gtk_tree_path_down(childpath); + for (;;) { + if( ! gtk_tree_model_get_iter(MODEL(dive_list), &child, childpath)) + break; + gtk_tree_store_insert_after(STORE(dive_list), &newiter, NULL, lastiter); + copy_tree_node(&child, &newiter); + /* we need to track what was selected */ + gtk_tree_model_get(MODEL(dive_list), &child, DIVE_INDEX, &idx, -1); + dive = get_dive(idx); + if (dive->selected) + gtk_tree_selection_select_iter(selection, &newiter); + if (force_no_trip) + dive->tripflag = NO_TRIP; + else + dive->tripflag = TF_NONE; + if (!dive_trip) + dive_trip = dive->divetrip; + dive->divetrip = NULL; + /* this removes the child - now childpath points to the next child */ + gtk_tree_store_remove(STORE(dive_list), &child); + lastiter = &newiter; + } + /* finally, remove the trip */ + gtk_tree_store_remove(STORE(dive_list), &parent); + delete_trip(FIND_TRIP(dive_trip->when)); + free(dive_trip); +#ifdef DEBUG_TRIP + dump_trip_list(); +#endif +} + +void remove_trip_cb(GtkWidget *menuitem, GtkTreePath *trippath) +{ + remove_trip(trippath, TRUE); +} + +void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath) +{ + GtkTreePath *prevpath; + GtkTreeIter thistripiter, prevtripiter, newiter, iter; + GtkTreeModel *tm = MODEL(dive_list); + GList *trip, *prevtrip; + time_t when; + + /* this only gets called when we are on a trip and there is another trip right before */ + prevpath = gtk_tree_path_copy(trippath); + gtk_tree_path_prev(prevpath); + gtk_tree_model_get_iter(tm, &thistripiter, trippath); + gtk_tree_model_get(tm, &thistripiter, DIVE_DATE, &when, -1); + trip = find_matching_trip(when); + gtk_tree_model_get_iter(tm, &prevtripiter, prevpath); + gtk_tree_model_get(tm, &prevtripiter, DIVE_DATE, &when, -1); + prevtrip = find_matching_trip(when); + while (gtk_tree_model_iter_children(tm, &iter, &thistripiter)) { + int idx; + gtk_tree_store_insert_before(STORE(dive_list), &newiter, &prevtripiter, NULL); + idx = copy_tree_node(&iter, &newiter); + gtk_tree_store_remove(STORE(dive_list), &iter); + get_dive(idx)->divetrip = DIVE_TRIP(prevtrip); + } + update_trip_timestamp(&prevtripiter, DIVE_TRIP(prevtrip)); + free(DIVE_TRIP(trip)); + delete_trip(trip); + gtk_tree_store_remove(STORE(dive_list), &thistripiter); +} + +static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button, GdkEventButton *event) { GtkWidget *menu, *menuitem, *image; char editlabel[] = "Edit dives"; + GtkTreePath *path, *prevpath, *nextpath; + GtkTreeIter iter, previter, nextiter; + int idx, previdx, nextidx; + struct dive *dive; + + if (!gtk_tree_view_get_path_at_pos(tree_view, event->x, event->y, &path, NULL, NULL, NULL)) + return; + gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); + gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1); menu = gtk_menu_new(); menuitem = gtk_image_menu_item_new_with_label("Add dive"); @@ -1157,12 +1592,69 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - if (amount_selected) { - if (amount_selected == 1) - editlabel[strlen(editlabel) - 1] = '\0'; - menuitem = gtk_menu_item_new_with_label(editlabel); - g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_cb), model); + + if (idx < 0) { + /* mouse pointer is on a trip summary entry */ + menuitem = gtk_menu_item_new_with_label("Edit Trip Summary"); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_trip_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + prevpath = gtk_tree_path_copy(path); + if (gtk_tree_path_prev(prevpath) && + gtk_tree_model_get_iter(MODEL(dive_list), &previter, prevpath)) { + gtk_tree_model_get(MODEL(dive_list), &previter, DIVE_INDEX, &previdx, -1); + if (previdx < 0) { + menuitem = gtk_menu_item_new_with_label("Merge trip with trip above"); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + nextpath = gtk_tree_path_copy(path); + gtk_tree_path_next(nextpath); + if (gtk_tree_model_get_iter(MODEL(dive_list), &nextiter, nextpath)) { + gtk_tree_model_get(MODEL(dive_list), &nextiter, DIVE_INDEX, &nextidx, -1); + if (nextidx < 0) { + menuitem = gtk_menu_item_new_with_label("Merge trip with trip below"); + g_signal_connect(menuitem, "activate", G_CALLBACK(merge_trips_cb), nextpath); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } + menuitem = gtk_menu_item_new_with_label("Remove Trip"); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_trip_cb), path); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } else { + dive = get_dive(idx); + /* if we right click on selected dive(s), edit those */ + if (dive->selected) { + if (amount_selected == 1) + editlabel[strlen(editlabel) - 1] = '\0'; + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_selected_dives_cb), NULL); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } else { + editlabel[strlen(editlabel) - 1] = '\0'; + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_from_path_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + /* 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); + + if (depth == 1 || indices[1] > 0) { + menuitem = gtk_menu_item_new_with_label("Add new trip above"); + g_signal_connect(menuitem, "activate", G_CALLBACK(insert_trip_before_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"); + else + menuitem = gtk_menu_item_new_with_label("Remove dive from trip"); + g_signal_connect(menuitem, "activate", G_CALLBACK(remove_from_trip_cb), path); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } + } } menuitem = gtk_menu_item_new_with_label("Expand all"); g_signal_connect(menuitem, "activate", G_CALLBACK(expand_all_cb), tree_view); @@ -1178,14 +1670,14 @@ static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) { - popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0); + popup_divelist_menu(tree_view, MODEL(dive_list), 0, NULL); } static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata) { /* Right-click? Bring up the menu */ if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - popup_divelist_menu(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(dive_list.model), 3); + popup_divelist_menu(GTK_TREE_VIEW(treeview), MODEL(dive_list), 3, event); return TRUE; } return FALSE; @@ -1276,9 +1768,9 @@ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) if (dive_list.model != currentmodel) { GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); - gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model)); + gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), MODEL(dive_list)); update_column_and_order(colid); - gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), set_selected, selection); + gtk_tree_model_foreach(MODEL(dive_list), set_selected, selection); } else { if (order != sortorder[colid]) { update_column_and_order(colid); @@ -1293,7 +1785,7 @@ GtkWidget *dive_list_create(void) dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS, G_TYPE_INT, /* index */ G_TYPE_INT, /* nr */ - G_TYPE_INT, /* Date */ + G_TYPE_LONG, /* Date */ G_TYPE_INT, /* Star rating */ G_TYPE_INT, /* Depth */ G_TYPE_INT, /* Duration */ @@ -1309,7 +1801,7 @@ GtkWidget *dive_list_create(void) dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, G_TYPE_INT, /* index */ G_TYPE_INT, /* nr */ - G_TYPE_INT, /* Date */ + G_TYPE_LONG, /* Date */ G_TYPE_INT, /* Star rating */ G_TYPE_INT, /* Depth */ G_TYPE_INT, /* Duration */ @@ -1323,7 +1815,7 @@ GtkWidget *dive_list_create(void) G_TYPE_STRING /* Location */ ); dive_list.model = dive_list.treemodel; - dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model)); + dive_list.tree_view = gtk_tree_view_new_with_model(TREEMODEL(dive_list)); set_divelist_font(divelist_font); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); @@ -1383,3 +1875,26 @@ int unsaved_changes() { return dive_list.changed; } + +void remove_autogen_trips() +{ + GtkTreeIter iter; + GtkTreePath *path; + time_t when; + int idx; + GList *trip; + + /* start with the first top level entry and walk all of them */ + path = gtk_tree_path_new_from_string("0"); + while(gtk_tree_model_get_iter(TREEMODEL(dive_list), &iter, path)) { + gtk_tree_model_get(TREEMODEL(dive_list), &iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); + if (idx < 0) { + trip = FIND_TRIP(when); + if (DIVE_TRIP(trip)->tripflag == IN_TRIP) { /* this was autogen */ + remove_trip(path, FALSE); + continue; + } + } + gtk_tree_path_next(path); + } +} diff --git a/divelist.h b/divelist.h index c170941db..45c1a18f8 100644 --- a/divelist.h +++ b/divelist.h @@ -10,4 +10,5 @@ extern void flush_divelist(struct dive *); extern void update_cylinder_related_info(struct dive *); extern void mark_divelist_changed(int); extern int unsaved_changes(void); +extern void remove_autogen_trips(void); #endif diff --git a/dives/test21.xml b/dives/test21.xml index 5f5f6c981..ce6b82f35 100644 --- a/dives/test21.xml +++ b/dives/test21.xml @@ -1,6 +1,6 @@ <dives> <program name='subsurface' version='1'></program> -<trip date='2011-12-02' /> +<trip date='2011-12-02' time='14:00:00' /> <dive number='20' tripflag='INTRIP' date='2011-12-02' time='14:00:00' duration='30:00 min'> <depth max='20.0 m' mean='15.0 m' /> <location>20th test dive - this should be in a trip with same location</location> diff --git a/dives/test22.xml b/dives/test22.xml index a61032327..e8112287d 100644 --- a/dives/test22.xml +++ b/dives/test22.xml @@ -1,6 +1,6 @@ <dives> <program name='subsurface' version='1'></program> -<trip date='2011-12-02' location='trip location' /> +<trip date='2011-12-02' location='trip location' time='15:00:00' /> <dive number='21' tripflag='INTRIP' date='2011-12-02' time='15:00:00' duration='30:00 min'> <depth max='20.0 m' mean='15.0 m' /> <location>21st test dive - this should be in a trip with a trip location</location> diff --git a/dives/test23.xml b/dives/test23.xml index c61ad2db9..defdfdd90 100644 --- a/dives/test23.xml +++ b/dives/test23.xml @@ -1,7 +1,12 @@ <dives> <program name='subsurface' version='1'></program> -<trip date='2011-12-02' location='trip location' /> -<dive number='22' tripflag='NOTRIP' date='2011-12-09' time='6:00:00' duration='30:00 min'> +<trip date='2011-12-02' time='6:00:00' location='trip location' /> +<dive number='23' tripflag='INTRIP' date='2011-12-02' time='6:00:00' duration='30:00 min'> + <depth max='20.0 m' mean='15.0 m' /> + <location>23rd test dive - this should be in a trip</location> + <notes>We are testing that the NOTRIP flag works</notes> +</dive> +<dive number='22' tripflag='NOTRIP' date='2011-12-03' time='7:00:00' duration='30:00 min'> <depth max='20.0 m' mean='15.0 m' /> <location>22nd test dive - this should not be in a trip</location> <notes>We are testing that the NOTRIP flag works</notes> @@ -634,6 +634,14 @@ static void selectevents_dialog(GtkWidget *w, gpointer data) gtk_widget_destroy(dialog); } +static void autogroup_cb(GtkWidget *w, gpointer data) +{ + autogroup = !autogroup; + if (! autogroup) + remove_autogen_trips(); + dive_list_update_dives(); +} + static void renumber_dialog(GtkWidget *w, gpointer data) { int result; @@ -757,10 +765,15 @@ static GtkActionEntry menu_items[] = { { "ViewProfile", NULL, "Profile", CTRLCHAR "2", NULL, G_CALLBACK(view_profile) }, { "ViewInfo", NULL, "Info", CTRLCHAR "3", NULL, G_CALLBACK(view_info) }, { "ViewThree", NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) }, - { "ToggleZoom", NULL, "Toggle Zoom", CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom) }, }; static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); +static GtkToggleActionEntry toggle_items[] = { + { "Autogroup", NULL, "Autogroup", NULL, NULL, G_CALLBACK(autogroup_cb), FALSE }, + { "ToggleZoom", NULL, "Toggle Zoom", CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom), FALSE }, +}; +static gint ntoggle_items = sizeof (toggle_items) / sizeof (toggle_items[0]); + static const gchar* ui_string = " \ <ui> \ <menubar name=\"MainMenu\"> \ @@ -779,6 +792,7 @@ static const gchar* ui_string = " \ <menuitem name=\"Add Dive\" action=\"AddDive\" /> \ <separator name=\"Separator\"/> \ <menuitem name=\"Renumber\" action=\"Renumber\" /> \ + <menuitem name=\"Autogroup\" action=\"Autogroup\" /> \ <menuitem name=\"Toggle Zoom\" action=\"ToggleZoom\" /> \ <menu name=\"View\" action=\"ViewMenuAction\"> \ <menuitem name=\"List\" action=\"ViewList\" /> \ @@ -801,6 +815,8 @@ static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager) { GtkActionGroup *action_group = gtk_action_group_new("Menu"); gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0); + toggle_items[0].is_active = autogroup; + gtk_action_group_add_toggle_actions(action_group, toggle_items, ntoggle_items, 0); gtk_ui_manager_insert_action_group(ui_manager, action_group, 0); GError* error = 0; @@ -415,6 +415,24 @@ static void save_dive_info_changes(struct dive *dive, struct dive *master, struc } } +static void dive_trip_widget(GtkWidget *box, struct dive *trip, struct dive_info *info) +{ + GtkWidget *hbox, *label; + char buffer[80] = "Edit trip summary"; + + label = gtk_label_new(buffer); + gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); + + info->location = text_entry(box, "Location", location_list, trip->location); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); + + info->notes = text_view(box, "Notes", READ_WRITE); + if (trip->notes && *trip->notes) + gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), trip->notes, -1); +} + static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info, gboolean multi) { GtkWidget *hbox, *label, *frame, *equipment; @@ -489,7 +507,49 @@ void update_equipment_data(struct dive *dive, struct dive *master) memcpy(dive->weightsystem, master->weightsystem, WS_BYTES); } -/* A negative index means "all selected" */ +gboolean edit_trip(struct dive *trip) +{ + GtkWidget *dialog, *vbox; + int success; + gboolean changed = FALSE; + char *old_text, *new_text; + struct dive_info info; + + memset(&info, 0, sizeof(struct dive_info)); + dialog = gtk_dialog_new_with_buttons("Edit Trip Info", + GTK_WINDOW(main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + NULL); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + dive_trip_widget(vbox, trip, &info); + gtk_widget_show_all(dialog); + success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; + if (success) { + /* we need to store the edited location and notes */ + new_text = get_combo_box_entry_text(info.location, &trip->location, trip->location); + if (new_text) { + add_location(new_text); + changed = TRUE; + } + if (info.notes) { + old_text = trip->notes; + trip->notes = get_text(info.notes); + if (text_changed(old_text, trip->notes)) + changed = TRUE; + if (old_text) + g_free(old_text); + } + if (changed) { + mark_divelist_changed(TRUE); + flush_divelist(trip); + } + } + gtk_widget_destroy(dialog); + return changed; +} + int edit_multi_dive_info(struct dive *single_dive) { int success; diff --git a/parse-xml.c b/parse-xml.c index 59e9278ad..1c4db7d9d 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -39,11 +39,6 @@ void record_dive(struct dive *dive) dive_table.nr = nr+1; } -void record_trip(struct dive *trip) -{ - dive_trip_list = insert_trip(trip, dive_trip_list); -} - static void delete_dive_renumber(struct dive **dives, int i, int nr) { struct dive *dive = dives[i]; @@ -1165,10 +1160,10 @@ static void try_to_fill_trip(struct dive **divep, const char *name, char *buf) struct dive *dive = *divep; - if (MATCH(".date", divedate, &dive->when)) { - dive->when = utc_mktime(&cur_tm); + if (MATCH(".date", divedate, &dive->when)) + return; + if (MATCH(".time", divetime, &dive->when)) return; - } if (MATCH(".location", utf8_string, &dive->location)) return; if (MATCH(".notes", utf8_string, &dive->notes)) @@ -1213,7 +1208,7 @@ static void trip_end(void) { if (!cur_trip) return; - record_trip(cur_trip); + insert_trip(&cur_trip); cur_trip = NULL; } diff --git a/save-xml.c b/save-xml.c index b797475e5..3bea2adfa 100644 --- a/save-xml.c +++ b/save-xml.c @@ -291,6 +291,8 @@ static void save_trip(FILE *f, struct dive *trip) fprintf(f, "<trip"); fprintf(f, " date='%04u-%02u-%02u'", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); + fprintf(f, " time='%02u:%02u:%02u'", + tm->tm_hour, tm->tm_min, tm->tm_sec); if (trip->location) show_utf8(f, trip->location, " location=\'","\'", 1); fprintf(f, " />\n"); @@ -341,7 +343,7 @@ void save_dives(const char *filename) fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION); /* save the trips */ - while ((trip = NEXT_TRIP(trip, dive_trip_list)) != 0) + while ((trip = NEXT_TRIP(trip)) != NULL) save_trip(f, trip->data); /* save the dives */ |