summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--display-gtk.h2
-rw-r--r--display.h7
-rw-r--r--dive.c9
-rw-r--r--dive.h71
-rw-r--r--divelist.c514
-rw-r--r--dives/test21.xml9
-rw-r--r--dives/test22.xml9
-rw-r--r--dives/test23.xml9
-rw-r--r--equipment.c19
-rw-r--r--file.c7
-rw-r--r--gtk-gui.c84
-rw-r--r--info.c79
-rw-r--r--libdivecomputer.c54
-rw-r--r--libdivecomputer.h2
-rw-r--r--main.c6
-rw-r--r--parse-xml.c66
-rw-r--r--print.c322
-rw-r--r--profile.c65
-rw-r--r--save-xml.c53
-rw-r--r--statistics.c23
-rw-r--r--uemis.h2
21 files changed, 1029 insertions, 383 deletions
diff --git a/display-gtk.h b/display-gtk.h
index 1f143077e..dd7e2c4a0 100644
--- a/display-gtk.h
+++ b/display-gtk.h
@@ -77,7 +77,7 @@ extern GtkWidget *dive_list_create(void);
unsigned int amount_selected;
-extern void process_selected_dives(GList *, int *, GtkTreeModel *);
+extern void process_selected_dives(void);
typedef void (*data_func_t)(GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
diff --git a/display.h b/display.h
index 341b1616c..ef52e99f5 100644
--- a/display.h
+++ b/display.h
@@ -26,4 +26,11 @@ extern void plot(struct graphics_context *gc, cairo_rectangle_int_t *drawing_are
extern void init_profile_background(struct graphics_context *gc);
extern void attach_tooltip(int x, int y, int w, int h, const char *text);
+struct options {
+ enum { PRETTY, TABLE } type;
+ int print_selected;
+};
+
+extern char zoomed_plot;
+
#endif
diff --git a/dive.c b/dive.c
index aee09d53a..54784cdba 100644
--- a/dive.c
+++ b/dive.c
@@ -478,11 +478,12 @@ struct dive *fixup_dive(struct dive *dive)
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) {
+ /* Some sanity checks against insane dives */
+ if (duration < 2)
duration = 2;
- asc_desc_time = 1;
- }
+ if (asc_desc_time * 2 >= duration)
+ asc_desc_time = duration/2;
+
dive->meandepth.mm = depth*(duration-asc_desc_time)/duration;
return dive;
}
diff --git a/dive.h b/dive.h
index 41f427a2c..dffe75325 100644
--- a/dive.h
+++ b/dive.h
@@ -95,6 +95,7 @@ typedef struct {
extern gboolean cylinder_none(void *_data);
extern gboolean no_cylinders(cylinder_t *cyl);
extern gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2);
+extern void copy_cylinders(cylinder_t *cyl1, cylinder_t *cyl2);
extern gboolean no_weightsystems(weightsystem_t *ws);
extern gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
@@ -234,8 +235,13 @@ struct event {
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
+typedef enum { TF_NONE, NO_TRIP, IN_TRIP, NUM_TRIPFLAGS } tripflag_t;
+extern const char *tripflag_names[NUM_TRIPFLAGS];
+
struct dive {
int number;
+ tripflag_t tripflag;
+ int selected;
time_t when;
char *location;
char *notes;
@@ -255,6 +261,58 @@ struct dive {
struct sample sample[];
};
+extern GList *dive_trip_list;
+extern gboolean autogroup;
+/* random threashold: three days without diving -> new trip
+ * this works very well for people who usually dive as part of a trip and don't
+ * regularly dive at a local facility; this is why trips are an optional feature */
+#define TRIP_THRESHOLD 3600*24*3
+
+#define UNGROUPED_DIVE(_dive) ((_dive)->tripflag == NO_TRIP)
+#define DIVE_IN_TRIP(_dive) ((_dive)->tripflag == IN_TRIP)
+#define NEXT_TRIP(_entry, _list) ((_entry) ? g_list_next(_entry) : (_list))
+#define PREV_TRIP(_entry, _list) ((_entry) ? g_list_previous(_entry) : g_list_last(_list))
+#define DIVE_TRIP(_trip) ((struct dive *)(_trip)->data)
+#define DIVE_FITS_TRIP(_dive, _dive_trip) ((_dive_trip)->when - TRIP_THRESHOLD <= (_dive)->when)
+
+static inline int dive_date_cmp(gconstpointer _a, gconstpointer _b) {
+ return ((struct dive *)(_a))->when - ((struct dive *)(_b))->when;
+}
+
+#define FIND_TRIP(_trip, _list) g_list_find_custom((_list), (_trip), dive_date_cmp)
+
+#ifdef DEBUG_TRIP
+static void dump_trip_list(void)
+{
+ GList *p = NULL;
+ int i=0;
+ while ((p = NEXT_TRIP(p, dive_trip_list))) {
+ struct tm *tm = gmtime(&DIVE_TRIP(p)->when);
+ printf("trip %d to \"%s\" on %04u-%02u-%02u\n", ++i, DIVE_TRIP(p)->location,
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday);
+ }
+ printf("-----\n");
+}
+#endif
+
+/* insert the trip into the list - but ensure you don't have two trips
+ * for the same date; but if you have, make sure you don't keep the
+ * one with less information */
+static inline GList *insert_trip(struct dive *_trip, GList *_list)
+{
+ GList *result = FIND_TRIP(_trip, _list);
+ if (result) {
+ if (! DIVE_TRIP(result)->location)
+ DIVE_TRIP(result)->location = _trip->location;
+ } else {
+ result = g_list_insert_sorted((_list), (_trip), dive_date_cmp);
+ }
+#ifdef DEBUG_TRIP
+ dump_trip_list();
+#endif
+ return result;
+}
+
/*
* We keep our internal data in well-specified units, but
* the input and output may come in some random format. This
@@ -284,7 +342,6 @@ struct dive_table {
extern struct dive_table dive_table;
-extern int *selectiontracker;
extern int selected_dive;
#define current_dive (get_dive(selected_dive))
@@ -295,6 +352,16 @@ static inline struct dive *get_dive(unsigned int nr)
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);
@@ -355,7 +422,7 @@ extern void evn_foreach(void (*callback)(const char *, int *, void *), void *dat
extern int add_new_dive(struct dive *dive);
extern int edit_dive_info(struct dive *dive);
-extern int edit_multi_dive_info(int nr, int *indices);
+extern int edit_multi_dive_info(struct dive *single_dive);
extern void dive_list_update_dives(void);
extern void flush_divelist(struct dive *dive);
diff --git a/divelist.c b/divelist.c
index 0cb03f326..1e1d1c921 100644
--- a/divelist.c
+++ b/divelist.c
@@ -31,6 +31,10 @@ struct DiveList {
};
static struct DiveList dive_list;
+GList *dive_trip_list;
+gboolean autogroup = FALSE;
+
+const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP" };
/*
* The dive list has the dive data in both string format (for showing)
@@ -54,19 +58,22 @@ enum {
DIVELIST_COLUMNS
};
-/* magic numbers that indicate (as negative values) model entries that
- * are summary entries for a divetrip */
-#define NEW_TRIP 1
-
#ifdef DEBUG_MODEL
static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
char *location;
- int idx, nr, rating, depth;
+ int idx, nr, duration;
+ struct dive *dive;
+
+ gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1);
+ printf("entry #%d : nr %d duration %d location %s ", idx, nr, duration, location);
+ dive = get_dive(idx);
+ if (dive)
+ printf("tripflag %d\n", dive->tripflag);
+ else
+ printf("without matching dive\n");
- gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_RATING, &rating, DIVE_DEPTH, &depth, DIVE_LOCATION, &location, -1);
- printf("entry #%d : nr %d rating %d depth %d location %s \n", idx, nr, rating, depth, location);
free(location);
return FALSE;
@@ -78,84 +85,20 @@ static void dump_model(GtkListStore *store)
}
#endif
-static GList *selected_dives;
-static int st_size = 0;
-
-gboolean is_in_st(int idx, int *atpos)
-{
- int i;
-
- for (i = 0; i < amount_selected; i++)
- if (selectiontracker[i] == idx) {
- if (atpos)
- *atpos = i;
- return TRUE;
- }
- return FALSE;
-}
-
#if DEBUG_SELECTION_TRACKING
void dump_selection(void)
{
int i;
+ struct dive *dive;
- printf("currently selected are ");
- for (i = 0; i < amount_selected; i++)
- printf("%d ", selectiontracker[i]);
- printf("\n");
-}
-#endif
-
-void track_select(int idx)
-{
- if (idx < 0)
- return;
-
-#if DEBUG_SELECTION_TRACKING
- printf("add %d to selection of %d entries\n", idx, amount_selected);
-#endif
- if (is_in_st(idx, NULL))
- return;
- if (amount_selected >= st_size) {
- selectiontracker = realloc(selectiontracker, dive_table.nr * sizeof(int));
- st_size = dive_table.nr;
+ printf("currently selected are %d dives:", amount_selected);
+ for_each_dive(i, dive) {
+ if (dive->selected)
+ printf(" %d", i);
}
- selectiontracker[amount_selected] = idx;
- amount_selected++;
- if (amount_selected == 1)
- selected_dive = idx;
-#if DEBUG_SELECTION_TRACKING
- printf("increased amount_selected to %d\n", amount_selected);
- dump_selection();
-#endif
+ printf("\n");
}
-
-void track_unselect(int idx)
-{
- if (idx < 0)
- return;
-
-#if DEBUG_SELECTION_TRACKING
- printf("remove %d from selection of %d entries\n", idx, amount_selected);
-#endif
- int atpos;
-
- if (! is_in_st(idx, &atpos))
- return;
- memmove(selectiontracker + atpos,
- selectiontracker + atpos + 1,
- (amount_selected - atpos - 1) * sizeof(int));
- amount_selected--;
-#if DEBUG_SELECTION_TRACKING
- printf("removed %d at pos %d and decreased amount_selected to %d\n", idx, atpos, amount_selected);
- dump_selection();
#endif
-}
-
-void clear_tracker(void)
-{
- amount_selected = 0;
-}
/* when subsurface starts we want to have the last dive selected. So we simply
walk to the first leaf (and skip the summary entries - which have negative
@@ -177,97 +120,171 @@ static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx)
}
}
-/* if we click on a summary dive, we actually want to select / unselect
- all the dives "below" it */
-static void select_children(GtkTreeModel *model, GtkTreeSelection * selection,
- GtkTreeIter *iter, gboolean was_selected)
+/* make sure that if we expand a summary row that is selected, the children show
+ up as selected, too */
+void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
{
- int i, nr_children;
- gboolean expanded = FALSE;
- GtkTreeIter parent;
- GtkTreePath *tpath;
+ GtkTreeIter child;
+ GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
- memcpy(&parent, iter, sizeof(parent));
-
- tpath = gtk_tree_model_get_path(model, &parent);
- expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath);
- nr_children = gtk_tree_model_iter_n_children(model, &parent);
- for (i = 0; i < nr_children; i++) {
- gtk_tree_model_iter_nth_child(model, iter, &parent, i);
-
- /* if the parent is expanded, just (un)select the children and we'll
- track their selection status in the callback
- otherwise just change the selection status directly without
- bothering gtk */
- if (expanded) {
- if (was_selected)
- gtk_tree_selection_unselect_iter(selection, iter);
- else
- gtk_tree_selection_select_iter(selection, iter);
- } else {
- int idx;
- gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
- if (was_selected)
- track_unselect(idx);
- else
- track_select(idx);
- }
- }
+ if (!gtk_tree_model_iter_children(model, &child, iter))
+ return;
+
+ do {
+ int idx;
+ struct dive *dive;
+
+ gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1);
+ dive = get_dive(idx);
+
+ if (dive->selected)
+ gtk_tree_selection_select_iter(selection, &child);
+ else
+ gtk_tree_selection_unselect_iter(selection, &child);
+ } while (gtk_tree_model_iter_next(model, &child));
}
-/* make sure that if we expand a summary row that is selected, the children show
- up as selected, too */
-void row_expanded_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
+static int selected_children(GtkTreeModel *model, GtkTreeIter *iter)
{
+ GtkTreeIter child;
+
+ if (!gtk_tree_model_iter_children(model, &child, iter))
+ return FALSE;
+
+ do {
+ int idx;
+ struct dive *dive;
+
+ gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1);
+ dive = get_dive(idx);
+
+ if (dive->selected)
+ return TRUE;
+ } while (gtk_tree_model_iter_next(model, &child));
+ return FALSE;
+}
+
+/* Make sure that if we collapse a summary row with any selected children, the row
+ shows up as selected too */
+void row_collapsed_cb(GtkTreeView *tree_view, GtkTreeIter *iter, GtkTreePath *path, gpointer data)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
- if (gtk_tree_selection_path_is_selected(selection, path))
- select_children(GTK_TREE_MODEL(dive_list.model), selection, iter, FALSE);
+ if (selected_children(model, iter))
+ gtk_tree_selection_select_iter(selection, iter);
}
-/* this is called _before_ the selection is changed, for every single entry;
- * we simply have it call down the tree to make sure that summary items toggle
- * their children */
+static GList *selection_changed = NULL;
+
+/*
+ * This is called _before_ the selection is changed, for every single entry;
+ *
+ * We simply create a list of all changed entries, and make sure that the
+ * group entries go at the end of the list.
+ */
gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model,
GtkTreePath *path, gboolean was_selected, gpointer userdata)
{
- GtkTreeIter iter;
- int dive_idx;
+ GtkTreeIter iter, *p;
- /* if gtk thinks nothing is selected we should clear out our
- tracker as well - otherwise hidden selected rows can stay
- "stuck". The down side is that we now have a different bug:
- If you select a dive, collapse the dive trip and ctrl-click
- another dive trip, the initial dive is no longer selected.
- Just don't do that, ok? */
- if (gtk_tree_selection_count_selected_rows(selection) == 0)
- clear_tracker();
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ return TRUE;
- if (gtk_tree_model_get_iter(model, &iter, path)) {
- gtk_tree_model_get(model, &iter, DIVE_INDEX, &dive_idx, -1);
- /* turns out we need to move the selectiontracker here */
+ /* Add the group entries to the end */
+ p = gtk_tree_iter_copy(&iter);
+ if (gtk_tree_model_iter_has_child(model, p))
+ selection_changed = g_list_append(selection_changed, p);
+ else
+ selection_changed = g_list_prepend(selection_changed, p);
+ return TRUE;
+}
-#if DEBUG_SELECTION_TRACKING
- printf("modify_selection_cb with idx %d (according to gtk was %sselected)\n",
- dive_idx, was_selected ? "" : "un");
-#endif
- if (dive_idx >= 0) {
- if (was_selected)
- track_unselect(dive_idx);
- else
- track_select(dive_idx);
- } else {
- select_children(model, selection, &iter, was_selected);
- }
+static void select_dive(struct dive *dive, int selected)
+{
+ if (dive->selected != selected) {
+ amount_selected += selected ? 1 : -1;
+ dive->selected = selected;
}
- /* allow this selection to proceed */
- return TRUE;
+}
+
+/*
+ * This gets called when a dive group has changed selection.
+ */
+static void select_dive_group(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter, int selected)
+{
+ int first = 1;
+ GtkTreeIter child;
+
+ if (selected == selected_children(model, iter))
+ return;
+
+ if (!gtk_tree_model_iter_children(model, &child, iter))
+ return;
+
+ do {
+ int idx;
+ struct dive *dive;
+
+ gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1);
+ if (first && selected)
+ selected_dive = idx;
+ first = 0;
+ dive = get_dive(idx);
+ if (dive->selected == selected)
+ continue;
+
+ select_dive(dive, selected);
+ if (selected)
+ gtk_tree_selection_select_iter(selection, &child);
+ else
+ gtk_tree_selection_unselect_iter(selection, &child);
+ } while (gtk_tree_model_iter_next(model, &child));
+}
+
+/*
+ * This gets called _after_ the selections have changed, for each entry that
+ * may have changed. Check if the gtk selection state matches our internal
+ * selection state to verify.
+ *
+ * The group entries are at the end, this guarantees that we have handled
+ * all the dives before we handle groups.
+ */
+static void check_selection_cb(GtkTreeIter *iter, GtkTreeSelection *selection)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
+ struct dive *dive;
+ int idx, gtk_selected;
+
+ gtk_tree_model_get(model, iter,
+ DIVE_INDEX, &idx,
+ -1);
+ dive = get_dive(idx);
+ gtk_selected = gtk_tree_selection_iter_is_selected(selection, iter);
+ if (idx < 0)
+ select_dive_group(model, selection, iter, gtk_selected);
+ else {
+ select_dive(dive, gtk_selected);
+ if (gtk_selected)
+ selected_dive = idx;
+ }
+ gtk_tree_iter_free(iter);
}
/* this is called when gtk thinks that the selection has changed */
-static void selection_cb(GtkTreeSelection *selection, gpointer userdata)
+static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
{
- process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model));
+ GList *changed = selection_changed;
+
+ selection_changed = NULL;
+ g_list_foreach(changed, (GFunc) check_selection_cb, selection);
+ g_list_free(changed);
+#if DEBUG_SELECTION_TRACKING
+ dump_selection();
+#endif
+
+ process_selected_dives();
repaint_dive();
}
@@ -311,22 +328,20 @@ static void date_data_func(GtkTreeViewColumn *col,
time_t when;
char buffer[40];
- gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, DIVE_NR, &nr, -1);
-
+ gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1);
+ nr = gtk_tree_model_iter_n_children(model, iter);
/* 2038 problem */
when = val;
tm = gmtime(&when);
- switch(idx) {
- case -NEW_TRIP:
+ if (idx < 0) {
snprintf(buffer, sizeof(buffer),
"Trip %s, %s %d, %d (%d dive%s)",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
nr, nr > 1 ? "s" : "");
- break;
- default:
+ } else {
snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %02d:%02d",
weekday(tm->tm_wday),
@@ -867,75 +882,97 @@ void update_dive_list_col_visibility(void)
return;
}
-/* random heuristic - not diving in three days implies new dive trip */
-#define TRIP_THRESHOLD 3600*24*3
-static int new_group(struct dive *dive, struct dive **last_dive, time_t *tm_date)
-{
- if (!last_dive)
- return TRUE;
- if (*last_dive) {
- struct dive *ldive = *last_dive;
- if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) {
- *last_dive = dive;
- return FALSE;
- }
- }
- *last_dive = dive;
- if (tm_date) {
- struct tm *tm1 = gmtime(&dive->when);
- tm1->tm_sec = 0;
- tm1->tm_min = 0;
- tm1->tm_hour = 0;
- *tm_date = mktime(tm1);
- }
- return TRUE;
-}
-
static void fill_dive_list(void)
{
- int i, group_size;
- GtkTreeIter iter, parent_iter;
+ int i;
+ GtkTreeIter iter, parent_iter, *parent_ptr = NULL;
GtkTreeStore *liststore, *treestore;
- struct dive *last_dive = NULL;
- struct dive *last_trip_dive = NULL;
- const char *last_location = NULL;
- time_t dive_date;
+ struct dive *last_trip = NULL;
+ GList *trip;
+ struct dive *dive_trip = NULL;
+
+ /* if we have pre-existing trips, start on the last one */
+ trip = g_list_last(dive_trip_list);
treestore = GTK_TREE_STORE(dive_list.treemodel);
liststore = GTK_TREE_STORE(dive_list.listmodel);
i = dive_table.nr;
while (--i >= 0) {
- struct dive *dive = dive_table.dives[i];
-
- if (new_group(dive, &last_dive, &dive_date))
- {
- /* make sure we display the first date of the trip in previous summary */
- if (last_trip_dive)
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_NR, group_size,
- DIVE_DATE, last_trip_dive->when,
- DIVE_LOCATION, last_location,
- -1);
+ struct dive *dive = get_dive(i);
- gtk_tree_store_append(treestore, &parent_iter, NULL);
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_INDEX, -NEW_TRIP,
- DIVE_NR, 1,
- DIVE_TEMPERATURE, 0,
- DIVE_SAC, 0,
+ /* make sure we display the first date of the trip in previous summary */
+ if (dive_trip && parent_ptr) {
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
-1);
-
- group_size = 0;
- /* This might be NULL */
- last_location = dive->location;
}
- group_size++;
- last_trip_dive = dive;
- if (dive->location)
- last_location = dive->location;
+ /* the dive_trip info might have been killed by a previous UNGROUPED dive */
+ if (trip)
+ dive_trip = DIVE_TRIP(trip);
+ /* tripflag defines how dives are handled;
+ * TF_NONE "not handled yet" - create time based group if autogroup == TRUE
+ * NO_TRIP "set as no group" - simply leave at top level
+ * IN_TRIP "use the trip with the largest trip time (when) that is <= this dive"
+ */
+ if (UNGROUPED_DIVE(dive)) {
+ /* first dives that go to the top level */
+ parent_ptr = NULL;
+ dive_trip = NULL;
+ } else if (autogroup && !DIVE_IN_TRIP(dive)) {
+ if ( ! dive_trip || ! DIVE_FITS_TRIP(dive, dive_trip)) {
+ /* allocate new trip - all fields default to 0
+ and get filled in further down */
+ dive_trip = alloc_dive();
+ dive_trip_list = insert_trip(dive_trip, dive_trip_list);
+ trip = FIND_TRIP(dive_trip, dive_trip_list);
+ }
+ } else { /* either the dive has a trip or we aren't creating trips */
+ if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) {
+ GList *last_trip = trip;
+ trip = PREV_TRIP(trip, dive_trip_list);
+ if (! (trip && DIVE_FITS_TRIP(dive, DIVE_TRIP(trip)))) {
+ /* we could get here if there are no trips in the XML file
+ * and we aren't creating trips, either.
+ * Otherwise we need to create a new trip */
+ if (autogroup) {
+ dive_trip = alloc_dive();
+ dive_trip_list = insert_trip(dive_trip, dive_trip_list);
+ trip = FIND_TRIP(dive_trip, dive_trip_list);
+ } else {
+ /* let's go back to the last valid trip */
+ trip = last_trip;
+ }
+ } else {
+ dive_trip = trip->data;
+ }
+ }
+ }
+ /* update dive_trip time and (if necessary) location */
+ if (dive_trip) {
+ dive->tripflag = IN_TRIP;
+ dive_trip->when = dive->when;
+ if (!dive_trip->location && dive->location)
+ dive_trip->location = dive->location;
+ if (dive_trip != last_trip) {
+ last_trip = dive_trip;
+ /* create trip entry */
+ gtk_tree_store_append(treestore, &parent_iter, NULL);
+ parent_ptr = &parent_iter;
+ /* a duration of 0 (and negative index) identifies a group */
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_INDEX, -1,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
+ DIVE_DURATION, 0,
+ -1);
+ }
+ }
+
+ /* store dive */
update_cylinder_related_info(dive);
- gtk_tree_store_append(treestore, &iter, &parent_iter);
+ gtk_tree_store_append(treestore, &iter, parent_ptr);
gtk_tree_store_set(treestore, &iter,
DIVE_INDEX, i,
DIVE_NR, dive->number,
@@ -964,13 +1001,11 @@ static void fill_dive_list(void)
}
/* make sure we display the first date of the trip in previous summary */
- if (last_trip_dive)
- gtk_tree_store_set(treestore, &parent_iter,
- DIVE_NR, group_size,
- DIVE_DATE, last_trip_dive->when,
- DIVE_LOCATION, last_location,
+ if (parent_ptr && dive_trip)
+ gtk_tree_store_set(treestore, parent_ptr,
+ DIVE_DATE, dive_trip->when,
+ DIVE_LOCATION, dive_trip->location,
-1);
-
update_dive_list_units();
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) {
GtkTreeSelection *selection;
@@ -1098,7 +1133,7 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data)
void edit_dive_cb(GtkWidget *menuitem, gpointer data)
{
- edit_multi_dive_info(amount_selected, selectiontracker);
+ edit_multi_dive_info(NULL);
}
static void expand_all_cb(GtkWidget *menuitem, GtkTreeView *tree_view)
@@ -1161,8 +1196,6 @@ static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpoi
when gtk_tree_selection_select_path is called. We also need to
keep copies of the sort order so we can restore that as well after
switching models. */
-static int *oldselection;
-static int old_nr_selected;
static gboolean second_call = FALSE;
static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, };
static int lastcol = DIVE_DATE;
@@ -1170,20 +1203,27 @@ static int lastcol = DIVE_DATE;
/* Check if this dive was selected previously and select it again in the new model;
* This is used after we switch models to maintain consistent selections.
* We always return FALSE to iterate through all dives */
-static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path,
+static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
- int i, idx;
GtkTreeSelection *selection = GTK_TREE_SELECTION(data);
+ int idx, selected;
+ struct dive *dive;
- gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
- for (i = 0; i < old_nr_selected; i++)
- if (oldselection[i] == idx) {
- gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path);
- gtk_tree_selection_select_path(selection, path);
-
- return FALSE;
- }
+ gtk_tree_model_get(model, iter,
+ DIVE_INDEX, &idx,
+ -1);
+ if (idx < 0) {
+ GtkTreeIter child;
+ if (gtk_tree_model_iter_children(model, &child, iter))
+ gtk_tree_model_get(model, &child, DIVE_INDEX, &idx, -1);
+ }
+ dive = get_dive(idx);
+ selected = dive && dive->selected;
+ if (selected) {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path);
+ gtk_tree_selection_select_path(selection, path);
+ }
return FALSE;
}
@@ -1236,20 +1276,9 @@ static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data)
if (dive_list.model != currentmodel) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
- /* remember what is currently selected, switch models and reselect the selected rows */
- old_nr_selected = amount_selected;
- oldselection = malloc(old_nr_selected * sizeof(int));
- if (amount_selected)
- memcpy(oldselection, selectiontracker, amount_selected * sizeof(int));
gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model));
-
update_column_and_order(colid);
-
- if (old_nr_selected) {
- /* we need to select all the dives that were selected */
- /* this is fundamentally an n^2 algorithm as implemented - YUCK */
- gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), select_selected, selection);
- }
+ gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), set_selected, selection);
} else {
if (order != sortorder[colid]) {
update_column_and_order(colid);
@@ -1326,9 +1355,10 @@ GtkWidget *dive_list_create(void)
g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL);
g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL);
g_signal_connect(dive_list.tree_view, "row-expanded", G_CALLBACK(row_expanded_cb), NULL);
+ g_signal_connect(dive_list.tree_view, "row-collapsed", G_CALLBACK(row_collapsed_cb), NULL);
g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL);
g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL);
- g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), NULL);
+ g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model);
g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
diff --git a/dives/test21.xml b/dives/test21.xml
new file mode 100644
index 000000000..5f5f6c981
--- /dev/null
+++ b/dives/test21.xml
@@ -0,0 +1,9 @@
+<dives>
+<program name='subsurface' version='1'></program>
+<trip date='2011-12-02' />
+<dive number='20' tripflag='INTRIP' date='2011-12-02' time='14:00:00' duration='30:00 min'>
+ <depth max='20.0 m' mean='15.0 m' />
+ <location>20th test dive - this should be in a trip with same location</location>
+ <notes>We are testing that the location of the dive is picked up in the trip if the trip has no location</notes>
+</dive>
+</dives>
diff --git a/dives/test22.xml b/dives/test22.xml
new file mode 100644
index 000000000..a61032327
--- /dev/null
+++ b/dives/test22.xml
@@ -0,0 +1,9 @@
+<dives>
+<program name='subsurface' version='1'></program>
+<trip date='2011-12-02' location='trip location' />
+<dive number='21' tripflag='INTRIP' date='2011-12-02' time='15:00:00' duration='30:00 min'>
+ <depth max='20.0 m' mean='15.0 m' />
+ <location>21st test dive - this should be in a trip with a trip location</location>
+ <notes>We are testing that the location of the dive is not picked up in the trip if the trip has a location</notes>
+</dive>
+</dives>
diff --git a/dives/test23.xml b/dives/test23.xml
new file mode 100644
index 000000000..c61ad2db9
--- /dev/null
+++ b/dives/test23.xml
@@ -0,0 +1,9 @@
+<dives>
+<program name='subsurface' version='1'></program>
+<trip date='2011-12-02' location='trip location' />
+<dive number='22' tripflag='NOTRIP' date='2011-12-09' time='6:00:00' duration='30:00 min'>
+ <depth max='20.0 m' mean='15.0 m' />
+ <location>22nd test dive - this should not be in a trip</location>
+ <notes>We are testing that the NOTRIP flag works</notes>
+</dive>
+</dives>
diff --git a/equipment.c b/equipment.c
index 43bb29d59..d676fc05d 100644
--- a/equipment.c
+++ b/equipment.c
@@ -461,13 +461,11 @@ gboolean description_equal(const char *desc1, const char *desc2)
}
/* when checking for the same cylinder we want the size and description to match
- but don't compare the start and end pressures */
+ but don't compare the start and end pressures, nor the Nitrox/He values */
static gboolean one_cylinder_equal(cylinder_t *cyl1, cylinder_t *cyl2)
{
return cyl1->type.size.mliter == cyl2->type.size.mliter &&
cyl1->type.workingpressure.mbar == cyl2->type.workingpressure.mbar &&
- cyl1->gasmix.o2.permille == cyl2->gasmix.o2.permille &&
- cyl1->gasmix.he.permille == cyl2->gasmix.he.permille &&
description_equal(cyl1->type.description, cyl2->type.description);
}
@@ -481,6 +479,21 @@ gboolean cylinders_equal(cylinder_t *cyl1, cylinder_t *cyl2)
return TRUE;
}
+/* copy size and description of all cylinders from cyl1 to cyl2 */
+void copy_cylinders(cylinder_t *cyl1, cylinder_t *cyl2)
+{
+ int i;
+
+ for (i = 0; i < MAX_CYLINDERS; i++) {
+ cyl2[i].type.size.mliter = cyl1[i].type.size.mliter;
+ cyl2[i].type.workingpressure.mbar = cyl1[i].type.workingpressure.mbar;
+ if (cyl1[i].type.description)
+ cyl2[i].type.description = strdup(cyl1[i].type.description);
+ else
+ cyl2[i].type.description = NULL;
+ }
+}
+
static gboolean weightsystem_none(void *_data)
{
weightsystem_t *ws = _data;
diff --git a/file.c b/file.c
index e0163909e..da498995c 100644
--- a/file.c
+++ b/file.c
@@ -8,6 +8,11 @@
#include "dive.h"
#include "file.h"
+/* Crazy windows sh*t */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
static int readfile(const char *filename, struct memblock *mem)
{
int ret, fd;
@@ -17,7 +22,7 @@ static int readfile(const char *filename, struct memblock *mem)
mem->buffer = NULL;
mem->size = 0;
- fd = open(filename, O_RDONLY);
+ fd = open(filename, O_RDONLY | O_BINARY);
if (fd < 0)
return fd;
ret = fstat(fd, &st);
diff --git a/gtk-gui.c b/gtk-gui.c
index 306e1a5e7..7db777ab9 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -98,7 +98,7 @@ void report_error(GError* error)
{
return;
}
-
+
if (error_info_bar == NULL)
{
error_count = 1;
@@ -108,11 +108,11 @@ void report_error(GError* error)
g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar),
GTK_MESSAGE_ERROR);
-
+
error_label = gtk_label_new(error->message);
GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar));
gtk_container_add(GTK_CONTAINER(container), error_label);
-
+
gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0);
gtk_widget_show_all(main_vbox);
}
@@ -151,7 +151,7 @@ static void file_open(GtkWidget *w, gpointer data)
GSList *filenames, *fn_glist;
char *filename;
filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
-
+
GError *error = NULL;
while(filenames != NULL) {
filename = filenames->data;
@@ -162,7 +162,7 @@ static void file_open(GtkWidget *w, gpointer data)
g_error_free(error);
error = NULL;
}
-
+
g_free(filename);
filenames = g_slist_next(filenames);
}
@@ -172,10 +172,43 @@ static void file_open(GtkWidget *w, gpointer data)
gtk_widget_destroy(dialog);
}
+/* return the path and the file component contained in the full path */
+static char *path_and_file(char *pathin, char **fileout) {
+ char *slash = pathin, *next;
+ char *result;
+ size_t len, n;
+
+ if (! pathin) {
+ *fileout = strdup("");
+ return strdup("");
+ }
+ while ((next = strpbrk(slash + 1, "\\/")))
+ slash = next;
+ if (pathin != slash)
+ slash++;
+ *fileout = strdup(slash);
+
+ /* strndup(pathin, slash - pathin) */
+ n = slash - pathin;
+ len = strlen(pathin);
+ if (n < len)
+ len = n;
+
+ result = (char *)malloc(len + 1);
+ if (!result)
+ return 0;
+
+ result[len] = '\0';
+ return (char *)memcpy(result, pathin, len);
+}
+
static void file_save_as(GtkWidget *w, gpointer data)
{
GtkWidget *dialog;
char *filename = NULL;
+ char *current_file;
+ char *current_dir;
+
dialog = gtk_file_chooser_dialog_new("Save File As",
GTK_WINDOW(main_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
@@ -184,7 +217,13 @@ static void file_save_as(GtkWidget *w, gpointer data)
NULL);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
- gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), existing_filename);
+ current_dir = path_and_file(existing_filename, &current_file);
+ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir);
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file);
+
+ free(current_dir);
+ free(current_file);
+
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
}
@@ -223,7 +262,7 @@ static gboolean ask_save_changes()
label = gtk_label_new (
"You have unsaved changes\nWould you like to save those before exiting the program?");
} else {
- char *label_text = (char*) malloc(sizeof(char) * (92 + strlen(existing_filename)));
+ char *label_text = (char*) malloc(sizeof(char) * (93 + strlen(existing_filename)));
sprintf(label_text,
"You have unsaved changes to file: %s \nWould you like to save those before exiting the program?",
existing_filename);
@@ -389,6 +428,7 @@ OPTIONCALLBACK(temperature_toggle, visible_cols.temperature)
OPTIONCALLBACK(totalweight_toggle, visible_cols.totalweight)
OPTIONCALLBACK(suit_toggle, visible_cols.suit)
OPTIONCALLBACK(cylinder_toggle, visible_cols.cylinder)
+OPTIONCALLBACK(autogroup_toggle, autogroup)
static void event_toggle(GtkWidget *w, gpointer _data)
{
@@ -484,8 +524,22 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
+ frame = gtk_frame_new("Divelist Font");
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
font = gtk_font_button_new_with_font(divelist_font);
- gtk_box_pack_start(GTK_BOX(vbox), font, FALSE, FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(frame),font);
+
+ frame = gtk_frame_new("Misc. Options");
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
+
+ box = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(frame), box);
+
+ button = gtk_check_button_new_with_label("Automatically group dives in trips");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), autogroup);
+ gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
+ g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(autogroup_toggle), NULL);
gtk_widget_show_all(dialog);
result = gtk_dialog_run(GTK_DIALOG(dialog));
@@ -514,6 +568,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(visible_cols.sac));
subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(visible_cols.otu));
subsurface_set_conf("divelist_font", PREF_STRING, divelist_font);
+ subsurface_set_conf("autogroup", PREF_BOOL, BOOL_TO_PTR(autogroup));
/* Flush the changes out to the system */
subsurface_flush_conf();
@@ -674,6 +729,13 @@ static void view_three(GtkWidget *w, gpointer data)
gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6);
}
+static void toggle_zoom(GtkWidget *w, gpointer data)
+{
+ zoomed_plot = (zoomed_plot)?0 : 1;
+ /*Update dive*/
+ repaint_dive();
+}
+
static GtkActionEntry menu_items[] = {
{ "FileMenuAction", NULL, "File", NULL, NULL, NULL},
{ "LogMenuAction", NULL, "Log", NULL, NULL, NULL},
@@ -694,7 +756,8 @@ static GtkActionEntry menu_items[] = {
{ "ViewList", NULL, "List", CTRLCHAR "1", NULL, G_CALLBACK(view_list) },
{ "ViewProfile", NULL, "Profile", CTRLCHAR "2", NULL, G_CALLBACK(view_profile) },
{ "ViewInfo", NULL, "Info", CTRLCHAR "3", NULL, G_CALLBACK(view_info) },
- { "ViewThree", NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) },
+ { "ViewThree", NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) },
+ { "ToggleZoom", NULL, "Toggle Zoom", CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom) },
};
static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
@@ -716,6 +779,7 @@ static const gchar* ui_string = " \
<menuitem name=\"Add Dive\" action=\"AddDive\" /> \
<separator name=\"Separator\"/> \
<menuitem name=\"Renumber\" action=\"Renumber\" /> \
+ <menuitem name=\"Toggle Zoom\" action=\"ToggleZoom\" /> \
<menu name=\"View\" action=\"ViewMenuAction\"> \
<menuitem name=\"List\" action=\"ViewList\" /> \
<menuitem name=\"Profile\" action=\"ViewProfile\" /> \
@@ -794,6 +858,8 @@ void init_ui(int *argcp, char ***argvp)
divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
+ autogroup = PTR_TO_BOOL(subsurface_get_conf("autogroup", PREF_BOOL));
+
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
diff --git a/info.c b/info.c
index adc6c5902..d9379ac68 100644
--- a/info.c
+++ b/info.c
@@ -1,7 +1,7 @@
/* info.c */
-/* creates the UI for the info frame -
+/* creates the UI for the info frame -
* controlled through the following interfaces:
- *
+ *
* void show_dive_info(struct dive *dive)
*
* called from gtk-ui:
@@ -166,7 +166,7 @@ static int delete_dive_info(struct dive *dive)
static void info_menu_edit_cb(GtkMenuItem *menuitem, gpointer user_data)
{
- edit_multi_dive_info(amount_selected, selectiontracker);
+ edit_multi_dive_info(NULL);
}
static void info_menu_delete_cb(GtkMenuItem *menuitem, gpointer user_data)
@@ -482,22 +482,22 @@ void update_equipment_data(struct dive *dive, struct dive *master)
if ( ! cylinders_equal(remember_cyl, master->cylinder) &&
(no_cylinders(dive->cylinder) ||
cylinders_equal(dive->cylinder, remember_cyl)))
- memcpy(dive->cylinder, master->cylinder, CYL_BYTES);
+ copy_cylinders(master->cylinder, dive->cylinder);
if (! weightsystems_equal(remember_ws, master->weightsystem) &&
(no_weightsystems(dive->weightsystem) ||
weightsystems_equal(dive->weightsystem, remember_ws)))
memcpy(dive->weightsystem, master->weightsystem, WS_BYTES);
}
-int edit_multi_dive_info(int nr, int *indices)
+/* A negative index means "all selected" */
+int edit_multi_dive_info(struct dive *single_dive)
{
- int success, i;
+ int success;
GtkWidget *dialog, *vbox;
struct dive_info info;
struct dive *master;
+ gboolean multi;
- if (!nr)
- return 0;
dialog = gtk_dialog_new_with_buttons("Dive Info",
GTK_WINDOW(main_window),
GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -506,34 +506,46 @@ int edit_multi_dive_info(int nr, int *indices)
NULL);
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- /* SCARY STUFF - IS THIS THE BEST WAY TO DO THIS???
- *
- * current_dive is one of our selected dives - and that is
- * the one that is used to pre-fill the edit widget. Its
- * data is used as the starting point for all selected dives
- * I think it would be better to somehow collect and combine
- * info from all the selected dives */
- master = current_dive;
- dive_info_widget(vbox, master, &info, (nr > 1));
+ master = single_dive;
+ if (!master)
+ master = current_dive;
+
+ /* See if we should use multi dive mode */
+ multi = FALSE;
+ if (!single_dive) {
+ int i;
+ struct dive *dive;
+
+ for_each_dive(i, dive) {
+ if (dive != master && dive->selected) {
+ multi = TRUE;
+ break;
+ }
+ }
+ }
+
+ dive_info_widget(vbox, master, &info, multi);
show_dive_equipment(master, W_IDX_SECONDARY);
save_equipment_data(master);
gtk_widget_show_all(dialog);
success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT;
if (success) {
- /* Update the other non-current dives first */
- for (i = 0; i < nr; i++) {
- int idx = indices[i];
- struct dive *dive = get_dive(idx);
-
- if (!dive || dive == master)
- 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 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! */
@@ -548,12 +560,9 @@ int edit_multi_dive_info(int nr, int *indices)
int edit_dive_info(struct dive *dive)
{
- int idx;
-
if (!dive)
return 0;
- idx = dive->number;
- return edit_multi_dive_info(1, &idx);
+ return edit_multi_dive_info(dive);
}
static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...)
diff --git a/libdivecomputer.c b/libdivecomputer.c
index e362d1d2b..d96d2769f 100644
--- a/libdivecomputer.c
+++ b/libdivecomputer.c
@@ -305,13 +305,6 @@ static dc_status_t import_device_data(dc_device_t *device, device_data_t *device
return dc_device_foreach(device, dive_cb, devicedata);
}
-static dc_status_t device_open(const char *devname,
- dc_descriptor_t *descriptor,
- dc_device_t **device)
-{
- return dc_device_open(device, descriptor, devname);
-}
-
static void event_cb(dc_device_t *device, dc_event_type_t event, const void *data, void *userdata)
{
@@ -351,42 +344,53 @@ cancel_cb(void *userdata)
return import_thread_cancelled;
}
-static const char *do_libdivecomputer_import(device_data_t *data)
+static const char *do_device_import(device_data_t *data)
{
- dc_device_t *device = NULL;
dc_status_t rc;
-
- import_dive_number = 0;
- rc = device_open(data->devname, data->descriptor, &device);
- if (rc != DC_STATUS_SUCCESS)
- return "Unable to open %s %s (%s)";
- data->device = device;
+ dc_device_t *device = data->device;
// Register the event handler.
int events = DC_EVENT_WAITING | DC_EVENT_PROGRESS | DC_EVENT_DEVINFO | DC_EVENT_CLOCK;
rc = dc_device_set_events(device, events, event_cb, data);
- if (rc != DC_STATUS_SUCCESS) {
- dc_device_close(device);
+ if (rc != DC_STATUS_SUCCESS)
return "Error registering the event handler.";
- }
// Register the cancellation handler.
rc = dc_device_set_cancel(device, cancel_cb, data);
- if (rc != DC_STATUS_SUCCESS) {
- dc_device_close(device);
+ if (rc != DC_STATUS_SUCCESS)
return "Error registering the cancellation handler.";
- }
rc = import_device_data(device, data);
- if (rc != DC_STATUS_SUCCESS) {
- dc_device_close(device);
+ if (rc != DC_STATUS_SUCCESS)
return "Dive data import error";
- }
- dc_device_close(device);
+ /* All good */
return NULL;
}
+static const char *do_libdivecomputer_import(device_data_t *data)
+{
+ dc_status_t rc;
+ const char *err;
+
+ import_dive_number = 0;
+ data->device = NULL;
+ data->context = NULL;
+
+ rc = dc_context_new(&data->context);
+ if (rc != DC_STATUS_SUCCESS)
+ return "Unable to create libdivecomputer context";
+
+ err = "Unable to open %s %s (%s)";
+ rc = dc_device_open(&data->device, data->context, data->descriptor, data->devname);
+ if (rc == DC_STATUS_SUCCESS) {
+ err = do_device_import(data);
+ dc_device_close(data->device);
+ }
+ dc_context_free(data->context);
+ return err;
+}
+
static void *pthread_wrapper(void *_data)
{
device_data_t *data = _data;
diff --git a/libdivecomputer.h b/libdivecomputer.h
index 8d77a25be..6430c8448 100644
--- a/libdivecomputer.h
+++ b/libdivecomputer.h
@@ -4,7 +4,6 @@
/* libdivecomputer */
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
-#include <libdivecomputer/utils.h>
/* handling uemis Zurich SDA files */
#include "uemis.h"
@@ -15,6 +14,7 @@ typedef struct device_data_t {
dc_descriptor_t *descriptor;
const char *vendor, *product, *devname;
dc_device_t *device;
+ dc_context_t *context;
progressbar_t progress;
int preexisting;
} device_data_t;
diff --git a/main.c b/main.c
index 8e579f88c..2489473b9 100644
--- a/main.c
+++ b/main.c
@@ -172,7 +172,7 @@ static void parse_argument(const char *arg)
if (strncmp(arg, "-psn_", 5) == 0) {
return;
}
- /* fallthrough */
+ /* fallthrough */
default:
fprintf(stderr, "Bad argument '%s'\n", arg);
exit(1);
@@ -217,7 +217,7 @@ int main(int argc, char **argv)
parse_xml_init();
init_ui(&argc, &argv);
-
+
for (i = 1; i < argc; i++) {
const char *a = argv[i];
@@ -227,7 +227,7 @@ int main(int argc, char **argv)
}
GError *error = NULL;
parse_file(a, &error);
-
+
if (error != NULL)
{
report_error(error);
diff --git a/parse-xml.c b/parse-xml.c
index 173314dd4..5159a334f 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -39,6 +39,11 @@ void record_dive(struct dive *dive)
dive_table.nr = nr+1;
}
+void record_trip(struct dive *trip)
+{
+ dive_trip_list = insert_trip(trip, dive_trip_list);
+}
+
static void delete_dive_renumber(struct dive **dives, int i, int nr)
{
struct dive *dive = dives[i];
@@ -156,7 +161,7 @@ const struct units IMPERIAL_units = {
/*
* Dive info as it is being built up..
*/
-static struct dive *cur_dive;
+static struct dive *cur_dive, *cur_trip = NULL;
static struct sample *cur_sample;
static struct {
int active;
@@ -535,6 +540,17 @@ static void get_index(char *buffer, void *_i)
free(buffer);
}
+static void get_tripflag(char *buffer, void *_tf)
+{
+ tripflag_t *tf = _tf;
+ tripflag_t i;
+
+ *tf = TF_NONE;
+ for (i = NO_TRIP; i < NUM_TRIPFLAGS; i++)
+ if(! strcmp(buffer, tripflag_names[i]))
+ *tf = i;
+}
+
static void centibar(char *buffer, void *_pressure)
{
pressure_t *pressure = _pressure;
@@ -1062,6 +1078,8 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
if (MATCH(".number", get_index, &dive->number))
return;
+ if (MATCH(".tripflag", get_tripflag, &dive->tripflag))
+ return;
if (MATCH(".date", divedate, &dive->when))
return;
if (MATCH(".time", divetime, &dive->when))
@@ -1138,6 +1156,27 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
nonmatch("dive", name, buf);
}
+/* We're in the top-level trip xml. Try to convert whatever value to a trip value */
+static void try_to_fill_trip(struct dive **divep, const char *name, char *buf)
+{
+ int len = strlen(name);
+
+ start_match("trip", name, buf);
+
+ struct dive *dive = *divep;
+
+ if (MATCH(".date", divedate, &dive->when)) {
+ dive->when = utc_mktime(&cur_tm);
+ return;
+ }
+ if (MATCH(".location", utf8_string, &dive->location))
+ return;
+ if (MATCH(".notes", utf8_string, &dive->notes))
+ return;
+
+ nonmatch("trip", name, buf);
+}
+
/*
* File boundaries are dive boundaries. But sometimes there are
* multiple dives per file, so there can be other events too that
@@ -1162,6 +1201,22 @@ static void dive_end(void)
cur_ws_index = 0;
}
+static void trip_start(void)
+{
+ if (cur_trip)
+ return;
+ cur_trip = alloc_dive();
+ memset(&cur_tm, 0, sizeof(cur_tm));
+}
+
+static void trip_end(void)
+{
+ if (!cur_trip)
+ return;
+ record_trip(cur_trip);
+ cur_trip = NULL;
+}
+
static void event_start(void)
{
memset(&cur_event, 0, sizeof(cur_event));
@@ -1225,6 +1280,10 @@ static void entry(const char *name, int size, const char *raw)
try_to_fill_sample(cur_sample, name, buf);
return;
}
+ if (cur_trip) {
+ try_to_fill_trip(&cur_trip, name, buf);
+ return;
+ }
if (cur_dive) {
try_to_fill_dive(&cur_dive, name, buf);
return;
@@ -1350,6 +1409,7 @@ static struct nesting {
} nesting[] = {
{ "dive", dive_start, dive_end },
{ "Dive", dive_start, dive_end },
+ { "trip", trip_start, trip_end },
{ "sample", sample_start, sample_end },
{ "waypoint", sample_start, sample_end },
{ "SAMPLE", sample_start, sample_end },
@@ -1420,8 +1480,8 @@ void parse_xml_buffer(const char *url, const char *buffer, int size, GError **er
}
return;
}
- /* we assume that the last (or only) filename passed as argument is a
- * great filename to use as default when saving the dives */
+ /* we assume that the last (or only) filename passed as argument is a
+ * great filename to use as default when saving the dives */
set_filename(url);
reset_all();
dive_start();
diff --git a/print.c b/print.c
index bdebcfe15..0e49bc9c3 100644
--- a/print.c
+++ b/print.c
@@ -11,20 +11,41 @@
#define FONT_SMALL (FONT_NORMAL / 1.2)
#define FONT_LARGE (FONT_NORMAL * 1.2)
-static void set_font(PangoLayout *layout, PangoFontDescription *font, double size, int align)
+static struct options print_options;
+
+/* Return the 'i'th dive for printing, taking our dive selection into account */
+static struct dive *get_dive_for_printing(int idx)
+{
+ if (print_options.print_selected) {
+ int i;
+ struct dive *dive;
+ for_each_dive(i, dive) {
+ if (!dive->selected)
+ continue;
+ if (!idx)
+ return dive;
+ idx--;
+ }
+ return NULL;
+ }
+ return get_dive(idx);
+}
+
+static void set_font(PangoLayout *layout, PangoFontDescription *font,
+ double size, int align)
{
pango_font_description_set_size(font, size * PANGO_SCALE);
pango_layout_set_font_description(layout, font);
pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
pango_layout_set_alignment(layout, align);
-
}
/*
* You know what? Maybe somebody can do a real Pango layout thing.
* This is hacky.
*/
-static void show_dive_text(struct dive *dive, cairo_t *cr, double w, double h, PangoFontDescription *font)
+static void show_dive_text(struct dive *dive, cairo_t *cr, double w,
+ double h, PangoFontDescription *font)
{
double depth;
const char *unit;
@@ -125,7 +146,143 @@ static void show_dive_text(struct dive *dive, cairo_t *cr, double w, double h, P
g_object_unref(layout);
}
-static void show_dive_profile(struct dive *dive, cairo_t *cr, double w, double h)
+static void show_table_header(cairo_t *cr, double w, double h,
+ PangoFontDescription *font)
+{
+ int maxwidth, maxheight, colwidth, i, curwidth;
+ PangoLayout *layout;
+ char headers[7][80]= { "Dive#", "Date", "Depth", "Time", "Master",
+ "Buddy", "Location" };
+
+ maxwidth = w * PANGO_SCALE;
+ maxheight = h * PANGO_SCALE * 0.9;
+ colwidth = maxwidth / 7;
+
+ layout = pango_cairo_create_layout(cr);
+
+ cairo_move_to(cr, 0, 0);
+ pango_layout_set_height(layout, maxheight);
+ set_font(layout, font, FONT_LARGE, PANGO_ALIGN_LEFT);
+
+
+ curwidth = 0;
+ for (i = 0; i < 7; i++) {
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ if (i == 0 || i == 2 || i == 3 ){
+ // Column 0, 2 and 3 (Dive #, Depth and Time) get 1/2 width
+ pango_layout_set_width(layout, colwidth/ (double) 2);
+ curwidth = curwidth + (colwidth / 2);
+ } else {
+ pango_layout_set_width(layout, colwidth);
+ curwidth = curwidth + colwidth;
+ }
+ pango_layout_set_text(layout, headers[i], -1);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ }
+
+ cairo_move_to(cr, 0, 0);
+ g_object_unref(layout);
+}
+
+static void show_dive_table(struct dive *dive, cairo_t *cr, double w,
+ double h, PangoFontDescription *font)
+{
+ double depth;
+ const char *unit;
+ int len, decimals, maxwidth, maxheight, colwidth, curwidth;
+ PangoLayout *layout;
+ struct tm *tm;
+ char buffer[160], divenr[20];
+
+ maxwidth = w * PANGO_SCALE;
+ maxheight = h * PANGO_SCALE * 0.9;
+
+ colwidth = maxwidth / 7;
+
+ layout = pango_cairo_create_layout(cr);
+
+ cairo_move_to(cr, 0, 0);
+ pango_layout_set_width(layout, colwidth);
+ pango_layout_set_height(layout, maxheight);
+ set_font(layout, font, FONT_NORMAL, PANGO_ALIGN_LEFT);
+ cairo_move_to(cr, 0, 0);
+ curwidth = 0;
+
+ // Col 1: Dive #
+ *divenr = 0;
+ if (dive->number)
+ snprintf(divenr, sizeof(divenr), "#%d", dive->number);
+ pango_layout_set_width(layout, colwidth/ (double) 2);
+ pango_layout_set_text(layout, divenr, -1);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + (colwidth / 2);
+
+ // Col 2: Date #
+ pango_layout_set_width(layout, colwidth);
+ tm = gmtime(&dive->when);
+ len = snprintf(buffer, sizeof(buffer),
+ "%s, %s %d, %d %dh%02d",
+ weekday(tm->tm_wday),
+ monthname(tm->tm_mon),
+ tm->tm_mday, tm->tm_year + 1900,
+ tm->tm_hour, tm->tm_min
+ );
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_text(layout, buffer, len);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + colwidth;
+
+ // Col 3: Depth
+ depth = get_depth_units(dive->maxdepth.mm, &decimals, &unit);
+ len = snprintf(buffer, sizeof(buffer),
+ "%.*f %s", decimals, depth, unit);
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_width(layout, colwidth/ (double) 2);
+ pango_layout_set_text(layout, buffer, len);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + (colwidth / 2);
+
+ // Col 4: Time
+ len = snprintf(buffer, sizeof(buffer),
+ "%d min",(dive->duration.seconds+59) / 60);
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_width(layout, colwidth/ (double) 2);
+ pango_layout_set_text(layout, buffer, len);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + (colwidth / 2);
+
+ // Col 5: Master
+ pango_layout_set_width(layout, colwidth);
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_text(layout, dive->divemaster ? : " ", -1);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + colwidth;
+
+ // Col 6: Buddy
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_text(layout, dive->buddy ? : " ", -1);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+ curwidth = curwidth + colwidth;
+
+ // Col 7: Location
+ cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
+ pango_layout_set_width(layout, maxwidth - curwidth);
+ pango_layout_set_text(layout, dive->location ? : " ", -1);
+ pango_layout_set_justify(layout, 1);
+ pango_cairo_show_layout(cr, layout);
+
+ g_object_unref(layout);
+}
+
+static void show_dive_profile(struct dive *dive, cairo_t *cr, double w,
+ double h)
{
cairo_rectangle_int_t drawing_area = { w/20.0, h/20.0, w, h};
struct graphics_context gc = {
@@ -137,11 +294,12 @@ static void show_dive_profile(struct dive *dive, cairo_t *cr, double w, double h
cairo_restore(cr);
}
-static void print(int divenr, cairo_t *cr, double x, double y, double w, double h, PangoFontDescription *font)
+static void print(int divenr, cairo_t *cr, double x, double y, double w,
+ double h, PangoFontDescription *font)
{
struct dive *dive;
- dive = get_dive(divenr);
+ dive = get_dive_for_printing(divenr);
if (!dive)
return;
cairo_save(cr);
@@ -165,6 +323,47 @@ static void print(int divenr, cairo_t *cr, double x, double y, double w, double
cairo_restore(cr);
}
+static void print_table_header(cairo_t *cr, double x, double y,
+ double w, double h, PangoFontDescription *font)
+{
+ cairo_save(cr);
+ cairo_translate(cr, x, y);
+
+ /* Plus 5% on all sides */
+ cairo_translate(cr, w/20, h/20);
+ w *= 0.9; h *= 0.9;
+
+ /* We actually want to scale the text and the lines now */
+ cairo_scale(cr, 0.5, 0.5);
+
+ show_table_header(cr, w*2, h*2, font);
+
+ cairo_restore(cr);
+}
+
+static void print_table(int divenr, cairo_t *cr, double x, double y,
+ double w, double h, PangoFontDescription *font)
+{
+ struct dive *dive;
+
+ dive = get_dive_for_printing(divenr);
+ if (!dive)
+ return;
+ cairo_save(cr);
+ cairo_translate(cr, x, y);
+
+ /* Plus 5% on all sides */
+ cairo_translate(cr, w/20, h/20);
+ w *= 0.9; h *= 0.9;
+
+ /* We actually want to scale the text and the lines now */
+ cairo_scale(cr, 0.5, 0.5);
+
+ show_dive_table(dive, cr, w*2, h*2, font);
+
+ cairo_restore(cr);
+}
+
static void draw_page(GtkPrintOperation *operation,
GtkPrintContext *context,
gint page_nr,
@@ -192,26 +391,129 @@ static void draw_page(GtkPrintOperation *operation,
pango_font_description_free(font);
}
+static void draw_table(GtkPrintOperation *operation,
+ GtkPrintContext *context,
+ gint page_nr,
+ gpointer user_data)
+{
+ int i, nr;
+ int n_dive_per_page = 25;
+ cairo_t *cr;
+ double w, h;
+ PangoFontDescription *font;
+
+ cr = gtk_print_context_get_cairo_context(context);
+ font = pango_font_description_from_string("Sans");
+
+ w = gtk_print_context_get_width(context);
+ h = gtk_print_context_get_height(context)/(n_dive_per_page+1);
+
+ nr = page_nr*n_dive_per_page;
+ print_table_header(cr, 0, 0+h, w, h, font);
+ for (i = 0; i < n_dive_per_page; i++) {
+ print_table(nr+i, cr, 0, h*1.5+h*i, w, h, font);
+ }
+
+ pango_font_description_free(font);
+}
+
+static int nr_selected_dives(void)
+{
+ int i, dives;
+ struct dive *dive;
+
+ dives = 0;
+ for_each_dive(i, dive)
+ dives += dive->selected;
+ return dives;
+}
+
static void begin_print(GtkPrintOperation *operation, gpointer user_data)
{
+ int pages, dives;
+ int dives_per_page;
+
+ dives = nr_selected_dives();
+ print_options.print_selected = dives > 1;
+ if (dives <= 1)
+ dives = dive_table.nr;
+
+ if (print_options.type == PRETTY) {
+ dives_per_page = 6;
+ } else {
+ dives_per_page = 25;
+ }
+ pages = (dives + dives_per_page - 1) / dives_per_page;
+ gtk_print_operation_set_n_pages(operation, pages);
+}
+
+
+#define OPTIONCALLBACK(name, type, value) \
+static void name(GtkWidget *w, gpointer data) \
+{\
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \
+ print_options.type = value; \
+}
+
+OPTIONCALLBACK(set_pretty, type, PRETTY)
+OPTIONCALLBACK(set_table, type, TABLE)
+
+static GtkWidget *print_dialog(GtkPrintOperation *operation, gpointer user_data)
+{
+ GtkWidget *vbox, *radio1, *radio2, *frame, *box;
+ gtk_print_operation_set_custom_tab_label(operation, "Dive details");
+
+ vbox = gtk_vbox_new(TRUE, 5);
+
+ frame = gtk_frame_new("Print type");
+ gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 1);
+
+ box = gtk_hbox_new(FALSE, 2);
+ gtk_container_add(GTK_CONTAINER(frame), box);
+
+ radio1 = gtk_radio_button_new_with_label (NULL, "Pretty print");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio1),
+ print_options.type == PRETTY);
+ radio2 = gtk_radio_button_new_with_label_from_widget (
+ GTK_RADIO_BUTTON (radio1), "Table print");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2),
+ print_options.type == TABLE);
+ gtk_box_pack_start (GTK_BOX (box), radio1, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), radio2, TRUE, TRUE, 0);
+
+ g_signal_connect(radio1, "toggled", G_CALLBACK(set_pretty), NULL);
+ g_signal_connect(radio2, "toggled", G_CALLBACK(set_table), NULL);
+
+ gtk_widget_show_all(vbox);
+ return vbox;
+}
+
+static void print_dialog_apply(GtkPrintOperation *operation, GtkWidget *widget, gpointer user_data)
+{
+ if (print_options.type == PRETTY) {
+ g_signal_connect(operation, "draw_page",
+ G_CALLBACK(draw_page), NULL);
+ } else {
+ g_signal_connect(operation, "draw_page",
+ G_CALLBACK(draw_table), NULL);
+ }
}
static GtkPrintSettings *settings = NULL;
void do_print(void)
{
- int pages;
GtkPrintOperation *print;
GtkPrintOperationResult res;
repaint_dive();
print = gtk_print_operation_new();
+ gtk_print_operation_set_unit(print, GTK_UNIT_POINTS);
if (settings != NULL)
gtk_print_operation_set_print_settings(print, settings);
- pages = (dive_table.nr + 5) / 6;
- gtk_print_operation_set_n_pages(print, pages);
+ g_signal_connect(print, "create-custom-widget", G_CALLBACK(print_dialog), NULL);
+ g_signal_connect(print, "custom-widget-apply", G_CALLBACK(print_dialog_apply), NULL);
g_signal_connect(print, "begin_print", G_CALLBACK(begin_print), NULL);
- g_signal_connect(print, "draw_page", G_CALLBACK(draw_page), NULL);
res = gtk_print_operation_run(print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
GTK_WINDOW(main_window), NULL);
if (res == GTK_PRINT_OPERATION_RESULT_APPLY) {
diff --git a/profile.c b/profile.c
index d3362432e..0039867ef 100644
--- a/profile.c
+++ b/profile.c
@@ -1,5 +1,5 @@
/* profile.c */
-/* creates all the necessary data for drawing the dive profile
+/* creates all the necessary data for drawing the dive profile
* uses cairo to draw it
*/
#include <stdio.h>
@@ -14,7 +14,7 @@
#include "color.h"
int selected_dive = 0;
-int *selectiontracker;
+char zoomed_plot = 0;
typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t;
@@ -181,22 +181,40 @@ static void dump_pi (struct plot_info *pi)
* When showing dive profiles, we scale things to the
* current dive. However, we don't scale past less than
* 30 minutes or 90 ft, just so that small dives show
- * up as such.
- * we also need to add 180 seconds at the end so the min/max
+ * up as such unless zoom is enabled.
+ * We also need to add 180 seconds at the end so the min/max
* plots correctly
*/
static int get_maxtime(struct plot_info *pi)
{
int seconds = pi->maxtime;
- /* min 30 minutes, rounded up to 5 minutes, with at least 2.5 minutes to spare */
- return MAX(30*60, ROUND_UP(seconds+150, 60*5));
+ if (zoomed_plot) {
+ /* Rounded up to one minute, with at least 2.5 minutes to
+ * spare.
+ * For dive times shorter than 10 minutes, we use seconds/4 to
+ * calculate the space dynamically.
+ * This is seamless since 600/4 = 150.
+ */
+ if ( seconds < 600 )
+ return ROUND_UP(seconds+seconds/4, 60);
+ else
+ return ROUND_UP(seconds+150, 60);
+ } else {
+ /* min 30 minutes, rounded up to 5 minutes, with at least 2.5 minutes to spare */
+ return MAX(30*60, ROUND_UP(seconds+150, 60*5));
+ }
}
static int get_maxdepth(struct plot_info *pi)
{
unsigned mm = pi->maxdepth;
- /* Minimum 30m, rounded up to 10m, with at least 3m to spare */
- return MAX(30000, ROUND_UP(mm+3000, 10000));
+ if (zoomed_plot) {
+ /* Rounded up to 10m, with at least 3m to spare */
+ return ROUND_UP(mm+3000, 10000);
+ } else {
+ /* Minimum 30m, rounded up to 10m, with at least 3m to spare */
+ return MAX(30000, ROUND_UP(mm+3000, 10000));
+ }
}
typedef struct {
@@ -444,20 +462,21 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi
int sec, depth;
struct plot_data *entry;
int maxtime, maxdepth, marker;
- int increments[4] = { 5*60, 10*60, 15*60, 30*60 };
+ int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 };
/* Get plot scaling limits */
maxtime = get_maxtime(pi);
maxdepth = get_maxdepth(pi);
- /* Time markers: at most every 5 min, but no more than 12 markers
- * and for convenience we do 5, 10, 15 or 30 min intervals.
+ /* Time markers: at most every 10 seconds, but no more than 12 markers.
+ * We start out with 10 seconds and increment up to 30 minutes,
+ * depending on the dive time.
* This allows for 6h dives - enough (I hope) for even the craziest
* divers - but just in case, for those 8h depth-record-breaking dives,
* we double the interval if this still doesn't get us to 12 or fewer
* time markers */
i = 0;
- while (maxtime / increments[i] > 12 && i < 4)
+ while (maxtime / increments[i] > 12 && i < 8)
i++;
incr = increments[i];
while (maxtime / incr > 12)
@@ -474,11 +493,17 @@ static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi
}
cairo_stroke(cr);
- /* now the text on every second time marker */
+ /* now the text on the time markers */
text_render_options_t tro = {10, TIME_TEXT, CENTER, TOP};
- for (i = incr; i < maxtime; i += 2 * incr)
- plot_text(gc, &tro, i, 1, "%d", i/60);
-
+ if (maxtime < 600) {
+ /* Be a bit more verbose with shorter dives */
+ for (i = incr; i < maxtime; i += incr)
+ plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60);
+ } else {
+ /* Only render the time on every second marker for normal dives */
+ for (i = incr; i < maxtime; i += 2 * incr)
+ plot_text(gc, &tro, i, 1, "%d", i/60);
+ }
/* Depth markers: every 30 ft or 10 m*/
gc->leftx = 0; gc->rightx = 1.0;
gc->topy = 0; gc->bottomy = maxdepth;
@@ -874,7 +899,7 @@ static velocity_t velocity(int speed)
else if (speed < -25) /* -5ft/min */
v = SLOW;
else if (speed < 25) /* very hard to find data, but it appears that the recommendations
- for descent are usually about 2x ascent rate; still, we want
+ for descent are usually about 2x ascent rate; still, we want
stable to mean stable */
v = STABLE;
else if (speed < 152) /* between 5 and 30ft/min is considered slow */
@@ -930,7 +955,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi)
int past = -2;
while (i+past > 0 && entry[0].sec - entry[past].sec < 15)
past--;
- entry->velocity = velocity((entry[0].depth - entry[past].depth) /
+ entry->velocity = velocity((entry[0].depth - entry[past].depth) /
(entry[0].sec - entry[past].sec));
}
} else
@@ -942,7 +967,7 @@ static struct plot_info *analyze_plot_info(struct plot_info *pi)
struct plot_data *entry = pi->entry +i;
analyze_plot_info_minmax(entry, pi->entry, pi->entry+nr);
}
-
+
return pi;
}
@@ -1362,6 +1387,8 @@ void plot(struct graphics_context *gc, cairo_rectangle_int_t *drawing_area, stru
int duration = dive->duration.seconds;
int maxdepth = dive->maxdepth.mm;
int asc_desc_time = dive->maxdepth.mm*60/9000;
+ if (asc_desc_time * 2 >= duration)
+ asc_desc_time = duration / 2;
sample = fake;
fake[1].time.seconds = asc_desc_time;
fake[1].depth.mm = maxdepth;
diff --git a/save-xml.c b/save-xml.c
index 37d6d062e..b797475e5 100644
--- a/save-xml.c
+++ b/save-xml.c
@@ -67,10 +67,9 @@ static void show_pressure(FILE *f, pressure_t pressure, const char *pre, const c
* characters, but at least libxml2 doesn't like them. It doesn't even
* allow them quoted. So we just skip them and replace them with '?'.
*
- * Nothing else (and if we ever do this using attributes, we'd need to
- * quote the quotes we use too).
+ * If we do this for attributes, we need to quote the quotes we use too.
*/
-static void quote(FILE *f, const char *text)
+static void quote(FILE *f, const char *text, int is_attribute)
{
const char *p = text;
@@ -97,6 +96,16 @@ static void quote(FILE *f, const char *text)
case '&':
escape = "&amp;";
break;
+ case '\'':
+ if (!is_attribute)
+ continue;
+ escape = "&apos;";
+ break;
+ case '\"':
+ if (!is_attribute)
+ continue;
+ escape = "&quot;";
+ break;
}
fwrite(text, (p - text - 1), 1, f);
if (!escape)
@@ -106,7 +115,7 @@ static void quote(FILE *f, const char *text)
}
}
-static void show_utf8(FILE *f, const char *text, const char *pre, const char *post)
+static void show_utf8(FILE *f, const char *text, const char *pre, const char *post, int is_attribute)
{
int len;
@@ -121,7 +130,7 @@ static void show_utf8(FILE *f, const char *text, const char *pre, const char *po
len--;
/* FIXME! Quoting! */
fputs(pre, f);
- quote(f, text);
+ quote(f, text, is_attribute);
fputs(post, f);
}
@@ -171,7 +180,7 @@ static void show_location(FILE *f, struct dive *dive)
}
prefix = buffer;
}
- show_utf8(f, dive->location, prefix,"</location>\n");
+ show_utf8(f, dive->location, prefix,"</location>\n", 0);
}
static void save_overview(FILE *f, struct dive *dive)
@@ -180,10 +189,10 @@ static void save_overview(FILE *f, struct dive *dive)
save_temperatures(f, dive);
show_duration(f, dive->surfacetime, " <surfacetime>", "</surfacetime>\n");
show_location(f, dive);
- show_utf8(f, dive->divemaster, " <divemaster>","</divemaster>\n");
- show_utf8(f, dive->buddy, " <buddy>","</buddy>\n");
- show_utf8(f, dive->notes, " <notes>","</notes>\n");
- show_utf8(f, dive->suit, " <suit>","</suit>\n");
+ show_utf8(f, dive->divemaster, " <divemaster>","</divemaster>\n", 0);
+ show_utf8(f, dive->buddy, " <buddy>","</buddy>\n", 0);
+ show_utf8(f, dive->notes, " <notes>","</notes>\n", 0);
+ show_utf8(f, dive->suit, " <suit>","</suit>\n", 0);
}
static void save_cylinder_info(FILE *f, struct dive *dive)
@@ -262,7 +271,7 @@ static void save_one_event(FILE *f, struct event *ev)
show_index(f, ev->type, "type='", "'");
show_index(f, ev->flags, "flags='", "'");
show_index(f, ev->value, "value='", "'");
- show_utf8(f, ev->name, " name='", "'");
+ show_utf8(f, ev->name, " name='", "'", 1);
fprintf(f, " />\n");
}
@@ -275,6 +284,18 @@ static void save_events(FILE *f, struct event *ev)
}
}
+static void save_trip(FILE *f, struct dive *trip)
+{
+ struct tm *tm = gmtime(&trip->when);
+
+ fprintf(f, "<trip");
+ fprintf(f, " date='%04u-%02u-%02u'",
+ tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
+ if (trip->location)
+ show_utf8(f, trip->location, " location=\'","\'", 1);
+ fprintf(f, " />\n");
+}
+
static void save_dive(FILE *f, struct dive *dive)
{
int i;
@@ -283,6 +304,8 @@ static void save_dive(FILE *f, struct dive *dive)
fputs("<dive", f);
if (dive->number)
fprintf(f, " number='%d'", dive->number);
+ if (dive->tripflag != TF_NONE)
+ fprintf(f, " tripflag='%s'", tripflag_names[dive->tripflag]);
if (dive->rating)
fprintf(f, " rating='%d'", dive->rating);
fprintf(f, " date='%04u-%02u-%02u'",
@@ -305,6 +328,8 @@ static void save_dive(FILE *f, struct dive *dive)
void save_dives(const char *filename)
{
int i;
+ GList *trip = NULL;
+
FILE *f = fopen(filename, "w");
if (!f)
@@ -314,6 +339,12 @@ void save_dives(const char *filename)
update_dive(current_dive);
fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION);
+
+ /* save the trips */
+ while ((trip = NEXT_TRIP(trip, dive_trip_list)) != 0)
+ save_trip(f, trip->data);
+
+ /* save the dives */
for (i = 0; i < dive_table.nr; i++)
save_dive(f, get_dive(i));
fprintf(f, "</dives>\n");
diff --git a/statistics.c b/statistics.c
index 0f6fbe047..a53617337 100644
--- a/statistics.c
+++ b/statistics.c
@@ -143,24 +143,21 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
}
/* make sure we skip the selected summary entries */
-void process_selected_dives(GList *selected_dives, int *selectiontracker, GtkTreeModel *model)
+void process_selected_dives(void)
{
- struct dive *dp;
- unsigned int i;
- int idx;
+ struct dive *dive;
+ unsigned int i, nr;
memset(&stats_selection, 0, sizeof(stats_selection));
- for (i = 0; i < amount_selected; ++i) {
- idx = selectiontracker[i];
- if (idx > 0) {
- dp = get_dive(idx);
- if (dp) {
- process_dive(dp, &stats_selection);
- }
+ nr = 0;
+ for_each_dive(i, dive) {
+ if (dive->selected) {
+ process_dive(dive, &stats_selection);
+ nr++;
}
}
- stats_selection.selection_size = amount_selected;
+ stats_selection.selection_size = nr;
}
static void set_label(GtkWidget *w, const char *fmt, ...)
@@ -214,7 +211,7 @@ static void show_single_dive_stats(struct dive *dive)
set_label(single_w.date, buf);
set_label(single_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);
if (prev_dive)
- set_label(single_w.surf_intv,
+ set_label(single_w.surf_intv,
get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4));
else
set_label(single_w.surf_intv, "unknown");
diff --git a/uemis.h b/uemis.h
index 29a168f5d..ba9a23402 100644
--- a/uemis.h
+++ b/uemis.h
@@ -28,7 +28,7 @@ typedef struct {
uint16_t consumption; // (units unclear)
uint8_t rgt; // (remaining gas time in minutes)
uint8_t cns;
- uint8_t flags[8];
+ uint8_t flags[8];
} __attribute((packed)) uemis_sample_t;
#endif /* DIVE_H */