From: Linus Torvalds Date: Tue, 28 Aug 2012 20:20:23 +0000 (-0700) Subject: Merge branch 'freediving-tweaks' of git://github.com/mguentner/subsurface X-Git-Url: http://git.tdb.fi/?p=ext%2Fsubsurface.git;a=commitdiff_plain;h=9d46581913ce33664e333a4d6eaf7f4e26e20b1c;hp=03eb488dd238bcec4b4758eab4559b3c40e6435c Merge branch 'freediving-tweaks' of git://github.com/mguentner/subsurface Merge freediving tweaks (zoom in on short dives etc) from Maximilian Güntner. Trivial conflicts in display.h due to unrelated printing stuff just happening to be added nearby. * 'freediving-tweaks' of git://github.com/mguentner/subsurface: moved zoomed_plot to display.h plot the time with a fixed padding (leading zero) updated/corrected comment added "Zoom" button and improved scaling fixed indentation use increments that make sense for 600 seconds Plot shorter (apnea) dives with a reasonable scale --- diff --git a/display.h b/display.h index 5f99849..4e15116 100644 --- a/display.h +++ b/display.h @@ -26,6 +26,11 @@ extern void plot(struct graphics_context *gc, cairo_rectangle_int_t *drawing_are extern void init_profile_background(struct graphics_context *gc); extern void attach_tooltip(int x, int y, int w, int h, const char *text); +struct options { + enum { PRETTY, TABLE } type; + gboolean print_profiles; +}; + extern char zoomed_plot; #endif diff --git a/dive.h b/dive.h index ab854e3..dffe753 100644 --- a/dive.h +++ b/dive.h @@ -95,6 +95,7 @@ typedef struct { 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); @@ -234,8 +235,12 @@ struct event { #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; @@ -256,6 +261,58 @@ struct dive { 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 diff --git a/divelist.c b/divelist.c index 30bd2d8..a773b60 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,102 @@ 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); 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, @@ -974,13 +1006,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; diff --git a/dives/test21.xml b/dives/test21.xml new file mode 100644 index 0000000..5f5f6c9 --- /dev/null +++ b/dives/test21.xml @@ -0,0 +1,9 @@ + + + + + + 20th test dive - this should be in a trip with same location + We are testing that the location of the dive is picked up in the trip if the trip has no location + + diff --git a/dives/test22.xml b/dives/test22.xml new file mode 100644 index 0000000..a610323 --- /dev/null +++ b/dives/test22.xml @@ -0,0 +1,9 @@ + + + + + + 21st test dive - this should be in a trip with a trip location + We are testing that the location of the dive is not picked up in the trip if the trip has a location + + diff --git a/dives/test23.xml b/dives/test23.xml new file mode 100644 index 0000000..c61ad2d --- /dev/null +++ b/dives/test23.xml @@ -0,0 +1,9 @@ + + + + + + 22nd test dive - this should not be in a trip + We are testing that the NOTRIP flag works + + diff --git a/equipment.c b/equipment.c index 43bb29d..d676fc0 100644 --- a/equipment.c +++ b/equipment.c @@ -461,13 +461,11 @@ gboolean description_equal(const char *desc1, const char *desc2) } /* when checking for the same cylinder we want the size and description to match - but don't compare the start and end pressures */ + but don't compare the start and end pressures, nor the Nitrox/He values */ static gboolean one_cylinder_equal(cylinder_t *cyl1, cylinder_t *cyl2) { return cyl1->type.size.mliter == cyl2->type.size.mliter && cyl1->type.workingpressure.mbar == cyl2->type.workingpressure.mbar && - cyl1->gasmix.o2.permille == cyl2->gasmix.o2.permille && - cyl1->gasmix.he.permille == cyl2->gasmix.he.permille && description_equal(cyl1->type.description, cyl2->type.description); } @@ -481,6 +479,21 @@ gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2) return TRUE; } +/* copy size and description of all cylinders from cyl1 to cyl2 */ +void copy_cylinders(cylinder_t *cyl1, cylinder_t *cyl2) +{ + int i; + + for (i = 0; i < MAX_CYLINDERS; i++) { + cyl2[i].type.size.mliter = cyl1[i].type.size.mliter; + cyl2[i].type.workingpressure.mbar = cyl1[i].type.workingpressure.mbar; + if (cyl1[i].type.description) + cyl2[i].type.description = strdup(cyl1[i].type.description); + else + cyl2[i].type.description = NULL; + } +} + static gboolean weightsystem_none(void *_data) { weightsystem_t *ws = _data; diff --git a/file.c b/file.c index e016390..da49899 100644 --- a/file.c +++ b/file.c @@ -8,6 +8,11 @@ #include "dive.h" #include "file.h" +/* Crazy windows sh*t */ +#ifndef O_BINARY +#define O_BINARY 0 +#endif + static int readfile(const char *filename, struct memblock *mem) { int ret, fd; @@ -17,7 +22,7 @@ static int readfile(const char *filename, struct memblock *mem) mem->buffer = NULL; mem->size = 0; - fd = open(filename, O_RDONLY); + fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return fd; ret = fstat(fd, &st); diff --git a/gtk-gui.c b/gtk-gui.c index 9bd4f13..7db777a 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -98,7 +98,7 @@ void report_error(GError* error) { return; } - + if (error_info_bar == NULL) { error_count = 1; @@ -108,11 +108,11 @@ void report_error(GError* error) 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); } @@ -151,7 +151,7 @@ static void file_open(GtkWidget *w, gpointer data) 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; @@ -162,7 +162,7 @@ static void file_open(GtkWidget *w, gpointer data) g_error_free(error); error = NULL; } - + g_free(filename); filenames = g_slist_next(filenames); } @@ -172,10 +172,43 @@ static void file_open(GtkWidget *w, gpointer data) 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, @@ -184,7 +217,13 @@ static void file_save_as(GtkWidget *w, gpointer data) 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)); } @@ -223,7 +262,7 @@ static gboolean ask_save_changes() label = gtk_label_new ( "You have unsaved changes\nWould you like to save those before exiting the program?"); } else { - char *label_text = (char*) malloc(sizeof(char) * (92 + strlen(existing_filename))); + char *label_text = (char*) malloc(sizeof(char) * (93 + strlen(existing_filename))); sprintf(label_text, "You have unsaved changes to file: %s \nWould you like to save those before exiting the program?", existing_filename); @@ -389,6 +428,7 @@ OPTIONCALLBACK(temperature_toggle, visible_cols.temperature) 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) { @@ -484,8 +524,22 @@ static void preferences_dialog(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)); @@ -514,6 +568,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data) 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(); @@ -803,6 +858,8 @@ void init_ui(int *argcp, char ***argvp) 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); diff --git a/info.c b/info.c index 8db6063..d9379ac 100644 --- a/info.c +++ b/info.c @@ -1,7 +1,7 @@ /* info.c */ -/* creates the UI for the info frame - +/* creates the UI for the info frame - * controlled through the following interfaces: - * + * * void show_dive_info(struct dive *dive) * * called from gtk-ui: @@ -482,7 +482,7 @@ void update_equipment_data(struct dive *dive, struct dive *master) if ( ! cylinders_equal(remember_cyl, master->cylinder) && (no_cylinders(dive->cylinder) || cylinders_equal(dive->cylinder, remember_cyl))) - memcpy(dive->cylinder, master->cylinder, CYL_BYTES); + copy_cylinders(master->cylinder, dive->cylinder); if (! weightsystems_equal(remember_ws, master->weightsystem) && (no_weightsystems(dive->weightsystem) || weightsystems_equal(dive->weightsystem, remember_ws))) @@ -496,6 +496,7 @@ int edit_multi_dive_info(struct dive *single_dive) GtkWidget *dialog, *vbox; struct dive_info info; struct dive *master; + gboolean multi; dialog = gtk_dialog_new_with_buttons("Dive Info", GTK_WINDOW(main_window), @@ -508,7 +509,22 @@ int edit_multi_dive_info(struct dive *single_dive) master = single_dive; if (!master) master = current_dive; - dive_info_widget(vbox, master, &info, !single_dive); + + /* See if we should use multi dive mode */ + multi = FALSE; + if (!single_dive) { + int i; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive != master && dive->selected) { + multi = TRUE; + break; + } + } + } + + dive_info_widget(vbox, master, &info, multi); show_dive_equipment(master, W_IDX_SECONDARY); save_equipment_data(master); gtk_widget_show_all(dialog); diff --git a/libdivecomputer.c b/libdivecomputer.c index e362d1d..d96d276 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -305,13 +305,6 @@ static dc_status_t import_device_data(dc_device_t *device, device_data_t *device return dc_device_foreach(device, dive_cb, devicedata); } -static dc_status_t device_open(const char *devname, - dc_descriptor_t *descriptor, - dc_device_t **device) -{ - return dc_device_open(device, descriptor, devname); -} - static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata) { @@ -351,42 +344,53 @@ cancel_cb(void *userdata) return import_thread_cancelled; } -static const char *do_libdivecomputer_import(device_data_t *data) +static const char *do_device_import(device_data_t *data) { - dc_device_t *device = NULL; dc_status_t rc; - - import_dive_number = 0; - rc = device_open(data->devname, data->descriptor, &device); - if (rc != DC_STATUS_SUCCESS) - return "Unable to open %s %s (%s)"; - data->device = device; + dc_device_t *device = data->device; // Register the event handler. int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK; rc = dc_device_set_events(device, events, event_cb, data); - if (rc != DC_STATUS_SUCCESS) { - dc_device_close(device); + if (rc != DC_STATUS_SUCCESS) return "Error registering the event handler."; - } // Register the cancellation handler. rc = dc_device_set_cancel(device, cancel_cb, data); - if (rc != DC_STATUS_SUCCESS) { - dc_device_close(device); + if (rc != DC_STATUS_SUCCESS) return "Error registering the cancellation handler."; - } rc = import_device_data(device, data); - if (rc != DC_STATUS_SUCCESS) { - dc_device_close(device); + if (rc != DC_STATUS_SUCCESS) return "Dive data import error"; - } - dc_device_close(device); + /* All good */ return NULL; } +static const char *do_libdivecomputer_import(device_data_t *data) +{ + dc_status_t rc; + const char *err; + + import_dive_number = 0; + data->device = NULL; + data->context = NULL; + + rc = dc_context_new(&data->context); + if (rc != DC_STATUS_SUCCESS) + return "Unable to create libdivecomputer context"; + + err = "Unable to open %s %s (%s)"; + rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname); + if (rc == DC_STATUS_SUCCESS) { + err = do_device_import(data); + dc_device_close(data->device); + } + dc_context_free(data->context); + return err; +} + static void *pthread_wrapper(void *_data) { device_data_t *data = _data; diff --git a/libdivecomputer.h b/libdivecomputer.h index 8d77a25..6430c84 100644 --- a/libdivecomputer.h +++ b/libdivecomputer.h @@ -4,7 +4,6 @@ /* libdivecomputer */ #include #include -#include /* handling uemis Zurich SDA files */ #include "uemis.h" @@ -15,6 +14,7 @@ typedef struct device_data_t { dc_descriptor_t *descriptor; const char *vendor, *product, *devname; dc_device_t *device; + dc_context_t *context; progressbar_t progress; int preexisting; } device_data_t; diff --git a/main.c b/main.c index 8e579f8..2489473 100644 --- a/main.c +++ b/main.c @@ -172,7 +172,7 @@ static void parse_argument(const char *arg) if (strncmp(arg, "-psn_", 5) == 0) { return; } - /* fallthrough */ + /* fallthrough */ default: fprintf(stderr, "Bad argument '%s'\n", arg); exit(1); @@ -217,7 +217,7 @@ int main(int argc, char **argv) parse_xml_init(); init_ui(&argc, &argv); - + for (i = 1; i < argc; i++) { const char *a = argv[i]; @@ -227,7 +227,7 @@ int main(int argc, char **argv) } GError *error = NULL; parse_file(a, &error); - + if (error != NULL) { report_error(error); diff --git a/parse-xml.c b/parse-xml.c index 173314d..5159a33 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -39,6 +39,11 @@ 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]; @@ -156,7 +161,7 @@ const struct units IMPERIAL_units = { /* * 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; @@ -535,6 +540,17 @@ static void get_index(char *buffer, void *_i) 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; @@ -1062,6 +1078,8 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf) 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)) @@ -1138,6 +1156,27 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf) 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 @@ -1162,6 +1201,22 @@ static void dive_end(void) 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)); @@ -1225,6 +1280,10 @@ static void entry(const char *name, int size, const char *raw) 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; @@ -1350,6 +1409,7 @@ static struct nesting { } 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 }, @@ -1420,8 +1480,8 @@ void parse_xml_buffer(const char *url, const char *buffer, int size, GError **er } 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(); diff --git a/print.c b/print.c index 0bd3110..5a1a784 100644 --- a/print.c +++ b/print.c @@ -11,7 +11,19 @@ #define FONT_SMALL (FONT_NORMAL / 1.2) #define FONT_LARGE (FONT_NORMAL * 1.2) -static void set_font(PangoLayout *layout, PangoFontDescription *font, double size, int align) +static struct options print_options; + +#define PRETTYOPTIONCALLBACK(name, option) \ +static void name(GtkWidget *w, gpointer data) \ +{ \ + option = GTK_TOGGLE_BUTTON(w)->active; \ +} + +PRETTYOPTIONCALLBACK(print_profiles_toggle, print_options.print_profiles) + + +static void set_font(PangoLayout *layout, PangoFontDescription *font, + double size, int align) { pango_font_description_set_size(font, size * PANGO_SCALE); pango_layout_set_font_description(layout, font); @@ -24,7 +36,8 @@ static void set_font(PangoLayout *layout, PangoFontDescription *font, double siz * You know what? Maybe somebody can do a real Pango layout thing. * This is hacky. */ -static void show_dive_text(struct dive *dive, cairo_t *cr, double w, double h, PangoFontDescription *font) +static void show_dive_text(struct dive *dive, cairo_t *cr, double w, + double h, PangoFontDescription *font) { double depth; const char *unit; @@ -125,7 +138,103 @@ static void show_dive_text(struct dive *dive, cairo_t *cr, double w, double h, P g_object_unref(layout); } -static void show_dive_profile(struct dive *dive, cairo_t *cr, double w, double h) +static void show_table_header(cairo_t *cr, double w, double h, + PangoFontDescription *font) +{ + int len, width, height, maxwidth, maxheight; + PangoLayout *layout; + char buffer[160]; + + maxwidth = w * PANGO_SCALE; + maxheight = h * PANGO_SCALE * 0.9; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_width(layout, maxwidth); + pango_layout_set_height(layout, maxheight); + + len = snprintf(buffer, sizeof(buffer), + "Dive# - Date - Depth - Time - Master" + " Buddy -- Location"); + + set_font(layout, font, FONT_LARGE, PANGO_ALIGN_LEFT); + pango_layout_set_text(layout, buffer, len); + pango_layout_get_size(layout, &width, &height); + + //cairo_move_to(cr, 0, 0); + pango_cairo_show_layout(cr, layout); +} + +static void show_dive_table(struct dive *dive, cairo_t *cr, double w, + double h, PangoFontDescription *font) +{ + double depth; + const char *unit; + int len, decimals, width, height, maxwidth, maxheight; + PangoLayout *layout; + struct tm *tm; + char buffer[160], divenr[20]; + + maxwidth = w * PANGO_SCALE; + maxheight = h * PANGO_SCALE * 0.9; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_width(layout, maxwidth); + pango_layout_set_height(layout, maxheight); + + *divenr = 0; + if (dive->number) + snprintf(divenr, sizeof(divenr), "#%d -", dive->number); + + depth = get_depth_units(dive->maxdepth.mm, &decimals, &unit); + + tm = gmtime(&dive->when); + len = snprintf(buffer, sizeof(buffer), + "%s %s, %s %d, %d %dh%02d - %.*f %s - %d min - %s %s -- %s", + divenr, + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, + decimals, + depth, + unit, + (dive->duration.seconds+59) / 60, + dive->divemaster ? : " ", + dive->buddy ? : " ", + dive->location ? : " " + ); + + set_font(layout, font, FONT_NORMAL, PANGO_ALIGN_LEFT); + pango_layout_set_text(layout, buffer, len); + pango_layout_get_size(layout, &width, &height); + + cairo_move_to(cr, 0, 0); + pango_cairo_show_layout(cr, layout); + + ///* + //* Show the dive notes + //*/ + if (dive->notes) { + /* Move down by the size of the location (x2) */ + height = height * 1.3; + cairo_translate(cr, 20, height / (double) PANGO_SCALE); + maxheight -= height; + + /* Use the full width and remaining height for notes */ + pango_layout_set_height(layout, maxheight); + pango_layout_set_width(layout, maxwidth); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_justify(layout, 1); + pango_layout_set_text(layout, dive->notes, -1); + + cairo_move_to(cr, 0, 0); + pango_cairo_show_layout(cr, layout); + } + g_object_unref(layout); +} + +static void show_dive_profile(struct dive *dive, cairo_t *cr, double w, + double h) { cairo_rectangle_int_t drawing_area = { w/20.0, h/20.0, w, h}; struct graphics_context gc = { @@ -137,7 +246,8 @@ static void show_dive_profile(struct dive *dive, cairo_t *cr, double w, double h cairo_restore(cr); } -static void print(int divenr, cairo_t *cr, double x, double y, double w, double h, PangoFontDescription *font) +static void print(int divenr, cairo_t *cr, double x, double y, double w, + double h, PangoFontDescription *font) { struct dive *dive; @@ -165,6 +275,70 @@ static void print(int divenr, cairo_t *cr, double x, double y, double w, double cairo_restore(cr); } +static void print_pretty_table(int divenr, cairo_t *cr, double x, double y, + double w, double h, PangoFontDescription *font) +{ + struct dive *dive; + + dive = get_dive(divenr); + if (!dive) + return; + cairo_save(cr); + cairo_translate(cr, x, y); + + /* Plus 5% on all sides */ + cairo_translate(cr, w/20, h/20); + w *= 0.9; h *= 0.9; + + /* We actually want to scale the text and the lines now */ + cairo_scale(cr, 0.5, 0.5); + + show_dive_text(dive, cr, w*2, h*2, font); + + cairo_restore(cr); +} + +static void print_table_header(cairo_t *cr, double x, double y, + double w, double h, PangoFontDescription *font) +{ + cairo_save(cr); + cairo_translate(cr, x, y); + + /* Plus 5% on all sides */ + cairo_translate(cr, w/20, h/20); + w *= 0.9; h *= 0.9; + + /* We actually want to scale the text and the lines now */ + cairo_scale(cr, 0.5, 0.5); + + show_table_header(cr, w*2, h*2, font); + + cairo_restore(cr); +} + +static void print_table(int divenr, cairo_t *cr, double x, double y, + double w, double h, PangoFontDescription *font) +{ + struct dive *dive; + + dive = get_dive(divenr); + if (!dive) + return; + cairo_save(cr); + cairo_translate(cr, x, y); + + /* Plus 5% on all sides */ + cairo_translate(cr, w/20, h/20); + w *= 0.9; h *= 0.9; + + /* We actually want to scale the text and the lines now */ + cairo_scale(cr, 0.5, 0.5); + + show_dive_table(dive, cr, w*2, h*2, font); + + cairo_restore(cr); +} + static void draw_page(GtkPrintOperation *operation, GtkPrintContext *context, gint page_nr, @@ -192,44 +366,169 @@ static void draw_page(GtkPrintOperation *operation, pango_font_description_free(font); } +static void draw_pretty_table(GtkPrintOperation *operation, + GtkPrintContext *context, + gint page_nr, + gpointer user_data) +{ + int nr; + cairo_t *cr; + double w, h; + PangoFontDescription *font; + + cr = gtk_print_context_get_cairo_context(context); + font = pango_font_description_from_string("Sans"); + + w = gtk_print_context_get_width(context); + h = gtk_print_context_get_height(context)/15; + + nr = page_nr*15; + int i; + for (i = 0; i < 15; i++) { + print_pretty_table(nr+i, cr, 0, 0+h*i, w, h, font); + } + + pango_font_description_free(font); +} + +static void draw_table(GtkPrintOperation *operation, + GtkPrintContext *context, + gint page_nr, + gpointer user_data) +{ + int nr; + int n_dive_per_page = 25; + cairo_t *cr; + double w, h; + PangoFontDescription *font; + + cr = gtk_print_context_get_cairo_context(context); + font = pango_font_description_from_string("Sans"); + + w = gtk_print_context_get_width(context); + h = gtk_print_context_get_height(context)/(n_dive_per_page+1); + + nr = page_nr*n_dive_per_page; + print_table_header(cr, 0, 0+h, w, h, font); + int i; + for (i = 0; i < n_dive_per_page; i++) { + print_table(nr+i, cr, 0, h*1.5+h*i, w, h, font); + } + + pango_font_description_free(font); +} + static void begin_print(GtkPrintOperation *operation, gpointer user_data) { + int dives_per_page = 1; + if (print_options.type == PRETTY) { + if (print_options.print_profiles){ + dives_per_page = 6; + } else { + dives_per_page = 15; + } + } else { + dives_per_page = 25; + } + int pages; + pages = (dive_table.nr + dives_per_page - 1) / dives_per_page; + gtk_print_operation_set_n_pages(operation, pages); } +static void update_print_window(GtkWidget *w) { + if (print_options.type == TABLE) { + // type == table - disable the profile option + gtk_widget_set_sensitive(w, FALSE); + } else { + // type == pretty - enable the profile option + gtk_widget_set_sensitive(w, TRUE); + } +} + +#define OPTIONCALLBACK(name, type, value) \ +static void name(GtkWidget *w, gpointer data) \ +{\ + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \ + print_options.type = value; \ + update_print_window(data); \ +} + +OPTIONCALLBACK(set_pretty, type, PRETTY) +OPTIONCALLBACK(set_table, type, TABLE) + static GtkWidget *print_dialog(GtkPrintOperation *operation, gpointer user_data) { - GtkWidget *vbox, *hbox, *label; + GtkWidget *vbox, *button, *radio1, *radio2, *frame, *box; gtk_print_operation_set_custom_tab_label(operation, "Dive details"); vbox = gtk_vbox_new(TRUE, 5); - label = gtk_label_new("Print Dive details"); - gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + + frame = gtk_frame_new("Print type"); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 1); + + box = gtk_hbox_new(FALSE, 2); + gtk_container_add(GTK_CONTAINER(frame), box); + + radio1 = gtk_radio_button_new_with_label (NULL, "Pretty print"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio1), + print_options.type == PRETTY); + radio2 = gtk_radio_button_new_with_label_from_widget ( + GTK_RADIO_BUTTON (radio1), "Table print"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2), + print_options.type == TABLE); + gtk_box_pack_start (GTK_BOX (box), radio1, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), radio2, TRUE, TRUE, 0); + + + frame = gtk_frame_new("Print options"); + gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 1); + + box = gtk_hbox_new(FALSE, 3); + gtk_container_add(GTK_CONTAINER(frame), box); + + button = gtk_check_button_new_with_label("Show profiles"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), print_options.print_profiles); + gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 2); + g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(print_profiles_toggle), NULL); + + g_signal_connect(radio1, "toggled", G_CALLBACK(set_pretty), button); + g_signal_connect(radio2, "toggled", G_CALLBACK(set_table), button); + gtk_widget_show_all(vbox); return vbox; } static void print_dialog_apply(GtkPrintOperation *operation, GtkWidget *widget, gpointer user_data) { + if (print_options.type == PRETTY) { + if (print_options.print_profiles){ + g_signal_connect(operation, "draw_page", + G_CALLBACK(draw_page), NULL); + } else { + g_signal_connect(operation, "draw_page", + G_CALLBACK(draw_pretty_table), NULL); + } + } else { + g_signal_connect(operation, "draw_page", + G_CALLBACK(draw_table), NULL); + } } static GtkPrintSettings *settings = NULL; void do_print(void) { - int pages; GtkPrintOperation *print; GtkPrintOperationResult res; repaint_dive(); print = gtk_print_operation_new(); + gtk_print_operation_set_unit(print, GTK_UNIT_POINTS); if (settings != NULL) gtk_print_operation_set_print_settings(print, settings); - pages = (dive_table.nr + 5) / 6; - gtk_print_operation_set_n_pages(print, pages); g_signal_connect(print, "create-custom-widget", G_CALLBACK(print_dialog), NULL); g_signal_connect(print, "custom-widget-apply", G_CALLBACK(print_dialog_apply), NULL); g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), NULL); - g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), NULL); res = gtk_print_operation_run(print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(main_window), NULL); if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { diff --git a/profile.c b/profile.c index 3ad11c0..d4e88c0 100644 --- a/profile.c +++ b/profile.c @@ -1,5 +1,5 @@ /* profile.c */ -/* creates all the necessary data for drawing the dive profile +/* creates all the necessary data for drawing the dive profile * uses cairo to draw it */ #include @@ -899,7 +899,7 @@ static velocity_t velocity(int speed) else if (speed < -25) /* -5ft/min */ v = SLOW; else if (speed < 25) /* very hard to find data, but it appears that the recommendations - for descent are usually about 2x ascent rate; still, we want + for descent are usually about 2x ascent rate; still, we want stable to mean stable */ v = STABLE; else if (speed < 152) /* between 5 and 30ft/min is considered slow */ @@ -955,7 +955,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) int past = -2; while (i+past > 0 && entry[0].sec - entry[past].sec < 15) past--; - entry->velocity = velocity((entry[0].depth - entry[past].depth) / + entry->velocity = velocity((entry[0].depth - entry[past].depth) / (entry[0].sec - entry[past].sec)); } } else @@ -967,7 +967,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi) struct plot_data *entry = pi->entry +i; analyze_plot_info_minmax(entry, pi->entry, pi->entry+nr); } - + return pi; } diff --git a/save-xml.c b/save-xml.c index 37d6d06..b797475 100644 --- a/save-xml.c +++ b/save-xml.c @@ -67,10 +67,9 @@ static void show_pressure(FILE *f, pressure_t pressure, const char *pre, const c * 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; @@ -97,6 +96,16 @@ static void quote(FILE *f, const char *text) case '&': escape = "&"; break; + case '\'': + if (!is_attribute) + continue; + escape = "'"; + break; + case '\"': + if (!is_attribute) + continue; + escape = """; + break; } fwrite(text, (p - text - 1), 1, f); if (!escape) @@ -106,7 +115,7 @@ static void quote(FILE *f, const char *text) } } -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; @@ -121,7 +130,7 @@ static void show_utf8(FILE *f, const char *text, const char *pre, const char *po len--; /* FIXME! Quoting! */ fputs(pre, f); - quote(f, text); + quote(f, text, is_attribute); fputs(post, f); } @@ -171,7 +180,7 @@ static void show_location(FILE *f, struct dive *dive) } prefix = buffer; } - show_utf8(f, dive->location, prefix,"\n"); + show_utf8(f, dive->location, prefix,"\n", 0); } static void save_overview(FILE *f, struct dive *dive) @@ -180,10 +189,10 @@ static void save_overview(FILE *f, struct dive *dive) save_temperatures(f, dive); show_duration(f, dive->surfacetime, " ", "\n"); show_location(f, dive); - show_utf8(f, dive->divemaster, " ","\n"); - show_utf8(f, dive->buddy, " ","\n"); - show_utf8(f, dive->notes, " ","\n"); - show_utf8(f, dive->suit, " ","\n"); + show_utf8(f, dive->divemaster, " ","\n", 0); + show_utf8(f, dive->buddy, " ","\n", 0); + show_utf8(f, dive->notes, " ","\n", 0); + show_utf8(f, dive->suit, " ","\n", 0); } static void save_cylinder_info(FILE *f, struct dive *dive) @@ -262,7 +271,7 @@ static void save_one_event(FILE *f, struct event *ev) 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"); } @@ -275,6 +284,18 @@ static void save_events(FILE *f, struct event *ev) } } +static void save_trip(FILE *f, struct dive *trip) +{ + struct tm *tm = gmtime(&trip->when); + + fprintf(f, "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; @@ -283,6 +304,8 @@ static void save_dive(FILE *f, struct dive *dive) fputs("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'", @@ -305,6 +328,8 @@ static void save_dive(FILE *f, struct dive *dive) void save_dives(const char *filename) { int i; + GList *trip = NULL; + FILE *f = fopen(filename, "w"); if (!f) @@ -314,6 +339,12 @@ void save_dives(const char *filename) update_dive(current_dive); fprintf(f, "\n\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, "\n"); diff --git a/statistics.c b/statistics.c index b9d2c3b..a536173 100644 --- a/statistics.c +++ b/statistics.c @@ -211,7 +211,7 @@ static void show_single_dive_stats(struct dive *dive) set_label(single_w.date, buf); set_label(single_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60); if (prev_dive) - set_label(single_w.surf_intv, + set_label(single_w.surf_intv, get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4)); else set_label(single_w.surf_intv, "unknown"); diff --git a/uemis.h b/uemis.h index 29a168f..ba9a234 100644 --- a/uemis.h +++ b/uemis.h @@ -28,7 +28,7 @@ typedef struct { uint16_t consumption; // (units unclear) uint8_t rgt; // (remaining gas time in minutes) uint8_t cns; - uint8_t flags[8]; + uint8_t flags[8]; } __attribute((packed)) uemis_sample_t; #endif /* DIVE_H */