]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Merge branch 'weight' of git://subsurface.hohndel.org/subsurface
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 24 Mar 2012 04:07:53 +0000 (21:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 24 Mar 2012 04:07:53 +0000 (21:07 -0700)
Pull weight management from Dirk Hohndel:
 "This is the fifth or sixth version of this code, I'm begining to lose
  track.  I still struggle with the balance between code duplication and
  unnecessary indirectness and complexity.  Maybe I'm just not finding
  the right level of abstraction.  Maybe I'm just trying too hard.

  The code here is reasonably well tested.  Works for me :-)

  It can import DivingLog xml files with weight systems and correctly
  parses those.  It obviously can read and write weight systems in its
  own file format.  It adds a KG/lbs unit default (and correctly stores
  that).

  The thing I still worry about is the code in equipment.c.  You'll see
  that I tried to abstract things in a way that weight systems and
  cylinders share quite a bit of code - but there's more very similar
  code that isn't shared as my attempts to do so turned into ugly and
  hard to read code.  It always felt like trying to write C++ in C..."

* 'weight' of git://subsurface.hohndel.org/subsurface:
  Add weight system tracking

Fix up some trivial conflicts due to various renaming of globals and
simplification in function interfaces.

1  2 
dive.h
equipment.c
gtk-gui.c
parse-xml.c

diff --combined dive.h
index befa0cdcf3b50e13c95a6fe017979fad23442f91,bfaa076b1eba08085a6329da989a230dd79d7e87..39f203738833519c594c50032146d13f867c49a2
--- 1/dive.h
--- 2/dive.h
+++ b/dive.h
@@@ -87,13 -87,29 +87,29 @@@ typedef struct 
        pressure_t start, end, sample_start, sample_end;
  } cylinder_t;
  
- extern int cylinder_none(cylinder_t *cyl);
+ typedef struct {
+       weight_t weight;
+       const char *description;        /* "integrated", "belt", "ankle" */
+ } weightsystem_t;
+ extern int cylinder_none(void *_data);
+ extern int weightsystem_none(void *_data);
  
  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);
  
+ static inline double grams_to_lbs(int grams)
+ {
+       return grams / 453.6;
+ }
+ static inline int lbs_to_grams(double lbs)
+ {
+       return lbs * 453.6 + 0.5;
+ }
  static inline double ml_to_cuft(int ml)
  {
        return ml / 28316.8466;
@@@ -194,6 -210,7 +210,7 @@@ struct event 
  };
  
  #define MAX_CYLINDERS (8)
+ #define MAX_WEIGHTSYSTEMS (4)
  
  struct dive {
        int number;
        depth_t visibility;
        temperature_t airtemp, watertemp;
        cylinder_t cylinder[MAX_CYLINDERS];
+       weightsystem_t weightsystem[MAX_WEIGHTSYSTEMS];
        int sac, otu;
        struct event *events;
        int samples, alloc_samples;
@@@ -254,11 -272,9 +272,11 @@@ static inline struct dive *get_dive(uns
  }
  
  extern void parse_xml_init(void);
 -extern void parse_xml_file(const char *filename, GError **error);
 +extern void parse_xml_buffer(const char *url, const char *buf, int size, GError **error);
  extern void set_filename(const char *filename);
  
 +extern void parse_file(const char *filename, GError **error);
 +
  #ifdef XSLT
  extern xmlDoc *test_xslt_transforms(xmlDoc *doc);
  #endif
@@@ -283,7 -299,7 +301,7 @@@ extern struct dive *alloc_dive(void)
  extern void record_dive(struct dive *dive);
  
  extern struct sample *prepare_sample(struct dive **divep);
 -extern void finish_sample(struct dive *dive, struct sample *sample);
 +extern void finish_sample(struct dive *dive);
  
  extern void report_dives(gboolean imported);
  extern struct dive *fixup_dive(struct dive *dive);
@@@ -328,6 -344,4 +346,6 @@@ const char *monthname(int mon)
  #define FIVE_STARS    UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR
  extern const char *star_strings[];
  
 +#define AIR_PERMILLE 209
 +
  #endif /* DIVE_H */
diff --combined equipment.c
index e4f880f64e584ca1eb29183e0ceddaca973360ef,579c4554207041a7030c8d7d08c940e37db1efc9..abfb0e7b5979bba41b9af71b1d5391d610ed4359
@@@ -18,7 -18,7 +18,7 @@@
  #include "display-gtk.h"
  #include "divelist.h"
  
- static GtkListStore *cylinder_model;
+ static GtkListStore *cylinder_model, *weightsystem_model;
  
  enum {
        CYL_DESC,
        CYL_COLUMNS
  };
  
- static struct {
+ enum {
+       WS_DESC,
+       WS_WEIGHT,
+       WS_COLUMNS
+ };
+ struct equipment_list {
        int max_index;
        GtkListStore *model;
        GtkWidget *edit, *add, *del;
- } cylinder_list;
+ };
+ static struct equipment_list cylinder_list, weightsystem_list;
  
  struct cylinder_widget {
        int index, changed;
        GtkWidget *o2, *he, *gasmix_button;
  };
  
+ struct ws_widget {
+       int index, changed;
+       const char *name;
+       GtkWidget *hbox;
+       GtkComboBox *description;
+       GtkSpinButton *weight;
+ };
  /* we want bar - so let's not use our unit functions */
  static int convert_pressure(int mbar, double *p)
  {
@@@ -64,8 -81,9 +81,8 @@@
        return decimals;
  }
  
 -static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
 +static void convert_volume_pressure(int ml, int mbar, double *v, double *p)
  {
 -      int decimals = 1;
        double volume, pressure;
  
        volume = ml / 1000.0;
  
                if (output_units.pressure == PSI) {
                        pressure = mbar_to_PSI(mbar);
 -                      decimals = 0;
                } else
                        pressure = mbar / 1000.0;
        }
        *v = volume;
        *p = pressure;
 -      return decimals;
  }
  
+ static int convert_weight(int grams, double *m)
+ {
+       int decimals = 1; /* not sure - do people do less than whole lbs/kg ? */
+       double weight;
+       if (output_units.weight == LBS)
+               weight = grams_to_lbs(grams);
+       else
+               weight = grams / 1000.0;
+       *m = weight;
+       return decimals;
+ }
  static void set_cylinder_type_spinbuttons(struct cylinder_widget *cylinder, int ml, int mbar)
  {
        double volume, pressure;
@@@ -116,6 -149,14 +146,14 @@@ static void set_cylinder_pressure_spinb
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->end), pressure);
  }
  
+ static void set_weight_weight_spinbutton(struct ws_widget *ws_widget, int grams)
+ {
+       double weight;
+       convert_weight(grams, &weight);
+       gtk_spin_button_set_value(ws_widget->weight, weight);
+ }
  /*
   * The gtk_tree_model_foreach() interface is bad. It could have
   * returned whether the callback ever returned true
  static GtkTreeIter *found_match = NULL;
  static GtkTreeIter match_iter;
  
- static gboolean match_cylinder(GtkTreeModel *model,
-                               GtkTreePath *path,
-                               GtkTreeIter *iter,
-                               gpointer data)
+ static gboolean match_desc(GtkTreeModel *model,       GtkTreePath *path,
+                       GtkTreeIter *iter, gpointer data)
  {
        int match;
        gchar *name;
        return match;
  }
  
- static int get_active_cylinder(GtkComboBox *combo_box, GtkTreeIter *iter)
+ static int get_active_item(GtkComboBox *combo_box, GtkTreeIter *iter, GtkListStore *model)
  {
        char *desc;
  
        desc = gtk_combo_box_get_active_text(combo_box);
  
        found_match = NULL;
-       gtk_tree_model_foreach(GTK_TREE_MODEL(cylinder_model), match_cylinder, (void *)desc);
+       gtk_tree_model_foreach(GTK_TREE_MODEL(model), match_desc, (void *)desc);
  
        g_free(desc);
        if (!found_match)
@@@ -172,7 -211,7 +208,7 @@@ static void cylinder_cb(GtkComboBox *co
        cylinder_t *cyl = current_dive->cylinder + cylinder->index;
  
        /* Did the user set it to some non-standard value? */
-       if (!get_active_cylinder(combo_box, &iter)) {
+       if (!get_active_item(combo_box, &iter, cylinder_model)) {
                cylinder->changed = 1;
                return;
        }
        set_cylinder_type_spinbuttons(cylinder, ml, mbar);
  }
  
+ static void weight_cb(GtkComboBox *combo_box, gpointer data)
+ {
+       GtkTreeIter iter;
+       GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
+       int weight;
+       struct ws_widget *ws_widget = data;
+       weightsystem_t *ws = current_dive->weightsystem + ws_widget->index;
+       /* Did the user set it to some non-standard value? */
+       if (!get_active_item(combo_box, &iter, weightsystem_model)) {
+               ws_widget->changed = 1;
+               return;
+       }
+       /*
+        * We get "change" signal callbacks just because we set
+        * the description by hand. Whatever. So ignore them if
+        * they are no-ops.
+        */
+       if (!ws_widget->changed && ws->description) {
+               int same;
+               char *desc = gtk_combo_box_get_active_text(combo_box);
+               same = !strcmp(desc, ws->description);
+               g_free(desc);
+               if (same)
+                       return;
+       }
+       ws_widget->changed = 1;
+       gtk_tree_model_get(model, &iter,
+               WS_WEIGHT, &weight,
+               -1);
+       set_weight_weight_spinbutton(ws_widget, weight);
+ }
  static GtkTreeIter *add_cylinder_type(const char *desc, int ml, int mbar, GtkTreeIter *iter)
  {
        GtkTreeModel *model;
  
        found_match = NULL;
        model = GTK_TREE_MODEL(cylinder_model);
-       gtk_tree_model_foreach(model, match_cylinder, (void *)desc);
+       gtk_tree_model_foreach(model, match_desc, (void *)desc);
  
        if (!found_match) {
                GtkListStore *store = GTK_LIST_STORE(model);
        return found_match;
  }
  
+ static GtkTreeIter *add_weightsystem_type(const char *desc, int weight, GtkTreeIter *iter)
+ {
+       GtkTreeModel *model;
+       found_match = NULL;
+       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,
+                       -1);
+               return iter;
+       }
+       return found_match;
+ }
  /*
   * When adding a dive, we'll add all the pre-existing cylinder
   * information from that dive to our cylinder model.
@@@ -255,6 -352,29 +349,29 @@@ static void add_cylinder(struct cylinde
                gtk_combo_box_set_active_iter(cylinder->description, match);
  }
  
+ void add_weightsystem_description(weightsystem_t *weightsystem)
+ {
+       GtkTreeIter iter;
+       const char *desc;
+       unsigned int weight;
+       desc = weightsystem->description;
+       if (!desc)
+               return;
+       weight = weightsystem->weight.grams;
+       add_weightsystem_type(desc, weight, &iter);
+ }
+ static void add_weightsystem(struct ws_widget *ws_widget, const char *desc, int weight)
+ {
+       GtkTreeIter iter, *match;
+       ws_widget->name = desc;
+       match = add_weightsystem_type(desc, weight, &iter);
+       if (match)
+               gtk_combo_box_set_active_iter(ws_widget->description, match);
+ }
  static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
  {
        const char *desc;
        o2 = cyl->gasmix.o2.permille / 10.0;
        he = cyl->gasmix.he.permille / 10.0;
        if (!o2)
 -              o2 = 21.0;
 +              o2 = AIR_PERMILLE / 10.0;
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->o2), o2);
        gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->he), he);
  }
  
int cylinder_none(cylinder_t *cyl)
static void show_weightsystem(weightsystem_t *ws, struct ws_widget *weightsystem_widget)
  {
+       const char *desc;
+       int grams;
+       /* Don't show uninitialized widgets */
+       if (!weightsystem_widget->description)
+               return;
+       desc = ws->description;
+       if (!desc)
+               desc = "";
+       grams = ws->weight.grams;
+       add_weightsystem(weightsystem_widget, desc, grams);
+       set_weight_weight_spinbutton(weightsystem_widget, ws->weight.grams);
+ }
+ int cylinder_none(void *_data)
+ {
+       cylinder_t *cyl = _data;
        return  !cyl->type.size.mliter &&
                !cyl->type.workingpressure.mbar &&
                !cyl->type.description &&
                !cyl->end.mbar;
  }
  
- static void set_one_cylinder(cylinder_t *cyl, GtkListStore *model, GtkTreeIter *iter)
+ int weightsystem_none(void *_data)
+ {
+       weightsystem_t *ws = _data;
+       return !ws->weight.grams && !ws->description;
+ }
 -static void set_one_cylinder(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
++static void set_one_cylinder(void *_data, GtkListStore *model, GtkTreeIter *iter)
  {
+       cylinder_t *cyl = _data;
        unsigned int start, end;
  
        start = cyl->start.mbar ? : cyl->sample_start.mbar;
                -1);
  }
  
- void show_dive_equipment(struct dive *dive)
 -static void set_one_weightsystem(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
++static void set_one_weightsystem(void *_data, GtkListStore *model, GtkTreeIter *iter)
+ {
+       weightsystem_t *ws = _data;
+       gtk_list_store_set(model, iter,
+               WS_DESC, ws->description ? : "unspecified",
+               WS_WEIGHT, ws->weight.grams,
+               -1);
+ }
+ static void *cyl_ptr(struct dive *dive, int idx)
+ {
+       if (idx < 0 || idx >= MAX_CYLINDERS)
+               return NULL;
+       return &dive->cylinder[idx];
+ }
+ static void *ws_ptr(struct dive *dive, int idx)
  {
-       int i, max;
+       if (idx < 0 || idx >= MAX_WEIGHTSYSTEMS)
+               return NULL;
+       return &dive->weightsystem[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 *),
 -                      void(*set_one_function)(int, void*, GtkListStore*, GtkTreeIter *))
++                      void(*set_one_function)(void*, GtkListStore*, GtkTreeIter *))
+ {
+       int i, used;
+       void *data;
        GtkTreeIter iter;
-       GtkListStore *model;
+       GtkListStore *model = equipment_list->model;
  
-       model = cylinder_list.model;
        gtk_list_store_clear(model);
-       max = MAX_CYLINDERS;
+       used = max;
        do {
-               cylinder_t *cyl = &dive->cylinder[max-1];
-               if (!cylinder_none(cyl))
+               data = ptr_function(dive, used-1);
+               if (!none_function(data))
                        break;
-       } while (--max);
+       } while (--used);
  
-       cylinder_list.max_index = max;
+       equipment_list->max_index = used;
  
-       gtk_widget_set_sensitive(cylinder_list.edit, 0);
-       gtk_widget_set_sensitive(cylinder_list.del, 0);
-       gtk_widget_set_sensitive(cylinder_list.add, max < MAX_CYLINDERS);
-       for (i = 0; i < max; i++) {
-               cylinder_t *cyl = dive->cylinder+i;
+       gtk_widget_set_sensitive(equipment_list->edit, 0);
+       gtk_widget_set_sensitive(equipment_list->del, 0);
+       gtk_widget_set_sensitive(equipment_list->add, used < max);
  
+       for (i = 0; i < used; i++) {
+               data = ptr_function(dive, i);
                gtk_list_store_append(model, &iter);
-               set_one_cylinder(cyl, model, &iter);
 -              set_one_function(i, data, model, &iter);
++              set_one_function(data, model, &iter);
        }
  }
  
+ void show_dive_equipment(struct dive *dive)
+ {
+       show_equipment(dive, MAX_CYLINDERS, &cylinder_list,
+               &cyl_ptr, &cylinder_none, &set_one_cylinder);
+       show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list,
+               &ws_ptr, &weightsystem_none, &set_one_weightsystem);
+ }
  static GtkWidget *create_spinbutton(GtkWidget *vbox, const char *name, double min, double max, double incr)
  {
        GtkWidget *frame, *hbox, *button;
@@@ -440,6 -620,29 +617,29 @@@ static void record_cylinder_changes(cyl
        fill_cylinder_info(cylinder, cyl, desc, volume, pressure, start, end, o2, he);
  }
  
+ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *weightsystem_widget)
+ {
+       const gchar *desc;
+       GtkComboBox *box;
+       int grams;
+       double value;
+       /* Ignore uninitialized cylinder widgets */
+       box = weightsystem_widget->description;
+       if (!box)
+               return;
+       desc = gtk_combo_box_get_active_text(box);
+       value = gtk_spin_button_get_value(weightsystem_widget->weight);
+       if (output_units.weight == LBS)
+               grams = lbs_to_grams(value);
+       else
+               grams = value * 1000;
+       ws->weight.grams = grams;
+       ws->description = desc;
+ }
  /*
   * We hardcode the most common standard cylinders,
   * we should pick up any other names from the dive
@@@ -527,6 -730,38 +727,38 @@@ static void fill_tank_list(GtkListStor
        }
  }
  
+ /*
+  * We hardcode the most common weight system types
+  * This is a bit odd as the weight system types don't usually encode weight
+  */
+ 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 },
+       { "bar", 0 },
+       { "clip-on", 0 },
+ };
+ static void fill_ws_list(GtkListStore *store)
+ {
+       GtkTreeIter iter;
+       struct ws_info *info = ws_info;
+       while (info->name) {
+               gtk_list_store_append(store, &iter);
+               gtk_list_store_set(store, &iter,
+                       0, info->name,
+                       1, info->grams,
+                       -1);
+               info++;
+       }
+ }
  static void gasmix_cb(GtkToggleButton *button, gpointer data)
  {
        struct cylinder_widget *cylinder = data;
@@@ -668,6 -903,63 +900,63 @@@ static void cylinder_widget(GtkWidget *
        g_signal_connect(cylinder->gasmix_button, "toggled", G_CALLBACK(gasmix_cb), cylinder);
  }
  
+ static gboolean weight_completion_cb(GtkEntryCompletion *widget, GtkTreeModel *model, GtkTreeIter *iter, struct ws_widget *ws_widget)
+ {
+       const char *desc;
+       unsigned int weight;
+       gtk_tree_model_get(model, iter, WS_DESC, &desc, WS_WEIGHT, &weight, -1);
+       add_weightsystem(ws_widget, desc, weight);
+       return TRUE;
+ }
+ static void weight_activate_cb(GtkComboBox *combo_box, gpointer data)
+ {
+       struct ws_widget *ws_widget = data;
+       weight_cb(ws_widget->description, data);
+ }
+ static void ws_widget(GtkWidget *vbox, struct ws_widget *ws_widget, GtkListStore *model)
+ {
+       GtkWidget *frame, *hbox;
+       GtkEntryCompletion *completion;
+       GtkWidget *widget;
+       GtkEntry *entry;
+       /*
+        * weight_system: description and weight
+        */
+       frame = gtk_frame_new("Weight");
+       hbox = gtk_hbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), hbox);
+       widget = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
+       gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
+       ws_widget->description = GTK_COMBO_BOX(widget);
+       g_signal_connect(widget, "changed", G_CALLBACK(weight_cb), ws_widget);
+       entry = GTK_ENTRY(GTK_BIN(widget)->child);
+       g_signal_connect(entry, "activate", G_CALLBACK(weight_activate_cb), ws_widget);
+       completion = gtk_entry_completion_new();
+       gtk_entry_completion_set_text_column(completion, 0);
+       gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(model));
+       g_signal_connect(completion, "match-selected", G_CALLBACK(weight_completion_cb), ws_widget);
+       gtk_entry_set_completion(entry, completion);
+       hbox = gtk_hbox_new(FALSE, 3);
+       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
+       gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0);
+       if ( output_units.weight == KG)
+               widget = create_spinbutton(hbox, "kg", 0, 50, 0.5);
+       else
+               widget = create_spinbutton(hbox, "lbs", 0, 110, 1);
+       ws_widget->weight = GTK_SPIN_BUTTON(widget);
+ }
  static int edit_cylinder_dialog(int index, cylinder_t *cyl)
  {
        int success;
        return success;
  }
  
+ static int edit_weightsystem_dialog(int index, weightsystem_t *ws)
+ {
+       int success;
+       GtkWidget *dialog, *vbox;
+       struct ws_widget weightsystem_widget;
+       struct dive *dive;
+       weightsystem_widget.index = index;
+       weightsystem_widget.changed = 0;
+       dive = current_dive;
+       if (!dive)
+               return 0;
+       *ws = dive->weightsystem[index];
+       dialog = gtk_dialog_new_with_buttons("Weight System",
+               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));
+       ws_widget(vbox, &weightsystem_widget, weightsystem_model);
+       show_weightsystem(ws, &weightsystem_widget);
+       gtk_widget_show_all(dialog);
+       success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT;
+       if (success) {
+               record_weightsystem_changes(ws, &weightsystem_widget);
+               dive->weightsystem[index] = *ws;
+               mark_divelist_changed(TRUE);
+               flush_divelist(dive);
+       }
+       gtk_widget_destroy(dialog);
+       return success;
+ }
  static int get_model_index(GtkListStore *model, GtkTreeIter *iter)
  {
        int *p, index;
@@@ -740,7 -1073,7 +1070,7 @@@ static void edit_cb(GtkButton *button, 
        if (!edit_cylinder_dialog(index, &cyl))
                return;
  
 -      set_one_cylinder(index, &cyl, model, &iter);
 +      set_one_cylinder(&cyl, model, &iter);
        repaint_dive();
  }
  
@@@ -756,7 -1089,7 +1086,7 @@@ static void add_cb(GtkButton *button, G
                return;
  
        gtk_list_store_append(model, &iter);
 -      set_one_cylinder(index, &cyl, model, &iter);
 +      set_one_cylinder(&cyl, model, &iter);
  
        selection = gtk_tree_view_get_selection(tree_view);
        gtk_tree_selection_select_iter(selection, &iter);
@@@ -802,6 -1135,86 +1132,86 @@@ static void del_cb(GtkButton *button, G
        gtk_widget_set_sensitive(cylinder_list.add, 1);
  }
  
 -      set_one_weightsystem(index, &ws, model, &iter);
+ static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view)
+ {
+       int index;
+       GtkTreeIter iter;
+       GtkListStore *model = weightsystem_list.model;
+       GtkTreeSelection *selection;
+       weightsystem_t ws;
+       selection = gtk_tree_view_get_selection(tree_view);
+       /* Nothing selected? This shouldn't happen, since the button should be inactive */
+       if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+               return;
+       index = get_model_index(model, &iter);
+       if (!edit_weightsystem_dialog(index, &ws))
+               return;
 -      set_one_weightsystem(index, &ws, model, &iter);
++      set_one_weightsystem(&ws, model, &iter);
+       repaint_dive();
+ }
+ static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view)
+ {
+       int index = weightsystem_list.max_index;
+       GtkTreeIter iter;
+       GtkListStore *model = weightsystem_list.model;
+       GtkTreeSelection *selection;
+       weightsystem_t ws;
+       if (!edit_weightsystem_dialog(index, &ws))
+               return;
+       gtk_list_store_append(model, &iter);
++      set_one_weightsystem(&ws, model, &iter);
+       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);
+ }
+ static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view)
+ {
+       int index, nr;
+       GtkTreeIter iter;
+       GtkListStore *model = weightsystem_list.model;
+       GtkTreeSelection *selection;
+       struct dive *dive;
+       weightsystem_t *ws;
+       selection = gtk_tree_view_get_selection(tree_view);
+       /* Nothing selected? This shouldn't happen, since the button should be inactive */
+       if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+               return;
+       index = get_model_index(model, &iter);
+       dive = current_dive;
+       if (!dive)
+               return;
+       ws = dive->weightsystem + index;
+       nr = weightsystem_list.max_index - index - 1;
+       gtk_list_store_remove(model, &iter);
+       weightsystem_list.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);
+ }
  static GtkListStore *create_tank_size_model(void)
  {
        GtkListStore *model;
        return model;
  }
  
+ static GtkListStore *create_weightsystem_model(void)
+ {
+       GtkListStore *model;
+       model = gtk_list_store_new(2,
+               G_TYPE_STRING,          /* Weightsystem description */
+               G_TYPE_INT,             /* Weight in grams */
+               -1);
+       fill_ws_list(model);
+       return model;
+ }
  static void size_data_func(GtkTreeViewColumn *col,
                           GtkCellRenderer *renderer,
                           GtkTreeModel *model,
        g_object_set(renderer, "text", buffer, NULL);
  }
  
+ static void weight_data_func(GtkTreeViewColumn *col,
+                          GtkCellRenderer *renderer,
+                          GtkTreeModel *model,
+                          GtkTreeIter *iter,
+                          gpointer data)
+ {
+       int idx = (long)data;
+       int grams, decimals;
+       double value;
+       char buffer[10];
+       gtk_tree_model_get(model, iter, idx, &grams, -1);
+       decimals = convert_weight(grams, &value);
+       if (grams)
+               snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
+       else
+               strcpy(buffer, "unkn");
+       g_object_set(renderer, "text", buffer, NULL);
+ }
  static void pressure_data_func(GtkTreeViewColumn *col,
                           GtkCellRenderer *renderer,
                           GtkTreeModel *model,
@@@ -873,14 -1319,14 +1316,14 @@@ static void percentage_data_func(GtkTre
        g_object_set(renderer, "text", buffer, NULL);
  }
  
- static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
+ static void selection_cb(GtkTreeSelection *selection, struct equipment_list *list)
  {
        GtkTreeIter iter;
        int selected;
  
        selected = gtk_tree_selection_get_selected(selection, NULL, &iter);
-       gtk_widget_set_sensitive(cylinder_list.edit, selected);
-       gtk_widget_set_sensitive(cylinder_list.del, selected);
+       gtk_widget_set_sensitive(list->edit, selected);
+       gtk_widget_set_sensitive(list->del, selected);
  }
  
  static void row_activated_cb(GtkTreeView *tree_view,
@@@ -904,7 -1350,7 +1347,7 @@@ GtkWidget *cylinder_list_widget(void
  
        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), model);
+       g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list);
  
        g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
                                          "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH,
        return tree_view;
  }
  
+ GtkWidget *weightsystem_list_widget(void)
+ {
+       GtkListStore *model = weightsystem_list.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);
+       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_object_set(G_OBJECT(tree_view), "headers-visible", TRUE,
+                                         "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH,
+                                         NULL);
+       tree_view_column(tree_view, WS_DESC, "Type", NULL, ALIGN_LEFT | UNSORTABLE);
+       tree_view_column(tree_view, WS_WEIGHT, "weight",
+                       weight_data_func, ALIGN_RIGHT | UNSORTABLE);
+       return tree_view;
+ }
  static GtkWidget *cylinder_list_create(void)
  {
        GtkListStore *model;
        return cylinder_list_widget();
  }
  
+ static GtkWidget *weightsystem_list_create(void)
+ {
+       GtkListStore *model;
+       model = gtk_list_store_new(WS_COLUMNS,
+               G_TYPE_STRING,          /* WS_DESC: utf8 */
+               G_TYPE_INT              /* WS_WEIGHT: grams */
+               );
+       weightsystem_list.model = model;
+       return weightsystem_list_widget();
+ }
  GtkWidget *equipment_widget(void)
  {
        GtkWidget *vbox, *hbox, *frame, *framebox, *tree_view;
         * another dive.
         */
        cylinder_model = create_tank_size_model();
        tree_view = cylinder_list_create();
  
        hbox = gtk_hbox_new(FALSE, 3);
        g_signal_connect(add, "clicked", G_CALLBACK(add_cb), tree_view);
        g_signal_connect(del, "clicked", G_CALLBACK(del_cb), tree_view);
  
+       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();
+       frame = gtk_frame_new("Weight");
+       gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, FALSE, 3);
+       framebox = gtk_vbox_new(FALSE, 3);
+       gtk_container_add(GTK_CONTAINER(frame), framebox);
+       hbox = gtk_hbox_new(FALSE, 3);
+       gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+       gtk_box_pack_start(GTK_BOX(hbox), tree_view, TRUE, FALSE, 3);
+       hbox = gtk_hbox_new(TRUE, 3);
+       gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+       edit = gtk_button_new_from_stock(GTK_STOCK_EDIT);
+       add = gtk_button_new_from_stock(GTK_STOCK_ADD);
+       del = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+       gtk_box_pack_start(GTK_BOX(hbox), edit, FALSE, FALSE, 0);
+       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;
+       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);
        return vbox;
  }
diff --combined gtk-gui.c
index 402e91a6e5f2f1f29c15e9ea07129f1e9a2df019,371a0308fdb014c925d8e679f58b878ca765394d..7d0e95c43ecdf5b8885ff44dd4a1693b75b744c6
+++ b/gtk-gui.c
@@@ -7,7 -7,6 +7,7 @@@
  #include <string.h>
  #include <stdlib.h>
  #include <time.h>
 +#include <unistd.h>
  
  #include "dive.h"
  #include "divelist.h"
@@@ -23,6 -22,7 +23,6 @@@ GtkWidget *error_label
  GtkWidget *vpane, *hpane;
  int        error_count;
  
 -#define DIVELIST_DEFAULT_FONT "Sans 8"
  const char *divelist_font;
  
  struct units output_units;
@@@ -107,14 -107,14 +107,14 @@@ static void file_open(GtkWidget *w, gpo
        gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
  
        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
 -              GSList *filenames;
 +              GSList *filenames, *fn_glist;
                char *filename;
 -              filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
 +              filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
                
                GError *error = NULL;
                while(filenames != NULL) {
                        filename = filenames->data;
 -                      parse_xml_file(filename, &error);
 +                      parse_file(filename, &error);
                        if (error != NULL)
                        {
                                report_error(error);
                        g_free(filename);
                        filenames = g_slist_next(filenames);
                }
 -              g_slist_free(filenames);
 +              g_slist_free(fn_glist);
                report_dives(FALSE);
        }
        gtk_widget_destroy(dialog);
@@@ -245,7 -245,7 +245,7 @@@ GtkTreeViewColumn *tree_view_column(Gtk
        return col;
  }
  
 -static void create_radio(GtkWidget *vbox, const char *name, ...)
 +static void create_radio(GtkWidget *vbox, const char *w_name, ...)
  {
        va_list args;
        GtkRadioButton *group = NULL;
        box = gtk_hbox_new(TRUE, 10);
        gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
  
 -      label = gtk_label_new(name);
 +      label = gtk_label_new(w_name);
        gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
  
 -      va_start(args, name);
 +      va_start(args, w_name);
        for (;;) {
                int enabled;
                const char *name;
@@@ -296,6 -296,8 +296,8 @@@ UNITCALLBACK(set_liter, volume, LITER
  UNITCALLBACK(set_cuft, volume, CUFT)
  UNITCALLBACK(set_celsius, temperature, CELSIUS)
  UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT)
+ UNITCALLBACK(set_kg, weight, KG)
+ UNITCALLBACK(set_lbs, weight, LBS)
  
  #define OPTIONCALLBACK(name, option) \
  static void name(GtkWidget *w, gpointer data) \
@@@ -357,6 -359,11 +359,11 @@@ static void preferences_dialog(GtkWidge
                "Fahrenheit",  set_fahrenheit, (output_units.temperature == FAHRENHEIT),
                NULL);
  
+       create_radio(box, "Weight:",
+               "kg", set_kg, (output_units.weight == KG),
+               "lbs",  set_lbs, (output_units.weight == LBS),
+               NULL);
        frame = gtk_frame_new("Columns");
        gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, FALSE, FALSE, 5);
  
                subsurface_set_conf("psi", PREF_BOOL, BOOL_TO_PTR(output_units.pressure == PSI));
                subsurface_set_conf("cuft", PREF_BOOL, BOOL_TO_PTR(output_units.volume == CUFT));
                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("CYLINDER", PREF_BOOL, BOOL_TO_PTR(visible_cols.cylinder));
                subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(visible_cols.nitrox));
@@@ -523,7 -531,11 +531,7 @@@ static void about_dialog(GtkWidget *w, 
        GdkPixbuf *logo = NULL;
  
        if (need_icon) {
 -#if defined __linux__ || defined __APPLE__
 -              GtkWidget *image = gtk_image_new_from_file("subsurface.svg");
 -#elif defined WIN32
 -              GtkWidget *image = gtk_image_new_from_file("subsurface.ico");
 -#endif
 +              GtkWidget *image = gtk_image_new_from_file(subsurface_icon_name());
  
                if (image) {
                        logo = gtk_image_get_pixbuf(GTK_IMAGE(image));
@@@ -573,19 -585,19 +581,19 @@@ static GtkActionEntry menu_items[] = 
        { "ViewMenuAction",  GTK_STOCK_FILE, "View", NULL, NULL, NULL},
        { "FilterMenuAction",  GTK_STOCK_FILE, "Filter", NULL, NULL, NULL},
        { "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
 -      { "OpenFile",       GTK_STOCK_OPEN, NULL,   "<control>O", NULL, G_CALLBACK(file_open) },
 -      { "SaveFile",       GTK_STOCK_SAVE, NULL,   "<control>S", NULL, G_CALLBACK(file_save) },
 -      { "Print",          GTK_STOCK_PRINT, NULL,  "<control>P", NULL, G_CALLBACK(do_print) },
 +      { "OpenFile",       GTK_STOCK_OPEN, NULL,   CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
 +      { "SaveFile",       GTK_STOCK_SAVE, NULL,   CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
 +      { "Print",          GTK_STOCK_PRINT, NULL,  CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
        { "Import",         NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
 -      { "Preferences",    NULL, "Preferences", NULL, NULL, G_CALLBACK(preferences_dialog) },
 +      { "Preferences",    NULL, "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,   "<control>Q", NULL, G_CALLBACK(quit) },
 +      { "Quit",           GTK_STOCK_QUIT, NULL,   CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
        { "About",          GTK_STOCK_ABOUT, NULL,  NULL, NULL, G_CALLBACK(about_dialog) },
 -      { "ViewList",       NULL, "List",  "<control>1", NULL, G_CALLBACK(view_list) },
 -      { "ViewProfile",    NULL, "Profile", "<control>2", NULL, G_CALLBACK(view_profile) },
 -      { "ViewInfo",       NULL, "Info", "<control>3", NULL, G_CALLBACK(view_info) },
 -      { "ViewThree",       NULL, "Three", "<control>4", NULL, G_CALLBACK(view_three) },
 +      { "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) },
  };
  static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
  
@@@ -622,11 -634,12 +630,11 @@@ static const gchar* ui_string = " 
        </ui> \
  ";
  
 -static GtkWidget *get_menubar_menu(GtkWidget *window)
 +static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
  {
        GtkActionGroup *action_group = gtk_action_group_new("Menu");
        gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
  
 -      GtkUIManager *ui_manager = gtk_ui_manager_new();
        gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
        GError* error = 0;
        gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
@@@ -646,14 -659,15 +654,14 @@@ void init_ui(int *argcp, char ***argvp
  {
        GtkWidget *win;
        GtkWidget *notebook;
 -      GtkWidget *dive_info;
 +      GtkWidget *nb_page;
        GtkWidget *dive_list;
 -      GtkWidget *equipment;
 -      GtkWidget *stats;
        GtkWidget *menubar;
        GtkWidget *vbox;
        GdkScreen *screen;
        GtkIconTheme *icon_theme=NULL;
        GtkSettings *settings;
 +      GtkUIManager *ui_manager;
  
        gtk_init(argcp, argvp);
        settings = gtk_settings_get_default();
                output_units.volume = CUFT;
        if (subsurface_get_conf("fahrenheit", PREF_BOOL))
                output_units.temperature = FAHRENHEIT;
+       if (subsurface_get_conf("lbs", PREF_BOOL))
+               output_units.weight = LBS;
        /* 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));
  
        divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
  
 -      if (!divelist_font)
 -              divelist_font = DIVELIST_DEFAULT_FONT;
 -
        error_info_bar = NULL;
        win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        g_set_application_name ("subsurface");
                        gtk_window_set_default_icon_name ("subsurface");
                }
        }
 -      if (need_icon)
 -#if defined __linux__ || defined __APPLE__
 -              gtk_window_set_icon_from_file(GTK_WINDOW(win), "subsurface.svg", NULL);
 -#elif defined WIN32
 -              gtk_window_set_icon_from_file(GTK_WINDOW(win), "subsurface.ico", NULL);
 -#endif
 +      if (need_icon) {
 +              const char *icon_name = subsurface_icon_name();
 +              if (!access(icon_name, R_OK))
 +                      gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL);
 +      }
        g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL);
        g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL);
        main_window = win;
        gtk_container_add(GTK_CONTAINER(win), vbox);
        main_vbox = vbox;
  
 -      menubar = get_menubar_menu(win);
 -      gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
 +      ui_manager = gtk_ui_manager_new();
 +      menubar = get_menubar_menu(win, ui_manager);
 +
 +      subsurface_ui_setup(settings, menubar, vbox, ui_manager);
  
        vpane = gtk_vpaned_new();
        gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3);
        gtk_paned_add2(GTK_PANED(hpane), dive_profile);
  
        /* Frame for extended dive info */
 -      dive_info = extended_dive_info_widget();
 -      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_info, gtk_label_new("Dive Notes"));
 +      nb_page = extended_dive_info_widget();
 +      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes"));
  
        /* Frame for dive equipment */
 -      equipment = equipment_widget();
 -      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
 +      nb_page = equipment_widget();
 +      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment"));
 +
 +      /* Frame for single dive statistics */
 +      nb_page = single_stats_widget();
 +      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Info"));
  
 -      /* Frame for dive statistics */
 -      stats = stats_widget();
 -      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), stats, gtk_label_new("Info & Stats"));
 +      /* Frame for total dive statistics */
 +      nb_page = total_stats_widget();
 +      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Stats"));
  
        gtk_widget_set_app_paintable(win, TRUE);
        gtk_widget_show_all(win);
@@@ -961,7 -975,7 +971,7 @@@ static GtkWidget *xml_file_selector(Gtk
  static void do_import_file(gpointer data, gpointer user_data)
  {
        GError *error = NULL;
 -      parse_xml_file(data, &error);
 +      parse_file(data, &error);
  
        if (error != NULL)
        {
diff --combined parse-xml.c
index 2877e3a9966781f368d009564cb6700570c3e670,34afdb9f690dc22fa83d6f08a94aaaddd13f1ce4..e920a11f6f0d798b18900ccb23317c95c8ec5162
@@@ -76,6 -76,7 +76,7 @@@ struct units input_units
   * technically the SI unit for pressure is Pascal, but
   * we default to bar (10^5 pascal), which people
   * actually use. Similarly, C instead of Kelvin.
+  * And kg instead of g.
   */
  const struct units SI_units = {
        .length = METERS,
@@@ -96,16 -97,16 +97,16 @@@ const struct units IMPERIAL_units = 
  /*
   * Dive info as it is being built up..
   */
 -static struct dive *dive;
 -static struct sample *sample;
 +static struct dive *cur_dive;
 +static struct sample *cur_sample;
  static struct {
        int active;
        duration_t time;
        int type, flags, value;
        const char *name;
 -} event;
 -static struct tm tm;
 -static int cylinder_index, ws_index;
 +} cur_event;
 +static struct tm cur_tm;
- static int cur_cylinder_index;
++static int cur_cylinder_index, cur_ws_index;
  
  static enum import_source {
        UNKNOWN,
@@@ -151,22 -152,22 +152,22 @@@ static void divedate(char *buffer, voi
        time_t *when = _when;
        int success = 0;
  
 -      success = tm.tm_sec | tm.tm_min | tm.tm_hour;
 +      success = cur_tm.tm_sec | cur_tm.tm_min | cur_tm.tm_hour;
        if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) {
 -              tm.tm_year = y;
 -              tm.tm_mon = m-1;
 -              tm.tm_mday = d;
 +              cur_tm.tm_year = y;
 +              cur_tm.tm_mon = m-1;
 +              cur_tm.tm_mday = d;
        } else if (sscanf(buffer, "%d-%d-%d", &y, &m, &d) == 3) {
 -              tm.tm_year = y;
 -              tm.tm_mon = m-1;
 -              tm.tm_mday = d;
 +              cur_tm.tm_year = y;
 +              cur_tm.tm_mon = m-1;
 +              cur_tm.tm_mday = d;
        } else {
                fprintf(stderr, "Unable to parse date '%s'\n", buffer);
                success = 0;
        }
  
        if (success)
 -              *when = utc_mktime(&tm);
 +              *when = utc_mktime(&cur_tm);
  
        free(buffer);
  }
@@@ -177,11 -178,11 +178,11 @@@ static void divetime(char *buffer, voi
        time_t *when = _when;
  
        if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
 -              tm.tm_hour = h;
 -              tm.tm_min = m;
 -              tm.tm_sec = s;
 -              if (tm.tm_year)
 -                      *when = utc_mktime(&tm);
 +              cur_tm.tm_hour = h;
 +              cur_tm.tm_min = m;
 +              cur_tm.tm_sec = s;
 +              if (cur_tm.tm_year)
 +                      *when = utc_mktime(&cur_tm);
        }
        free(buffer);
  }
@@@ -195,13 -196,13 +196,13 @@@ static void divedatetime(char *buffer, 
  
        if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
                &y, &m, &d, &hr, &min, &sec) == 6) {
 -              tm.tm_year = y;
 -              tm.tm_mon = m-1;
 -              tm.tm_mday = d;
 -              tm.tm_hour = hr;
 -              tm.tm_min = min;
 -              tm.tm_sec = sec;
 -              *when = utc_mktime(&tm);
 +              cur_tm.tm_year = y;
 +              cur_tm.tm_mon = m-1;
 +              cur_tm.tm_mday = d;
 +              cur_tm.tm_hour = hr;
 +              cur_tm.tm_min = min;
 +              cur_tm.tm_sec = sec;
 +              *when = utc_mktime(&cur_tm);
        }
        free(buffer);
  }
@@@ -298,6 -299,27 +299,27 @@@ static void depth(char *buffer, void *_
        free(buffer);
  }
  
+ static void weight(char *buffer, void *_weight)
+ {
+       weight_t *weight = _weight;
+       union int_or_float val;
+       switch (integer_or_float(buffer, &val)) {
+       case FLOAT:
+               switch (input_units.weight) {
+               case KG:
+                       weight->grams = val.fp * 1000 + 0.5;
+                       break;
+               case LBS:
+                       weight->grams = val.fp * 453.6 + 0.5;
+                       break;
+               }
+               break;
+       default:
+               printf("Strange depth reading %s\n", buffer);
+       }
+ }
  static void temperature(char *buffer, void *_temperature)
  {
        temperature_t *temperature = _temperature;
@@@ -376,7 -398,7 +398,7 @@@ static void gasmix(char *buffer, void *
        /* libdivecomputer does negative percentages. */
        if (*buffer == '-')
                return;
 -      if (cylinder_index < MAX_CYLINDERS)
 +      if (cur_cylinder_index < MAX_CYLINDERS)
                percent(buffer, _fraction);
  }
  
@@@ -573,8 -595,8 +595,8 @@@ static void eventtime(char *buffer, voi
  {
        duration_t *duration = _duration;
        sampletime(buffer, duration);
 -      if (sample)
 -              duration->seconds += sample->time.seconds;
 +      if (cur_sample)
 +              duration->seconds += cur_sample->time.seconds;
  }
  
  static void try_to_fill_event(const char *name, char *buf)
        int len = strlen(name);
  
        start_match("event", name, buf);
 -      if (MATCH(".event", utf8_string, &event.name))
 +      if (MATCH(".event", utf8_string, &cur_event.name))
                return;
 -      if (MATCH(".name", utf8_string, &event.name))
 +      if (MATCH(".name", utf8_string, &cur_event.name))
                return;
 -      if (MATCH(".time", eventtime, &event.time))
 +      if (MATCH(".time", eventtime, &cur_event.time))
                return;
 -      if (MATCH(".type", get_index, &event.type))
 +      if (MATCH(".type", get_index, &cur_event.type))
                return;
 -      if (MATCH(".flags", get_index, &event.flags))
 +      if (MATCH(".flags", get_index, &cur_event.flags))
                return;
 -      if (MATCH(".value", get_index, &event.value))
 +      if (MATCH(".value", get_index, &cur_event.value))
                return;
        nonmatch("event", name, buf);
  }
@@@ -804,7 -826,7 +826,7 @@@ static int uemis_gas_template
  static int uemis_cylinder_index(void *_cylinder)
  {
        cylinder_t *cylinder = _cylinder;
 -      unsigned int index = cylinder - dive->cylinder;
 +      unsigned int index = cylinder - cur_dive->cylinder;
  
        if (index > 6) {
                fprintf(stderr, "Uemis cylinder pointer calculations broken\n");
@@@ -836,14 -858,14 +858,14 @@@ static void uemis_cylindersize(char *bu
  {
        int index = uemis_cylinder_index(_cylinder);
        if (index >= 0)
 -              cylindersize(buffer, &dive->cylinder[index].type.size);
 +              cylindersize(buffer, &cur_dive->cylinder[index].type.size);
  }
  
  static void uemis_percent(char *buffer, void *_cylinder)
  {
        int index = uemis_cylinder_index(_cylinder);
        if (index >= 0)
 -              percent(buffer, &dive->cylinder[index].gasmix.o2);
 +              percent(buffer, &cur_dive->cylinder[index].gasmix.o2);
  }
  
  static int uemis_dive_match(struct dive **divep, const char *name, int len, char *buf)
@@@ -1026,22 -1048,27 +1048,27 @@@ static void try_to_fill_dive(struct div
                return;
        if (MATCH(".rating", get_index, &dive->rating))
                return;
 -      if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cylinder_index].type.size))
 +      if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cur_cylinder_index].type.size))
                return;
 -      if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cylinder_index].type.workingpressure))
 +      if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cur_cylinder_index].type.workingpressure))
                return;
 -      if (MATCH(".cylinder.description", utf8_string, &dive->cylinder[cylinder_index].type.description))
 +      if (MATCH(".cylinder.description", utf8_string, &dive->cylinder[cur_cylinder_index].type.description))
                return;
 -      if (MATCH(".cylinder.start", pressure, &dive->cylinder[cylinder_index].start))
 +      if (MATCH(".cylinder.start", pressure, &dive->cylinder[cur_cylinder_index].start))
                return;
 -      if (MATCH(".cylinder.end", pressure, &dive->cylinder[cylinder_index].end))
 +      if (MATCH(".cylinder.end", pressure, &dive->cylinder[cur_cylinder_index].end))
                return;
 -      if (MATCH(".weightsystem.description", utf8_string, &dive->weightsystem[ws_index].description))
++      if (MATCH(".weightsystem.description", utf8_string, &dive->weightsystem[cur_ws_index].description))
+               return;
 -      if (MATCH(".weightsystem.weight", weight, &dive->weightsystem[ws_index].weight))
++      if (MATCH(".weightsystem.weight", weight, &dive->weightsystem[cur_ws_index].weight))
+               return;
 -      if (MATCH("weight", weight, &dive->weightsystem[ws_index].weight))
++      if (MATCH("weight", weight, &dive->weightsystem[cur_ws_index].weight))
+               return;
 -      if (MATCH(".o2", gasmix, &dive->cylinder[cylinder_index].gasmix.o2))
 +      if (MATCH(".o2", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.o2))
                return;
 -      if (MATCH(".n2", gasmix_nitrogen, &dive->cylinder[cylinder_index].gasmix))
 +      if (MATCH(".n2", gasmix_nitrogen, &dive->cylinder[cur_cylinder_index].gasmix))
                return;
 -      if (MATCH(".he", gasmix, &dive->cylinder[cylinder_index].gasmix.he))
 +      if (MATCH(".he", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.he))
                return;
  
        nonmatch("dive", name, buf);
   */
  static void dive_start(void)
  {
 -      if (dive)
 +      if (cur_dive)
                return;
 -      dive = alloc_dive();
 -      memset(&tm, 0, sizeof(tm));
 -}
 -
 -static void sanitize_gasmix(struct gasmix *mix)
 -{
 -      unsigned int o2, he;
 -
 -      o2 = mix->o2.permille;
 -      he = mix->he.permille;
 -
 -      /* Regular air: leave empty */
 -      if (!he) {
 -              if (!o2)
 -                      return;
 -              /* 20.9% or 21% O2 is just air */
 -              if (o2 >= 209 && o2 <= 210) {
 -                      mix->o2.permille = 0;
 -                      return;
 -              }
 -      }
 -
 -      /* Sane mix? */
 -      if (o2 <= 1000 && he <= 1000 && o2+he <= 1000)
 -              return;
 -      fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he);
 -      memset(mix, 0, sizeof(*mix));
 -}
 -
 -/*
 - * See if the size/workingpressure looks like some standard cylinder
 - * size, eg "AL80".
 - */
 -static void match_standard_cylinder(cylinder_type_t *type)
 -{
 -      double cuft;
 -      int psi, len;
 -      const char *fmt;
 -      char buffer[20], *p;
 -
 -      /* Do we already have a cylinder description? */
 -      if (type->description)
 -              return;
 -
 -      cuft = ml_to_cuft(type->size.mliter);
 -      cuft *= to_ATM(type->workingpressure);
 -      psi = to_PSI(type->workingpressure);
 -
 -      switch (psi) {
 -      case 2300 ... 2500:     /* 2400 psi: LP tank */
 -              fmt = "LP%d";
 -              break;
 -      case 2600 ... 2700:     /* 2640 psi: LP+10% */
 -              fmt = "LP%d";
 -              break;
 -      case 2900 ... 3100:     /* 3000 psi: ALx tank */
 -              fmt = "AL%d";
 -              break;
 -      case 3400 ... 3500:     /* 3442 psi: HP tank */
 -              fmt = "HP%d";
 -              break;
 -      case 3700 ... 3850:     /* HP+10% */
 -              fmt = "HP%d+";
 -              break;
 -      default:
 -              return;
 -      }
 -      len = snprintf(buffer, sizeof(buffer), fmt, (int) (cuft+0.5));
 -      p = malloc(len+1);
 -      if (!p)
 -              return;
 -      memcpy(p, buffer, len+1);
 -      type->description = p;
 -}
 -
 -
 -/*
 - * There are two ways to give cylinder size information:
 - *  - total amount of gas in cuft (depends on working pressure and physical size)
 - *  - physical size
 - *
 - * where "physical size" is the one that actually matters and is sane.
 - *
 - * We internally use physical size only. But we save the workingpressure
 - * so that we can do the conversion if required.
 - */
 -static void sanitize_cylinder_type(cylinder_type_t *type)
 -{
 -      double volume_of_air, atm, volume;
 -
 -      /* If we have no working pressure, it had *better* be just a physical size! */
 -      if (!type->workingpressure.mbar)
 -              return;
 -
 -      /* No size either? Nothing to go on */
 -      if (!type->size.mliter)
 -              return;
 -
 -      if (input_units.volume == CUFT) {
 -              /* confusing - we don't really start from ml but millicuft !*/
 -              volume_of_air = cuft_to_l(type->size.mliter);
 -              atm = to_ATM(type->workingpressure);            /* working pressure in atm */
 -              volume = volume_of_air / atm;                   /* milliliters at 1 atm: "true size" */
 -              type->size.mliter = volume + 0.5;
 -      }
 -
 -      /* Ok, we have both size and pressure: try to match a description */
 -      match_standard_cylinder(type);
 -}
 -
 -static void sanitize_cylinder_info(struct dive *dive)
 -{
 -      int i;
 -
 -      for (i = 0; i < MAX_CYLINDERS; i++) {
 -              sanitize_gasmix(&dive->cylinder[i].gasmix);
 -              sanitize_cylinder_type(&dive->cylinder[i].type);
 -      }
 +      cur_dive = alloc_dive();
 +      memset(&cur_tm, 0, sizeof(cur_tm));
  }
  
  static void dive_end(void)
  {
 -      if (!dive)
 +      if (!cur_dive)
                return;
 -      sanitize_cylinder_info(dive);
 -      record_dive(dive);
 -      dive = NULL;
 -      cylinder_index = 0;
 -      ws_index = 0;
 +      record_dive(cur_dive);
 +      cur_dive = NULL;
 +      cur_cylinder_index = 0;
++      cur_ws_index = 0;
  }
  
  static void event_start(void)
  {
 -      memset(&event, 0, sizeof(event));
 -      event.active = 1;
 +      memset(&cur_event, 0, sizeof(cur_event));
 +      cur_event.active = 1;
  }
  
  static void event_end(void)
  {
 -      if (event.name && strcmp(event.name, "surface") != 0)
 -              add_event(dive, event.time.seconds, event.type, event.flags, event.value, event.name);
 -      event.active = 0;
 +      if (cur_event.name && strcmp(cur_event.name, "surface") != 0)
 +              add_event(cur_dive, cur_event.time.seconds,
 +                      cur_event.type, cur_event.flags,
 +                      cur_event.value, cur_event.name);
 +      cur_event.active = 0;
  }
  
  static void cylinder_start(void)
  
  static void cylinder_end(void)
  {
 -      cylinder_index++;
 +      cur_cylinder_index++;
  }
  
 -      ws_index++;
+ static void ws_start(void)
+ {
+ }
+ static void ws_end(void)
+ {
++      cur_ws_index++;
+ }
  static void sample_start(void)
  {
 -      sample = prepare_sample(&dive);
 +      cur_sample = prepare_sample(&cur_dive);
  }
  
  static void sample_end(void)
  {
 -      if (!dive)
 +      if (!cur_dive)
                return;
  
 -      finish_sample(dive, sample);
 -      sample = NULL;
 +      finish_sample(cur_dive);
 +      cur_sample = NULL;
  }
  
  static void entry(const char *name, int size, const char *raw)
                return;
        memcpy(buf, raw, size);
        buf[size] = 0;
 -      if (event.active) {
 +      if (cur_event.active) {
                try_to_fill_event(name, buf);
                return;
        }
 -      if (sample) {
 -              try_to_fill_sample(sample, name, buf);
 +      if (cur_sample) {
 +              try_to_fill_sample(cur_sample, name, buf);
                return;
        }
 -      if (dive) {
 -              try_to_fill_dive(&dive, name, buf);
 +      if (cur_dive) {
 +              try_to_fill_dive(&cur_dive, name, buf);
                return;
        }
  }
@@@ -1256,6 -1408,7 +1293,7 @@@ static struct nesting 
        { "event", event_start, event_end },
        { "gasmix", cylinder_start, cylinder_end },
        { "cylinder", cylinder_start, cylinder_end },
+       { "weightsystem", ws_start, ws_end },
        { "P", sample_start, sample_end },
  
        /* Import type recognition */
@@@ -1302,25 -1455,25 +1340,25 @@@ static void reset_all(void
        import_source = UNKNOWN;
  }
  
 -void parse_xml_file(const char *filename, GError **error)
 +void parse_xml_buffer(const char *url, const char *buffer, int size, GError **error)
  {
        xmlDoc *doc;
  
 -      doc = xmlReadFile(filename, NULL, 0);
 +      doc = xmlReadMemory(buffer, size, url, NULL, 0);
        if (!doc) {
 -              fprintf(stderr, "Failed to parse '%s'.\n", filename);
 +              fprintf(stderr, "Failed to parse '%s'.\n", url);
                if (error != NULL)
                {
                        *error = g_error_new(g_quark_from_string("subsurface"),
                                             DIVE_ERROR_PARSE,
                                             "Failed to parse '%s'",
 -                                           filename);
 +                                           url);
                }
                return;
        }
        /* 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(filename);
 +      set_filename(url);
        reset_all();
        dive_start();
  #ifdef XSLT