Merge the initial 'track trips explicitly' code from Dirk Hohndel.
Fix up trivial conflicts in save-xml.c due to the new 'is_attribute'
flag.
* 'trips' of git://git.hohndel.org/subsurface:
Fix an issue with trips that have dives from multiple input files
Some simple test dives for the trips code
First cut of explicit trip tracking
extern gboolean cylinder_none(void *_data);
extern gboolean no_cylinders(cylinder_t *cyl);
extern gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2);
+extern void copy_cylinders(cylinder_t *cyl1, cylinder_t *cyl2);
extern gboolean no_weightsystems(weightsystem_t *ws);
extern gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
+ typedef enum { TF_NONE, NO_TRIP, IN_TRIP, NUM_TRIPFLAGS } tripflag_t;
+ extern const char *tripflag_names[NUM_TRIPFLAGS];
+
struct dive {
int number;
+ tripflag_t tripflag;
int selected;
time_t when;
char *location;
struct sample sample[];
};
+ extern GList *dive_trip_list;
+ extern gboolean autogroup;
+ /* random threashold: three days without diving -> new trip
+ * this works very well for people who usually dive as part of a trip and don't
+ * regularly dive at a local facility; this is why trips are an optional feature */
+ #define TRIP_THRESHOLD 3600*24*3
+
+ #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 DIVE_TRIP(_trip) ((struct dive *)(_trip)->data)
+ #define DIVE_FITS_TRIP(_dive, _dive_trip) ((_dive_trip)->when - TRIP_THRESHOLD <= (_dive)->when)
+
+ static inline int dive_date_cmp(gconstpointer _a, gconstpointer _b) {
+ return ((struct dive *)(_a))->when - ((struct dive *)(_b))->when;
+ }
+
+ #define FIND_TRIP(_trip, _list) g_list_find_custom((_list), (_trip), dive_date_cmp)
+
+ #ifdef DEBUG_TRIP
+ static void dump_trip_list(void)
+ {
+ GList *p = NULL;
+ int i=0;
+ while ((p = NEXT_TRIP(p, dive_trip_list))) {
+ 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("-----\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)
+ {
+ GList *result = FIND_TRIP(_trip, _list);
+ if (result) {
+ if (! DIVE_TRIP(result)->location)
+ DIVE_TRIP(result)->location = _trip->location;
+ } else {
+ result = g_list_insert_sorted((_list), (_trip), dive_date_cmp);
+ }
+ #ifdef DEBUG_TRIP
+ dump_trip_list();
+ #endif
+ return result;
+ }
+
/*
* We keep our internal data in well-specified units, but
* the input and output may come in some random format. This
return dive_table.dives[nr];
}
+/*
+ * Iterate over each dive, with the first parameter being the index
+ * iterator variable, and the second one being the dive one.
+ *
+ * I don't think anybody really wants the index, and we could make
+ * it local to the for-loop, but that would make us requires C99.
+ */
+#define for_each_dive(_i,_x) \
+ for ((_i) = 0; ((_x) = get_dive(_i)) != NULL; (_i)++)
+
extern void parse_xml_init(void);
extern void parse_xml_buffer(const char *url, const char *buf, int size, GError **error);
extern void set_filename(const char *filename);
extern int add_new_dive(struct dive *dive);
extern int edit_dive_info(struct dive *dive);
-extern int edit_multi_dive_info(int idx);
+extern int edit_multi_dive_info(struct dive *single_dive);
extern void dive_list_update_dives(void);
extern void flush_divelist(struct dive *dive);
};
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)
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;
struct dive *dive;
printf("currently selected are %d dives:", amount_selected);
- for (i = 0; (dive = get_dive(i)) != NULL; i++) {
+ for_each_dive(i, dive) {
if (dive->selected)
printf(" %d", i);
}
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),
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);
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;
+ /* the dive_trip info might have been killed by a previous UNGROUPED dive */
+ if (trip)
+ dive_trip = DIVE_TRIP(trip);
+ /* 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,
}
/* 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;
void edit_dive_cb(GtkWidget *menuitem, gpointer data)
{
- edit_multi_dive_info(-1);
+ edit_multi_dive_info(NULL);
}
static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view)
{
return;
}
-
+
if (error_info_bar == NULL)
{
error_count = 1;
g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar),
GTK_MESSAGE_ERROR);
-
+
error_label = gtk_label_new(error->message);
GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar));
gtk_container_add(GTK_CONTAINER(container), error_label);
-
+
gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0);
gtk_widget_show_all(main_vbox);
}
GSList *filenames, *fn_glist;
char *filename;
filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
-
+
GError *error = NULL;
while(filenames != NULL) {
filename = filenames->data;
g_error_free(error);
error = NULL;
}
-
+
g_free(filename);
filenames = g_slist_next(filenames);
}
gtk_widget_destroy(dialog);
}
+/* return the path and the file component contained in the full path */
+static char *path_and_file(char *pathin, char **fileout) {
+ char *slash = pathin, *next;
+ char *result;
+ size_t len, n;
+
+ if (! pathin) {
+ *fileout = strdup("");
+ return strdup("");
+ }
+ while ((next = strpbrk(slash + 1, "\\/")))
+ slash = next;
+ if (pathin != slash)
+ slash++;
+ *fileout = strdup(slash);
+
+ /* strndup(pathin, slash - pathin) */
+ n = slash - pathin;
+ len = strlen(pathin);
+ if (n < len)
+ len = n;
+
+ result = (char *)malloc(len + 1);
+ if (!result)
+ return 0;
+
+ result[len] = '\0';
+ return (char *)memcpy(result, pathin, len);
+}
+
static void file_save_as(GtkWidget *w, gpointer data)
{
GtkWidget *dialog;
char *filename = NULL;
+ char *current_file;
+ char *current_dir;
+
dialog = gtk_file_chooser_dialog_new("Save File As",
GTK_WINDOW(main_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
- gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), existing_filename);
+ current_dir = path_and_file(existing_filename, ¤t_file);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir);
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file);
+
+ free(current_dir);
+ free(current_file);
+
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
}
OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight)
OPTIONCALLBACK(suit_toggle, visible_cols.suit)
OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder)
+ OPTIONCALLBACK(autogroup_toggle, autogroup)
static void event_toggle(GtkWidget *w, gpointer _data)
{
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
+ frame = gtk_frame_new("Divelist Font");
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
font = gtk_font_button_new_with_font(divelist_font);
- gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(frame),font);
+
+ frame = gtk_frame_new("Misc. Options");
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
+ box = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(frame), box);
+
+ button = gtk_check_button_new_with_label("Automatically group dives in trips");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autogroup);
+ gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
+ g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(autogroup_toggle), NULL);
gtk_widget_show_all(dialog);
result = gtk_dialog_run(GTK_DIALOG(dialog));
subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac));
subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu));
subsurface_set_conf("divelist_font", PREF_STRING, divelist_font);
+ subsurface_set_conf("autogroup", PREF_BOOL, BOOL_TO_PTR(autogroup));
/* Flush the changes out to the system */
subsurface_flush_conf();
divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
+ autogroup = PTR_TO_BOOL(subsurface_get_conf("autogroup", PREF_BOOL));
+
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
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];
/*
* Dive info as it is being built up..
*/
- static struct dive *cur_dive;
+ static struct dive *cur_dive, *cur_trip = NULL;
static struct sample *cur_sample;
static struct {
int active;
free(buffer);
}
+ static void get_tripflag(char *buffer, void *_tf)
+ {
+ tripflag_t *tf = _tf;
+ tripflag_t i;
+
+ *tf = TF_NONE;
+ for (i = NO_TRIP; i < NUM_TRIPFLAGS; i++)
+ if(! strcmp(buffer, tripflag_names[i]))
+ *tf = i;
+ }
+
static void centibar(char *buffer, void *_pressure)
{
pressure_t *pressure = _pressure;
if (MATCH(".number", get_index, &dive->number))
return;
+ if (MATCH(".tripflag", get_tripflag, &dive->tripflag))
+ return;
if (MATCH(".date", divedate, &dive->when))
return;
if (MATCH(".time", divetime, &dive->when))
nonmatch("dive", name, buf);
}
+ /* We're in the top-level trip xml. Try to convert whatever value to a trip value */
+ static void try_to_fill_trip(struct dive **divep, const char *name, char *buf)
+ {
+ int len = strlen(name);
+
+ start_match("trip", name, buf);
+
+ struct dive *dive = *divep;
+
+ if (MATCH(".date", divedate, &dive->when)) {
+ dive->when = utc_mktime(&cur_tm);
+ return;
+ }
+ if (MATCH(".location", utf8_string, &dive->location))
+ return;
+ if (MATCH(".notes", utf8_string, &dive->notes))
+ return;
+
+ nonmatch("trip", name, buf);
+ }
+
/*
* File boundaries are dive boundaries. But sometimes there are
* multiple dives per file, so there can be other events too that
cur_ws_index = 0;
}
+ static void trip_start(void)
+ {
+ if (cur_trip)
+ return;
+ cur_trip = alloc_dive();
+ memset(&cur_tm, 0, sizeof(cur_tm));
+ }
+
+ static void trip_end(void)
+ {
+ if (!cur_trip)
+ return;
+ record_trip(cur_trip);
+ cur_trip = NULL;
+ }
+
static void event_start(void)
{
memset(&cur_event, 0, sizeof(cur_event));
try_to_fill_sample(cur_sample, name, buf);
return;
}
+ if (cur_trip) {
+ try_to_fill_trip(&cur_trip, name, buf);
+ return;
+ }
if (cur_dive) {
try_to_fill_dive(&cur_dive, name, buf);
return;
} nesting[] = {
{ "dive", dive_start, dive_end },
{ "Dive", dive_start, dive_end },
+ { "trip", trip_start, trip_end },
{ "sample", sample_start, sample_end },
{ "waypoint", sample_start, sample_end },
{ "SAMPLE", sample_start, sample_end },
}
return;
}
- /* we assume that the last (or only) filename passed as argument is a
- * great filename to use as default when saving the dives */
+ /* we assume that the last (or only) filename passed as argument is a
+ * great filename to use as default when saving the dives */
set_filename(url);
reset_all();
dive_start();
* characters, but at least libxml2 doesn't like them. It doesn't even
* allow them quoted. So we just skip them and replace them with '?'.
*
- * Nothing else (and if we ever do this using attributes, we'd need to
- * quote the quotes we use too).
+ * If we do this for attributes, we need to quote the quotes we use too.
*/
-static void quote(FILE *f, const char *text)
+static void quote(FILE *f, const char *text, int is_attribute)
{
const char *p = text;
escape = "&";
break;
case '\'':
+ if (!is_attribute)
+ continue;
escape = "'";
break;
case '\"':
+ if (!is_attribute)
+ continue;
escape = """;
break;
}
}
}
-static void show_utf8(FILE *f, const char *text, const char *pre, const char *post)
+static void show_utf8(FILE *f, const char *text, const char *pre, const char *post, int is_attribute)
{
int len;
len--;
/* FIXME! Quoting! */
fputs(pre, f);
- quote(f, text);
+ quote(f, text, is_attribute);
fputs(post, f);
}
}
prefix = buffer;
}
- show_utf8(f, dive->location, prefix,"</location>\n");
+ show_utf8(f, dive->location, prefix,"</location>\n", 0);
}
static void save_overview(FILE *f, struct dive *dive)
save_temperatures(f, dive);
show_duration(f, dive->surfacetime, " <surfacetime>", "</surfacetime>\n");
show_location(f, dive);
- show_utf8(f, dive->divemaster, " <divemaster>","</divemaster>\n");
- show_utf8(f, dive->buddy, " <buddy>","</buddy>\n");
- show_utf8(f, dive->notes, " <notes>","</notes>\n");
- show_utf8(f, dive->suit, " <suit>","</suit>\n");
+ show_utf8(f, dive->divemaster, " <divemaster>","</divemaster>\n", 0);
+ show_utf8(f, dive->buddy, " <buddy>","</buddy>\n", 0);
+ show_utf8(f, dive->notes, " <notes>","</notes>\n", 0);
+ show_utf8(f, dive->suit, " <suit>","</suit>\n", 0);
}
static void save_cylinder_info(FILE *f, struct dive *dive)
show_index(f, ev->type, "type='", "'");
show_index(f, ev->flags, "flags='", "'");
show_index(f, ev->value, "value='", "'");
- show_utf8(f, ev->name, " name='", "'");
+ show_utf8(f, ev->name, " name='", "'", 1);
fprintf(f, " />\n");
}
}
}
- show_utf8(f, trip->location, " location=\'","\'");
+ static void save_trip(FILE *f, struct dive *trip)
+ {
+ struct tm *tm = gmtime(&trip->when);
+
+ fprintf(f, "<trip");
+ fprintf(f, " date='%04u-%02u-%02u'",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
+ if (trip->location)
++ show_utf8(f, trip->location, " location=\'","\'", 1);
+ fprintf(f, " />\n");
+ }
+
static void save_dive(FILE *f, struct dive *dive)
{
int i;
fputs("<dive", f);
if (dive->number)
fprintf(f, " number='%d'", dive->number);
+ if (dive->tripflag != TF_NONE)
+ fprintf(f, " tripflag='%s'", tripflag_names[dive->tripflag]);
if (dive->rating)
fprintf(f, " rating='%d'", dive->rating);
fprintf(f, " date='%04u-%02u-%02u'",
void save_dives(const char *filename)
{
int i;
+ GList *trip = NULL;
+
FILE *f = fopen(filename, "w");
if (!f)
update_dive(current_dive);
fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION);
+
+ /* save the trips */
+ while ((trip = NEXT_TRIP(trip, dive_trip_list)) != 0)
+ save_trip(f, trip->data);
+
+ /* save the dives */
for (i = 0; i < dive_table.nr; i++)
save_dive(f, get_dive(i));
fprintf(f, "</dives>\n");