Add capability of custom sorts to divelist columns

.. and use this for the nitrox column, which can now be more complex
than just a single number.

The rule for the "nitrox" column is now:

 - we look up the highest Oxygen and Helium mix for the dive

   (Note: we look them up independently, so if you have a EAN50 deco
   bottle, and a 20% Helium low-oxygen bottle for the deep portion, then
   we'll consider the dive to be a "50% Oxygen, 20% Helium" dive, even
   though you obviously never used that combination at the same time)

 - we sort by Helium first, Oxygen second.  So a dive with a 10% Helium
   mix is considered to be "stronger" than a 50% Nitrox mix.

 - If Helium is non-zero, we show "O2/He", otherwise we show just "O2"
   (or "air").  So "21/20" means "21% oxygen, 20% Helium", while "40"
   means "Ean 40".

 - I got rid of the decimals.  We save them, and you can see them in the
   dive equipment details, but for the dive list we just use rounded
   percentages.

Let's see how many bugs I introduced.  I don't actually have any trimix
dives, but I edited a few for (very limited) testing.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2011-12-11 14:38:58 -08:00
parent 4df81487fe
commit 8cca540444
2 changed files with 105 additions and 22 deletions

View file

@ -57,6 +57,11 @@ typedef void (*data_func_t)(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data); gpointer data);
typedef gint (*sort_func_t)(GtkTreeModel *model,
GtkTreeIter *a,
GtkTreeIter *b,
gpointer user_data);
#define ALIGN_LEFT 1 #define ALIGN_LEFT 1
#define ALIGN_RIGHT 2 #define ALIGN_RIGHT 2
#define INVISIBLE 4 #define INVISIBLE 4

View file

@ -45,7 +45,7 @@ enum {
DIVE_DURATION, /* int: in seconds */ DIVE_DURATION, /* int: in seconds */
DIVE_TEMPERATURE, /* int: in mkelvin */ DIVE_TEMPERATURE, /* int: in mkelvin */
DIVE_CYLINDER, DIVE_CYLINDER,
DIVE_NITROX, /* int: in permille */ DIVE_NITROX, /* int: dummy */
DIVE_SAC, /* int: in ml/min */ DIVE_SAC, /* int: in ml/min */
DIVE_OTU, /* int: in OTUs */ DIVE_OTU, /* int: in OTUs */
DIVE_LOCATION, /* "2nd Cathedral, Lanai" */ DIVE_LOCATION, /* "2nd Cathedral, Lanai" */
@ -223,19 +223,66 @@ static void temperature_data_func(GtkTreeViewColumn *col,
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
/* Get max O2/He permille levels for a dive for the dive summary */
static void get_dive_gas(struct dive *dive, int *o2, int *he)
{
int i;
int maxo2 = -1, maxhe = -1;
for (i = 0; i < MAX_CYLINDERS; i++) {
struct gasmix *mix = &dive->cylinder[i].gasmix;
if (mix->o2.permille > maxo2)
maxo2 = mix->o2.permille;
if (mix->he.permille > maxhe)
maxhe = mix->he.permille;
}
*o2 = maxo2;
*he = maxhe;
}
static gint nitrox_sort_func(GtkTreeModel *model,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b,
gpointer user_data)
{
int index_a, index_b;
struct dive *a, *b;
int a_o2, b_o2;
int a_he, b_he;
gtk_tree_model_get(model, iter_a, DIVE_INDEX, &index_a, -1);
gtk_tree_model_get(model, iter_b, DIVE_INDEX, &index_b, -1);
a = get_dive(index_a);
b = get_dive(index_b);
get_dive_gas(a, &a_o2, &a_he);
get_dive_gas(b, &b_o2, &b_he);
/* Sort by Helium first, O2 second */
if (a_he == b_he)
return a_o2 - b_o2;
return a_he - b_he;
}
static void nitrox_data_func(GtkTreeViewColumn *col, static void nitrox_data_func(GtkTreeViewColumn *col,
GtkCellRenderer *renderer, GtkCellRenderer *renderer,
GtkTreeModel *model, GtkTreeModel *model,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int value; int index, o2, he;
char buffer[80]; char buffer[80];
struct dive *dive;
gtk_tree_model_get(model, iter, DIVE_NITROX, &value, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1);
dive = get_dive(index);
get_dive_gas(dive, &o2, &he);
o2 = (o2 + 5) / 10;
he = (he + 5) / 10;
if (value) if (he)
snprintf(buffer, sizeof(buffer), "%.1f", value/10.0); snprintf(buffer, sizeof(buffer), "%d/%d", o2, he);
else if (o2)
snprintf(buffer, sizeof(buffer), "%d", o2);
else else
strcpy(buffer, "air"); strcpy(buffer, "air");
@ -433,7 +480,6 @@ static void fill_one_dive(struct dive *dive,
DIVE_RATING, dive->rating, DIVE_RATING, dive->rating,
DIVE_SAC, dive->sac, DIVE_SAC, dive->sac,
DIVE_OTU, dive->otu, DIVE_OTU, dive->otu,
DIVE_NITROX, dive->cylinder[0].gasmix.o2,
-1); -1);
} }
@ -517,7 +563,6 @@ static void fill_dive_list(void)
DIVE_DURATION, dive->duration.seconds, DIVE_DURATION, dive->duration.seconds,
DIVE_LOCATION, "location", DIVE_LOCATION, "location",
DIVE_TEMPERATURE, dive->watertemp.mkelvin, DIVE_TEMPERATURE, dive->watertemp.mkelvin,
DIVE_NITROX, dive->cylinder[0].gasmix.o2,
DIVE_SAC, 0, DIVE_SAC, 0,
-1); -1);
} }
@ -537,12 +582,45 @@ void dive_list_update_dives(void)
repaint_dive(); repaint_dive();
} }
static GtkTreeViewColumn *divelist_column(struct DiveList *dl, int index, const char *title, static struct divelist_column {
data_func_t data_func, unsigned int flags, int visible) const char *header;
data_func_t data;
sort_func_t sort;
unsigned int flags;
int *visible;
} column[] = {
[DIVE_NR] = { "#", NULL, NULL, ALIGN_RIGHT | UNSORTABLE },
[DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT },
[DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT },
[DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT },
[DIVE_DURATION] = { "min", duration_data_func, NULL, ALIGN_RIGHT },
[DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &visible_cols.temperature },
[DIVE_CYLINDER] = { "Cyl", NULL, NULL, 0, &visible_cols.cylinder },
[DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox },
[DIVE_SAC] = { "SAC", sac_data_func, NULL, 0, &visible_cols.sac },
[DIVE_OTU] = { "OTU", otu_data_func, NULL, 0, &visible_cols.otu },
[DIVE_LOCATION] = { "Location", NULL, NULL, ALIGN_LEFT },
};
static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_column *col)
{ {
if (!visible) int index = col - &column[0];
const char *title = col->header;
data_func_t data_func = col->data;
sort_func_t sort_func = col->sort;
unsigned int flags = col->flags;
int *visible = col->visible;
GtkWidget *tree_view = dl->tree_view;
GtkListStore *model = dl->model;
GtkTreeViewColumn *ret;
if (visible && !*visible)
flags |= INVISIBLE; flags |= INVISIBLE;
return tree_view_column(dl->tree_view, index, title, data_func, flags); ret = tree_view_column(tree_view, index, title, data_func, flags);
if (sort_func)
gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), index, sort_func, NULL, NULL);
return ret;
} }
/* /*
@ -594,17 +672,17 @@ GtkWidget *dive_list_create(void)
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
gtk_widget_set_size_request(dive_list.tree_view, 200, 200); gtk_widget_set_size_request(dive_list.tree_view, 200, 200);
dive_list.nr = divelist_column(&dive_list, DIVE_NR, "#", NULL, ALIGN_RIGHT | UNSORTABLE, TRUE); dive_list.nr = divelist_column(&dive_list, column + DIVE_NR);
dive_list.date = divelist_column(&dive_list, DIVE_DATE, "Date", date_data_func, ALIGN_LEFT, TRUE); dive_list.date = divelist_column(&dive_list, column + DIVE_DATE);
dive_list.stars = divelist_column(&dive_list, DIVE_RATING, UTF8_BLACKSTAR, star_data_func, ALIGN_LEFT, TRUE); dive_list.stars = divelist_column(&dive_list, column + DIVE_RATING);
dive_list.depth = divelist_column(&dive_list, DIVE_DEPTH, "ft", depth_data_func, ALIGN_RIGHT, TRUE); dive_list.depth = divelist_column(&dive_list, column + DIVE_DEPTH);
dive_list.duration = divelist_column(&dive_list, DIVE_DURATION, "min", duration_data_func, ALIGN_RIGHT, TRUE); dive_list.duration = divelist_column(&dive_list, column + DIVE_DURATION);
dive_list.temperature = divelist_column(&dive_list, DIVE_TEMPERATURE, UTF8_DEGREE "F", temperature_data_func, ALIGN_RIGHT, visible_cols.temperature); dive_list.temperature = divelist_column(&dive_list, column + DIVE_TEMPERATURE);
dive_list.cylinder = divelist_column(&dive_list, DIVE_CYLINDER, "Cyl", NULL, 0, visible_cols.cylinder); dive_list.cylinder = divelist_column(&dive_list, column + DIVE_CYLINDER);
dive_list.nitrox = divelist_column(&dive_list, DIVE_NITROX, "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, 0, visible_cols.nitrox); dive_list.nitrox = divelist_column(&dive_list, column + DIVE_NITROX);
dive_list.sac = divelist_column(&dive_list, DIVE_SAC, "SAC", sac_data_func, 0, visible_cols.sac); dive_list.sac = divelist_column(&dive_list, column + DIVE_SAC);
dive_list.otu = divelist_column(&dive_list, DIVE_OTU, "OTU", otu_data_func, 0, visible_cols.otu); dive_list.otu = divelist_column(&dive_list, column + DIVE_OTU);
dive_list.location = divelist_column(&dive_list, DIVE_LOCATION, "Location", NULL, ALIGN_LEFT, TRUE); dive_list.location = divelist_column(&dive_list, column + DIVE_LOCATION);
fill_dive_list(); fill_dive_list();