mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
75765be14c
This allows this code to easily be shared by Gtk and Qt UI. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
867 lines
20 KiB
C
867 lines
20 KiB
C
/* divelist.c */
|
|
/* core logic for the dive list -
|
|
* accessed through the following interfaces:
|
|
*
|
|
* dive_trip_t *dive_trip_list;
|
|
* unsigned int amount_selected;
|
|
* void dump_selection(void)
|
|
* dive_trip_t *find_trip_by_idx(int idx)
|
|
* int trip_has_selected_dives(dive_trip_t *trip)
|
|
* void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
|
|
* int total_weight(struct dive *dive)
|
|
* int get_divenr(struct dive *dive)
|
|
* double init_decompression(struct dive *dive)
|
|
* void update_cylinder_related_info(struct dive *dive)
|
|
* void get_location(struct dive *dive, char **str)
|
|
* void get_cylinder(struct dive *dive, char **str)
|
|
* void get_suit(struct dive *dive, char **str)
|
|
* void dump_trip_list(void)
|
|
* dive_trip_t *find_matching_trip(timestamp_t when)
|
|
* void insert_trip(dive_trip_t **dive_trip_p)
|
|
* void remove_dive_from_trip(struct dive *dive)
|
|
* void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
|
|
* dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
|
|
* void autogroup_dives(void)
|
|
* void clear_trip_indexes(void)
|
|
* void delete_single_dive(int idx)
|
|
* void add_single_dive(int idx, struct dive *dive)
|
|
* void merge_dive_index(int i, struct dive *a)
|
|
* void select_dive(int idx)
|
|
* void deselect_dive(int idx)
|
|
* void mark_divelist_changed(int changed)
|
|
* int unsaved_changes()
|
|
* void remove_autogen_trips()
|
|
*/
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <glib/gi18n.h>
|
|
#include <assert.h>
|
|
#ifdef LIBZIP
|
|
#include <zip.h>
|
|
#endif
|
|
#ifdef XSLT
|
|
#include <libxslt/transform.h>
|
|
#endif
|
|
|
|
#include "dive.h"
|
|
#include "divelist.h"
|
|
#include "display.h"
|
|
#include "webservice.h"
|
|
|
|
static short dive_list_changed = FALSE;
|
|
|
|
dive_trip_t *dive_trip_list;
|
|
|
|
unsigned int amount_selected;
|
|
|
|
#if DEBUG_SELECTION_TRACKING
|
|
void dump_selection(void)
|
|
{
|
|
int i;
|
|
struct dive *dive;
|
|
|
|
printf("currently selected are %u dives:", amount_selected);
|
|
for_each_dive(i, dive) {
|
|
if (dive->selected)
|
|
printf(" %d", i);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
dive_trip_t *find_trip_by_idx(int idx)
|
|
{
|
|
dive_trip_t *trip = dive_trip_list;
|
|
|
|
if (idx >= 0)
|
|
return NULL;
|
|
idx = -idx;
|
|
while (trip) {
|
|
if (trip->index == idx)
|
|
return trip;
|
|
trip = trip->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b)
|
|
{
|
|
struct dive *a, *b;
|
|
dive_trip_t *tripa = NULL, *tripb = NULL;
|
|
|
|
if (idx_a < 0) {
|
|
a = NULL;
|
|
tripa = find_trip_by_idx(idx_a);
|
|
} else {
|
|
a = get_dive(idx_a);
|
|
if (a)
|
|
tripa = a->divetrip;
|
|
}
|
|
|
|
if (idx_b < 0) {
|
|
b = NULL;
|
|
tripb = find_trip_by_idx(idx_b);
|
|
} else {
|
|
b = get_dive(idx_b);
|
|
if (b)
|
|
tripb = b->divetrip;
|
|
}
|
|
|
|
/*
|
|
* Compare dive dates within the same trip (or when there
|
|
* are no trips involved at all). But if we have two
|
|
* different trips use the trip dates for comparison
|
|
*/
|
|
if (tripa != tripb) {
|
|
if (tripa)
|
|
when_a = tripa->when;
|
|
if (tripb)
|
|
when_b = tripb->when;
|
|
}
|
|
return when_a - when_b;
|
|
}
|
|
|
|
int trip_has_selected_dives(dive_trip_t *trip)
|
|
{
|
|
struct dive *dive;
|
|
for (dive = trip->dives; dive; dive = dive->next) {
|
|
if (dive->selected)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Get the values as we want to show them. Whole feet. But meters with one decimal for
|
|
* values less than 20m, without decimals for larger values */
|
|
void get_depth_values(int depth, int *depth_int, int *depth_decimal, int *show_decimal)
|
|
{
|
|
int integer, frac;
|
|
|
|
*show_decimal = 1;
|
|
switch (prefs.units.length) {
|
|
case METERS:
|
|
/* To tenths of meters */
|
|
depth = (depth + 49) / 100;
|
|
integer = depth / 10;
|
|
frac = depth % 10;
|
|
if (integer < 20)
|
|
break;
|
|
if (frac >= 5)
|
|
integer++;
|
|
*show_decimal = 0;
|
|
break;
|
|
case FEET:
|
|
integer = mm_to_feet(depth) + 0.5;
|
|
*show_decimal = 0;
|
|
break;
|
|
default:
|
|
/* can't happen */
|
|
return;
|
|
}
|
|
*depth_int = integer;
|
|
if (*show_decimal)
|
|
*depth_decimal = frac;
|
|
}
|
|
|
|
/*
|
|
* Get "maximal" dive gas for a dive.
|
|
* Rules:
|
|
* - Trimix trumps nitrox (highest He wins, O2 breaks ties)
|
|
* - Nitrox trumps air (even if hypoxic)
|
|
* These are the same rules as the inter-dive sorting rules.
|
|
*/
|
|
void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
|
|
{
|
|
int i;
|
|
int maxo2 = -1, maxhe = -1, mino2 = 1000;
|
|
|
|
|
|
for (i = 0; i < MAX_CYLINDERS; i++) {
|
|
cylinder_t *cyl = dive->cylinder + i;
|
|
struct gasmix *mix = &cyl->gasmix;
|
|
int o2 = mix->o2.permille;
|
|
int he = mix->he.permille;
|
|
struct divecomputer *dc = &dive->dc;
|
|
int used = 0;
|
|
int first_gas_explicit = 0;
|
|
|
|
while (dc){
|
|
struct event *event = dc->events;
|
|
while(event){
|
|
if (event->value) {
|
|
if (event->name && !strcmp(event->name, "gaschange")) {
|
|
unsigned int event_he = event->value >> 16;
|
|
unsigned int event_o2 = event->value & 0xffff;
|
|
|
|
if (event->time.seconds < 30)
|
|
first_gas_explicit = 1;
|
|
if (is_air(o2, he)){
|
|
if (is_air(event_o2 * 10, event_he * 10))
|
|
used = 1;
|
|
}
|
|
else {
|
|
if (he == event_he*10 && o2 == event_o2*10)
|
|
used = 1;
|
|
}
|
|
}
|
|
}
|
|
event = event->next;
|
|
}
|
|
dc = dc->next;
|
|
}
|
|
|
|
/* Unless explicity set, the first gas to use has index 0 */
|
|
if (i == 0 && !first_gas_explicit)
|
|
used = 1;
|
|
|
|
if (!used)
|
|
continue;
|
|
|
|
if (cylinder_none(cyl))
|
|
continue;
|
|
if (!o2)
|
|
o2 = O2_IN_AIR;
|
|
if (o2 < mino2)
|
|
mino2 = o2;
|
|
if (he > maxhe)
|
|
goto newmax;
|
|
if (he < maxhe)
|
|
continue;
|
|
if (o2 <= maxo2)
|
|
continue;
|
|
newmax:
|
|
maxhe = he;
|
|
maxo2 = o2;
|
|
}
|
|
/* All air? Show/sort as "air"/zero */
|
|
if (!maxhe && maxo2 == O2_IN_AIR && mino2 == maxo2)
|
|
maxo2 = mino2 = 0;
|
|
*o2_p = maxo2;
|
|
*he_p = maxhe;
|
|
*o2low_p = mino2;
|
|
}
|
|
|
|
int total_weight(struct dive *dive)
|
|
{
|
|
int i, total_grams = 0;
|
|
|
|
if (dive)
|
|
for (i=0; i< MAX_WEIGHTSYSTEMS; i++)
|
|
total_grams += dive->weightsystem[i].weight.grams;
|
|
return total_grams;
|
|
}
|
|
|
|
static int active_o2(struct dive *dive, struct divecomputer *dc, duration_t time)
|
|
{
|
|
int o2permille = dive->cylinder[0].gasmix.o2.permille;
|
|
struct event *event = dc->events;
|
|
|
|
if (!o2permille)
|
|
o2permille = O2_IN_AIR;
|
|
|
|
for (event = dc->events; event; event = event->next) {
|
|
if (event->time.seconds > time.seconds)
|
|
break;
|
|
if (strcmp(event->name, "gaschange"))
|
|
continue;
|
|
o2permille = 10*(event->value & 0xffff);
|
|
}
|
|
return o2permille;
|
|
}
|
|
|
|
/* calculate OTU for a dive - this only takes the first diveomputer into account */
|
|
static int calculate_otu(struct dive *dive)
|
|
{
|
|
int i;
|
|
double otu = 0.0;
|
|
struct divecomputer *dc = &dive->dc;
|
|
|
|
for (i = 1; i < dc->samples; i++) {
|
|
int t;
|
|
int po2;
|
|
struct sample *sample = dc->sample + i;
|
|
struct sample *psample = sample - 1;
|
|
t = sample->time.seconds - psample->time.seconds;
|
|
if (sample->po2) {
|
|
po2 = sample->po2;
|
|
} else {
|
|
int o2 = active_o2(dive, dc, sample->time);
|
|
po2 = o2 / 1000.0 * depth_to_mbar(sample->depth.mm, dive);
|
|
}
|
|
if (po2 >= 500)
|
|
otu += pow((po2 - 500) / 1000.0, 0.83) * t / 30.0;
|
|
}
|
|
return otu + 0.5;
|
|
}
|
|
/*
|
|
* Return air usage (in liters).
|
|
*/
|
|
static double calculate_airuse(struct dive *dive)
|
|
{
|
|
int airuse = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_CYLINDERS; i++) {
|
|
pressure_t start, end;
|
|
cylinder_t *cyl = dive->cylinder + i;
|
|
|
|
start = cyl->start.mbar ? cyl->start : cyl->sample_start;
|
|
end = cyl->end.mbar ? cyl->end : cyl->sample_end;
|
|
|
|
airuse += gas_volume(cyl, start) - gas_volume(cyl, end);
|
|
}
|
|
return airuse / 1000.0;
|
|
}
|
|
|
|
/* this only uses the first divecomputer to calculate the SAC rate */
|
|
static int calculate_sac(struct dive *dive)
|
|
{
|
|
struct divecomputer *dc = &dive->dc;
|
|
double airuse, pressure, sac;
|
|
int duration, meandepth;
|
|
|
|
airuse = calculate_airuse(dive);
|
|
if (!airuse)
|
|
return 0;
|
|
|
|
duration = dc->duration.seconds;
|
|
if (!duration)
|
|
return 0;
|
|
|
|
meandepth = dc->meandepth.mm;
|
|
if (!meandepth)
|
|
return 0;
|
|
|
|
/* Mean pressure in ATM (SAC calculations are in atm*l/min) */
|
|
pressure = (double) depth_to_mbar(meandepth, dive) / SURFACE_PRESSURE;
|
|
sac = airuse / pressure * 60 / duration;
|
|
|
|
/* milliliters per minute.. */
|
|
return sac * 1000;
|
|
}
|
|
|
|
/* for now we do this based on the first divecomputer */
|
|
static void add_dive_to_deco(struct dive *dive)
|
|
{
|
|
struct divecomputer *dc = &dive->dc;
|
|
int i;
|
|
|
|
if (!dc)
|
|
return;
|
|
for (i = 1; i < dc->samples; i++) {
|
|
struct sample *psample = dc->sample + i - 1;
|
|
struct sample *sample = dc->sample + i;
|
|
int t0 = psample->time.seconds;
|
|
int t1 = sample->time.seconds;
|
|
int j;
|
|
|
|
for (j = t0; j < t1; j++) {
|
|
int depth = interpolate(psample->depth.mm, sample->depth.mm, j - t0, t1 - t0);
|
|
(void) add_segment(depth_to_mbar(depth, dive) / 1000.0,
|
|
&dive->cylinder[sample->sensor].gasmix, 1, sample->po2, dive);
|
|
}
|
|
}
|
|
}
|
|
|
|
int get_divenr(struct dive *dive)
|
|
{
|
|
int divenr = -1;
|
|
while (++divenr < dive_table.nr && get_dive(divenr) != dive)
|
|
;
|
|
return divenr;
|
|
}
|
|
|
|
static struct gasmix air = { .o2.permille = O2_IN_AIR };
|
|
|
|
/* take into account previous dives until there is a 48h gap between dives */
|
|
double init_decompression(struct dive *dive)
|
|
{
|
|
int i, divenr = -1;
|
|
unsigned int surface_time;
|
|
timestamp_t when, lasttime = 0;
|
|
gboolean deco_init = FALSE;
|
|
double tissue_tolerance, surface_pressure;
|
|
|
|
if (!dive)
|
|
return 0.0;
|
|
divenr = get_divenr(dive);
|
|
when = dive->when;
|
|
i = divenr;
|
|
while (i && --i) {
|
|
struct dive* pdive = get_dive(i);
|
|
/* we don't want to mix dives from different trips as we keep looking
|
|
* for how far back we need to go */
|
|
if (dive->divetrip && pdive->divetrip != dive->divetrip)
|
|
continue;
|
|
if (!pdive || pdive->when > when || pdive->when + pdive->duration.seconds + 48 * 60 * 60 < when)
|
|
break;
|
|
when = pdive->when;
|
|
lasttime = when + pdive->duration.seconds;
|
|
}
|
|
while (++i < divenr) {
|
|
struct dive* pdive = get_dive(i);
|
|
/* again skip dives from different trips */
|
|
if (dive->divetrip && dive->divetrip != pdive->divetrip)
|
|
continue;
|
|
surface_pressure = get_surface_pressure_in_mbar(pdive, TRUE) / 1000.0;
|
|
if (!deco_init) {
|
|
clear_deco(surface_pressure);
|
|
deco_init = TRUE;
|
|
#if DECO_CALC_DEBUG & 2
|
|
dump_tissues();
|
|
#endif
|
|
}
|
|
add_dive_to_deco(pdive);
|
|
#if DECO_CALC_DEBUG & 2
|
|
printf("added dive #%d\n", pdive->number);
|
|
dump_tissues();
|
|
#endif
|
|
if (pdive->when > lasttime) {
|
|
surface_time = pdive->when - lasttime;
|
|
lasttime = pdive->when + pdive->duration.seconds;
|
|
tissue_tolerance = add_segment(surface_pressure, &air, surface_time, 0, dive);
|
|
#if DECO_CALC_DEBUG & 2
|
|
printf("after surface intervall of %d:%02u\n", FRACTION(surface_time,60));
|
|
dump_tissues();
|
|
#endif
|
|
}
|
|
}
|
|
/* add the final surface time */
|
|
if (lasttime && dive->when > lasttime) {
|
|
surface_time = dive->when - lasttime;
|
|
surface_pressure = get_surface_pressure_in_mbar(dive, TRUE) / 1000.0;
|
|
tissue_tolerance = add_segment(surface_pressure, &air, surface_time, 0, dive);
|
|
#if DECO_CALC_DEBUG & 2
|
|
printf("after surface intervall of %d:%02u\n", FRACTION(surface_time,60));
|
|
dump_tissues();
|
|
#endif
|
|
}
|
|
if (!deco_init) {
|
|
double surface_pressure = get_surface_pressure_in_mbar(dive, TRUE) / 1000.0;
|
|
clear_deco(surface_pressure);
|
|
#if DECO_CALC_DEBUG & 2
|
|
printf("no previous dive\n");
|
|
dump_tissues();
|
|
#endif
|
|
}
|
|
return tissue_tolerance;
|
|
}
|
|
|
|
void update_cylinder_related_info(struct dive *dive)
|
|
{
|
|
if (dive != NULL) {
|
|
dive->sac = calculate_sac(dive);
|
|
dive->otu = calculate_otu(dive);
|
|
}
|
|
}
|
|
|
|
static void get_string(char **str, const char *s)
|
|
{
|
|
int len;
|
|
char *n;
|
|
|
|
if (!s)
|
|
s = "";
|
|
len = g_utf8_strlen(s, -1);
|
|
if (len > 60)
|
|
len = 60;
|
|
n = malloc(len * sizeof(gunichar) + 1);
|
|
g_utf8_strncpy(n, s, len);
|
|
*str = n;
|
|
}
|
|
|
|
void get_location(struct dive *dive, char **str)
|
|
{
|
|
get_string(str, dive->location);
|
|
}
|
|
|
|
void get_cylinder(struct dive *dive, char **str)
|
|
{
|
|
get_string(str, dive->cylinder[0].type.description);
|
|
}
|
|
|
|
void get_suit(struct dive *dive, char **str)
|
|
{
|
|
get_string(str, dive->suit);
|
|
}
|
|
|
|
#define MAX_DATE_STRING 256
|
|
/* caller needs to free the string */
|
|
char *get_dive_date_string(struct tm *tm) {
|
|
char *buffer = malloc(MAX_DATE_STRING);
|
|
if (buffer)
|
|
snprintf(buffer, MAX_DATE_STRING,
|
|
/*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */
|
|
_("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"),
|
|
weekday(tm->tm_wday),
|
|
monthname(tm->tm_mon),
|
|
tm->tm_mday, tm->tm_year + 1900,
|
|
tm->tm_hour, tm->tm_min);
|
|
return buffer;
|
|
}
|
|
|
|
/* caller needs to free the string */
|
|
char *get_trip_date_string(struct tm *tm, int nr) {
|
|
char *buffer = malloc(MAX_DATE_STRING);
|
|
if (buffer)
|
|
snprintf(buffer, MAX_DATE_STRING,
|
|
/*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */
|
|
ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)",
|
|
"Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr),
|
|
weekday(tm->tm_wday),
|
|
monthname(tm->tm_mon),
|
|
tm->tm_mday, tm->tm_year + 1900,
|
|
nr);
|
|
return buffer;
|
|
}
|
|
|
|
/*
|
|
* helper functions for dive_trip handling
|
|
*/
|
|
#ifdef DEBUG_TRIP
|
|
void dump_trip_list(void)
|
|
{
|
|
dive_trip_t *trip;
|
|
int i=0;
|
|
timestamp_t last_time = 0;
|
|
|
|
for (trip = dive_trip_list; trip; trip = trip->next) {
|
|
struct tm tm;
|
|
utc_mkdate(trip->when, &tm);
|
|
if (trip->when < last_time)
|
|
printf("\n\ndive_trip_list OUT OF ORDER!!!\n\n\n");
|
|
printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u (%d dives - %p)\n",
|
|
trip->autogen ? "autogen " : "",
|
|
++i, trip->location,
|
|
tm.tm_year + 1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
|
|
trip->nrdives, trip);
|
|
last_time = trip->when;
|
|
}
|
|
printf("-----\n");
|
|
}
|
|
#endif
|
|
|
|
/* this finds the last trip that at or before the time given */
|
|
dive_trip_t *find_matching_trip(timestamp_t when)
|
|
{
|
|
dive_trip_t *trip = dive_trip_list;
|
|
|
|
if (!trip || trip->when > when) {
|
|
#ifdef DEBUG_TRIP
|
|
printf("no matching trip\n");
|
|
#endif
|
|
return NULL;
|
|
}
|
|
while (trip->next && trip->next->when <= when)
|
|
trip = trip->next;
|
|
#ifdef DEBUG_TRIP
|
|
{
|
|
struct tm tm;
|
|
utc_mkdate(trip->when, &tm);
|
|
printf("found trip %p @ %04d-%02d-%02d %02d:%02d:%02d\n",
|
|
trip,
|
|
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
}
|
|
#endif
|
|
return trip;
|
|
}
|
|
|
|
/* 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 */
|
|
void insert_trip(dive_trip_t **dive_trip_p)
|
|
{
|
|
dive_trip_t *dive_trip = *dive_trip_p;
|
|
dive_trip_t **p = &dive_trip_list;
|
|
dive_trip_t *trip;
|
|
struct dive *divep;
|
|
|
|
/* Walk the dive trip list looking for the right location.. */
|
|
while ((trip = *p) != NULL && trip->when < dive_trip->when)
|
|
p = &trip->next;
|
|
|
|
if (trip && trip->when == dive_trip->when) {
|
|
if (! trip->location)
|
|
trip->location = dive_trip->location;
|
|
if (! trip->notes)
|
|
trip->notes = dive_trip->notes;
|
|
divep = dive_trip->dives;
|
|
while (divep) {
|
|
add_dive_to_trip(divep, trip);
|
|
divep = divep->next;
|
|
}
|
|
*dive_trip_p = trip;
|
|
} else {
|
|
dive_trip->next = trip;
|
|
*p = dive_trip;
|
|
}
|
|
#ifdef DEBUG_TRIP
|
|
dump_trip_list();
|
|
#endif
|
|
}
|
|
|
|
static void delete_trip(dive_trip_t *trip)
|
|
{
|
|
dive_trip_t **p, *tmp;
|
|
|
|
assert(!trip->dives);
|
|
|
|
/* Remove the trip from the list of trips */
|
|
p = &dive_trip_list;
|
|
while ((tmp = *p) != NULL) {
|
|
if (tmp == trip) {
|
|
*p = trip->next;
|
|
break;
|
|
}
|
|
p = &tmp->next;
|
|
}
|
|
|
|
/* .. and free it */
|
|
if (trip->location)
|
|
free(trip->location);
|
|
if (trip->notes)
|
|
free(trip->notes);
|
|
free(trip);
|
|
}
|
|
|
|
static void find_new_trip_start_time(dive_trip_t *trip)
|
|
{
|
|
struct dive *dive = trip->dives;
|
|
timestamp_t when = dive->when;
|
|
|
|
while ((dive = dive->next) != NULL) {
|
|
if (dive->when < when)
|
|
when = dive->when;
|
|
}
|
|
trip->when = when;
|
|
}
|
|
|
|
void remove_dive_from_trip(struct dive *dive)
|
|
{
|
|
struct dive *next, **pprev;
|
|
dive_trip_t *trip = dive->divetrip;
|
|
|
|
if (!trip)
|
|
return;
|
|
|
|
/* Remove the dive from the trip's list of dives */
|
|
next = dive->next;
|
|
pprev = dive->pprev;
|
|
*pprev = next;
|
|
if (next)
|
|
next->pprev = pprev;
|
|
|
|
dive->divetrip = NULL;
|
|
dive->tripflag = NO_TRIP;
|
|
assert(trip->nrdives > 0);
|
|
if (!--trip->nrdives)
|
|
delete_trip(trip);
|
|
else if (trip->when == dive->when)
|
|
find_new_trip_start_time(trip);
|
|
}
|
|
|
|
void add_dive_to_trip(struct dive *dive, dive_trip_t *trip)
|
|
{
|
|
if (dive->divetrip == trip)
|
|
return;
|
|
assert(trip->when);
|
|
remove_dive_from_trip(dive);
|
|
trip->nrdives++;
|
|
dive->divetrip = trip;
|
|
dive->tripflag = ASSIGNED_TRIP;
|
|
|
|
/* Add it to the trip's list of dives*/
|
|
dive->next = trip->dives;
|
|
if (dive->next)
|
|
dive->next->pprev = &dive->next;
|
|
trip->dives = dive;
|
|
dive->pprev = &trip->dives;
|
|
|
|
if (dive->when && trip->when > dive->when)
|
|
trip->when = dive->when;
|
|
}
|
|
|
|
dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
|
|
{
|
|
dive_trip_t *dive_trip = calloc(sizeof(dive_trip_t),1);
|
|
dive_trip->when = dive->when;
|
|
if (dive->location)
|
|
dive_trip->location = strdup(dive->location);
|
|
insert_trip(&dive_trip);
|
|
|
|
dive->tripflag = IN_TRIP;
|
|
add_dive_to_trip(dive, dive_trip);
|
|
return dive_trip;
|
|
}
|
|
|
|
/*
|
|
* Walk the dives from the oldest dive, and see if we can autogroup them
|
|
*/
|
|
void autogroup_dives(void)
|
|
{
|
|
int i;
|
|
struct dive *dive, *lastdive = NULL;
|
|
|
|
for_each_dive(i, dive) {
|
|
dive_trip_t *trip;
|
|
|
|
if (dive->divetrip) {
|
|
lastdive = dive;
|
|
continue;
|
|
}
|
|
|
|
if (!DIVE_NEEDS_TRIP(dive)) {
|
|
lastdive = NULL;
|
|
continue;
|
|
}
|
|
|
|
/* Do we have a trip we can combine this into? */
|
|
if (lastdive && dive->when < lastdive->when + TRIP_THRESHOLD) {
|
|
dive_trip_t *trip = lastdive->divetrip;
|
|
add_dive_to_trip(dive, trip);
|
|
if (dive->location && !trip->location)
|
|
trip->location = strdup(dive->location);
|
|
lastdive = dive;
|
|
continue;
|
|
}
|
|
|
|
lastdive = dive;
|
|
trip = create_and_hookup_trip_from_dive(dive);
|
|
trip->autogen = 1;
|
|
}
|
|
|
|
#ifdef DEBUG_TRIP
|
|
dump_trip_list();
|
|
#endif
|
|
}
|
|
|
|
void clear_trip_indexes(void)
|
|
{
|
|
dive_trip_t *trip;
|
|
|
|
for (trip = dive_trip_list; trip != NULL; trip = trip->next)
|
|
trip->index = 0;
|
|
}
|
|
|
|
/* this implements the mechanics of removing the dive from the table,
|
|
* but doesn't deal with updating dive trips, etc */
|
|
void delete_single_dive(int idx)
|
|
{
|
|
int i;
|
|
struct dive *dive = get_dive(idx);
|
|
if (!dive)
|
|
return; /* this should never happen */
|
|
remove_dive_from_trip(dive);
|
|
for (i = idx; i < dive_table.nr - 1; i++)
|
|
dive_table.dives[i] = dive_table.dives[i+1];
|
|
dive_table.dives[--dive_table.nr] = NULL;
|
|
if (dive->selected)
|
|
amount_selected--;
|
|
/* free all allocations */
|
|
free(dive->dc.sample);
|
|
if (dive->location)
|
|
free((void *)dive->location);
|
|
if (dive->notes)
|
|
free((void *)dive->notes);
|
|
if (dive->divemaster)
|
|
free((void *)dive->divemaster);
|
|
if (dive->buddy)
|
|
free((void *)dive->buddy);
|
|
if (dive->suit)
|
|
free((void *)dive->suit);
|
|
free(dive);
|
|
}
|
|
|
|
void add_single_dive(int idx, struct dive *dive)
|
|
{
|
|
int i;
|
|
dive_table.nr++;
|
|
if (dive->selected)
|
|
amount_selected++;
|
|
for (i = idx; i < dive_table.nr ; i++) {
|
|
struct dive *tmp = dive_table.dives[i];
|
|
dive_table.dives[i] = dive;
|
|
dive = tmp;
|
|
}
|
|
}
|
|
|
|
void merge_dive_index(int i, struct dive *a)
|
|
{
|
|
struct dive *b = get_dive(i+1);
|
|
struct dive *res;
|
|
|
|
res = merge_dives(a, b, b->when - a->when, FALSE);
|
|
if (!res)
|
|
return;
|
|
|
|
add_single_dive(i, res);
|
|
delete_single_dive(i+1);
|
|
delete_single_dive(i+1);
|
|
|
|
dive_list_update_dives();
|
|
mark_divelist_changed(TRUE);
|
|
}
|
|
|
|
void select_dive(int idx)
|
|
{
|
|
struct dive *dive = get_dive(idx);
|
|
if (dive && !dive->selected) {
|
|
dive->selected = 1;
|
|
amount_selected++;
|
|
selected_dive = idx;
|
|
}
|
|
}
|
|
|
|
void deselect_dive(int idx)
|
|
{
|
|
struct dive *dive = get_dive(idx);
|
|
if (dive && dive->selected) {
|
|
dive->selected = 0;
|
|
amount_selected--;
|
|
if (selected_dive == idx && amount_selected > 0) {
|
|
/* pick a different dive as selected */
|
|
while (--selected_dive >= 0) {
|
|
dive = get_dive(selected_dive);
|
|
if (dive && dive->selected)
|
|
return;
|
|
}
|
|
selected_dive = idx;
|
|
while (++selected_dive < dive_table.nr) {
|
|
dive = get_dive(selected_dive);
|
|
if (dive && dive->selected)
|
|
return;
|
|
}
|
|
}
|
|
if (amount_selected == 0)
|
|
selected_dive = -1;
|
|
}
|
|
}
|
|
|
|
void mark_divelist_changed(int changed)
|
|
{
|
|
dive_list_changed = changed;
|
|
}
|
|
|
|
int unsaved_changes()
|
|
{
|
|
return dive_list_changed;
|
|
}
|
|
|
|
void remove_autogen_trips()
|
|
{
|
|
int i;
|
|
struct dive *dive;
|
|
|
|
for_each_dive(i, dive) {
|
|
dive_trip_t *trip = dive->divetrip;
|
|
|
|
if (trip && trip->autogen)
|
|
remove_dive_from_trip(dive);
|
|
}
|
|
}
|
|
|