]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Merge branch 'ui' of git://github.com/dirkhh/subsurface
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 24 Oct 2011 05:03:22 +0000 (07:03 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 24 Oct 2011 05:03:22 +0000 (07:03 +0200)
* 'ui' of git://github.com/dirkhh/subsurface:
  Disable sorting by dive number
  Fix oversight in preference implementation
  Make columns for temperature, cylinder, and nitrox optional
  Show dive number in dive list
  Improve time marker handling and add printing of some time labels

dive.c
dive.h
equipment.c
info.c
profile.c

diff --git a/dive.c b/dive.c
index 0341d09928cd4ef9ad9a974858a17c037db55a4c..b57205bc5ff096841cc0d0f3626265a650f9228f 100644 (file)
--- a/dive.c
+++ b/dive.c
@@ -240,6 +240,14 @@ struct dive *fixup_dive(struct dive *dive)
        update_temperature(&dive->watertemp, mintemp);
        update_depth(&dive->maxdepth, maxdepth);
 
+       add_people(dive->buddy);
+       add_people(dive->divemaster);
+       add_location(dive->location);
+       for (i = 0; i < MAX_CYLINDERS; i++) {
+               cylinder_type_t *type = &dive->cylinder[i].type;
+               add_cylinder_description(type);
+       }
+
        return dive;
 }
 
diff --git a/dive.h b/dive.h
index f29712e2c382e437e66db5a7cc2e460e992bc482..7c64c21e25fcf849f848e55ef64db34a8afe9a6e 100644 (file)
--- a/dive.h
+++ b/dive.h
@@ -259,6 +259,10 @@ extern void run_ui(void);
 
 extern void report_error(GError* error);
 
+extern void add_cylinder_description(cylinder_type_t *);
+extern void add_people(const char *string);
+extern void add_location(const char *string);
+
 extern void dive_list_update_dives(void);
 extern void flush_divelist(struct dive *dive);
 
index 5ff8f0fe6d9bb56aa0bfb1fea3f4f43e33527f78..12f39ae3f7c8bedab61ca1a91fa47c2c080f9a4e 100644 (file)
@@ -19,7 +19,7 @@
 #include "display-gtk.h"
 #include "divelist.h"
 
-GtkListStore *cylinder_model;
+static GtkListStore *cylinder_model;
 
 enum {
        CYL_DESC,
@@ -87,13 +87,18 @@ static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
        return decimals;
 }
 
-static void set_cylinder_spinbuttons(struct cylinder_widget *cylinder, int ml, int mbar, int start, int end)
+static void set_cylinder_type_spinbuttons(struct cylinder_widget *cylinder, int ml, int mbar)
 {
        double volume, pressure;
 
        convert_volume_pressure(ml, mbar, &volume, &pressure);
        gtk_spin_button_set_value(cylinder->size, volume);
        gtk_spin_button_set_value(cylinder->pressure, pressure);
+}
+
+static void set_cylinder_pressure_spinbuttons(struct cylinder_widget *cylinder, int start, int end)
+{
+       double pressure;
 
        convert_pressure(start, &pressure);
        gtk_spin_button_set_value(cylinder->start, pressure);
@@ -101,16 +106,63 @@ static void set_cylinder_spinbuttons(struct cylinder_widget *cylinder, int ml, i
        gtk_spin_button_set_value(cylinder->end, pressure);
 }
 
+/*
+ * 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)
+{
+       int match;
+       gchar *name;
+       const char *desc = data;
+
+       gtk_tree_model_get(model, iter, 0, &name, -1);
+       match = !strcmp(desc, name);
+       g_free(name);
+       if (match) {
+               match_iter = *iter;
+               found_match = &match_iter;
+       }
+       return match;
+}
+
+static int get_active_cylinder(GtkComboBox *combo_box, GtkTreeIter *iter)
+{
+       char *desc;
+
+       if (gtk_combo_box_get_active_iter(combo_box, iter))
+               return TRUE;
+
+       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);
+
+       g_free(desc);
+       if (!found_match)
+               return FALSE;
+
+       *iter = *found_match;
+       gtk_combo_box_set_active_iter(combo_box, iter);
+       return TRUE;
+}
+
 static void cylinder_cb(GtkComboBox *combo_box, gpointer data)
 {
        GtkTreeIter iter;
        GtkTreeModel *model = gtk_combo_box_get_model(combo_box);
-       int ml, mbar, start, end;
+       int ml, mbar;
        struct cylinder_widget *cylinder = data;
        cylinder_t *cyl = current_dive->cylinder + cylinder->index;
 
        /* Did the user set it to some non-standard value? */
-       if (!gtk_combo_box_get_active_iter(combo_box, &iter)) {
+       if (!get_active_cylinder(combo_box, &iter)) {
                cylinder->changed = 1;
                return;
        }
@@ -134,58 +186,63 @@ static void cylinder_cb(GtkComboBox *combo_box, gpointer data)
        gtk_tree_model_get(model, &iter,
                CYL_SIZE, &ml,
                CYL_WORKP, &mbar,
-               CYL_STARTP, &start,
-               CYL_ENDP, &end,
                -1);
 
-       set_cylinder_spinbuttons(cylinder, ml, mbar, start, end);
+       set_cylinder_type_spinbuttons(cylinder, ml, mbar);
 }
 
-/*
- * The gtk_tree_model_foreach() interface is bad. It could have
- * returned whether the callback ever returned true
- */
-static int found_match = 0;
-
-static gboolean match_cylinder(GtkTreeModel *model,
-                               GtkTreePath *path,
-                               GtkTreeIter *iter,
-                               gpointer data)
-{
-       const char *name;
-       struct cylinder_widget *cylinder = data;
-       GValue value = {0, };
-
-       gtk_tree_model_get_value(model, iter, 0, &value);
-       name = g_value_get_string(&value);
-       if (strcmp(cylinder->name, name))
-               return FALSE;
-       gtk_combo_box_set_active_iter(cylinder->description, iter);
-       found_match = 1;
-       return TRUE;
-}
-
-static void add_cylinder(struct cylinder_widget *cylinder, const char *desc, int ml, int mbar)
+static GtkTreeIter *add_cylinder_type(const char *desc, int ml, int mbar, GtkTreeIter *iter)
 {
        GtkTreeModel *model;
 
-       found_match = 0;
-       model = gtk_combo_box_get_model(cylinder->description);
-       cylinder->name = desc;
-       gtk_tree_model_foreach(model, match_cylinder, cylinder);
+       /* Don't even bother adding stuff without a size */
+       if (!ml)
+               return NULL;
+
+       found_match = NULL;
+       model = GTK_TREE_MODEL(cylinder_model);
+       gtk_tree_model_foreach(model, match_cylinder, (void *)desc);
 
        if (!found_match) {
                GtkListStore *store = GTK_LIST_STORE(model);
-               GtkTreeIter iter;
 
-               gtk_list_store_append(store, &iter);
-               gtk_list_store_set(store, &iter,
+               gtk_list_store_append(store, iter);
+               gtk_list_store_set(store, iter,
                        0, desc,
                        1, ml,
                        2, mbar,
                        -1);
-               gtk_combo_box_set_active_iter(cylinder->description, &iter);
+               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.
+ */
+void add_cylinder_description(cylinder_type_t *type)
+{
+       GtkTreeIter iter;
+       const char *desc;
+       unsigned int size, workp;
+
+       desc = type->description;
+       if (!desc)
+               return;
+       size = type->size.mliter;
+       workp = type->workingpressure.mbar;
+       add_cylinder_type(desc, size, workp, &iter);
+}
+
+static void add_cylinder(struct cylinder_widget *cylinder, const char *desc, int ml, int mbar)
+{
+       GtkTreeIter iter, *match;
+
+       cylinder->name = desc;
+       match = add_cylinder_type(desc, ml, mbar, &iter);
+       if (match)
+               gtk_combo_box_set_active_iter(cylinder->description, match);
 }
 
 static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
@@ -205,8 +262,9 @@ static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
        mbar = cyl->type.workingpressure.mbar;
        add_cylinder(cylinder, desc, ml, mbar);
 
-       set_cylinder_spinbuttons(cylinder,
-               cyl->type.size.mliter, cyl->type.workingpressure.mbar,
+       set_cylinder_type_spinbuttons(cylinder,
+               cyl->type.size.mliter, cyl->type.workingpressure.mbar);
+       set_cylinder_pressure_spinbuttons(cylinder,
                cyl->start.mbar, cyl->end.mbar);
        o2 = cyl->gasmix.o2.permille / 10.0;
        gtk_widget_set_sensitive(cylinder->o2, !!o2);
@@ -430,9 +488,27 @@ static void nitrox_cb(GtkToggleButton *button, gpointer data)
        gtk_widget_set_sensitive(cylinder->o2, state);
 }
 
+static gboolean completion_cb(GtkEntryCompletion *widget, GtkTreeModel *model, GtkTreeIter *iter, struct cylinder_widget *cylinder)
+{
+       const char *desc;
+       unsigned int ml, mbar;
+
+       gtk_tree_model_get(model, iter, CYL_DESC, &desc, CYL_SIZE, &ml, CYL_WORKP, &mbar, -1);
+       add_cylinder(cylinder, desc, ml, mbar);
+       return TRUE;
+}
+
+static void cylinder_activate_cb(GtkComboBox *combo_box, gpointer data)
+{
+       struct cylinder_widget *cylinder = data;
+       cylinder_cb(cylinder->description, data);
+}
+
 static void cylinder_widget(GtkWidget *vbox, struct cylinder_widget *cylinder, GtkListStore *model)
 {
        GtkWidget *frame, *hbox;
+       GtkEntry *entry;
+       GtkEntryCompletion *completion;
        GtkWidget *widget;
 
        /*
@@ -450,6 +526,15 @@ static void cylinder_widget(GtkWidget *vbox, struct cylinder_widget *cylinder, G
        cylinder->description = GTK_COMBO_BOX(widget);
        g_signal_connect(widget, "changed", G_CALLBACK(cylinder_cb), cylinder);
 
+       entry = GTK_ENTRY(GTK_BIN(widget)->child);
+       g_signal_connect(entry, "activate", G_CALLBACK(cylinder_activate_cb), cylinder);
+
+       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(completion_cb), cylinder);
+       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);
diff --git a/info.c b/info.c
index aa38c053ecaefd74482a0ee6d46c98fc6c3b7b89..83ba09b2a7e68398e90bc86e1fab304ba6e09219 100644 (file)
--- a/info.c
+++ b/info.c
@@ -18,7 +18,7 @@
 #include "display-gtk.h"
 #include "divelist.h"
 
-static GtkEntry *location, *buddy, *divemaster;
+static GtkComboBoxEntry *location, *buddy, *divemaster;
 static GtkTextBuffer *notes;
 static int location_changed = 1, notes_changed = 1;
 static int divemaster_changed = 1, buddy_changed = 1;
@@ -50,8 +50,9 @@ void flush_dive_info_changes(struct dive *dive)
                return;
 
        if (location_changed) {
+               char *new_text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(location));
                old_text = dive->location;
-               dive->location = gtk_editable_get_chars(GTK_EDITABLE(location), 0, -1);
+               dive->location = new_text;
                if (text_changed(old_text,dive->location))
                        changed = 1;
                if (old_text)
@@ -59,8 +60,9 @@ void flush_dive_info_changes(struct dive *dive)
        }
 
        if (divemaster_changed) {
+               char *new_text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(divemaster));
                old_text = dive->divemaster;
-               dive->divemaster = gtk_editable_get_chars(GTK_EDITABLE(divemaster), 0, -1);
+               dive->divemaster = new_text;
                if (text_changed(old_text,dive->divemaster))
                        changed = 1;
                if (old_text)
@@ -68,8 +70,9 @@ void flush_dive_info_changes(struct dive *dive)
        }
 
        if (buddy_changed) {
+               char *new_text = gtk_combo_box_get_active_text(GTK_COMBO_BOX(buddy));
                old_text = dive->buddy;
-               dive->buddy = gtk_editable_get_chars(GTK_EDITABLE(buddy), 0, -1);
+               dive->buddy = new_text;
                if (text_changed(old_text,dive->buddy))
                        changed = 1;
                if (old_text)
@@ -88,8 +91,14 @@ void flush_dive_info_changes(struct dive *dive)
                mark_divelist_changed(TRUE);
 }
 
+static void set_combo_box_entry_text(GtkComboBoxEntry *combo_box, const char *text)
+{
+       GtkEntry *entry = GTK_ENTRY(GTK_BIN(combo_box)->child);
+       gtk_entry_set_text(entry, text);
+}
+
 #define SET_TEXT_ENTRY(x) \
-       gtk_entry_set_text(x, dive && dive->x ? dive->x : "")
+       set_combo_box_entry_text(x, dive && dive->x ? dive->x : "")
 
 void show_dive_info(struct dive *dive)
 {
@@ -123,17 +132,29 @@ void show_dive_info(struct dive *dive)
        gtk_text_buffer_set_text(notes, dive && dive->notes ? dive->notes : "", -1);
 }
 
-static GtkEntry *text_entry(GtkWidget *box, const char *label)
+static GtkComboBoxEntry *text_entry(GtkWidget *box, const char *label, GtkListStore *completions)
 {
-       GtkWidget *entry;
+       GtkEntry *entry;
+       GtkWidget *combo_box;
        GtkWidget *frame = gtk_frame_new(label);
+       GtkEntryCompletion *completion;
 
        gtk_box_pack_start(GTK_BOX(box), frame, FALSE, TRUE, 0);
 
-       entry = gtk_entry_new();
-       gtk_container_add(GTK_CONTAINER(frame), entry);
+       combo_box = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(completions), 0);
+       gtk_container_add(GTK_CONTAINER(frame), combo_box);
+
+       entry = GTK_ENTRY(GTK_BIN(combo_box)->child);
+
+       completion = gtk_entry_completion_new();
+       gtk_entry_completion_set_text_column(completion, 0);
+       gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(completions));
+       gtk_entry_completion_set_inline_completion(completion, TRUE);
+       gtk_entry_completion_set_inline_selection(completion, TRUE);
+       gtk_entry_completion_set_popup_single_match(completion, FALSE);
+       gtk_entry_set_completion(entry, completion);
 
-       return GTK_ENTRY(entry);
+       return GTK_COMBO_BOX_ENTRY(combo_box);
 }
 
 static GtkTextBuffer *text_view(GtkWidget *box, const char *label)
@@ -162,19 +183,93 @@ static GtkTextBuffer *text_view(GtkWidget *box, const char *label)
        return buffer;
 }
 
+static enum {
+       MATCH_EXACT,
+       MATCH_PREPEND,
+       MATCH_AFTER
+} found_string_entry;
+static GtkTreeIter string_entry_location;
+
+static gboolean match_string_entry(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+       const char *string = data;
+       char *entry;
+       int cmp;
+
+       gtk_tree_model_get(model, iter, 0, &entry, -1);
+       cmp = strcmp(entry, string);
+
+       /* Stop. The entry is bigger than the new one */
+       if (cmp > 0)
+               return TRUE;
+
+       /* Exact match */
+       if (!cmp) {
+               found_string_entry = MATCH_EXACT;
+               return TRUE;
+       }
+
+       string_entry_location = *iter;
+       found_string_entry = MATCH_AFTER;
+       return FALSE;
+}
+
+static int match_list(GtkListStore *list, const char *string)
+{
+       found_string_entry = MATCH_PREPEND;
+       gtk_tree_model_foreach(GTK_TREE_MODEL(list), match_string_entry, (void *)string);
+       return found_string_entry;
+}
+
+static GtkListStore *location_list, *people_list;
+
+static void add_string_list_entry(const char *string, GtkListStore *list)
+{
+       GtkTreeIter *iter, loc;
+
+       if (!string || !*string)
+               return;
+
+       switch (match_list(list, string)) {
+       case MATCH_EXACT:
+               return;
+       case MATCH_PREPEND:
+               iter = NULL;
+               break;
+       case MATCH_AFTER:
+               iter = &string_entry_location;
+               break;
+       }
+       gtk_list_store_insert_after(list, &loc, iter);
+       gtk_list_store_set(list, &loc, 0, string, -1);
+}
+
+void add_people(const char *string)
+{
+       add_string_list_entry(string, people_list);
+}
+
+void add_location(const char *string)
+{
+       add_string_list_entry(string, location_list);
+}
+
 GtkWidget *extended_dive_info_widget(void)
 {
        GtkWidget *vbox, *hbox;
        vbox = gtk_vbox_new(FALSE, 6);
 
+       people_list = gtk_list_store_new(1, G_TYPE_STRING);
+       location_list = gtk_list_store_new(1, G_TYPE_STRING);
+
        gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
-       location = text_entry(vbox, "Location");
+       location = text_entry(vbox, "Location", location_list);
 
        hbox = gtk_hbox_new(FALSE, 3);
        gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
 
-       divemaster = text_entry(hbox, "Divemaster");
-       buddy = text_entry(hbox, "Buddy");
+       divemaster = text_entry(hbox, "Divemaster", people_list);
+       buddy = text_entry(hbox, "Buddy", people_list);
 
        notes = text_view(vbox, "Notes");
        return vbox;
index 6c1e8ce426d63cc3f68262f9e61a75458492c1ad..5dad475bcbc850de7464064afd3acc50e0e38efa 100644 (file)
--- a/profile.c
+++ b/profile.c
@@ -880,6 +880,68 @@ static void fill_missing_tank_pressures(struct dive *dive, struct plot_info *pi,
        }
 }
 
+static int get_cylinder_index(struct dive *dive, struct event *ev)
+{
+       int i;
+
+       /*
+        * Try to find a cylinder that matches the O2 percentage
+        * in the gas change event 'value' field.
+        *
+        * Crazy suunto gas change events. We really should do
+        * this in libdivecomputer or something.
+        */
+       for (i = 0; i < MAX_CYLINDERS; i++) {
+               cylinder_t *cyl = dive->cylinder+i;
+               int o2 = (cyl->gasmix.o2.permille + 5) / 10;
+               if (o2 == ev->value)
+                       return i;
+       }
+
+       return 0;
+}
+
+static struct event *get_next_gaschange(struct event *event)
+{
+       while (event) {
+               if (!strcmp(event->name, "gaschange"))
+                       return event;
+               event = event->next;
+       }
+       return event;
+}
+
+static int set_cylinder_index(struct plot_info *pi, int i, int cylinderindex, unsigned int end)
+{
+       while (i < pi->nr) {
+               struct plot_data *entry = pi->entry+i;
+               if (entry->sec > end)
+                       break;
+               if (entry->cylinderindex != cylinderindex) {
+                       entry->cylinderindex = cylinderindex;
+                       entry->pressure[0] = 0;
+               }
+               i++;
+       }
+       return i;
+}
+
+static void check_gas_change_events(struct dive *dive, struct plot_info *pi)
+{
+       int i = 0, cylinderindex = 0;
+       struct event *ev = get_next_gaschange(dive->events);
+
+       if (!ev)
+               return;
+
+       do {
+               i = set_cylinder_index(pi, i, cylinderindex, ev->time.seconds);
+               cylinderindex = get_cylinder_index(dive, ev);
+               ev = get_next_gaschange(ev->next);
+       } while (ev);
+       set_cylinder_index(pi, i, cylinderindex, ~0u);
+}
+
 /*
  * Create a plot-info with smoothing and ranged min/max
  *
@@ -907,9 +969,6 @@ static struct plot_info *create_plot_info(struct dive *dive)
        sec = 0;
        lastindex = 0;
        lastdepth = -1;
-       for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) /* initialize the start pressures */
-               track_pr[cyl] = pr_track_alloc(dive->cylinder[cyl].start.mbar, -1);
-       current = track_pr[dive->sample[0].cylinderindex];
        for (i = 0; i < dive->samples; i++) {
                int depth;
                struct sample *sample = dive->sample+i;
@@ -917,9 +976,29 @@ static struct plot_info *create_plot_info(struct dive *dive)
                entry = pi->entry + i + 2;
                sec = entry->sec = sample->time.seconds;
                depth = entry->depth = sample->depth.mm;
-               entry->same_cylinder = sample->cylinderindex == cylinderindex;
-               entry->cylinderindex = cylinderindex = sample->cylinderindex;
+               entry->cylinderindex = sample->cylinderindex;
                SENSOR_PRESSURE(entry) = sample->cylinderpressure.mbar;
+               entry->temperature = sample->temperature.mkelvin;
+
+               if (depth || lastdepth)
+                       lastindex = i+2;
+
+               lastdepth = depth;
+               if (depth > pi->maxdepth)
+                       pi->maxdepth = depth;
+       }
+
+       check_gas_change_events(dive, pi);
+
+       for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) /* initialize the start pressures */
+               track_pr[cyl] = pr_track_alloc(dive->cylinder[cyl].start.mbar, -1);
+       current = track_pr[dive->sample[0].cylinderindex];
+       for (i = 0; i < dive->samples; i++) {
+               entry = pi->entry + i + 2;
+
+               entry->same_cylinder = entry->cylinderindex == cylinderindex;
+               cylinderindex = entry->cylinderindex;
+
                /* track the segments per cylinder and their pressure/time integral */
                if (!entry->same_cylinder) {
                        current->end = SENSOR_PRESSURE(entry-1);
@@ -941,15 +1020,8 @@ static struct plot_info *create_plot_info(struct dive *dive)
                current->pressure_time += (entry->sec - (entry-1)->sec) *
                                                (1 + entry->depth / 10000.0);
                missing_pr |= !SENSOR_PRESSURE(entry);
-               entry->temperature = sample->temperature.mkelvin;
-
-               if (depth || lastdepth)
-                       lastindex = i+2;
-
-               lastdepth = depth;
-               if (depth > pi->maxdepth)
-                       pi->maxdepth = depth;
        }
+
        current->t_end = entry->sec;
        for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { /* initialize the end pressures */
                int pr = dive->cylinder[cyl].end.mbar;