Merge branch 'master' into freediving-tweaks

Signed-off-by: Maximilian Güntner <maximilian.guentner@gmail.com>
This commit is contained in:
Maximilian Güntner 2012-08-22 01:15:41 +02:00
commit 0c0ec7e4f6
20 changed files with 1818 additions and 639 deletions

View file

@ -51,19 +51,19 @@ libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a)
ifneq ($(strip $(libdc-local)),)
LIBDIVECOMPUTERDIR = /usr/local
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include/libdivecomputer
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
else ifneq ($(strip $(libdc-local64)),)
LIBDIVECOMPUTERDIR = /usr/local
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include/libdivecomputer
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
else ifneq ($(strip $(libdc-usr)),)
LIBDIVECOMPUTERDIR = /usr
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include/libdivecomputer
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
else ifneq ($(strip $(libdc-usr64)),)
LIBDIVECOMPUTERDIR = /usr
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include/libdivecomputer
LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
else
$(error Cannot find libdivecomputer - please edit Makefile)

View file

@ -150,6 +150,64 @@ static void parse_cochran_header(const char *filename,
free(buf);
}
/*
* Cochran export files show that depths seem to be in
* quarter feet (rounded up to tenths).
*
* Temperature seems to be exported in Fahrenheit.
*
* Cylinder pressure seems to be in multiples of 4 psi.
*
* The data seems to be some byte-stream where the pattern
* appears to be that the two high bits indicate type of
* data.
*
* For '00', the low six bits seem to be positive
* values with a distribution towards zero, probably depth
* deltas. '0 0' exists, but is very rare ("surface"?). 63
* exists, but is rare.
*
* For '01', the low six bits seem to be a signed binary value,
* with the most common being 0, and 1 and -1 (63) being the
* next most common values.
*
* NOTE! Don's CAN data is different. It shows the reverse pattern
* for 00 and 01 above: 00 looks like signed data, with 01 looking
* like unsigned data.
*
* For '10', there seems to be another positive value distribution,
* but unlike '00' the value 0 is common, and I see examples of 63
* too ("overflow"?) and a spike at '7'.
*
* Again, Don's data is different.
*
* The values for '11' seem to be some exception case. Possibly
* overflow handling, possibly warning events. It doesn't have
* any clear distribution: values 0, 1, 16, 33, 35, 48, 51, 55
* and 63 are common.
*
* For David and Don's data, '01' is the most common, with '00'
* and '10' not uncommon. '11' is two orders of magnitude less
* common.
*
* For Alex, '00' is the most common, with 01 about a third as
* common, and 02 a third of that. 11 is least common.
*
* There clearly are variations in the format here. And Alex has
* a different data offset than Don/David too (see the #ifdef DON).
* Christ. Maybe I've misread the patterns entirely.
*/
static void cochran_profile_write(const unsigned char *buf, int size)
{
int i;
for (i = 0; i < size; i++) {
unsigned char c = buf[i];
printf("%d %d\n",
c >> 6, c & 0x3f);
}
}
static void parse_cochran_dive(const char *filename, int dive,
const unsigned char *decode, unsigned mod,
const unsigned char *in, unsigned size)
@ -187,6 +245,7 @@ static void parse_cochran_dive(const char *filename, int dive,
printf("\n%s, dive %d\n\n", filename, dive);
cochran_debug_write(filename, buf, size);
cochran_profile_write(buf + offset, size - offset);
free(buf);
}

View file

@ -14,6 +14,8 @@ typedef struct {
typedef struct {
gboolean cylinder;
gboolean temperature;
gboolean totalweight;
gboolean suit;
gboolean nitrox;
gboolean sac;
gboolean otu;
@ -29,9 +31,11 @@ typedef enum {
#if defined __APPLE__
#define CTRLCHAR "<Meta>"
#define SHIFTCHAR "<Shift>"
#define PREFERENCE_ACCEL "<Meta>comma"
#else
#define CTRLCHAR "<Control>"
#define SHIFTCHAR "<Shift>"
#define PREFERENCE_ACCEL NULL
#endif
@ -54,6 +58,7 @@ extern const char *divelist_font;
extern void set_divelist_font(const char *);
extern void import_dialog(GtkWidget *, gpointer);
extern void add_dive_cb(GtkWidget *, gpointer);
extern void report_error(GError* error);
extern int process_ui_events(void);
extern void update_progressbar(progressbar_t *progress, double value);
@ -62,16 +67,17 @@ extern void update_progressbar_text(progressbar_t *progress, const char *text);
extern GtkWidget *dive_profile_widget(void);
extern GtkWidget *dive_info_frame(void);
extern GtkWidget *extended_dive_info_widget(void);
extern GtkWidget *equipment_widget(void);
extern GtkWidget *equipment_widget(int w_idx);
extern GtkWidget *single_stats_widget(void);
extern GtkWidget *total_stats_widget(void);
extern GtkWidget *cylinder_list_widget(void);
extern GtkWidget *cylinder_list_widget(int w_idx);
extern GtkWidget *weightsystem_list_widget(int w_idx);
extern GtkWidget *dive_list_create(void);
unsigned int amount_selected;
extern void process_selected_dives(GList *, GtkTreeModel *);
extern void process_selected_dives(void);
typedef void (*data_func_t)(GtkTreeViewColumn *col,
GtkCellRenderer *renderer,

42
dive.c
View file

@ -120,6 +120,28 @@ double get_depth_units(unsigned int mm, int *frac, const char **units)
return d;
}
double get_weight_units(unsigned int grams, int *frac, const char **units)
{
int decimals;
double value;
const char* unit;
if (output_units.weight == LBS) {
value = grams_to_lbs(grams);
unit = "lbs";
decimals = 0;
} else {
value = grams / 1000.0;
unit = "kg";
decimals = 1;
}
if (frac)
*frac = decimals;
if (units)
*units = unit;
return value;
}
struct dive *alloc_dive(void)
{
const int initial_samples = 5;
@ -450,8 +472,20 @@ struct dive *fixup_dive(struct dive *dive)
cyl->sample_end.mbar = 0;
}
}
if (end < 0)
if (end < 0) {
/* Assume an ascent/descent rate of 9 m/min */
int depth = dive->maxdepth.mm;
int asc_desc_time = depth*60/9000;
int duration = dive->duration.seconds;
/* Protect against insane dives - make mean be half of max */
if (duration <= asc_desc_time) {
duration = 2;
asc_desc_time = 1;
}
dive->meandepth.mm = depth*(duration-asc_desc_time)/duration;
return dive;
}
update_duration(&dive->duration, end - start);
if (start != end)
@ -464,6 +498,7 @@ struct dive *fixup_dive(struct dive *dive)
add_people(dive->buddy);
add_people(dive->divemaster);
add_location(dive->location);
add_suit(dive->suit);
for (i = 0; i < MAX_CYLINDERS; i++) {
cylinder_t *cyl = dive->cylinder + i;
add_cylinder_description(&cyl->type);
@ -472,6 +507,10 @@ struct dive *fixup_dive(struct dive *dive)
if (same_rounded_pressure(cyl->sample_end, cyl->end))
cyl->end.mbar = 0;
}
for (i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
weightsystem_t *ws = dive->weightsystem + i;
add_weightsystem_description(ws);
}
return dive;
}
@ -677,6 +716,7 @@ struct dive *try_to_merge(struct dive *a, struct dive *b)
MERGE_TXT(res, a, b, buddy);
MERGE_TXT(res, a, b, divemaster);
MERGE_MAX(res, a, b, rating);
MERGE_TXT(res, a, b, suit);
MERGE_MAX(res, a, b, number);
MERGE_MAX(res, a, b, maxdepth.mm);
res->meandepth.mm = 0;

54
dive.h
View file

@ -92,13 +92,17 @@ typedef struct {
const char *description; /* "integrated", "belt", "ankle" */
} weightsystem_t;
extern int cylinder_none(void *_data);
extern int weightsystem_none(void *_data);
extern gboolean cylinder_none(void *_data);
extern gboolean no_cylinders(cylinder_t *cyl);
extern gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2);
extern gboolean no_weightsystems(weightsystem_t *ws);
extern gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
extern int get_pressure_units(unsigned int mb, const char **units);
extern double get_depth_units(unsigned int mm, int *frac, const char **units);
extern double get_volume_units(unsigned int mm, int *frac, const char **units);
extern double get_temp_units(unsigned int mm, const char **units);
extern double get_volume_units(unsigned int ml, int *frac, const char **units);
extern double get_temp_units(unsigned int mk, const char **units);
extern double get_weight_units(unsigned int grams, int *frac, const char **units);
static inline double grams_to_lbs(int grams)
{
@ -125,21 +129,31 @@ static inline double mm_to_feet(int mm)
return mm * 0.00328084;
}
static inline unsigned long feet_to_mm(double feet)
{
return feet * 304.8 + 0.5;
}
static inline int to_feet(depth_t depth)
{
return mm_to_feet(depth.mm) + 0.5;
}
static double mkelvin_to_C(int mkelvin)
static inline double mkelvin_to_C(int mkelvin)
{
return (mkelvin - 273150) / 1000.0;
}
static double mkelvin_to_F(int mkelvin)
static inline double mkelvin_to_F(int mkelvin)
{
return mkelvin * 9 / 5000.0 - 459.670;
}
static inline unsigned long F_to_mkelvin(double f)
{
return (f-32) * 1000 / 1.8 + 273150.5;
}
static inline int to_C(temperature_t temp)
{
if (!temp.mkelvin)
@ -165,6 +179,12 @@ static inline double psi_to_bar(double psi)
{
return psi / 14.5037738;
}
static inline unsigned long psi_to_mbar(double psi)
{
return psi_to_bar(psi)*1000 + 0.5;
}
static inline int to_PSI(pressure_t pressure)
{
return pressure.mbar * 0.0145037738 + 0.5;
@ -211,9 +231,12 @@ struct event {
#define MAX_CYLINDERS (8)
#define MAX_WEIGHTSYSTEMS (4)
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
struct dive {
int number;
int selected;
time_t when;
char *location;
char *notes;
@ -226,6 +249,7 @@ struct dive {
temperature_t airtemp, watertemp;
cylinder_t cylinder[MAX_CYLINDERS];
weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS];
char *suit;
int sac, otu;
struct event *events;
int samples, alloc_samples;
@ -266,11 +290,21 @@ extern int selected_dive;
static inline struct dive *get_dive(unsigned int nr)
{
if (nr >= dive_table.nr)
if (nr >= dive_table.nr || nr < 0)
return NULL;
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);
@ -283,7 +317,7 @@ extern xmlDoc *test_xslt_transforms(xmlDoc *doc);
extern void show_dive_info(struct dive *);
extern void show_dive_equipment(struct dive *);
extern void show_dive_equipment(struct dive *, int w_idx);
extern void show_dive_stats(struct dive *);
@ -322,12 +356,16 @@ extern void exit_ui(void);
extern void report_error(GError* error);
extern void add_cylinder_description(cylinder_type_t *);
extern void add_weightsystem_description(weightsystem_t *);
extern void add_people(const char *string);
extern void add_location(const char *string);
extern void add_suit(const char *string);
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 int edit_dive_info(struct dive *dive);
extern int edit_multi_dive_info(struct dive *single_dive);
extern void dive_list_update_dives(void);
extern void flush_divelist(struct dive *dive);

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,10 @@
/* creates the UI for the equipment page -
* controlled through the following interfaces:
*
* void show_dive_equipment(struct dive *dive)
* void show_dive_equipment(struct dive *dive, int w_idx)
*
* called from gtk-ui:
* GtkWidget *equipment_widget(void)
* GtkWidget *equipment_widget(int w_idx)
*/
#include <stdio.h>
#include <string.h>
@ -40,10 +40,11 @@ enum {
struct equipment_list {
int max_index;
GtkListStore *model;
GtkTreeView *tree_view;
GtkWidget *edit, *add, *del;
};
static struct equipment_list cylinder_list, weightsystem_list;
static struct equipment_list cylinder_list[2], weightsystem_list[2];
struct cylinder_widget {
@ -308,13 +309,15 @@ static GtkTreeIter *add_weightsystem_type(const char *desc, int weight, GtkTreeI
model = GTK_TREE_MODEL(weightsystem_model);
gtk_tree_model_foreach(model, match_desc, (void *)desc);
if (!found_match) {
GtkListStore *store = GTK_LIST_STORE(model);
gtk_list_store_append(store, iter);
gtk_list_store_set(store, iter,
0, desc,
1, weight,
if (found_match) {
gtk_list_store_set(GTK_LIST_STORE(model), found_match,
WS_WEIGHT, weight,
-1);
} else if (desc && desc[0]) {
gtk_list_store_append(GTK_LIST_STORE(model), iter);
gtk_list_store_set(GTK_LIST_STORE(model), iter,
WS_DESC, desc,
WS_WEIGHT, weight,
-1);
return iter;
}
@ -425,7 +428,7 @@ static void show_weightsystem(weightsystem_t *ws, struct ws_widget *weightsystem
set_weight_weight_spinbutton(weightsystem_widget, ws->weight.grams);
}
int cylinder_none(void *_data)
gboolean cylinder_none(void *_data)
{
cylinder_t *cyl = _data;
return !cyl->type.size.mliter &&
@ -439,12 +442,77 @@ int cylinder_none(void *_data)
!cyl->end.mbar;
}
int weightsystem_none(void *_data)
gboolean no_cylinders(cylinder_t *cyl)
{
int i;
for (i = 0; i < MAX_CYLINDERS; i++)
if (!cylinder_none(cyl + i))
return FALSE;
return TRUE;
}
/* descriptions are equal if they are both NULL or both non-NULL
and the same text */
gboolean description_equal(const char *desc1, const char *desc2)
{
return ((! desc1 && ! desc2) ||
(desc1 && desc2 && strcmp(desc1, desc2) == 0));
}
/* when checking for the same cylinder we want the size and description to match
but don't compare the start and end pressures */
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);
}
gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2)
{
int i;
for (i = 0; i < MAX_CYLINDERS; i++)
if (!one_cylinder_equal(cyl1 + i, cyl2 + i))
return FALSE;
return TRUE;
}
static gboolean weightsystem_none(void *_data)
{
weightsystem_t *ws = _data;
return !ws->weight.grams && !ws->description;
}
gboolean no_weightsystems(weightsystem_t *ws)
{
int i;
for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
if (!weightsystem_none(ws + i))
return FALSE;
return TRUE;
}
static gboolean one_weightsystem_equal(weightsystem_t *ws1, weightsystem_t *ws2)
{
return ws1->weight.grams == ws2->weight.grams &&
description_equal(ws1->description, ws2->description);
}
gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2)
{
int i;
for (i = 0; i < MAX_WEIGHTSYSTEMS; i++)
if (!one_weightsystem_equal(ws1 + i, ws2 + i))
return FALSE;
return TRUE;
}
static void set_one_cylinder(void *_data, GtkListStore *model, GtkTreeIter *iter)
{
cylinder_t *cyl = _data;
@ -490,7 +558,7 @@ static void *ws_ptr(struct dive *dive, int idx)
static void show_equipment(struct dive *dive, int max,
struct equipment_list *equipment_list,
void*(*ptr_function)(struct dive*, int),
int(*none_function)(void *),
gboolean(*none_function)(void *),
void(*set_one_function)(void*, GtkListStore*, GtkTreeIter *))
{
int i, used;
@ -519,11 +587,11 @@ static void show_equipment(struct dive *dive, int max,
}
}
void show_dive_equipment(struct dive *dive)
void show_dive_equipment(struct dive *dive, int w_idx)
{
show_equipment(dive, MAX_CYLINDERS, &cylinder_list,
show_equipment(dive, MAX_CYLINDERS, &cylinder_list[w_idx],
&cyl_ptr, &cylinder_none, &set_one_cylinder);
show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list,
show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list[w_idx],
&ws_ptr, &weightsystem_none, &set_one_weightsystem);
}
@ -623,6 +691,7 @@ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *we
GtkComboBox *box;
int grams;
double value;
GtkTreeIter iter;
/* Ignore uninitialized cylinder widgets */
box = weightsystem_widget->description;
@ -638,6 +707,7 @@ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *we
grams = value * 1000;
ws->weight.grams = grams;
ws->description = desc;
add_weightsystem_type(desc, grams, &iter);
}
/*
@ -744,8 +814,6 @@ static struct ws_info {
const char *name;
int grams;
} ws_info[100] = {
/* Need an empty entry for the no weight system case */
{ "", },
{ "integrated", 0 },
{ "belt", 0 },
{ "ankle", 0 },
@ -1061,11 +1129,12 @@ static int get_model_index(GtkListStore *model, GtkTreeIter *iter)
return index;
}
static void edit_cb(GtkButton *button, GtkTreeView *tree_view)
static void edit_cb(GtkButton *button, int w_idx)
{
int index;
GtkTreeIter iter;
GtkListStore *model = cylinder_list.model;
GtkListStore *model = cylinder_list[w_idx].model;
GtkTreeView *tree_view = cylinder_list[w_idx].tree_view;
GtkTreeSelection *selection;
cylinder_t cyl;
@ -1083,11 +1152,12 @@ static void edit_cb(GtkButton *button, GtkTreeView *tree_view)
repaint_dive();
}
static void add_cb(GtkButton *button, GtkTreeView *tree_view)
static void add_cb(GtkButton *button, int w_idx)
{
int index = cylinder_list.max_index;
int index = cylinder_list[w_idx].max_index;
GtkTreeIter iter;
GtkListStore *model = cylinder_list.model;
GtkListStore *model = cylinder_list[w_idx].model;
GtkTreeView *tree_view = cylinder_list[w_idx].tree_view;
GtkTreeSelection *selection;
cylinder_t cyl;
@ -1100,15 +1170,16 @@ static void add_cb(GtkButton *button, GtkTreeView *tree_view)
selection = gtk_tree_view_get_selection(tree_view);
gtk_tree_selection_select_iter(selection, &iter);
cylinder_list.max_index++;
gtk_widget_set_sensitive(cylinder_list.add, cylinder_list.max_index < MAX_CYLINDERS);
cylinder_list[w_idx].max_index++;
gtk_widget_set_sensitive(cylinder_list[w_idx].add, cylinder_list[w_idx].max_index < MAX_CYLINDERS);
}
static void del_cb(GtkButton *button, GtkTreeView *tree_view)
static void del_cb(GtkButton *button, int w_idx)
{
int index, nr;
GtkTreeIter iter;
GtkListStore *model = cylinder_list.model;
GtkListStore *model = cylinder_list[w_idx].model;
GtkTreeView *tree_view = cylinder_list[w_idx].tree_view;
GtkTreeSelection *selection;
struct dive *dive;
cylinder_t *cyl;
@ -1125,27 +1196,28 @@ static void del_cb(GtkButton *button, GtkTreeView *tree_view)
if (!dive)
return;
cyl = dive->cylinder + index;
nr = cylinder_list.max_index - index - 1;
nr = cylinder_list[w_idx].max_index - index - 1;
gtk_list_store_remove(model, &iter);
cylinder_list.max_index--;
cylinder_list[w_idx].max_index--;
memmove(cyl, cyl+1, nr*sizeof(*cyl));
memset(cyl+nr, 0, sizeof(*cyl));
mark_divelist_changed(TRUE);
flush_divelist(dive);
gtk_widget_set_sensitive(cylinder_list.edit, 0);
gtk_widget_set_sensitive(cylinder_list.del, 0);
gtk_widget_set_sensitive(cylinder_list.add, 1);
gtk_widget_set_sensitive(cylinder_list[w_idx].edit, 0);
gtk_widget_set_sensitive(cylinder_list[w_idx].del, 0);
gtk_widget_set_sensitive(cylinder_list[w_idx].add, 1);
}
static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view)
static void ws_edit_cb(GtkButton *button, int w_idx)
{
int index;
GtkTreeIter iter;
GtkListStore *model = weightsystem_list.model;
GtkListStore *model = weightsystem_list[w_idx].model;
GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view;
GtkTreeSelection *selection;
weightsystem_t ws;
@ -1163,11 +1235,12 @@ static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view)
repaint_dive();
}
static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view)
static void ws_add_cb(GtkButton *button, int w_idx)
{
int index = weightsystem_list.max_index;
int index = weightsystem_list[w_idx].max_index;
GtkTreeIter iter;
GtkListStore *model = weightsystem_list.model;
GtkListStore *model = weightsystem_list[w_idx].model;
GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view;
GtkTreeSelection *selection;
weightsystem_t ws;
@ -1180,15 +1253,16 @@ static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view)
selection = gtk_tree_view_get_selection(tree_view);
gtk_tree_selection_select_iter(selection, &iter);
weightsystem_list.max_index++;
gtk_widget_set_sensitive(weightsystem_list.add, weightsystem_list.max_index < MAX_WEIGHTSYSTEMS);
weightsystem_list[w_idx].max_index++;
gtk_widget_set_sensitive(weightsystem_list[w_idx].add, weightsystem_list[w_idx].max_index < MAX_WEIGHTSYSTEMS);
}
static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view)
static void ws_del_cb(GtkButton *button, int w_idx)
{
int index, nr;
GtkTreeIter iter;
GtkListStore *model = weightsystem_list.model;
GtkListStore *model = weightsystem_list[w_idx].model;
GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view;
GtkTreeSelection *selection;
struct dive *dive;
weightsystem_t *ws;
@ -1205,20 +1279,20 @@ static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view)
if (!dive)
return;
ws = dive->weightsystem + index;
nr = weightsystem_list.max_index - index - 1;
nr = weightsystem_list[w_idx].max_index - index - 1;
gtk_list_store_remove(model, &iter);
weightsystem_list.max_index--;
weightsystem_list[w_idx].max_index--;
memmove(ws, ws+1, nr*sizeof(*ws));
memset(ws+nr, 0, sizeof(*ws));
mark_divelist_changed(TRUE);
flush_divelist(dive);
gtk_widget_set_sensitive(weightsystem_list.edit, 0);
gtk_widget_set_sensitive(weightsystem_list.del, 0);
gtk_widget_set_sensitive(weightsystem_list.add, 1);
gtk_widget_set_sensitive(weightsystem_list[w_idx].edit, 0);
gtk_widget_set_sensitive(weightsystem_list[w_idx].del, 0);
gtk_widget_set_sensitive(weightsystem_list[w_idx].add, 1);
}
static GtkListStore *create_tank_size_model(void)
@ -1338,33 +1412,33 @@ static void selection_cb(GtkTreeSelection *selection, struct equipment_list *lis
static void row_activated_cb(GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
GtkTreeModel *model)
int w_idx)
{
edit_cb(NULL, tree_view);
edit_cb(NULL, w_idx);
}
static void ws_row_activated_cb(GtkTreeView *tree_view,
GtkTreePath *path,
GtkTreeViewColumn *column,
GtkTreeModel *model)
int w_idx)
{
ws_edit_cb(NULL, tree_view);
ws_edit_cb(NULL, w_idx);
}
GtkWidget *cylinder_list_widget(void)
GtkWidget *cylinder_list_widget(int w_idx)
{
GtkListStore *model = cylinder_list.model;
GtkListStore *model = cylinder_list[w_idx].model;
GtkWidget *tree_view;
GtkTreeSelection *selection;
tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
gtk_widget_set_can_focus(tree_view, FALSE);
g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), model);
g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), GINT_TO_POINTER(w_idx));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list);
g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list[w_idx]);
g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH,
@ -1380,19 +1454,19 @@ GtkWidget *cylinder_list_widget(void)
return tree_view;
}
GtkWidget *weightsystem_list_widget(void)
GtkWidget *weightsystem_list_widget(int w_idx)
{
GtkListStore *model = weightsystem_list.model;
GtkListStore *model = weightsystem_list[w_idx].model;
GtkWidget *tree_view;
GtkTreeSelection *selection;
tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
gtk_widget_set_can_focus(tree_view, FALSE);
g_signal_connect(tree_view, "row-activated", G_CALLBACK(ws_row_activated_cb), model);
g_signal_connect(tree_view, "row-activated", G_CALLBACK(ws_row_activated_cb), GINT_TO_POINTER(w_idx));
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list);
g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list[w_idx]);
g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
"enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH,
@ -1405,7 +1479,7 @@ GtkWidget *weightsystem_list_widget(void)
return tree_view;
}
static GtkWidget *cylinder_list_create(void)
static GtkWidget *cylinder_list_create(int w_idx)
{
GtkListStore *model;
@ -1418,11 +1492,11 @@ static GtkWidget *cylinder_list_create(void)
G_TYPE_INT, /* CYL_O2: permille */
G_TYPE_INT /* CYL_HE: permille */
);
cylinder_list.model = model;
return cylinder_list_widget();
cylinder_list[w_idx].model = model;
return cylinder_list_widget(w_idx);
}
static GtkWidget *weightsystem_list_create(void)
static GtkWidget *weightsystem_list_create(int w_idx)
{
GtkListStore *model;
@ -1430,11 +1504,11 @@ static GtkWidget *weightsystem_list_create(void)
G_TYPE_STRING, /* WS_DESC: utf8 */
G_TYPE_INT /* WS_WEIGHT: grams */
);
weightsystem_list.model = model;
return weightsystem_list_widget();
weightsystem_list[w_idx].model = model;
return weightsystem_list_widget(w_idx);
}
GtkWidget *equipment_widget(void)
GtkWidget *equipment_widget(int w_idx)
{
GtkWidget *vbox, *hbox, *frame, *framebox, *tree_view;
GtkWidget *add, *del, *edit;
@ -1442,14 +1516,17 @@ GtkWidget *equipment_widget(void)
vbox = gtk_vbox_new(FALSE, 3);
/*
* We create the cylinder size model at startup, since
* we're going to share it across all cylinders and all
* dives. So if you add a new cylinder type in one dive,
* it will show up when you edit the cylinder types for
* another dive.
* We create the cylinder size (and weightsystem) models
* at startup for the primary cylinder / weightsystem widget,
* since we're going to share it across all cylinders and all
* dives. So if you add a new cylinder type or weightsystem in
* one dive, it will show up when you edit the cylinder types
* or weightsystems for another dive.
*/
cylinder_model = create_tank_size_model();
tree_view = cylinder_list_create();
if (w_idx == W_IDX_PRIMARY)
cylinder_model = create_tank_size_model();
tree_view = cylinder_list_create(w_idx);
cylinder_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view);
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
@ -1475,19 +1552,21 @@ GtkWidget *equipment_widget(void)
gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0);
cylinder_list.edit = edit;
cylinder_list.add = add;
cylinder_list.del = del;
cylinder_list[w_idx].edit = edit;
cylinder_list[w_idx].add = add;
cylinder_list[w_idx].del = del;
g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), tree_view);
g_signal_connect(add, "clicked", G_CALLBACK(add_cb), tree_view);
g_signal_connect(del, "clicked", G_CALLBACK(del_cb), tree_view);
g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), GINT_TO_POINTER(w_idx));
g_signal_connect(add, "clicked", G_CALLBACK(add_cb), GINT_TO_POINTER(w_idx));
g_signal_connect(del, "clicked", G_CALLBACK(del_cb), GINT_TO_POINTER(w_idx));
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
weightsystem_model = create_weightsystem_model();
tree_view = weightsystem_list_create();
if (w_idx == W_IDX_PRIMARY)
weightsystem_model = create_weightsystem_model();
tree_view = weightsystem_list_create(w_idx);
weightsystem_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view);
frame = gtk_frame_new("Weight");
gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, FALSE, 3);
@ -1510,13 +1589,13 @@ GtkWidget *equipment_widget(void)
gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0);
weightsystem_list.edit = edit;
weightsystem_list.add = add;
weightsystem_list.del = del;
weightsystem_list[w_idx].edit = edit;
weightsystem_list[w_idx].add = add;
weightsystem_list[w_idx].del = del;
g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), tree_view);
g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), tree_view);
g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), tree_view);
g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), GINT_TO_POINTER(w_idx));
g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), GINT_TO_POINTER(w_idx));
g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), GINT_TO_POINTER(w_idx));
return vbox;
}

129
file.c
View file

@ -10,7 +10,7 @@
static int readfile(const char *filename, struct memblock *mem)
{
int ret, fd = open(filename, O_RDONLY);
int ret, fd;
struct stat st;
char *buf;
@ -94,6 +94,125 @@ static int try_to_open_suunto(const char *filename, struct memblock *mem, GError
return success;
}
static time_t parse_date(const char *date)
{
int hour, min, sec;
struct tm tm;
char *p;
memset(&tm, 0, sizeof(tm));
tm.tm_mday = strtol(date, &p, 10);
if (tm.tm_mday < 1 || tm.tm_mday > 31)
return 0;
for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
if (!memcmp(p, monthname(tm.tm_mon), 3))
break;
}
if (tm.tm_mon > 11)
return 0;
date = p+3;
tm.tm_year = strtol(date, &p, 10);
if (date == p)
return 0;
if (tm.tm_year < 70)
tm.tm_year += 2000;
if (tm.tm_year < 100)
tm.tm_year += 1900;
if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) != 3)
return 0;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
return utc_mktime(&tm);
}
enum csv_format {
CSV_DEPTH, CSV_TEMP, CSV_PRESSURE
};
static void add_sample_data(struct sample *sample, enum csv_format type, double val)
{
switch (type) {
case CSV_DEPTH:
sample->depth.mm = feet_to_mm(val);
break;
case CSV_TEMP:
sample->temperature.mkelvin = F_to_mkelvin(val);
break;
case CSV_PRESSURE:
sample->cylinderpressure.mbar = psi_to_mbar(val*4);
break;
}
}
/*
* Cochran comma-separated values: depth in feet, temperature in F, pressure in psi.
*
* They start with eight comma-separated fields like:
*
* filename: {C:\Analyst4\can\T036785.can},{C:\Analyst4\can\K031892.can}
* divenr: %d
* datetime: {03Sep11 16:37:22},{15Dec11 18:27:02}
* ??: 1
* serialnr??: {CCI134},{CCI207}
* computer??: {GeminiII},{CommanderIII}
* computer??: {GeminiII},{CommanderIII}
* ??: 1
*
* Followed by the data values (all comma-separated, all one long line).
*/
static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_format type)
{
char *p = mem->buffer;
char *header[8];
int i, time;
time_t date;
struct dive *dive;
for (i = 0; i < 8; i++) {
header[i] = p;
p = strchr(p, ',');
if (!p)
return 0;
p++;
}
date = parse_date(header[2]);
if (!date)
return 0;
dive = alloc_dive();
dive->when = date;
dive->number = atoi(header[1]);
time = 0;
for (;;) {
char *end;
double val;
struct sample *sample;
errno = 0;
val = strtod(p,&end);
if (end == p)
break;
if (errno)
break;
sample = prepare_sample(&dive);
sample->time.seconds = time;
add_sample_data(sample, type, val);
finish_sample(dive);
time++;
dive->duration.seconds = time;
if (*end != ',')
break;
p = end+1;
}
record_dive(dive);
return 1;
}
static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error)
{
/* Suunto Dive Manager files: SDE */
@ -104,6 +223,14 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo
if (!strcasecmp(fmt, "CAN"))
return try_to_open_cochran(filename, mem, error);
/* Cochran export comma-separated-value files */
if (!strcasecmp(fmt, "DPT"))
return try_to_open_csv(filename, mem, CSV_DEPTH);
if (!strcasecmp(fmt, "TMP"))
return try_to_open_csv(filename, mem, CSV_TEMP);
if (!strcasecmp(fmt, "HP1"))
return try_to_open_csv(filename, mem, CSV_PRESSURE);
return 0;
}

224
gtk-gui.c
View file

@ -21,6 +21,8 @@ GtkWidget *main_vbox;
GtkWidget *error_info_bar;
GtkWidget *error_label;
GtkWidget *vpane, *hpane;
GtkWidget *notebook;
int error_count;
extern char zoomed_plot;
@ -32,12 +34,14 @@ static GtkWidget *dive_profile;
visible_cols_t visible_cols = {TRUE, FALSE};
static const char *default_dive_computer;
static const char *default_dive_computer_vendor;
static const char *default_dive_computer_product;
static const char *default_dive_computer_device;
static int is_default_dive_computer(const char *name)
static int is_default_dive_computer(const char *vendor, const char *product)
{
return default_dive_computer && !strcmp(name, default_dive_computer);
return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) &&
default_dive_computer_product && !strcmp(product, default_dive_computer_product);
}
static int is_default_dive_computer_device(const char *name)
@ -45,14 +49,18 @@ static int is_default_dive_computer_device(const char *name)
return default_dive_computer_device && !strcmp(name, default_dive_computer_device);
}
static void set_default_dive_computer(const char *name)
static void set_default_dive_computer(const char *vendor, const char *product)
{
if (!name || !*name)
if (!vendor || !*vendor)
return;
if (is_default_dive_computer(name))
if (!product || !*product)
return;
default_dive_computer = name;
subsurface_set_conf("dive_computer", PREF_STRING, name);
if (is_default_dive_computer(vendor, product))
return;
default_dive_computer_vendor = vendor;
default_dive_computer_product = product;
subsurface_set_conf("dive_computer_vendor", PREF_STRING, vendor);
subsurface_set_conf("dive_computer_product", PREF_STRING, product);
}
static void set_default_dive_computer_device(const char *name)
@ -165,48 +173,75 @@ static void file_open(GtkWidget *w, gpointer data)
gtk_widget_destroy(dialog);
}
static void file_save(GtkWidget *w, gpointer data)
static void file_save_as(GtkWidget *w, gpointer data)
{
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new("Save File",
char *filename = NULL;
dialog = gtk_file_chooser_dialog_new("Save File As",
GTK_WINDOW(main_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
if (!existing_filename) {
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
} else
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), existing_filename);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), existing_filename);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
}
gtk_widget_destroy(dialog);
if (filename){
save_dives(filename);
set_filename(filename);
g_free(filename);
mark_divelist_changed(FALSE);
}
gtk_widget_destroy(dialog);
}
static void ask_save_changes()
static void file_save(GtkWidget *w, gpointer data)
{
if (!existing_filename)
return file_save_as(w, data);
save_dives(existing_filename);
mark_divelist_changed(FALSE);
}
static gboolean ask_save_changes()
{
GtkWidget *dialog, *label, *content;
gboolean quit = TRUE;
dialog = gtk_dialog_new_with_buttons("Save Changes?",
GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
GTK_STOCK_NO, GTK_RESPONSE_NO,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
NULL);
content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
label = gtk_label_new ("You have unsaved changes\nWould you like to save those before exiting the program?");
if (!existing_filename){
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)));
sprintf(label_text,
"You have unsaved changes to file: %s \nWould you like to save those before exiting the program?",
existing_filename);
label = gtk_label_new (label_text);
g_free(label_text);
}
gtk_container_add (GTK_CONTAINER (content), label);
gtk_widget_show_all (dialog);
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
gint outcode = gtk_dialog_run(GTK_DIALOG(dialog));
if (outcode == GTK_RESPONSE_ACCEPT) {
file_save(NULL,NULL);
} else if (outcode == GTK_RESPONSE_CANCEL) {
quit = FALSE;
}
gtk_widget_destroy(dialog);
return quit;
}
static gboolean on_delete(GtkWidget* w, gpointer data)
@ -214,10 +249,15 @@ static gboolean on_delete(GtkWidget* w, gpointer data)
/* Make sure to flush any modified dive data */
update_dive(NULL);
gboolean quit = TRUE;
if (unsaved_changes())
ask_save_changes();
quit = ask_save_changes();
return FALSE; /* go ahead, kill the program, we're good now */
if (quit){
return FALSE; /* go ahead, kill the program, we're good now */
} else {
return TRUE; /* We are not leaving */
}
}
static void on_destroy(GtkWidget* w, gpointer data)
@ -230,9 +270,13 @@ static void quit(GtkWidget *w, gpointer data)
/* Make sure to flush any modified dive data */
update_dive(NULL);
gboolean quit = TRUE;
if (unsaved_changes())
ask_save_changes();
gtk_main_quit();
quit = ask_save_changes();
if (quit){
gtk_main_quit();
}
}
GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
@ -343,6 +387,8 @@ OPTIONCALLBACK(otu_toggle, visible_cols.otu)
OPTIONCALLBACK(sac_toggle, visible_cols.sac)
OPTIONCALLBACK(nitrox_toggle, visible_cols.nitrox)
OPTIONCALLBACK(temperature_toggle, visible_cols.temperature)
OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight)
OPTIONCALLBACK(suit_toggle, visible_cols.suit)
OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder)
static void event_toggle(GtkWidget *w, gpointer _data)
@ -398,37 +444,47 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
"lbs", set_lbs, (output_units.weight == LBS),
NULL);
frame = gtk_frame_new("Columns");
frame = gtk_frame_new("Show Columns");
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("Show Temp");
button = gtk_check_button_new_with_label("Temp");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.temperature);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL);
button = gtk_check_button_new_with_label("Show Cyl");
button = gtk_check_button_new_with_label("Cyl");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.cylinder);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL);
button = gtk_check_button_new_with_label("Show O" UTF8_SUBSCRIPT_2 "%");
button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.nitrox);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL);
button = gtk_check_button_new_with_label("Show SAC");
button = gtk_check_button_new_with_label("SAC");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.sac);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL);
button = gtk_check_button_new_with_label("Show OTU");
button = gtk_check_button_new_with_label("OTU");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.otu);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL);
button = gtk_check_button_new_with_label("Weight");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.totalweight);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL);
button = gtk_check_button_new_with_label("Suit");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), visible_cols.suit);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
font = gtk_font_button_new_with_font(divelist_font);
gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
@ -452,6 +508,8 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(output_units.temperature == FAHRENHEIT));
subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(output_units.weight == LBS));
subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(visible_cols.temperature));
subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(visible_cols.totalweight));
subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(visible_cols.suit));
subsurface_set_conf("CYLINDER", PREF_BOOL, BOOL_TO_PTR(visible_cols.cylinder));
subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox));
subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac));
@ -604,11 +662,17 @@ static void view_info(GtkWidget *w, gpointer data)
gtk_paned_set_position(GTK_PANED(hpane), 65535);
}
/* Ooh. I don't know how to get the half-way size. So I'm just using random numbers */
static void view_three(GtkWidget *w, gpointer data)
{
gtk_paned_set_position(GTK_PANED(hpane), 400);
gtk_paned_set_position(GTK_PANED(vpane), 200);
GtkAllocation alloc;
GtkRequisition requisition;
gtk_widget_get_allocation(hpane, &alloc);
gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2);
gtk_widget_get_allocation(vpane, &alloc);
gtk_widget_size_request(notebook, &requisition);
/* pick the requested size for the notebook plus 6 pixels for frame */
gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6);
}
static void toggle_zoom(GtkWidget *w, gpointer data)
@ -619,16 +683,18 @@ static void toggle_zoom(GtkWidget *w, gpointer data)
}
static GtkActionEntry menu_items[] = {
{ "FileMenuAction", GTK_STOCK_FILE, "File", NULL, NULL, NULL},
{ "LogMenuAction", GTK_STOCK_FILE, "Log", NULL, NULL, NULL},
{ "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
{ "FilterMenuAction", GTK_STOCK_FILE, "Filter", NULL, NULL, NULL},
{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
{ "FileMenuAction", NULL, "File", NULL, NULL, NULL},
{ "LogMenuAction", NULL, "Log", NULL, NULL, NULL},
{ "ViewMenuAction", NULL, "View", NULL, NULL, NULL},
{ "FilterMenuAction", NULL, "Filter", NULL, NULL, NULL},
{ "HelpMenuAction", NULL, "Help", NULL, NULL, NULL},
{ "OpenFile", GTK_STOCK_OPEN, NULL, CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
{ "SaveFile", GTK_STOCK_SAVE, NULL, CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
{ "SaveAsFile", GTK_STOCK_SAVE_AS, NULL, SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) },
{ "Print", GTK_STOCK_PRINT, NULL, CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
{ "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
{ "Preferences", NULL, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
{ "AddDive", GTK_STOCK_ADD, "Add Dive", NULL, NULL, G_CALLBACK(add_dive_cb) },
{ "Preferences", GTK_STOCK_PREFERENCES, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
{ "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
{ "SelectEvents", NULL, "SelectEvents", NULL, NULL, G_CALLBACK(selectevents_dialog) },
{ "Quit", GTK_STOCK_QUIT, NULL, CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
@ -647,6 +713,7 @@ static const gchar* ui_string = " \
<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
<menuitem name=\"Open\" action=\"OpenFile\" /> \
<menuitem name=\"Save\" action=\"SaveFile\" /> \
<menuitem name=\"Save As\" action=\"SaveAsFile\" /> \
<menuitem name=\"Print\" action=\"Print\" /> \
<separator name=\"Separator1\"/> \
<menuitem name=\"Preferences\" action=\"Preferences\" /> \
@ -655,6 +722,7 @@ static const gchar* ui_string = " \
</menu> \
<menu name=\"LogMenu\" action=\"LogMenuAction\"> \
<menuitem name=\"Import\" action=\"Import\" /> \
<menuitem name=\"Add Dive\" action=\"AddDive\" /> \
<separator name=\"Separator\"/> \
<menuitem name=\"Renumber\" action=\"Renumber\" /> \
<menuitem name=\"Toggle Zoom\" action=\"ToggleZoom\" /> \
@ -698,11 +766,11 @@ static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data)
void init_ui(int *argcp, char ***argvp)
{
GtkWidget *win;
GtkWidget *notebook;
GtkWidget *nb_page;
GtkWidget *dive_list;
GtkWidget *menubar;
GtkWidget *vbox;
GtkWidget *scrolled;
GdkScreen *screen;
GtkIconTheme *icon_theme=NULL;
GtkSettings *settings;
@ -728,13 +796,16 @@ void init_ui(int *argcp, char ***argvp)
/* an unset key is FALSE - all these are hidden by default */
visible_cols.cylinder = PTR_TO_BOOL(subsurface_get_conf("CYLINDER", PREF_BOOL));
visible_cols.temperature = PTR_TO_BOOL(subsurface_get_conf("TEMPERATURE", PREF_BOOL));
visible_cols.totalweight = PTR_TO_BOOL(subsurface_get_conf("TOTALWEIGHT", PREF_BOOL));
visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL));
visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL));
visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL));
visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL));
divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
default_dive_computer = subsurface_get_conf("dive_computer", PREF_STRING);
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);
error_info_bar = NULL;
@ -771,13 +842,16 @@ void init_ui(int *argcp, char ***argvp)
vpane = gtk_vpaned_new();
gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3);
hpane = gtk_hpaned_new();
gtk_paned_add1(GTK_PANED(vpane), hpane);
g_signal_connect_after(G_OBJECT(vbox), "realize", G_CALLBACK(view_three), NULL);
/* Notebook for dive info vs profile vs .. */
notebook = gtk_notebook_new();
gtk_paned_add1(GTK_PANED(hpane), notebook);
scrolled = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_paned_add1(GTK_PANED(hpane), scrolled);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), notebook);
g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL);
/* Create the actual divelist */
@ -795,7 +869,7 @@ void init_ui(int *argcp, char ***argvp)
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes"));
/* Frame for dive equipment */
nb_page = equipment_widget();
nb_page = equipment_widget(W_IDX_PRIMARY);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment"));
/* Frame for single dive statistics */
@ -924,20 +998,45 @@ static int fill_computer_list(GtkListStore *store)
{
int index = -1, i;
GtkTreeIter iter;
struct device_list *list = device_list;
dc_iterator_t *iterator = NULL;
dc_descriptor_t *descriptor = NULL;
i = 0;
dc_descriptor_iterator(&iterator);
while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) {
const char *vendor = dc_descriptor_get_vendor(descriptor);
const char *product = dc_descriptor_get_product(descriptor);
for (list = device_list, i = 0 ; list->name ; list++, i++) {
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter,
0, list->name,
1, list->type,
0, descriptor,
-1);
if (is_default_dive_computer(list->name))
if (is_default_dive_computer(vendor, product))
index = i;
i++;
}
dc_iterator_free(iterator);
return index;
}
void render_dive_computer(GtkCellLayout *cell,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
char buffer[40];
dc_descriptor_t *descriptor = NULL;
const char *vendor, *product;
gtk_tree_model_get(model, iter, 0, &descriptor, -1);
vendor = dc_descriptor_get_vendor(descriptor);
product = dc_descriptor_get_product(descriptor);
snprintf(buffer, sizeof(buffer), "%s %s", vendor, product);
g_object_set(renderer, "text", buffer, NULL);
}
static GtkComboBox *dive_computer_selector(GtkWidget *vbox)
{
GtkWidget *hbox, *combo_box, *frame;
@ -948,7 +1047,7 @@ static GtkComboBox *dive_computer_selector(GtkWidget *vbox)
hbox = gtk_hbox_new(FALSE, 6);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
model = gtk_list_store_new(1, G_TYPE_POINTER);
default_index = fill_computer_list(model);
frame = gtk_frame_new("Dive computer");
@ -959,7 +1058,7 @@ static GtkComboBox *dive_computer_selector(GtkWidget *vbox)
renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), renderer, "text", 0, NULL);
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(combo_box), renderer, render_dive_computer, NULL, NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), default_index);
@ -1104,10 +1203,9 @@ repeat:
gtk_widget_show_all(dialog);
result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result) {
int type;
dc_descriptor_t *descriptor;
GtkTreeIter iter;
GtkTreeModel *model;
const char *comp;
GSList *list;
case GTK_RESPONSE_ACCEPT:
/* what happened - did the user pick a file? In that case
@ -1116,17 +1214,23 @@ repeat:
gtk_widget_destroy(info);
list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(XMLchooser));
if (g_slist_length(list) == 0) {
const char *vendor, *product;
if (!gtk_combo_box_get_active_iter(computer, &iter))
break;
model = gtk_combo_box_get_model(computer);
gtk_tree_model_get(model, &iter,
0, &comp,
1, &type,
0, &descriptor,
-1);
devicedata.type = type;
devicedata.name = comp;
vendor = dc_descriptor_get_vendor(descriptor);
product = dc_descriptor_get_product(descriptor);
devicedata.descriptor = descriptor;
devicedata.vendor = vendor;
devicedata.product = product;
devicedata.devname = gtk_entry_get_text(device);
set_default_dive_computer(devicedata.name);
set_default_dive_computer(vendor, product);
set_default_dive_computer_device(devicedata.devname);
info = import_dive_computer(&devicedata, GTK_DIALOG(dialog));
if (info)
@ -1156,7 +1260,9 @@ void update_progressbar_text(progressbar_t *progress, const char *text)
void set_filename(const char *filename)
{
if (!existing_filename && filename)
if (existing_filename)
free(existing_filename);
existing_filename = NULL;
if (filename)
existing_filename = strdup(filename);
return;
}

352
info.c
View file

@ -12,15 +12,16 @@
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <sys/time.h>
#include "dive.h"
#include "display.h"
#include "display-gtk.h"
#include "divelist.h"
static GtkEntry *location, *buddy, *divemaster, *rating;
static GtkEntry *location, *buddy, *divemaster, *rating, *suit;
static GtkTextView *notes;
static GtkListStore *location_list, *people_list, *star_list;
static GtkListStore *location_list, *people_list, *star_list, *suit_list;
static char *get_text(GtkTextView *view)
{
@ -42,16 +43,50 @@ static int text_changed(const char *old, const char *new)
(!old && strcmp("",new));
}
static char *get_combo_box_entry_text(GtkComboBoxEntry *combo_box, char **textp)
static const char *skip_space(const char *str)
{
if (str) {
while (isspace(*str))
str++;
if (!*str)
str = NULL;
}
return str;
}
/*
* Get the string from a combo box.
*
* The "master" string is the string of the current dive - we only consider it
* changed if the old string is either empty, or matches that master string.
*/
static char *get_combo_box_entry_text(GtkComboBoxEntry *combo_box, char **textp, const char *master)
{
char *old = *textp;
const char *old_text;
const gchar *new;
GtkEntry *entry;
old_text = skip_space(old);
master = skip_space(master);
/*
* If we had a master string, and it doesn't match our old
* string, we will always pick the old value (it means that
* we're editing another dive's info that already had a
* valid value).
*/
if (master && old_text)
if (strcmp(master, old_text))
return NULL;
entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box)));
new = gtk_entry_get_text(entry);
while (isspace(*new))
new++;
/* If the master string didn't change, don't change other dives either! */
if (!text_changed(master,new))
return NULL;
if (!text_changed(old,new))
return NULL;
free(old);
@ -95,6 +130,7 @@ void show_dive_info(struct dive *dive)
SET_TEXT_VALUE(divemaster);
SET_TEXT_VALUE(buddy);
SET_TEXT_VALUE(location);
SET_TEXT_VALUE(suit);
gtk_entry_set_text(rating, star_strings[dive->rating]);
gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes),
dive && dive->notes ? dive->notes : "", -1);
@ -130,17 +166,26 @@ static int delete_dive_info(struct dive *dive)
static void info_menu_edit_cb(GtkMenuItem *menuitem, gpointer user_data)
{
edit_dive_info(current_dive);
edit_multi_dive_info(NULL);
}
static void info_menu_delete_cb(GtkMenuItem *menuitem, gpointer user_data)
{
/* this needs to delete all the selected dives as well, I guess? */
delete_dive_info(current_dive);
}
static void add_menu_item(GtkMenu *menu, const char *label, void (*cb)(GtkMenuItem *, gpointer))
static void add_menu_item(GtkMenu *menu, const char *label, const char *icon, void (*cb)(GtkMenuItem *, gpointer))
{
GtkWidget *item = gtk_menu_item_new_with_label(label);
GtkWidget *item;
if (icon) {
GtkWidget *image;
item = gtk_image_menu_item_new_with_label(label);
image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU);
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
} else {
item = gtk_menu_item_new_with_label(label);
}
g_signal_connect(item, "activate", G_CALLBACK(cb), NULL);
gtk_widget_show(item); /* Yes, really */
gtk_menu_prepend(menu, item);
@ -148,8 +193,8 @@ static void add_menu_item(GtkMenu *menu, const char *label, void (*cb)(GtkMenuIt
static void populate_popup_cb(GtkTextView *entry, GtkMenu *menu, gpointer user_data)
{
add_menu_item(menu, "Delete", info_menu_delete_cb);
add_menu_item(menu, "Edit", info_menu_edit_cb);
add_menu_item(menu, "Delete", GTK_STOCK_DELETE, info_menu_delete_cb);
add_menu_item(menu, "Edit", GTK_STOCK_EDIT, info_menu_edit_cb);
}
static GtkEntry *text_value(GtkWidget *box, const char *label)
@ -241,6 +286,8 @@ static gboolean match_string_entry(GtkTreeModel *model, GtkTreePath *path, GtkTr
gtk_tree_model_get(model, iter, 0, &entry, -1);
cmp = strcmp(entry, string);
if (entry)
free(entry);
/* Stop. The entry is bigger than the new one */
if (cmp > 0)
@ -295,6 +342,11 @@ void add_location(const char *string)
add_string_list_entry(string, location_list);
}
void add_suit(const char *string)
{
add_string_list_entry(string, suit_list);
}
static int get_rating(const char *string)
{
int rating_val = 0;
@ -307,61 +359,69 @@ static int get_rating(const char *string)
}
struct dive_info {
GtkComboBoxEntry *location, *divemaster, *buddy, *rating;
GtkComboBoxEntry *location, *divemaster, *buddy, *rating, *suit;
GtkTextView *notes;
};
static void save_dive_info_changes(struct dive *dive, struct dive_info *info)
static void save_dive_info_changes(struct dive *dive, struct dive *master, struct dive_info *info)
{
char *old_text, *new_text;
char *rating_string;
int changed = 0;
new_text = get_combo_box_entry_text(info->location, &dive->location);
new_text = get_combo_box_entry_text(info->location, &dive->location, master->location);
if (new_text) {
add_location(new_text);
changed = 1;
}
new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster);
new_text = get_combo_box_entry_text(info->divemaster, &dive->divemaster, master->divemaster);
if (new_text) {
add_people(new_text);
changed = 1;
}
new_text = get_combo_box_entry_text(info->buddy, &dive->buddy);
new_text = get_combo_box_entry_text(info->buddy, &dive->buddy, master->buddy);
if (new_text) {
add_people(new_text);
changed = 1;
}
new_text = get_combo_box_entry_text(info->suit, &dive->suit, master->suit);
if (new_text) {
add_suit(new_text);
changed = 1;
}
rating_string = strdup(star_strings[dive->rating]);
new_text = get_combo_box_entry_text(info->rating, &rating_string);
new_text = get_combo_box_entry_text(info->rating, &rating_string, star_strings[master->rating]);
if (new_text) {
dive->rating = get_rating(rating_string);
free(rating_string);
changed =1;
}
old_text = dive->notes;
dive->notes = get_text(info->notes);
if (text_changed(old_text,dive->notes))
changed = 1;
if (old_text)
g_free(old_text);
if (info->notes) {
old_text = dive->notes;
dive->notes = get_text(info->notes);
if (text_changed(old_text,dive->notes))
changed = 1;
if (old_text)
g_free(old_text);
}
if (changed) {
mark_divelist_changed(TRUE);
update_dive(dive);
}
}
static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info)
static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info, gboolean multi)
{
GtkWidget *hbox, *label, *cylinder, *frame;
char buffer[80];
GtkWidget *hbox, *label, *frame, *equipment;
char buffer[80] = "Edit multiple dives";
divename(buffer, sizeof(buffer), dive);
if (!multi)
divename(buffer, sizeof(buffer), dive);
label = gtk_label_new(buffer);
gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0);
@ -377,28 +437,65 @@ static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
info->rating = text_entry(hbox, "Rating", star_list, star_strings[dive->rating]);
info->suit = text_entry(hbox, "Suit", suit_list, dive->suit);
info->notes = text_view(box, "Notes", READ_WRITE);
if (dive->notes && *dive->notes)
gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1);
/* only show notes if editing a single dive */
if (multi) {
info->notes = NULL;
} else {
info->notes = text_view(box, "Notes", READ_WRITE);
if (dive->notes && *dive->notes)
gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1);
}
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
frame = gtk_frame_new("Cylinder");
cylinder = cylinder_list_widget();
gtk_container_add(GTK_CONTAINER(frame), cylinder);
/* create a secondary Equipment widget */
frame = gtk_frame_new("Equipment");
equipment = equipment_widget(W_IDX_SECONDARY);
gtk_container_add(GTK_CONTAINER(frame), equipment);
gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
}
int edit_dive_info(struct dive *dive)
/* we use these to find out if we edited the cylinder or weightsystem entries */
static cylinder_t remember_cyl[MAX_CYLINDERS];
static weightsystem_t remember_ws[MAX_WEIGHTSYSTEMS];
#define CYL_BYTES sizeof(cylinder_t) * MAX_CYLINDERS
#define WS_BYTES sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS
void save_equipment_data(struct dive *dive)
{
if (dive) {
memcpy(remember_cyl, dive->cylinder, CYL_BYTES);
memcpy(remember_ws, dive->weightsystem, WS_BYTES);
}
}
/* the editing happens on the master dive; we copy the equipment
data if it has changed in the master dive and the other dive
either has no entries for the equipment or the same entries
as the master dive had before it was edited */
void update_equipment_data(struct dive *dive, struct dive *master)
{
if (dive == master)
return;
if ( ! cylinders_equal(remember_cyl, master->cylinder) &&
(no_cylinders(dive->cylinder) ||
cylinders_equal(dive->cylinder, remember_cyl)))
memcpy(dive->cylinder, master->cylinder, CYL_BYTES);
if (! weightsystems_equal(remember_ws, master->weightsystem) &&
(no_weightsystems(dive->weightsystem) ||
weightsystems_equal(dive->weightsystem, remember_ws)))
memcpy(dive->weightsystem, master->weightsystem, WS_BYTES);
}
/* A negative index means "all selected" */
int edit_multi_dive_info(struct dive *single_dive)
{
int success;
GtkWidget *dialog, *vbox;
struct dive_info info;
if (!dive)
return 0;
struct dive *master;
dialog = gtk_dialog_new_with_buttons("Dive Info",
GTK_WINDOW(main_window),
@ -408,18 +505,191 @@ int edit_dive_info(struct dive *dive)
NULL);
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
dive_info_widget(vbox, dive, &info);
master = single_dive;
if (!master)
master = current_dive;
dive_info_widget(vbox, master, &info, !single_dive);
show_dive_equipment(master, W_IDX_SECONDARY);
save_equipment_data(master);
gtk_widget_show_all(dialog);
success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT;
if (success)
save_dive_info_changes(dive, &info);
if (success) {
/* Update the non-current selected dives first */
if (!single_dive) {
int i;
struct dive *dive;
for_each_dive(i, dive) {
if (dive == master || !dive->selected)
continue;
/* copy all "info" fields */
save_dive_info_changes(dive, master, &info);
/* copy the cylinders / weightsystems */
update_equipment_data(dive, master);
/* this is extremely inefficient... it loops through all
dives to find the right one - but we KNOW the index already */
flush_divelist(dive);
}
}
/* Update the master dive last! */
save_dive_info_changes(master, master, &info);
update_equipment_data(master, master);
flush_divelist(master);
}
gtk_widget_destroy(dialog);
return success;
}
int edit_dive_info(struct dive *dive)
{
if (!dive)
return 0;
return edit_multi_dive_info(dive);
}
static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...)
{
va_list ap;
char buffer[64];
GtkWidget *frame, *hbox;
va_start(ap, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, ap);
va_end(ap);
frame = gtk_frame_new(buffer);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
hbox = gtk_hbox_new(0, 3);
gtk_container_add(GTK_CONTAINER(frame), hbox);
return hbox;
}
/* Fixme - should do at least depths too - a dive without a depth is kind of pointless */
static time_t dive_time_widget(struct dive *dive)
{
GtkWidget *dialog;
GtkWidget *cal, *hbox, *vbox, *box;
GtkWidget *h, *m;
GtkWidget *duration, *depth;
GtkWidget *label;
guint yval, mval, dval;
struct tm tm, *time;
int success;
double depthinterval, val;
dialog = gtk_dialog_new_with_buttons("Date and Time",
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));
/* Calendar hbox */
hbox = frame_box(vbox, "Date:");
cal = gtk_calendar_new();
gtk_box_pack_start(GTK_BOX(hbox), cal, FALSE, TRUE, 0);
/* Time hbox */
hbox = frame_box(vbox, "Time");
h = gtk_spin_button_new_with_range (0.0, 23.0, 1.0);
m = gtk_spin_button_new_with_range (0.0, 59.0, 1.0);
/*
* If we have a dive selected, 'add dive' will default
* to one hour after the end of that dive. Otherwise,
* we'll just take the current time.
*/
if (amount_selected == 1) {
time_t when = current_dive->when;
when += current_dive->duration.seconds;
when += 60*60;
time = gmtime(&when);
} else {
time_t now;
struct timeval tv;
gettimeofday(&tv, NULL);
now = tv.tv_sec;
time = localtime(&now);
}
gtk_calendar_select_month(GTK_CALENDAR(cal), time->tm_mon, time->tm_year + 1900);
gtk_calendar_select_day(GTK_CALENDAR(cal), time->tm_mday);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(h), time->tm_hour);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(m), (time->tm_min / 5)*5);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(h), TRUE);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(m), TRUE);
gtk_box_pack_end(GTK_BOX(hbox), m, FALSE, FALSE, 0);
label = gtk_label_new(":");
gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(hbox), h, FALSE, FALSE, 0);
hbox = gtk_hbox_new(TRUE, 3);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
/* Duration hbox */
box = frame_box(hbox, "Duration (min)");
duration = gtk_spin_button_new_with_range (0.0, 1000.0, 1.0);
gtk_box_pack_end(GTK_BOX(box), duration, FALSE, FALSE, 0);
/* Depth box */
box = frame_box(hbox, "Depth (%s):", output_units.length == FEET ? "ft" : "m");
if (output_units.length == FEET) {
depthinterval = 1.0;
} else {
depthinterval = 0.1;
}
depth = gtk_spin_button_new_with_range (0.0, 1000.0, depthinterval);
gtk_box_pack_end(GTK_BOX(box), depth, FALSE, FALSE, 0);
/* All done, show it and wait for editing */
gtk_widget_show_all(dialog);
success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT;
if (!success) {
gtk_widget_destroy(dialog);
return 0;
}
memset(&tm, 0, sizeof(tm));
gtk_calendar_get_date(GTK_CALENDAR(cal), &yval, &mval, &dval);
tm.tm_year = yval;
tm.tm_mon = mval;
tm.tm_mday = dval;
tm.tm_hour = gtk_spin_button_get_value(GTK_SPIN_BUTTON(h));
tm.tm_min = gtk_spin_button_get_value(GTK_SPIN_BUTTON(m));
val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(depth));
if (output_units.length == FEET) {
dive->maxdepth.mm = feet_to_mm(val);
} else {
dive->maxdepth.mm = val * 1000 + 0.5;
}
dive->duration.seconds = gtk_spin_button_get_value(GTK_SPIN_BUTTON(duration))*60;
gtk_widget_destroy(dialog);
dive->when = utc_mktime(&tm);
return 1;
}
int add_new_dive(struct dive *dive)
{
if (!dive)
return 0;
if (!dive_time_widget(dive))
return 0;
return edit_dive_info(dive);
}
GtkWidget *extended_dive_info_widget(void)
{
GtkWidget *vbox, *hbox;
@ -434,6 +704,7 @@ GtkWidget *extended_dive_info_widget(void)
add_string_list_entry(THREE_STARS, star_list);
add_string_list_entry(FOUR_STARS, star_list);
add_string_list_entry(FIVE_STARS, star_list);
suit_list = gtk_list_store_new(1, G_TYPE_STRING);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
location = text_value(vbox, "Location");
@ -448,6 +719,7 @@ GtkWidget *extended_dive_info_widget(void)
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
rating = text_value(hbox, "Rating");
suit = text_value(hbox, "Suit");
notes = text_view(vbox, "Notes", READ_ONLY);
return vbox;

View file

@ -31,90 +31,22 @@ static GError *error(const char *fmt, ...)
return error;
}
static parser_status_t create_parser(device_data_t *devdata, parser_t **parser)
static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser)
{
switch (devdata->type) {
case DEVICE_TYPE_SUUNTO_SOLUTION:
return suunto_solution_parser_create(parser);
case DEVICE_TYPE_SUUNTO_EON:
return suunto_eon_parser_create(parser, 0);
case DEVICE_TYPE_SUUNTO_VYPER:
if (devdata->devinfo.model == 0x01)
return suunto_eon_parser_create(parser, 1);
return suunto_vyper_parser_create(parser);
case DEVICE_TYPE_SUUNTO_VYPER2:
case DEVICE_TYPE_SUUNTO_D9:
return suunto_d9_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_UWATEC_ALADIN:
case DEVICE_TYPE_UWATEC_MEMOMOUSE:
return uwatec_memomouse_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
case DEVICE_TYPE_UWATEC_SMART:
return uwatec_smart_parser_create(parser, devdata->devinfo.model, devdata->clock.devtime, devdata->clock.systime);
case DEVICE_TYPE_REEFNET_SENSUS:
return reefnet_sensus_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
case DEVICE_TYPE_REEFNET_SENSUSPRO:
return reefnet_sensuspro_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
case DEVICE_TYPE_REEFNET_SENSUSULTRA:
return reefnet_sensusultra_parser_create(parser, devdata->clock.devtime, devdata->clock.systime);
case DEVICE_TYPE_OCEANIC_VTPRO:
return oceanic_vtpro_parser_create(parser);
case DEVICE_TYPE_OCEANIC_VEO250:
return oceanic_veo250_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_OCEANIC_ATOM2:
return oceanic_atom2_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_MARES_DARWIN:
return mares_darwin_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_MARES_NEMO:
case DEVICE_TYPE_MARES_PUCK:
return mares_nemo_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_MARES_ICONHD:
return mares_iconhd_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_HW_OSTC:
return hw_ostc_parser_create(parser NOT_FROG);
#ifdef LIBDIVECOMPUTER_SUPPORTS_FROG
case DEVICE_TYPE_HW_FROG:
return hw_ostc_parser_create(parser, 1);
#endif
case DEVICE_TYPE_CRESSI_EDY:
case DEVICE_TYPE_ZEAGLE_N2ITION3:
return cressi_edy_parser_create(parser, devdata->devinfo.model);
case DEVICE_TYPE_ATOMICS_COBALT:
return atomics_cobalt_parser_create(parser);
default:
return PARSER_STATUS_ERROR;
}
return dc_parser_new(parser, devdata->device);
}
static int parse_gasmixes(device_data_t *devdata, struct dive *dive, parser_t *parser, int ngases)
static int parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_parser_t *parser, int ngases)
{
int i;
for (i = 0; i < ngases; i++) {
int rc;
gasmix_t gasmix = {0};
dc_gasmix_t gasmix = {0};
int o2, he;
rc = parser_get_field(parser, FIELD_TYPE_GASMIX, i, &gasmix);
if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED)
rc = dc_parser_get_field(parser, DC_FIELD_GASMIX, i, &gasmix);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED)
return rc;
if (i >= MAX_CYLINDERS)
@ -132,10 +64,10 @@ static int parse_gasmixes(device_data_t *devdata, struct dive *dive, parser_t *p
dive->cylinder[i].gasmix.o2.permille = o2;
dive->cylinder[i].gasmix.he.permille = he;
}
return PARSER_STATUS_SUCCESS;
return DC_STATUS_SUCCESS;
}
static void handle_event(struct dive *dive, struct sample *sample, parser_sample_value_t value)
static void handle_event(struct dive *dive, struct sample *sample, dc_sample_value_t value)
{
int type, time;
static const char *events[] = {
@ -173,7 +105,7 @@ static void handle_event(struct dive *dive, struct sample *sample, parser_sample
}
void
sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata)
sample_cb(dc_sample_type_t type, dc_sample_value_t value, void *userdata)
{
int i;
struct dive **divep = userdata;
@ -181,40 +113,40 @@ sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata
struct sample *sample;
/*
* We fill in the "previous" sample - except for SAMPLE_TYPE_TIME,
* We fill in the "previous" sample - except for DC_SAMPLE_TIME,
* which creates a new one.
*/
sample = dive->samples ? dive->sample+dive->samples-1 : NULL;
switch (type) {
case SAMPLE_TYPE_TIME:
case DC_SAMPLE_TIME:
sample = prepare_sample(divep);
sample->time.seconds = value.time;
finish_sample(*divep);
break;
case SAMPLE_TYPE_DEPTH:
case DC_SAMPLE_DEPTH:
sample->depth.mm = value.depth * 1000 + 0.5;
break;
case SAMPLE_TYPE_PRESSURE:
case DC_SAMPLE_PRESSURE:
sample->cylinderindex = value.pressure.tank;
sample->cylinderpressure.mbar = value.pressure.value * 1000 + 0.5;
break;
case SAMPLE_TYPE_TEMPERATURE:
case DC_SAMPLE_TEMPERATURE:
sample->temperature.mkelvin = (value.temperature + 273.15) * 1000 + 0.5;
break;
case SAMPLE_TYPE_EVENT:
case DC_SAMPLE_EVENT:
handle_event(dive, sample, value);
break;
case SAMPLE_TYPE_RBT:
case DC_SAMPLE_RBT:
printf(" <rbt>%u</rbt>\n", value.rbt);
break;
case SAMPLE_TYPE_HEARTBEAT:
case DC_SAMPLE_HEARTBEAT:
printf(" <heartbeat>%u</heartbeat>\n", value.heartbeat);
break;
case SAMPLE_TYPE_BEARING:
case DC_SAMPLE_BEARING:
printf(" <bearing>%u</bearing>\n", value.bearing);
break;
case SAMPLE_TYPE_VENDOR:
case DC_SAMPLE_VENDOR:
printf(" <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
for (i = 0; i < value.vendor.size; ++i)
printf("%02X", ((unsigned char *) value.vendor.data)[i]);
@ -238,10 +170,10 @@ static void dev_info(device_data_t *devdata, const char *fmt, ...)
static int import_dive_number = 0;
static int parse_samples(device_data_t *devdata, struct dive **divep, parser_t *parser)
static int parse_samples(device_data_t *devdata, struct dive **divep, dc_parser_t *parser)
{
// Parse the sample data.
return parser_samples_foreach(parser, sample_cb, divep);
return dc_parser_samples_foreach(parser, sample_cb, divep);
}
/*
@ -275,31 +207,31 @@ static int dive_cb(const unsigned char *data, unsigned int size,
void *userdata)
{
int rc;
parser_t *parser = NULL;
dc_parser_t *parser = NULL;
device_data_t *devdata = userdata;
dc_datetime_t dt = {0};
struct tm tm;
struct dive *dive;
rc = create_parser(devdata, &parser);
if (rc != PARSER_STATUS_SUCCESS) {
dev_info(devdata, "Unable to create parser for %s", devdata->name);
if (rc != DC_STATUS_SUCCESS) {
dev_info(devdata, "Unable to create parser for %s %s", devdata->vendor, devdata->product);
return rc;
}
rc = parser_set_data(parser, data, size);
if (rc != PARSER_STATUS_SUCCESS) {
rc = dc_parser_set_data(parser, data, size);
if (rc != DC_STATUS_SUCCESS) {
dev_info(devdata, "Error registering the data");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
import_dive_number++;
dive = alloc_dive();
rc = parser_get_datetime(parser, &dt);
if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
rc = dc_parser_get_datetime(parser, &dt);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
dev_info(devdata, "Error parsing the datetime");
parser_destroy (parser);
dc_parser_destroy(parser);
return rc;
}
@ -315,49 +247,49 @@ static int dive_cb(const unsigned char *data, unsigned int size,
dev_info(devdata, "Dive %d: %s %d %04d", import_dive_number,
monthname(tm.tm_mon), tm.tm_mday, year(tm.tm_year));
unsigned int divetime = 0;
rc = parser_get_field (parser, FIELD_TYPE_DIVETIME, 0, &divetime);
if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
rc = dc_parser_get_field (parser, DC_FIELD_DIVETIME, 0, &divetime);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
dev_info(devdata, "Error parsing the divetime");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
dive->duration.seconds = divetime;
// Parse the maxdepth.
double maxdepth = 0.0;
rc = parser_get_field(parser, FIELD_TYPE_MAXDEPTH, 0, &maxdepth);
if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
rc = dc_parser_get_field(parser, DC_FIELD_MAXDEPTH, 0, &maxdepth);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
dev_info(devdata, "Error parsing the maxdepth");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
dive->maxdepth.mm = maxdepth * 1000 + 0.5;
// Parse the gas mixes.
unsigned int ngases = 0;
rc = parser_get_field(parser, FIELD_TYPE_GASMIX_COUNT, 0, &ngases);
if (rc != PARSER_STATUS_SUCCESS && rc != PARSER_STATUS_UNSUPPORTED) {
rc = dc_parser_get_field(parser, DC_FIELD_GASMIX_COUNT, 0, &ngases);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
dev_info(devdata, "Error parsing the gas mix count");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
rc = parse_gasmixes(devdata, dive, parser, ngases);
if (rc != PARSER_STATUS_SUCCESS) {
if (rc != DC_STATUS_SUCCESS) {
dev_info(devdata, "Error parsing the gas mix");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
// Initialize the sample data.
rc = parse_samples(devdata, &dive, parser);
if (rc != PARSER_STATUS_SUCCESS) {
if (rc != DC_STATUS_SUCCESS) {
dev_info(devdata, "Error parsing the samples");
parser_destroy(parser);
dc_parser_destroy(parser);
return rc;
}
parser_destroy(parser);
dc_parser_destroy(parser);
/* If we already saw this dive, abort. */
if (find_dive(dive, devdata))
@ -368,112 +300,41 @@ static int dive_cb(const unsigned char *data, unsigned int size,
}
static device_status_t import_device_data(device_t *device, device_data_t *devicedata)
static dc_status_t import_device_data(dc_device_t *device, device_data_t *devicedata)
{
return device_foreach(device, dive_cb, devicedata);
return dc_device_foreach(device, dive_cb, devicedata);
}
static device_status_t device_open(const char *devname,
device_type_t type,
device_t **device)
static dc_status_t device_open(const char *devname,
dc_descriptor_t *descriptor,
dc_device_t **device)
{
switch (type) {
case DEVICE_TYPE_SUUNTO_SOLUTION:
return suunto_solution_device_open(device, devname);
case DEVICE_TYPE_SUUNTO_EON:
return suunto_eon_device_open(device, devname);
case DEVICE_TYPE_SUUNTO_VYPER:
return suunto_vyper_device_open(device, devname);
case DEVICE_TYPE_SUUNTO_VYPER2:
return suunto_vyper2_device_open(device, devname);
case DEVICE_TYPE_SUUNTO_D9:
return suunto_d9_device_open(device, devname);
case DEVICE_TYPE_UWATEC_ALADIN:
return uwatec_aladin_device_open(device, devname);
case DEVICE_TYPE_UWATEC_MEMOMOUSE:
return uwatec_memomouse_device_open(device, devname);
case DEVICE_TYPE_UWATEC_SMART:
return uwatec_smart_device_open(device);
case DEVICE_TYPE_REEFNET_SENSUS:
return reefnet_sensus_device_open(device, devname);
case DEVICE_TYPE_REEFNET_SENSUSPRO:
return reefnet_sensuspro_device_open(device, devname);
case DEVICE_TYPE_REEFNET_SENSUSULTRA:
return reefnet_sensusultra_device_open(device, devname);
case DEVICE_TYPE_OCEANIC_VTPRO:
return oceanic_vtpro_device_open(device, devname);
case DEVICE_TYPE_OCEANIC_VEO250:
return oceanic_veo250_device_open(device, devname);
case DEVICE_TYPE_OCEANIC_ATOM2:
return oceanic_atom2_device_open(device, devname);
case DEVICE_TYPE_MARES_DARWIN:
return mares_darwin_device_open(device, devname, 0); /// last parameter is model type (taken from example), 0 seems to be standard, 1 is DARWIN_AIR => Darwin Air wont work if this is fixed here?
case DEVICE_TYPE_MARES_NEMO:
return mares_nemo_device_open(device, devname);
case DEVICE_TYPE_MARES_PUCK:
return mares_puck_device_open(device, devname);
case DEVICE_TYPE_MARES_ICONHD:
return mares_iconhd_device_open(device, devname);
case DEVICE_TYPE_HW_OSTC:
return hw_ostc_device_open(device, devname);
case DEVICE_TYPE_CRESSI_EDY:
return cressi_edy_device_open(device, devname);
case DEVICE_TYPE_ZEAGLE_N2ITION3:
return zeagle_n2ition3_device_open(device, devname);
case DEVICE_TYPE_ATOMICS_COBALT:
return atomics_cobalt_device_open(device);
default:
return DEVICE_STATUS_ERROR;
}
return dc_device_open(device, descriptor, devname);
}
static void event_cb(device_t *device, device_event_t event, const void *data, void *userdata)
static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
{
const device_progress_t *progress = data;
const device_devinfo_t *devinfo = data;
const device_clock_t *clock = data;
const dc_event_progress_t *progress = data;
const dc_event_devinfo_t *devinfo = data;
const dc_event_clock_t *clock = data;
device_data_t *devdata = userdata;
switch (event) {
case DEVICE_EVENT_WAITING:
case DC_EVENT_WAITING:
dev_info(devdata, "Event: waiting for user action");
break;
case DEVICE_EVENT_PROGRESS:
case DC_EVENT_PROGRESS:
update_progressbar(&devdata->progress,
(double) progress->current / (double) progress->maximum);
break;
case DEVICE_EVENT_DEVINFO:
devdata->devinfo = *devinfo;
case DC_EVENT_DEVINFO:
dev_info(devdata, "model=%u (0x%08x), firmware=%u (0x%08x), serial=%u (0x%08x)",
devinfo->model, devinfo->model,
devinfo->firmware, devinfo->firmware,
devinfo->serial, devinfo->serial);
break;
case DEVICE_EVENT_CLOCK:
devdata->clock = *clock;
case DC_EVENT_CLOCK:
dev_info(devdata, "Event: systime=%"PRId64", devtime=%u\n",
(uint64_t)clock->systime, clock->devtime);
break;
@ -492,36 +353,37 @@ cancel_cb(void *userdata)
static const char *do_libdivecomputer_import(device_data_t *data)
{
device_t *device = NULL;
device_status_t rc;
dc_device_t *device = NULL;
dc_status_t rc;
import_dive_number = 0;
rc = device_open(data->devname, data->type, &device);
if (rc != DEVICE_STATUS_SUCCESS)
return "Unable to open %s (%s)";
rc = device_open(data->devname, data->descriptor, &device);
if (rc != DC_STATUS_SUCCESS)
return "Unable to open %s %s (%s)";
data->device = device;
// Register the event handler.
int events = DEVICE_EVENT_WAITING | DEVICE_EVENT_PROGRESS | DEVICE_EVENT_DEVINFO | DEVICE_EVENT_CLOCK;
rc = device_set_events(device, events, event_cb, data);
if (rc != DEVICE_STATUS_SUCCESS) {
device_close(device);
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);
return "Error registering the event handler.";
}
// Register the cancellation handler.
rc = device_set_cancel(device, cancel_cb, data);
if (rc != DEVICE_STATUS_SUCCESS) {
device_close(device);
rc = dc_device_set_cancel(device, cancel_cb, data);
if (rc != DC_STATUS_SUCCESS) {
dc_device_close(device);
return "Error registering the cancellation handler.";
}
rc = import_device_data(device, data);
if (rc != DEVICE_STATUS_SUCCESS) {
device_close(device);
if (rc != DC_STATUS_SUCCESS) {
dc_device_close(device);
return "Dive data import error";
}
device_close(device);
dc_device_close(device);
return NULL;
}
@ -548,42 +410,6 @@ GError *do_import(device_data_t *data)
if (pthread_join(pthread, &retval) < 0)
retval = "Odd pthread error return";
if (retval)
return error(retval, data->name, data->devname);
return error(retval, data->vendor, data->product, data->devname);
return NULL;
}
/*
* Taken from 'example.c' in libdivecomputer.
*
* I really wish there was some way to just have
* libdivecomputer tell us what devices it supports,
* rather than have the application have to know..
*/
struct device_list device_list[] = {
{ "Suunto Solution", DEVICE_TYPE_SUUNTO_SOLUTION },
{ "Suunto Eon", DEVICE_TYPE_SUUNTO_EON },
{ "Suunto Vyper", DEVICE_TYPE_SUUNTO_VYPER },
{ "Suunto Vyper Air", DEVICE_TYPE_SUUNTO_VYPER2 },
{ "Suunto D9", DEVICE_TYPE_SUUNTO_D9 },
{ "Uwatec Aladin", DEVICE_TYPE_UWATEC_ALADIN },
{ "Uwatec Memo Mouse", DEVICE_TYPE_UWATEC_MEMOMOUSE },
{ "Uwatec Smart", DEVICE_TYPE_UWATEC_SMART },
{ "ReefNet Sensus", DEVICE_TYPE_REEFNET_SENSUS },
{ "ReefNet Sensus Pro", DEVICE_TYPE_REEFNET_SENSUSPRO },
{ "ReefNet Sensus Ultra",DEVICE_TYPE_REEFNET_SENSUSULTRA },
{ "Oceanic VT Pro", DEVICE_TYPE_OCEANIC_VTPRO },
{ "Oceanic Veo250", DEVICE_TYPE_OCEANIC_VEO250 },
{ "Oceanic Atom 2", DEVICE_TYPE_OCEANIC_ATOM2 },
{ "Mares Darwin, M1, M2, Airlab", DEVICE_TYPE_MARES_DARWIN },
{ "Mares Nemo, Excel, Apneist", DEVICE_TYPE_MARES_NEMO },
{ "Mares Puck, Nemo Air, Nemo Wide", DEVICE_TYPE_MARES_PUCK },
{ "Mares Icon HD", DEVICE_TYPE_MARES_ICONHD },
{ "OSTC", DEVICE_TYPE_HW_OSTC },
#ifdef LIBDIVECOMPUTER_SUPPORTS_FROG
{ "OSTC Frog", DEVICE_TYPE_HW_FROG },
#endif
{ "Cressi Edy", DEVICE_TYPE_CRESSI_EDY },
{ "Zeagle N2iTiON 3", DEVICE_TYPE_ZEAGLE_N2ITION3 },
{ "Atomics Cobalt", DEVICE_TYPE_ATOMICS_COBALT },
{ NULL }
};

View file

@ -2,17 +2,9 @@
#define LIBDIVECOMPUTER_H
/* libdivecomputer */
#include <device.h>
#include <suunto.h>
#include <reefnet.h>
#include <uwatec.h>
#include <oceanic.h>
#include <mares.h>
#include <hw.h>
#include <cressi.h>
#include <zeagle.h>
#include <atomics.h>
#include <utils.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#include <libdivecomputer/utils.h>
/* handling uemis Zurich SDA files */
#include "uemis.h"
@ -20,20 +12,13 @@
/* don't forget to include the UI toolkit specific display-XXX.h first
to get the definition of progressbar_t */
typedef struct device_data_t {
device_type_t type;
const char *name, *devname;
dc_descriptor_t *descriptor;
const char *vendor, *product, *devname;
dc_device_t *device;
progressbar_t progress;
device_devinfo_t devinfo;
device_clock_t clock;
int preexisting;
} device_data_t;
struct device_list {
const char *name;
device_type_t type;
};
extern struct device_list device_list[];
extern GError *do_import(device_data_t *data);
#endif

View file

@ -98,10 +98,9 @@ void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
gtk_widget_hide (menubar);
gtk_osxapplication_set_menu_bar(osx_app, GTK_MENU_SHELL(menubar));
sep = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Separator3");
gtk_widget_destroy(sep);
sep = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Separator2");
gtk_widget_destroy(sep);
if (sep)
gtk_widget_destroy(sep);
menu_item = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Quit");
gtk_widget_hide (menu_item);

2
main.c
View file

@ -190,7 +190,7 @@ void update_dive(struct dive *new_dive)
}
if (new_dive) {
show_dive_info(new_dive);
show_dive_equipment(new_dive);
show_dive_equipment(new_dive, W_IDX_PRIMARY);
show_dive_stats(new_dive);
}
buffered_dive = new_dive;

View file

@ -647,6 +647,7 @@ static int uddf_fill_sample(struct sample *sample, const char *name, int len, ch
return MATCH(".divetime", sampletime, &sample->time) ||
MATCH(".depth", depth, &sample->depth) ||
MATCH(".temperature", temperature, &sample->temperature) ||
MATCH(".tankpressure", pressure, &sample->cylinderpressure) ||
0;
}
@ -1099,6 +1100,10 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
return;
if (MATCH(".location", utf8_string, &dive->location))
return;
if (MATCH(".suit", utf8_string, &dive->suit))
return;
if (MATCH(".divesuit", utf8_string, &dive->suit))
return;
if (MATCH(".notes", utf8_string, &dive->notes))
return;
if (MATCH(".divemaster", utf8_string, &dive->divemaster))

18
print.c
View file

@ -196,6 +196,22 @@ static void begin_print(GtkPrintOperation *operation, gpointer user_data)
{
}
static GtkWidget *print_dialog(GtkPrintOperation *operation, gpointer user_data)
{
GtkWidget *vbox, *hbox, *label;
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);
gtk_widget_show_all(vbox);
return vbox;
}
static void print_dialog_apply(GtkPrintOperation *operation, GtkWidget *widget, gpointer user_data)
{
}
static GtkPrintSettings *settings = NULL;
void do_print(void)
@ -210,6 +226,8 @@ void do_print(void)
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,

View file

@ -1074,16 +1074,18 @@ static void fill_missing_tank_pressures(struct plot_info *pi, pr_track_t **track
/* there may be multiple segments - so
* let's assemble the length */
nlist = list;
pt = list->pressure_time;
while (!nlist->end) {
nlist = nlist->next;
if (!nlist) {
/* oops - we have no end pressure,
* so this means this is a tank without
* gas consumption information */
break;
if (list) {
pt = list->pressure_time;
while (!nlist->end) {
nlist = nlist->next;
if (!nlist) {
/* oops - we have no end pressure,
* so this means this is a tank without
* gas consumption information */
break;
}
pt += nlist->pressure_time;
}
pt += nlist->pressure_time;
}
if (!nlist) {
/* just continue without calculating
@ -1380,12 +1382,15 @@ void plot(struct graphics_context *gc, cairo_rectangle_int_t *drawing_area, stru
int nr = dive->samples;
if (!nr) {
/* The dive has no samples, so create a few fake ones. This assumes an
ascent/descent rate of 9 m/min, which is just below the limit for FAST. */
int duration = dive->duration.seconds;
int maxdepth = dive->maxdepth.mm;
int asc_desc_time = dive->maxdepth.mm*60/9000;
sample = fake;
fake[1].time.seconds = duration * 0.05;
fake[1].time.seconds = asc_desc_time;
fake[1].depth.mm = maxdepth;
fake[2].time.seconds = duration * 0.95;
fake[2].time.seconds = duration - asc_desc_time;
fake[2].depth.mm = maxdepth;
fake[3].time.seconds = duration * 1.00;
nr = 4;

View file

@ -183,6 +183,7 @@ static void save_overview(FILE *f, struct dive *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");
}
static void save_cylinder_info(FILE *f, struct dive *dive)

View file

@ -91,6 +91,18 @@ static void process_dive(struct dive *dp, stats_t *stats)
stats->max_depth.mm = dp->maxdepth.mm;
if (stats->min_depth.mm == 0 || dp->maxdepth.mm < stats->min_depth.mm)
stats->min_depth.mm = dp->maxdepth.mm;
if (dp->watertemp.mkelvin) {
if (stats->min_temp == 0 || dp->watertemp.mkelvin < stats->min_temp)
stats->min_temp = dp->watertemp.mkelvin;
if (dp->watertemp.mkelvin > stats->max_temp)
stats->max_temp = dp->watertemp.mkelvin;
stats->combined_temp += get_temp_units(dp->watertemp.mkelvin, &unit);
stats->combined_count++;
}
/* Maybe we should drop zero-duration dives */
if (!dp->duration.seconds)
return;
stats->avg_depth.mm = (1.0 * old_tt * stats->avg_depth.mm +
dp->duration.seconds * dp->meandepth.mm) / stats->total_time.seconds;
if (dp->sac > 2800) { /* less than .1 cuft/min (2800ml/min) is bogus */
@ -103,14 +115,6 @@ static void process_dive(struct dive *dp, stats_t *stats)
stats->min_sac.mliter = dp->sac;
stats->total_sac_time = sac_time;
}
if (dp->watertemp.mkelvin) {
if (stats->min_temp == 0 || dp->watertemp.mkelvin < stats->min_temp)
stats->min_temp = dp->watertemp.mkelvin;
if (dp->watertemp.mkelvin > stats->max_temp)
stats->max_temp = dp->watertemp.mkelvin;
stats->combined_temp += get_temp_units(dp->watertemp.mkelvin, &unit);
stats->combined_count++;
}
}
static void process_all_dives(struct dive *dive, struct dive **prev_dive)
@ -138,25 +142,22 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
}
}
void process_selected_dives(GList *selected_dives, GtkTreeModel *model)
/* make sure we skip the selected summary entries */
void process_selected_dives(void)
{
struct dive *dp;
unsigned int i;
GtkTreeIter iter;
GtkTreePath *path;
struct dive *dive;
unsigned int i, nr;
memset(&stats_selection, 0, sizeof(stats_selection));
stats_selection.selection_size = amount_selected;
for (i = 0; i < amount_selected; ++i) {
GValue value = {0, };
path = g_list_nth_data(selected_dives, i);
if (gtk_tree_model_get_iter(model, &iter, path)) {
gtk_tree_model_get_value(model, &iter, 0, &value);
dp = get_dive(g_value_get_int(&value));
nr = 0;
for_each_dive(i, dive) {
if (dive->selected) {
process_dive(dive, &stats_selection);
nr++;
}
process_dive(dp, &stats_selection);
}
stats_selection.selection_size = nr;
}
static void set_label(GtkWidget *w, const char *fmt, ...)
@ -266,14 +267,11 @@ static void show_single_dive_stats(struct dive *dive)
static void show_total_dive_stats(struct dive *dive)
{
double value;
int decimals;
int decimals, seconds;
const char *unit;
stats_t *stats_ptr;
if (amount_selected < 2)
stats_ptr = &stats;
else
stats_ptr = &stats_selection;
stats_ptr = &stats_selection;
set_label(stats_w.selection_size, "%d", stats_ptr->selection_size);
if (stats_ptr->min_temp) {
@ -287,7 +285,10 @@ static void show_total_dive_stats(struct dive *dive)
set_label(stats_w.max_temp, "%.1f %s", value, unit);
}
set_label(stats_w.total_time, get_time_string(stats_ptr->total_time.seconds, 0));
set_label(stats_w.avg_time, get_time_string(stats_ptr->total_time.seconds / stats_ptr->selection_size, 0));
seconds = stats_ptr->total_time.seconds;
if (stats_ptr->selection_size)
seconds /= stats_ptr->selection_size;
set_label(stats_w.avg_time, get_time_string(seconds, 0));
set_label(stats_w.longest_time, get_time_string(stats_ptr->longest_time.seconds, 0));
set_label(stats_w.shortest_time, get_time_string(stats_ptr->shortest_time.seconds, 0));
value = get_depth_units(stats_ptr->max_depth.mm, &decimals, &unit);

View file

@ -64,6 +64,21 @@
</buddy>
</xsl:if>
<xsl:if test="Equipment/Suit != ''">
<suit>
<xsl:value-of select="Equipment/Suit"/>
</suit>
<xsl:value-of select="Equipment/Suit"/>
</xsl:if>
<xsl:if test="Equipment/Weight != ''">
<weightsystem>
<xsl:attribute name="weight">
<xsl:value-of select="Equipment/Weight"/>
</xsl:attribute>
</weightsystem>
</xsl:if>
<notes>
<xsl:if test="DiveActivity != ''">
Diveactivity: <xsl:value-of select="DiveActivity"/>
@ -73,15 +88,9 @@ Divetype: <xsl:value-of select="DiveType"/>
</xsl:if>
<xsl:if test="Equipment/Visibility != ''">
Visibility: <xsl:value-of select="Equipment/Visibility"/>
</xsl:if>
<xsl:if test="Equipment/Suit != ''">
Suit: <xsl:value-of select="Equipment/Suit"/>
</xsl:if>
<xsl:if test="Equipment/Gloves != ''">
Gloves: <xsl:value-of select="Equipment/Gloves"/>
</xsl:if>
<xsl:if test="Equipment/Weight != ''">
Weight: <xsl:value-of select="Equipment/Weight"/>
</xsl:if>
<xsl:if test="Comment != ''">
Comment: <xsl:value-of select="Comment"/>