]> git.tdb.fi Git - ext/subsurface.git/blob - divelist.c
Use renderer function for divelist depth field too
[ext/subsurface.git] / divelist.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5
6 #include "divelist.h"
7 #include "dive.h"
8 #include "display.h"
9
10 /*
11  * The dive list has the dive data in both string format (for showing)
12  * and in "raw" format (for sorting purposes)
13  */
14 enum {
15         DIVE_INDEX = 0,
16         DIVE_DATE,              /* time_t: dive->when */
17         DIVE_DEPTH,             /* int: dive->maxdepth in mm */
18         DIVE_DURATIONSTR,       /* "47" in minutes */
19         DIVE_DURATION,          /* int: in seconds */
20         DIVE_LOCATION,          /* "47" in minutes */
21         DIVE_TEMPSTR,           /* "78" in fahrenheit or whatever */
22         DIVE_TEMP,              /* int: in mkelvin */
23         DIVE_NITROXSTR,         /* "32.5" in percent */
24         DIVE_NITROX,            /* int: in permille */
25         DIVE_SACSTR,            /* "0.49" in cuft/min */
26         DIVE_SAC,               /* int: in ml/min or something */
27         DIVELIST_COLUMNS
28 };
29
30
31 static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
32 {
33         GtkTreeIter iter;
34         GValue value = {0, };
35
36         if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
37                 return;
38
39         gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value);
40         selected_dive = g_value_get_int(&value);
41         repaint_dive();
42 }
43
44 static const char *weekday(int wday)
45 {
46         static const char wday_array[7][4] = {
47                 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
48         };
49         return wday_array[wday];
50 }
51
52 static const char *monthname(int mon)
53 {
54         static const char month_array[12][4] = {
55                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
56                 "Jul", "Aug", "Oct", "Sep", "Nov", "Dec",
57         };
58         return month_array[mon];
59 }
60
61 static void date_data_func(GtkTreeViewColumn *col,
62                            GtkCellRenderer *renderer,
63                            GtkTreeModel *model,
64                            GtkTreeIter *iter,
65                            gpointer data)
66 {
67         int val;
68         struct tm *tm;
69         time_t when;
70         char buffer[40];
71
72         gtk_tree_model_get(model, iter, DIVE_DATE, &val, -1);
73
74         /* 2038 problem */
75         when = val;
76
77         tm = gmtime(&when);
78         snprintf(buffer, sizeof(buffer),
79                 "%s, %s %d, %d %02d:%02d",
80                 weekday(tm->tm_wday),
81                 monthname(tm->tm_mon),
82                 tm->tm_mday, tm->tm_year + 1900,
83                 tm->tm_hour, tm->tm_min);
84         g_object_set(renderer, "text", buffer, NULL);
85 }
86
87 static void depth_data_func(GtkTreeViewColumn *col,
88                             GtkCellRenderer *renderer,
89                             GtkTreeModel *model,
90                             GtkTreeIter *iter,
91                             gpointer data)
92 {
93         int depth, integer, frac, len;
94         char buffer[40];
95
96         gtk_tree_model_get(model, iter, DIVE_DEPTH, &depth, -1);
97
98         switch (output_units.length) {
99         case METERS:
100                 /* To tenths of meters */
101                 depth = (depth + 49) / 100;
102                 integer = depth / 10;
103                 frac = depth % 10;
104                 if (integer < 20)
105                         break;
106                 frac = -1;
107                 /* Rounding? */
108                 break;
109         case FEET:
110                 integer = mm_to_feet(depth) + 0.5;
111                 frac = -1;
112                 break;
113         default:
114                 return;
115         }
116         len = snprintf(buffer, sizeof(buffer), "%d", integer);
117         if (frac >= 0)
118                 len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac);
119
120         g_object_set(renderer, "text", buffer, NULL);
121 }
122
123 static void get_duration(struct dive *dive, int *val, char **str)
124 {
125         unsigned int sec = dive->duration.seconds;
126         char buffer[16];
127
128         *val = sec;
129         snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60);
130         *str = strdup(buffer);
131 }
132
133 static void get_location(struct dive *dive, char **str)
134 {
135         char buffer[16];
136
137         snprintf(buffer, sizeof(buffer), "%s", dive->location);
138         *str = strdup(buffer);
139 }
140
141 static void get_temp(struct dive *dive, int *val, char **str)
142 {
143         int value = dive->watertemp.mkelvin;
144         char buffer[80];
145
146         *val = value;
147         *str = "";
148         if (value) {
149                 double deg;
150                 switch (output_units.temperature) {
151                 case CELSIUS:
152                         deg = mkelvin_to_C(value);
153                         break;
154                 case FAHRENHEIT:
155                         deg = mkelvin_to_F(value);
156                         break;
157                 default:
158                         return;
159                 }
160                 snprintf(buffer, sizeof(buffer), "%.1f", deg);
161                 *str = strdup(buffer);
162         }
163 }
164
165 static void get_nitrox(struct dive *dive, int *val, char **str)
166 {
167         int value = dive->cylinder[0].gasmix.o2.permille;
168         char buffer[80];
169
170         *val = value;
171         *str = "";
172         if (value) {
173                 snprintf(buffer, sizeof(buffer), "%.1f", value/10.0);
174                 *str = strdup(buffer);
175         }
176 }
177
178 /*
179  * Return air usage (in liters).
180  */
181 static double calculate_airuse(struct dive *dive)
182 {
183         double airuse = 0;
184         int i;
185
186         for (i = 0; i < MAX_CYLINDERS; i++) {
187                 cylinder_t *cyl = dive->cylinder + i;
188                 int size = cyl->type.size.mliter;
189                 double kilo_atm;
190
191                 if (!size)
192                         continue;
193
194                 kilo_atm = (cyl->start.mbar - cyl->end.mbar) / 1013250.0;
195
196                 /* Liters of air at 1 atm == milliliters at 1k atm*/
197                 airuse += kilo_atm * size;
198         }
199         return airuse;
200 }
201
202 static void get_sac(struct dive *dive, int *val, char **str)
203 {
204         const double liters_per_cuft = 28.317;
205         double airuse, pressure, sac;
206         const char *fmt, *unit;
207         char buffer[20];
208
209         *val = 0;
210         *str = "";
211         airuse = calculate_airuse(dive);
212         if (!airuse)
213                 return;
214         if (!dive->duration.seconds)
215                 return;
216
217         /* Mean pressure in atm: 1 atm per 10m */
218         pressure = 1 + (dive->meandepth.mm / 10000.0);
219         sac = airuse / pressure * 60 / dive->duration.seconds;
220
221         /* milliliters per minute.. */
222         *val = sac * 1000;
223
224         switch (output_units.volume) {
225         case LITER:
226                 unit = "l";
227                 fmt = "%4.0f %s";
228                 break;
229         case CUFT:
230                 unit = "cuft";
231                 fmt = "%4.2f %s";
232                 airuse /= liters_per_cuft;
233                 sac /= liters_per_cuft;
234                 break;
235         }
236
237         snprintf(buffer, sizeof(buffer), fmt, sac, unit);
238         *str = strdup(buffer);
239 }
240
241 static void fill_one_dive(struct dive *dive,
242                           GtkTreeModel *model,
243                           GtkTreeIter *iter)
244 {
245         int duration, temp, nitrox, sac;
246         char *durationstr, *tempstr, *nitroxstr, *sacstr;
247         char *location;
248
249         get_duration(dive, &duration, &durationstr);
250         get_location(dive, &location);
251         get_temp(dive, &temp, &tempstr);
252         get_nitrox(dive, &nitrox, &nitroxstr);
253         get_sac(dive, &sac, &sacstr);
254
255         /*
256          * We only set the fields that changed: the strings.
257          * The core data itself is unaffected by units
258          */
259         gtk_list_store_set(GTK_LIST_STORE(model), iter,
260                 DIVE_DURATIONSTR, durationstr,
261                 DIVE_LOCATION, location,
262                 DIVE_TEMPSTR, tempstr,
263                 DIVE_TEMP, temp,
264                 DIVE_NITROXSTR, nitroxstr,
265                 DIVE_NITROX, nitrox,
266                 DIVE_SACSTR, sacstr,
267                 DIVE_SAC, sac,
268                 -1);
269 }
270
271 static gboolean set_one_dive(GtkTreeModel *model,
272                              GtkTreePath *path,
273                              GtkTreeIter *iter,
274                              gpointer data)
275 {
276         GValue value = {0, };
277         struct dive *dive;
278
279         /* Get the dive number */
280         gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value);
281         dive = get_dive(g_value_get_int(&value));
282         if (!dive)
283                 return TRUE;
284         if (data && dive != data)
285                 return FALSE;
286
287         fill_one_dive(dive, model, iter);
288         return dive == data;
289 }
290
291 void flush_divelist(struct DiveList *dive_list, struct dive *dive)
292 {
293         GtkTreeModel *model = GTK_TREE_MODEL(dive_list->model);
294
295         gtk_tree_model_foreach(model, set_one_dive, dive);
296 }
297
298 void update_dive_list_units(struct DiveList *dive_list)
299 {
300         const char *unit;
301         GtkTreeModel *model = GTK_TREE_MODEL(dive_list->model);
302
303         switch (output_units.length) {
304         case METERS:
305                 unit = "m";
306                 break;
307         case FEET:
308                 unit = "ft";
309                 break;
310         }
311         gtk_tree_view_column_set_title(dive_list->depth, unit);
312
313         gtk_tree_model_foreach(model, set_one_dive, NULL);
314 }
315
316 static void fill_dive_list(struct DiveList *dive_list)
317 {
318         int i;
319         GtkTreeIter iter;
320         GtkListStore *store;
321
322         store = GTK_LIST_STORE(dive_list->model);
323
324         for (i = 0; i < dive_table.nr; i++) {
325                 struct dive *dive = dive_table.dives[i];
326
327                 gtk_list_store_append(store, &iter);
328                 gtk_list_store_set(store, &iter,
329                         DIVE_INDEX, i,
330                         DIVE_DATE, dive->when,
331                         DIVE_DEPTH, dive->maxdepth,
332                         DIVE_DURATIONSTR, "duration",
333                         DIVE_DURATION, dive->duration.seconds,
334                         DIVE_LOCATION, "location",
335                         DIVE_TEMPSTR, "temp",
336                         DIVE_TEMP, dive->watertemp.mkelvin,
337                         DIVE_NITROXSTR, "21.0",
338                         DIVE_NITROX, dive->cylinder[0].gasmix.o2,
339                         DIVE_SACSTR, "sac",
340                         DIVE_SAC, 0,
341                         -1);
342         }
343
344         update_dive_list_units(dive_list);
345 }
346
347 void dive_list_update_dives(struct DiveList dive_list)
348 {
349         gtk_list_store_clear(GTK_LIST_STORE(dive_list.model));
350         fill_dive_list(&dive_list);
351         repaint_dive();
352 }
353
354 struct DiveList dive_list_create(void)
355 {
356         struct DiveList    dive_list;
357         GtkTreeSelection  *selection;
358         GtkCellRenderer   *renderer;
359         GtkTreeViewColumn *col;
360
361         dive_list.model = gtk_list_store_new(DIVELIST_COLUMNS,
362                                 G_TYPE_INT,                     /* index */
363                                 G_TYPE_INT,                     /* Date */
364                                 G_TYPE_INT,                     /* Depth */
365                                 G_TYPE_STRING, G_TYPE_INT,      /* Duration */
366                                 G_TYPE_STRING,                  /* Location */
367                                 G_TYPE_STRING, G_TYPE_INT,      /* Temperature */
368                                 G_TYPE_STRING, G_TYPE_INT,      /* Nitrox */
369                                 G_TYPE_STRING, G_TYPE_INT       /* SAC */
370                                 );
371         dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model));
372         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
373
374         gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE);
375         gtk_widget_set_size_request(dive_list.tree_view, 200, 100);
376
377         renderer = gtk_cell_renderer_text_new();
378         dive_list.date = col = gtk_tree_view_column_new();
379         gtk_tree_view_column_set_title(col, "Date");
380         gtk_tree_view_column_set_sort_column_id(col, DIVE_DATE);
381         gtk_tree_view_column_set_resizable (col, TRUE);
382         gtk_tree_view_column_pack_start(col, renderer, TRUE);
383         gtk_tree_view_column_set_cell_data_func(col, renderer, date_data_func, NULL, NULL);
384         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
385
386         renderer = gtk_cell_renderer_text_new();
387         dive_list.depth = col = gtk_tree_view_column_new();
388         gtk_tree_view_column_set_title(col, "ft");
389         gtk_tree_view_column_set_sort_column_id(col, DIVE_DEPTH);
390         gtk_tree_view_column_pack_start(col, renderer, FALSE);
391         gtk_tree_view_column_set_cell_data_func(col, renderer, depth_data_func, NULL, NULL);
392         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
393         gtk_object_set(GTK_OBJECT(renderer), "alignment", PANGO_ALIGN_RIGHT, NULL);
394         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 1.0, 0.5);
395
396         renderer = gtk_cell_renderer_text_new();
397         dive_list.duration = col = gtk_tree_view_column_new();
398         gtk_tree_view_column_set_title(col, "min");
399         gtk_tree_view_column_set_sort_column_id(col, DIVE_DURATION);
400         gtk_tree_view_column_pack_start(col, renderer, FALSE);
401         gtk_tree_view_column_add_attribute(col, renderer, "text", DIVE_DURATIONSTR);
402         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
403         gtk_object_set(GTK_OBJECT(renderer), "alignment", PANGO_ALIGN_RIGHT, NULL);
404         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 1.0, 0.5);
405
406         renderer = gtk_cell_renderer_text_new();
407         dive_list.location = col = gtk_tree_view_column_new();
408         gtk_tree_view_column_set_title(col, "Location");
409         gtk_tree_view_column_set_sort_column_id(col, DIVE_LOCATION);
410         gtk_tree_view_column_pack_start(col, renderer, FALSE);
411         gtk_tree_view_column_add_attribute(col, renderer, "text", DIVE_LOCATION);
412         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
413
414         renderer = gtk_cell_renderer_text_new();
415         dive_list.temperature = col = gtk_tree_view_column_new();
416         gtk_tree_view_column_set_title(col, "deg");
417         gtk_tree_view_column_set_sort_column_id(col, DIVE_TEMP);
418         gtk_tree_view_column_pack_start(col, renderer, FALSE);
419         gtk_tree_view_column_add_attribute(col, renderer, "text", DIVE_TEMPSTR);
420         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
421         gtk_object_set(GTK_OBJECT(renderer), "alignment", PANGO_ALIGN_RIGHT, NULL);
422         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 1.0, 0.5);
423
424         renderer = gtk_cell_renderer_text_new();
425         dive_list.nitrox = col = gtk_tree_view_column_new();
426         gtk_tree_view_column_set_title(col, "O2%");
427         gtk_tree_view_column_set_sort_column_id(col, DIVE_NITROX);
428         gtk_tree_view_column_pack_start(col, renderer, FALSE);
429         gtk_tree_view_column_add_attribute(col, renderer, "text", DIVE_NITROXSTR);
430         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
431         gtk_object_set(GTK_OBJECT(renderer), "alignment", PANGO_ALIGN_RIGHT, NULL);
432         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 1.0, 0.5);
433
434         renderer = gtk_cell_renderer_text_new();
435         dive_list.sac = col = gtk_tree_view_column_new();
436         gtk_tree_view_column_set_title(col, "SAC");
437         gtk_tree_view_column_set_sort_column_id(col, DIVE_SAC);
438         gtk_tree_view_column_pack_start(col, renderer, FALSE);
439         gtk_tree_view_column_add_attribute(col, renderer, "text", DIVE_SACSTR);
440         gtk_tree_view_append_column(GTK_TREE_VIEW(dive_list.tree_view), col);
441         gtk_object_set(GTK_OBJECT(renderer), "alignment", PANGO_ALIGN_RIGHT, NULL);
442         gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), 1.0, 0.5);
443
444         fill_dive_list(&dive_list);
445
446         g_object_set(G_OBJECT(dive_list.tree_view), "headers-visible", TRUE,
447                                           "search-column", 0,
448                                           "rules-hint", TRUE,
449                                           NULL);
450
451         g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model);
452
453         dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL);
454         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget),
455                                GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
456         gtk_container_add(GTK_CONTAINER(dive_list.container_widget), dive_list.tree_view);
457
458         return dive_list;
459 }