2 /* this creates the UI for the dive list -
3 * controlled through the following interfaces:
5 * void flush_divelist(struct dive *dive)
6 * GtkWidget dive_list_create(void)
7 * void dive_list_update_dives(void)
8 * void update_dive_list_units(void)
9 * void set_divelist_font(const char *font)
10 * void mark_divelist_changed(int changed)
11 * int unsaved_changes()
22 #include "display-gtk.h"
26 GtkWidget *container_widget;
27 GtkTreeStore *model, *listmodel, *treemodel;
28 GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location;
29 GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu;
33 static struct DiveList dive_list;
36 * The dive list has the dive data in both string format (for showing)
37 * and in "raw" format (for sorting purposes)
41 DIVE_NR, /* int: dive->nr */
42 DIVE_DATE, /* time_t: dive->when */
43 DIVE_RATING, /* int: 0-5 stars */
44 DIVE_DEPTH, /* int: dive->maxdepth in mm */
45 DIVE_DURATION, /* int: in seconds */
46 DIVE_TEMPERATURE, /* int: in mkelvin */
48 DIVE_NITROX, /* int: dummy */
49 DIVE_SAC, /* int: in ml/min */
50 DIVE_OTU, /* int: in OTUs */
51 DIVE_LOCATION, /* "2nd Cathedral, Lanai" */
55 /* magic numbers that indicate (as negative values) model entries that
56 * are summary entries for day / month / year */
62 static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
63 GtkTreeIter *iter, gpointer data)
66 int idx, nr, rating, depth;
68 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_RATING, &rating, DIVE_DEPTH, &depth, DIVE_LOCATION, &location, -1);
69 printf("entry #%d : nr %d rating %d depth %d location %s \n", idx, nr, rating, depth, location);
75 static void dump_model(GtkListStore *store)
77 gtk_tree_model_foreach(GTK_TREE_MODEL(store), dump_model_entry, NULL);
81 static GList *selected_dives;
83 static void selection_cb(GtkTreeSelection *selection, gpointer userdata)
88 int nr_selected = gtk_tree_selection_count_selected_rows(selection);
91 g_list_foreach (selected_dives, (GFunc) gtk_tree_path_free, NULL);
92 g_list_free (selected_dives);
94 selected_dives = gtk_tree_selection_get_selected_rows(selection, NULL);
96 switch (nr_selected) {
97 case 0: /* keep showing the last selected dive */
100 /* just pick that dive as selected */
102 path = g_list_nth_data(selected_dives, 0);
103 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) {
104 gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1);
105 /* a negative index means we picked a summary entry;
106 expand that entry and use first real child instead */
107 while (selected_dive < 0) {
110 memcpy(&parent, &iter, sizeof(parent));
111 tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(dive_list.model), &parent);
112 if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(dive_list.model), &iter, &parent))
113 /* we should never have a parent without child */
115 if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath))
116 gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath);
118 gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE);
119 gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1);
124 default: /* multiple selections - what now? At this point I
125 * don't want to change the selected dive unless
126 * there is exactly one dive selected; not sure this
127 * is the most intuitive solution.
128 * I do however want to keep around which dives have
130 amount_selected = g_list_length(selected_dives);
131 process_selected_dives(selected_dives, GTK_TREE_MODEL(dive_list.model));
137 const char *star_strings[] = {
146 static void star_data_func(GtkTreeViewColumn *col,
147 GtkCellRenderer *renderer,
155 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1);
159 if (nr_stars < 0 || nr_stars > 5)
161 snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]);
163 g_object_set(renderer, "text", buffer, NULL);
166 static void date_data_func(GtkTreeViewColumn *col,
167 GtkCellRenderer *renderer,
177 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1);
185 snprintf(buffer, sizeof(buffer),
187 weekday(tm->tm_wday),
188 monthname(tm->tm_mon),
189 tm->tm_mday, tm->tm_year + 1900);
192 snprintf(buffer, sizeof(buffer),
194 monthname(tm->tm_mon),
198 snprintf(buffer, sizeof(buffer),
199 "%d", tm->tm_year + 1900);
202 snprintf(buffer, sizeof(buffer),
203 "%s, %s %d, %d %02d:%02d",
204 weekday(tm->tm_wday),
205 monthname(tm->tm_mon),
206 tm->tm_mday, tm->tm_year + 1900,
207 tm->tm_hour, tm->tm_min);
209 g_object_set(renderer, "text", buffer, NULL);
212 static void depth_data_func(GtkTreeViewColumn *col,
213 GtkCellRenderer *renderer,
218 int depth, integer, frac, len, idx;
221 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DEPTH, &depth, -1);
226 switch (output_units.length) {
228 /* To tenths of meters */
229 depth = (depth + 49) / 100;
230 integer = depth / 10;
239 integer = mm_to_feet(depth) + 0.5;
245 len = snprintf(buffer, sizeof(buffer), "%d", integer);
247 len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac);
249 g_object_set(renderer, "text", buffer, NULL);
252 static void duration_data_func(GtkTreeViewColumn *col,
253 GtkCellRenderer *renderer,
262 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1);
266 snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60);
268 g_object_set(renderer, "text", buffer, NULL);
271 static void temperature_data_func(GtkTreeViewColumn *col,
272 GtkCellRenderer *renderer,
280 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_TEMPERATURE, &value, -1);
283 if (idx >= 0 && value) {
285 switch (output_units.temperature) {
287 deg = mkelvin_to_C(value);
290 deg = mkelvin_to_F(value);
295 snprintf(buffer, sizeof(buffer), "%.1f", deg);
298 g_object_set(renderer, "text", buffer, NULL);
301 static void nr_data_func(GtkTreeViewColumn *col,
302 GtkCellRenderer *renderer,
310 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, -1);
314 snprintf(buffer, sizeof(buffer), "%d", nr);
315 g_object_set(renderer, "text", buffer, NULL);
319 * Get "maximal" dive gas for a dive.
321 * - Trimix trumps nitrox (highest He wins, O2 breaks ties)
322 * - Nitrox trumps air (even if hypoxic)
323 * These are the same rules as the inter-dive sorting rules.
325 static void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
328 int maxo2 = -1, maxhe = -1, mino2 = 1000;
330 for (i = 0; i < MAX_CYLINDERS; i++) {
331 cylinder_t *cyl = dive->cylinder + i;
332 struct gasmix *mix = &cyl->gasmix;
333 int o2 = mix->o2.permille;
334 int he = mix->he.permille;
336 if (cylinder_none(cyl))
352 /* All air? Show/sort as "air"/zero */
353 if (!maxhe && maxo2 == AIR_PERMILLE && mino2 == maxo2)
360 static gint nitrox_sort_func(GtkTreeModel *model,
365 int index_a, index_b;
369 int a_o2low, b_o2low;
371 gtk_tree_model_get(model, iter_a, DIVE_INDEX, &index_a, -1);
372 gtk_tree_model_get(model, iter_b, DIVE_INDEX, &index_b, -1);
373 a = get_dive(index_a);
374 b = get_dive(index_b);
375 get_dive_gas(a, &a_o2, &a_he, &a_o2low);
376 get_dive_gas(b, &b_o2, &b_he, &b_o2low);
378 /* Sort by Helium first, O2 second */
381 return a_o2low - b_o2low;
387 #define UTF8_ELLIPSIS "\xE2\x80\xA6"
389 static void nitrox_data_func(GtkTreeViewColumn *col,
390 GtkCellRenderer *renderer,
395 int idx, o2, he, o2low;
399 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
404 dive = get_dive(idx);
405 get_dive_gas(dive, &o2, &he, &o2low);
408 o2low = (o2low + 5) / 10;
411 snprintf(buffer, sizeof(buffer), "%d/%d", o2, he);
414 snprintf(buffer, sizeof(buffer), "%d", o2);
416 snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
418 strcpy(buffer, "air");
420 g_object_set(renderer, "text", buffer, NULL);
423 /* Render the SAC data (integer value of "ml / min") */
424 static void sac_data_func(GtkTreeViewColumn *col,
425 GtkCellRenderer *renderer,
435 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_SAC, &value, -1);
437 if (idx < 0 || !value) {
442 sac = value / 1000.0;
443 switch (output_units.volume) {
449 sac = ml_to_cuft(sac * 1000);
452 snprintf(buffer, sizeof(buffer), fmt, sac);
454 g_object_set(renderer, "text", buffer, NULL);
457 /* Render the OTU data (integer value of "OTU") */
458 static void otu_data_func(GtkTreeViewColumn *col,
459 GtkCellRenderer *renderer,
467 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_OTU, &value, -1);
469 if (idx < 0 || !value)
472 snprintf(buffer, sizeof(buffer), "%d", value);
474 g_object_set(renderer, "text", buffer, NULL);
477 /* calculate OTU for a dive */
478 static int calculate_otu(struct dive *dive)
483 for (i = 1; i < dive->samples; i++) {
486 struct sample *sample = dive->sample + i;
487 struct sample *psample = sample - 1;
488 t = sample->time.seconds - psample->time.seconds;
489 int o2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille;
492 po2 = o2 / 1000.0 * (sample->depth.mm + 10000) / 10000.0;
494 otu += pow(po2 - 0.5, 0.83) * t / 30.0;
499 * Return air usage (in liters).
501 static double calculate_airuse(struct dive *dive)
506 for (i = 0; i < MAX_CYLINDERS; i++) {
507 pressure_t start, end;
508 cylinder_t *cyl = dive->cylinder + i;
509 int size = cyl->type.size.mliter;
515 start = cyl->start.mbar ? cyl->start : cyl->sample_start;
516 end = cyl->end.mbar ? cyl->end : cyl->sample_end;
517 kilo_atm = (to_ATM(start) - to_ATM(end)) / 1000.0;
519 /* Liters of air at 1 atm == milliliters at 1k atm*/
520 airuse += kilo_atm * size;
525 static int calculate_sac(struct dive *dive)
527 double airuse, pressure, sac;
530 airuse = calculate_airuse(dive);
533 if (!dive->duration.seconds)
536 /* find and eliminate long surface intervals */
537 duration = dive->duration.seconds;
538 for (i = 0; i < dive->samples; i++) {
539 if (dive->sample[i].depth.mm < 100) { /* less than 10cm */
541 while (end < dive->samples && dive->sample[end].depth.mm < 100)
543 /* we only want the actual surface time during a dive */
544 if (end < dive->samples) {
546 duration -= dive->sample[end].time.seconds -
547 dive->sample[i].time.seconds;
552 /* Mean pressure in atm: 1 atm per 10m */
553 pressure = 1 + (dive->meandepth.mm / 10000.0);
554 sac = airuse / pressure * 60 / duration;
556 /* milliliters per minute.. */
560 void update_cylinder_related_info(struct dive *dive)
563 dive->sac = calculate_sac(dive);
564 dive->otu = calculate_otu(dive);
568 static void get_string(char **str, const char *s)
584 static void get_location(struct dive *dive, char **str)
586 get_string(str, dive->location);
589 static void get_cylinder(struct dive *dive, char **str)
591 get_string(str, dive->cylinder[0].type.description);
595 * Set up anything that could have changed due to editing
596 * of dive information; we need to do this for both models,
597 * so we simply call set_one_dive again with the non-current model
599 /* forward declaration for recursion */
600 static gboolean set_one_dive(GtkTreeModel *model,
605 static void fill_one_dive(struct dive *dive,
609 char *location, *cylinder;
610 GtkTreeStore *othermodel;
612 get_cylinder(dive, &cylinder);
613 get_location(dive, &location);
615 gtk_tree_store_set(GTK_TREE_STORE(model), iter,
616 DIVE_NR, dive->number,
617 DIVE_LOCATION, location,
618 DIVE_CYLINDER, cylinder,
619 DIVE_RATING, dive->rating,
623 if (model == GTK_TREE_MODEL(dive_list.treemodel))
624 othermodel = dive_list.listmodel;
626 othermodel = dive_list.treemodel;
627 if (othermodel != dive_list.model)
629 gtk_tree_model_foreach(GTK_TREE_MODEL(othermodel), set_one_dive, dive);
632 static gboolean set_one_dive(GtkTreeModel *model,
640 /* Get the dive number */
641 gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
644 dive = get_dive(idx);
647 if (data && dive != data)
650 fill_one_dive(dive, model, iter);
654 void flush_divelist(struct dive *dive)
656 GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
658 gtk_tree_model_foreach(model, set_one_dive, dive);
661 void set_divelist_font(const char *font)
663 PangoFontDescription *font_desc = pango_font_description_from_string(font);
664 gtk_widget_modify_font(dive_list.tree_view, font_desc);
665 pango_font_description_free(font_desc);
668 void update_dive_list_units(void)
671 GtkTreeModel *model = GTK_TREE_MODEL(dive_list.model);
673 (void) get_depth_units(0, NULL, &unit);
674 gtk_tree_view_column_set_title(dive_list.depth, unit);
676 (void) get_temp_units(0, &unit);
677 gtk_tree_view_column_set_title(dive_list.temperature, unit);
679 gtk_tree_model_foreach(model, set_one_dive, NULL);
682 void update_dive_list_col_visibility(void)
684 gtk_tree_view_column_set_visible(dive_list.cylinder, visible_cols.cylinder);
685 gtk_tree_view_column_set_visible(dive_list.temperature, visible_cols.temperature);
686 gtk_tree_view_column_set_visible(dive_list.nitrox, visible_cols.nitrox);
687 gtk_tree_view_column_set_visible(dive_list.sac, visible_cols.sac);
688 gtk_tree_view_column_set_visible(dive_list.otu, visible_cols.otu);
692 static int new_date(struct dive *dive, struct dive **last_dive, const int flag, time_t *tm_date)
697 struct dive *ldive = *last_dive;
699 (void) gmtime_r(&dive->when, &tm1);
700 (void) gmtime_r(&ldive->when, &tm2);
701 if (tm1.tm_year == tm2.tm_year &&
702 (tm1.tm_mon == tm2.tm_mon || flag > NEW_MON) &&
703 (tm1.tm_mday == tm2.tm_mday || flag > NEW_DAY))
709 struct tm *tm1 = gmtime(&dive->when);
713 *tm_date = mktime(tm1);
718 static void fill_dive_list(void)
721 GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, };
722 GtkTreeStore *liststore, *treestore;
723 struct dive *last_dive = NULL;
726 treestore = GTK_TREE_STORE(dive_list.treemodel);
727 liststore = GTK_TREE_STORE(dive_list.listmodel);
731 struct dive *dive = dive_table.dives[i];
733 for (j = NEW_YR; j >= NEW_DAY; j--) {
734 if (new_date(dive, &last_dive, j, &dive_date))
736 gtk_tree_store_append(treestore, &parent_iter[j], parents[j+1]);
737 parents[j] = &parent_iter[j];
738 gtk_tree_store_set(treestore, parents[j],
741 DIVE_DATE, dive_date,
748 update_cylinder_related_info(dive);
749 gtk_tree_store_append(treestore, &iter, parents[NEW_DAY]);
750 gtk_tree_store_set(treestore, &iter,
752 DIVE_NR, dive->number,
753 DIVE_DATE, dive->when,
754 DIVE_DEPTH, dive->maxdepth,
755 DIVE_DURATION, dive->duration.seconds,
756 DIVE_LOCATION, dive->location,
757 DIVE_RATING, dive->rating,
758 DIVE_TEMPERATURE, dive->watertemp.mkelvin,
761 gtk_tree_store_append(liststore, &iter, NULL);
762 gtk_tree_store_set(liststore, &iter,
764 DIVE_NR, dive->number,
765 DIVE_DATE, dive->when,
766 DIVE_DEPTH, dive->maxdepth,
767 DIVE_DURATION, dive->duration.seconds,
768 DIVE_LOCATION, dive->location,
769 DIVE_RATING, dive->rating,
770 DIVE_TEMPERATURE, dive->watertemp.mkelvin,
775 update_dive_list_units();
776 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) {
777 GtkTreeSelection *selection;
778 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
779 gtk_tree_selection_select_iter(selection, &iter);
783 void dive_list_update_dives(void)
785 gtk_tree_store_clear(GTK_TREE_STORE(dive_list.treemodel));
786 gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel));
791 static struct divelist_column {
798 [DIVE_NR] = { "#", nr_data_func, NULL, ALIGN_RIGHT | UNSORTABLE },
799 [DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT },
800 [DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT },
801 [DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT },
802 [DIVE_DURATION] = { "min", duration_data_func, NULL, ALIGN_RIGHT },
803 [DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &visible_cols.temperature },
804 [DIVE_CYLINDER] = { "Cyl", NULL, NULL, 0, &visible_cols.cylinder },
805 [DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox },
806 [DIVE_SAC] = { "SAC", sac_data_func, NULL, 0, &visible_cols.sac },
807 [DIVE_OTU] = { "OTU", otu_data_func, NULL, 0, &visible_cols.otu },
808 [DIVE_LOCATION] = { "Location", NULL, NULL, ALIGN_LEFT },
812 static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_column *col)
814 int index = col - &dl_column[0];
815 const char *title = col->header;
816 data_func_t data_func = col->data;
817 sort_func_t sort_func = col->sort;
818 unsigned int flags = col->flags;
819 int *visible = col->visible;
820 GtkWidget *tree_view = dl->tree_view;
821 GtkTreeStore *model = dl->model;
822 GtkTreeViewColumn *ret;
824 if (visible && !*visible)
826 ret = tree_view_column(tree_view, index, title, data_func, flags);
828 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), index, sort_func, NULL, NULL);
833 * This is some crazy crap. The only way to get default focus seems
834 * to be to grab focus as the widget is being shown the first time.
836 static void realize_cb(GtkWidget *tree_view, gpointer userdata)
838 gtk_widget_grab_focus(tree_view);
841 static void row_activated_cb(GtkTreeView *tree_view,
843 GtkTreeViewColumn *column,
849 if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path))
851 gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &index, -1);
852 /* a negative index is special for the "group by date" entries */
854 edit_dive_info(get_dive(index));
857 void add_dive_cb(GtkWidget *menuitem, gpointer data)
862 if (add_new_dive(dive)) {
870 static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button)
872 GtkWidget *menu, *menuitem;
874 menu = gtk_menu_new();
875 menuitem = gtk_menu_item_new_with_label("Add dive");
876 g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL);
877 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
878 gtk_widget_show_all(menu);
880 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
881 button, gtk_get_current_event_time());
884 static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata)
886 popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0);
889 static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata)
891 /* Right-click? Bring up the menu */
892 if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
893 popup_divelist_menu(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(dive_list.model), 3);
899 /* If the sort column is date (default), show the tree model.
900 For every other sort column only show the list model.
901 If the model changed, inform the new model of the chosen sort column. */
902 static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data)
906 GtkTreeStore *currentmodel = dive_list.model;
908 gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order);
909 if(colid == DIVE_DATE)
910 dive_list.model = dive_list.treemodel;
912 dive_list.model = dive_list.listmodel;
913 if (dive_list.model != currentmodel) {
914 gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model));
915 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, order);
919 GtkWidget *dive_list_create(void)
921 GtkTreeSelection *selection;
923 dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS,
924 G_TYPE_INT, /* index */
926 G_TYPE_INT, /* Date */
927 G_TYPE_INT, /* Star rating */
928 G_TYPE_INT, /* Depth */
929 G_TYPE_INT, /* Duration */
930 G_TYPE_INT, /* Temperature */
931 G_TYPE_STRING, /* Cylinder */
932 G_TYPE_INT, /* Nitrox */
933 G_TYPE_INT, /* SAC */
934 G_TYPE_INT, /* OTU */
935 G_TYPE_STRING /* Location */
937 dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS,
938 G_TYPE_INT, /* index */
940 G_TYPE_INT, /* Date */
941 G_TYPE_INT, /* Star rating */
942 G_TYPE_INT, /* Depth */
943 G_TYPE_INT, /* Duration */
944 G_TYPE_INT, /* Temperature */
945 G_TYPE_STRING, /* Cylinder */
946 G_TYPE_INT, /* Nitrox */
947 G_TYPE_INT, /* SAC */
948 G_TYPE_INT, /* OTU */
949 G_TYPE_STRING /* Location */
951 dive_list.model = dive_list.treemodel;
952 dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model));
953 set_divelist_font(divelist_font);
955 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
957 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
958 gtk_widget_set_size_request(dive_list.tree_view, 200, 200);
960 dive_list.nr = divelist_column(&dive_list, dl_column + DIVE_NR);
961 dive_list.date = divelist_column(&dive_list, dl_column + DIVE_DATE);
962 dive_list.stars = divelist_column(&dive_list, dl_column + DIVE_RATING);
963 dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH);
964 dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION);
965 dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE);
966 dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER);
967 dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX);
968 dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC);
969 dive_list.otu = divelist_column(&dive_list, dl_column + DIVE_OTU);
970 dive_list.location = divelist_column(&dive_list, dl_column + DIVE_LOCATION);
974 g_object_set(G_OBJECT(dive_list.tree_view), "headers-visible", TRUE,
975 "search-column", DIVE_LOCATION,
979 g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL);
980 g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL);
981 g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL);
982 g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL);
983 g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), NULL);
984 g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
985 g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL);
987 dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL);
988 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget),
989 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
990 gtk_container_add(GTK_CONTAINER(dive_list.container_widget), dive_list.tree_view);
992 dive_list.changed = 0;
994 return dive_list.container_widget;
997 void mark_divelist_changed(int changed)
999 dive_list.changed = changed;
1002 int unsaved_changes()
1004 return dive_list.changed;