Allow date based grouping

This is the very first rough cut. It switches things over to a tree model
so we can have date based summary nodes.

It uses a DIVE_INDEX of -1 for summary nodes to easily tell them apart
from actual dives. All the data functions are changed so the summary
nodes only show the date they cover.

The commit also adds a couple of debug functions to be able to easily peek
into the model from the debugger.

Lots of things left to do. There is no longer a first dive selected when
starting subsurface. Sorting by columns other than date is messed up. We
almost certainly want month and year summary entries as well.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
Dirk Hohndel 2012-08-08 09:35:38 -07:00
parent 7fe652ab57
commit 1f3813eb3d

View file

@ -24,7 +24,7 @@
struct DiveList { struct DiveList {
GtkWidget *tree_view; GtkWidget *tree_view;
GtkWidget *container_widget; GtkWidget *container_widget;
GtkListStore *model; GtkTreeStore *model;
GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location;
GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu; GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu;
int changed; int changed;
@ -52,6 +52,26 @@ enum {
DIVELIST_COLUMNS DIVELIST_COLUMNS
}; };
#ifdef DEBUG_MODEL
static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
GtkTreeIter *iter, gpointer data)
{
char *location;
int idx, nr, rating, depth;
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_RATING, &rating, DIVE_DEPTH, &depth, DIVE_LOCATION, &location, -1);
printf("entry #%d : nr %d rating %d depth %d location %s \n", idx, nr, rating, depth, location);
free(location);
return FALSE;
}
static void dump_model(GtkListStore *store)
{
gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL);
}
#endif
static GList *selected_dives; static GList *selected_dives;
static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
@ -77,6 +97,8 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
path = g_list_nth_data(selected_dives, 0); path = g_list_nth_data(selected_dives, 0);
if (gtk_tree_model_get_iter(model, &iter, path)) { if (gtk_tree_model_get_iter(model, &iter, path)) {
gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value); gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value);
/* an index of -1 should mean select all dives from that day
* ===> still needs to be implemented */
selected_dive = g_value_get_int(&value); selected_dive = g_value_get_int(&value);
repaint_dive(); repaint_dive();
} }
@ -109,13 +131,17 @@ static void star_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int nr_stars; int nr_stars, idx;
char buffer[40]; char buffer[40];
gtk_tree_model_get(model, iter, DIVE_RATING, &nr_stars, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1);
if (idx == -1) {
*buffer = '\0';
} else {
if (nr_stars < 0 || nr_stars > 5) if (nr_stars < 0 || nr_stars > 5)
nr_stars = 0; nr_stars = 0;
snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]);
}
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
@ -125,17 +151,24 @@ static void date_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int val; int val, idx;
struct tm *tm; struct tm *tm;
time_t when; time_t when;
char buffer[40]; char buffer[40];
gtk_tree_model_get(model, iter, DIVE_DATE, &val, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1);
/* 2038 problem */ /* 2038 problem */
when = val; when = val;
tm = gmtime(&when); tm = gmtime(&when);
if (idx == -1)
snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900);
else
snprintf(buffer, sizeof(buffer), snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %02d:%02d", "%s, %s %d, %d %02d:%02d",
weekday(tm->tm_wday), weekday(tm->tm_wday),
@ -151,11 +184,14 @@ static void depth_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int depth, integer, frac, len; int depth, integer, frac, len, idx;
char buffer[40]; char buffer[40];
gtk_tree_model_get(model, iter, DIVE_DEPTH, &depth, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1);
if (idx == -1) {
*buffer = '\0';
} else {
switch (output_units.length) { switch (output_units.length) {
case METERS: case METERS:
/* To tenths of meters */ /* To tenths of meters */
@ -178,7 +214,7 @@ static void depth_data_func(GtkTreeViewColumn *col,
len = snprintf(buffer, sizeof(buffer), "%d", integer); len = snprintf(buffer, sizeof(buffer), "%d", integer);
if (frac >= 0) if (frac >= 0)
len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac);
}
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
@ -188,10 +224,13 @@ static void duration_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
unsigned int sec; unsigned int sec, idx;
char buffer[16]; char buffer[16];
gtk_tree_model_get(model, iter, DIVE_DURATION, &sec, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1);
if (idx == -1)
*buffer = '\0';
else
snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60);
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
@ -203,13 +242,13 @@ static void temperature_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int value; int value, idx;
char buffer[80]; char buffer[80];
gtk_tree_model_get(model, iter, DIVE_TEMPERATURE, &value, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1);
*buffer = 0; *buffer = 0;
if (value) { if (idx != -1 && value) {
double deg; double deg;
switch (output_units.temperature) { switch (output_units.temperature) {
case CELSIUS: case CELSIUS:
@ -227,6 +266,23 @@ static void temperature_data_func(GtkTreeViewColumn *col,
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
static void nr_data_func(GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
int idx, nr;
char buffer[40];
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1);
if (idx == -1)
*buffer = '\0';
else
snprintf(buffer, sizeof(buffer), "%d", nr);
g_object_set(renderer, "text", buffer, NULL);
}
/* /*
* Get "maximal" dive gas for a dive. * Get "maximal" dive gas for a dive.
* Rules: * Rules:
@ -309,6 +365,10 @@ static void nitrox_data_func(GtkTreeViewColumn *col,
struct dive *dive; struct dive *dive;
gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1);
if (index == -1) {
*buffer = '\0';
goto exit;
}
dive = get_dive(index); dive = get_dive(index);
get_dive_gas(dive, &o2, &he, &o2low); get_dive_gas(dive, &o2, &he, &o2low);
o2 = (o2 + 5) / 10; o2 = (o2 + 5) / 10;
@ -324,7 +384,7 @@ static void nitrox_data_func(GtkTreeViewColumn *col,
snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
else else
strcpy(buffer, "air"); strcpy(buffer, "air");
exit:
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
@ -335,16 +395,16 @@ static void sac_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int value; int value, idx;
const char *fmt; const char *fmt;
char buffer[16]; char buffer[16];
double sac; double sac;
gtk_tree_model_get(model, iter, DIVE_SAC, &value, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1);
if (!value) { if (idx == -1 || !value) {
g_object_set(renderer, "text", "", NULL); *buffer = '\0';
return; goto exit;
} }
sac = value / 1000.0; sac = value / 1000.0;
@ -358,7 +418,7 @@ static void sac_data_func(GtkTreeViewColumn *col,
break; break;
} }
snprintf(buffer, sizeof(buffer), fmt, sac); snprintf(buffer, sizeof(buffer), fmt, sac);
exit:
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
} }
@ -369,16 +429,14 @@ static void otu_data_func(GtkTreeViewColumn *col,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
int value; int value, idx;
char buffer[16]; char buffer[16];
gtk_tree_model_get(model, iter, DIVE_OTU, &value, -1); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1);
if (!value) {
g_object_set(renderer, "text", "", NULL);
return;
}
if (idx == -1 || !value)
*buffer = '\0';
else
snprintf(buffer, sizeof(buffer), "%d", value); snprintf(buffer, sizeof(buffer), "%d", value);
g_object_set(renderer, "text", buffer, NULL); g_object_set(renderer, "text", buffer, NULL);
@ -514,7 +572,7 @@ static void fill_one_dive(struct dive *dive,
get_cylinder(dive, &cylinder); get_cylinder(dive, &cylinder);
get_location(dive, &location); get_location(dive, &location);
gtk_list_store_set(GTK_LIST_STORE(model), iter, gtk_tree_store_set(GTK_TREE_STORE(model), iter,
DIVE_NR, dive->number, DIVE_NR, dive->number,
DIVE_LOCATION, location, DIVE_LOCATION, location,
DIVE_CYLINDER, cylinder, DIVE_CYLINDER, cylinder,
@ -529,12 +587,14 @@ static gboolean set_one_dive(GtkTreeModel *model,
GtkTreeIter *iter, GtkTreeIter *iter,
gpointer data) gpointer data)
{ {
GValue value = {0, }; int idx;
struct dive *dive; struct dive *dive;
/* Get the dive number */ /* Get the dive number */
gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value); gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
dive = get_dive(g_value_get_int(&value)); if (idx == -1)
return TRUE;
dive = get_dive(idx);
if (!dive) if (!dive)
return TRUE; return TRUE;
if (data && dive != data) if (data && dive != data)
@ -582,27 +642,79 @@ void update_dive_list_col_visibility(void)
return; return;
} }
static int new_day(struct dive *dive, struct dive **last_dive, time_t *tm_date)
{
if (!last_dive)
return TRUE;
if (!*last_dive) {
*last_dive = dive;
if (tm_date) {
struct tm *tm1 = gmtime(&dive->when);
tm1->tm_sec = 0;
tm1->tm_min = 0;
tm1->tm_hour = 0;
*tm_date = mktime(tm1);
}
return TRUE;
} else {
struct dive *ldive = *last_dive;
struct tm tm1, tm2;
(void) gmtime_r(&dive->when, &tm1);
(void) gmtime_r(&ldive->when, &tm2);
if (tm1.tm_year != tm2.tm_year ||
tm1.tm_mon != tm2.tm_mon ||
tm1.tm_mday != tm2.tm_mday) {
*last_dive = dive;
if (tm_date) {
tm1.tm_sec = 0;
tm1.tm_min = 0;
tm1.tm_hour = 0;
*tm_date = mktime(&tm1);
}
return TRUE;
}
}
return FALSE;
}
static void fill_dive_list(void) static void fill_dive_list(void)
{ {
int i; int i;
GtkTreeIter iter; GtkTreeIter iter, parent_iter, *parent = NULL;
GtkListStore *store; GtkTreeStore *store;
struct dive *last_dive = NULL;
time_t dive_date;
store = GTK_LIST_STORE(dive_list.model); store = GTK_TREE_STORE(dive_list.model);
i = dive_table.nr; i = dive_table.nr;
while (--i >= 0) { while (--i >= 0) {
struct dive *dive = dive_table.dives[i]; struct dive *dive = dive_table.dives[i];
if (new_day(dive, &last_dive, &dive_date))
{
gtk_tree_store_append(store, &parent_iter, NULL);
parent = &parent_iter;
gtk_tree_store_set(store, parent,
DIVE_INDEX, -1,
DIVE_NR, -1,
DIVE_DATE, dive_date,
DIVE_LOCATION, "",
DIVE_TEMPERATURE, 0,
DIVE_SAC, 0,
-1);
}
update_cylinder_related_info(dive); update_cylinder_related_info(dive);
gtk_list_store_append(store, &iter); gtk_tree_store_append(store, &iter, parent);
gtk_list_store_set(store, &iter, gtk_tree_store_set(store, &iter,
DIVE_INDEX, i, DIVE_INDEX, i,
DIVE_NR, dive->number, DIVE_NR, dive->number,
DIVE_DATE, dive->when, DIVE_DATE, dive->when,
DIVE_DEPTH, dive->maxdepth, DIVE_DEPTH, dive->maxdepth,
DIVE_DURATION, dive->duration.seconds, DIVE_DURATION, dive->duration.seconds,
DIVE_LOCATION, "location", DIVE_LOCATION, dive->location,
DIVE_RATING, dive->rating,
DIVE_TEMPERATURE, dive->watertemp.mkelvin, DIVE_TEMPERATURE, dive->watertemp.mkelvin,
DIVE_SAC, 0, DIVE_SAC, 0,
-1); -1);
@ -618,7 +730,7 @@ static void fill_dive_list(void)
void dive_list_update_dives(void) void dive_list_update_dives(void)
{ {
gtk_list_store_clear(GTK_LIST_STORE(dive_list.model)); gtk_tree_store_clear(GTK_TREE_STORE(dive_list.model));
fill_dive_list(); fill_dive_list();
repaint_dive(); repaint_dive();
} }
@ -630,7 +742,7 @@ static struct divelist_column {
unsigned int flags; unsigned int flags;
int *visible; int *visible;
} dl_column[] = { } dl_column[] = {
[DIVE_NR] = { "#", NULL, NULL, ALIGN_RIGHT | UNSORTABLE }, [DIVE_NR] = { "#", nr_data_func, NULL, ALIGN_RIGHT | UNSORTABLE },
[DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT }, [DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT },
[DIVE_RATING] = { UTF8_BLACKSTAR, star_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_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT },
@ -653,7 +765,7 @@ static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_c
unsigned int flags = col->flags; unsigned int flags = col->flags;
int *visible = col->visible; int *visible = col->visible;
GtkWidget *tree_view = dl->tree_view; GtkWidget *tree_view = dl->tree_view;
GtkListStore *model = dl->model; GtkTreeStore *model = dl->model;
GtkTreeViewColumn *ret; GtkTreeViewColumn *ret;
if (visible && !*visible) if (visible && !*visible)
@ -684,6 +796,8 @@ static void row_activated_cb(GtkTreeView *tree_view,
if (!gtk_tree_model_get_iter(model, &iter, path)) if (!gtk_tree_model_get_iter(model, &iter, path))
return; return;
gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1);
/* an index of -1 is special for the "group by date" entries */
if (index != -1)
edit_dive_info(get_dive(index)); edit_dive_info(get_dive(index));
} }
@ -734,7 +848,7 @@ GtkWidget *dive_list_create(void)
{ {
GtkTreeSelection *selection; GtkTreeSelection *selection;
dive_list.model = gtk_list_store_new(DIVELIST_COLUMNS, dive_list.model = gtk_tree_store_new(DIVELIST_COLUMNS,
G_TYPE_INT, /* index */ G_TYPE_INT, /* index */
G_TYPE_INT, /* nr */ G_TYPE_INT, /* nr */
G_TYPE_INT, /* Date */ G_TYPE_INT, /* Date */