diff options
-rw-r--r-- | display-gtk.h | 7 | ||||
-rw-r--r-- | dive.h | 7 | ||||
-rw-r--r-- | divelist.c | 573 | ||||
-rw-r--r-- | equipment.c | 158 | ||||
-rw-r--r-- | gtk-gui.c | 2 | ||||
-rw-r--r-- | info.c | 109 | ||||
-rw-r--r-- | main.c | 2 | ||||
-rw-r--r-- | statistics.c | 26 |
8 files changed, 669 insertions, 215 deletions
diff --git a/display-gtk.h b/display-gtk.h index 37326d590..059c6aa23 100644 --- a/display-gtk.h +++ b/display-gtk.h @@ -65,16 +65,17 @@ extern void update_progressbar_text(progressbar_t *progress, const char *text); extern GtkWidget *dive_profile_widget(void); extern GtkWidget *dive_info_frame(void); extern GtkWidget *extended_dive_info_widget(void); -extern GtkWidget *equipment_widget(void); +extern GtkWidget *equipment_widget(int w_idx); extern GtkWidget *single_stats_widget(void); extern GtkWidget *total_stats_widget(void); -extern GtkWidget *cylinder_list_widget(void); +extern GtkWidget *cylinder_list_widget(int w_idx); +extern GtkWidget *weightsystem_list_widget(int w_idx); extern GtkWidget *dive_list_create(void); unsigned int amount_selected; -extern void process_selected_dives(GList *, GtkTreeModel *); +extern void process_selected_dives(GList *, int *, GtkTreeModel *); typedef void (*data_func_t)(GtkTreeViewColumn *col, GtkCellRenderer *renderer, @@ -228,6 +228,8 @@ struct event { #define MAX_CYLINDERS (8) #define MAX_WEIGHTSYSTEMS (4) +#define W_IDX_PRIMARY 0 +#define W_IDX_SECONDARY 1 struct dive { int number; @@ -284,7 +286,7 @@ extern int selected_dive; static inline struct dive *get_dive(unsigned int nr) { - if (nr >= dive_table.nr) + if (nr >= dive_table.nr || nr < 0) return NULL; return dive_table.dives[nr]; } @@ -301,7 +303,7 @@ extern xmlDoc *test_xslt_transforms(xmlDoc *doc); extern void show_dive_info(struct dive *); -extern void show_dive_equipment(struct dive *); +extern void show_dive_equipment(struct dive *, int w_idx); extern void show_dive_stats(struct dive *); @@ -349,6 +351,7 @@ extern void evn_foreach(void (*callback)(const char *, int *, void *), void *dat extern int add_new_dive(struct dive *dive); extern int edit_dive_info(struct dive *dive); +extern int edit_multi_dive_info(int nr, int *indices); extern void dive_list_update_dives(void); extern void flush_divelist(struct dive *dive); diff --git a/divelist.c b/divelist.c index 36c5883b1..a85ac7c5e 100644 --- a/divelist.c +++ b/divelist.c @@ -24,7 +24,7 @@ struct DiveList { GtkWidget *tree_view; GtkWidget *container_widget; - GtkListStore *model; + GtkTreeStore *model, *listmodel, *treemodel; GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location; GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu; int changed; @@ -54,12 +54,101 @@ enum { DIVELIST_COLUMNS }; +/* magic numbers that indicate (as negative values) model entries that + * are summary entries for a divetrip */ +#define NEW_TRIP 1 + +#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 int *selectiontracker; + +/* when subsurface starts we want to have the last dive selected. So we simply + walk to the first leaf (and skip the summary entries - which have negative + DIVE_INDEX) */ +static void first_leaf(GtkTreeModel *model, GtkTreeIter *iter, int *diveidx) +{ + GtkTreeIter parent; + GtkTreePath *tpath; + + while (*diveidx < 0) { + memcpy(&parent, iter, sizeof(parent)); + tpath = gtk_tree_model_get_path(model, &parent); + if (!gtk_tree_model_iter_children(model, iter, &parent)) + /* we should never have a parent without child */ + return; + if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + gtk_tree_model_get(GTK_TREE_MODEL(model), iter, DIVE_INDEX, diveidx, -1); + } +} + +/* if we click on a summary dive, we actually want to select / unselect + all the dives "below" it */ +static void select_children(GtkTreeModel *model, GtkTreeSelection * selection, + GtkTreeIter *iter, gboolean was_selected) +{ + int i, nr_children; + GtkTreeIter parent; + GtkTreePath *tpath; -static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) + memcpy(&parent, iter, sizeof(parent)); + + tpath = gtk_tree_model_get_path(model, &parent); + if(!gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath)) + gtk_tree_view_expand_row(GTK_TREE_VIEW(dive_list.tree_view), tpath, FALSE); + + nr_children = gtk_tree_model_iter_n_children(model, &parent); + for (i = 0; i < nr_children; i++) { + gtk_tree_model_iter_nth_child(model, iter, &parent, i); + if (was_selected) + gtk_tree_selection_unselect_iter(selection, iter); + else + gtk_tree_selection_select_iter(selection, iter); + } +} + +/* this is called _before_ the selection is changed, for every single entry; + * we simply have it call down the tree to make sure that summary items toggle + * their children */ +gboolean modify_selection_cb(GtkTreeSelection *selection, GtkTreeModel *model, + GtkTreePath *path, gboolean was_selected, gpointer userdata) +{ + GtkTreeIter iter; + int dive_idx; + + if (gtk_tree_model_get_iter(model, &iter, path)) { + gtk_tree_model_get(model, &iter, DIVE_INDEX, &dive_idx, -1); + if (dive_idx < 0) { + select_children(model, selection, &iter, was_selected); + } + } + /* allow this selection to proceed */ + return TRUE; +} + +/* this is called when gtk thinks that the selection has changed */ +static void selection_cb(GtkTreeSelection *selection, gpointer userdata) { GtkTreeIter iter; - GValue value = {0, }; GtkTreePath *path; int nr_selected = gtk_tree_selection_count_selected_rows(selection); @@ -69,28 +158,36 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model) g_list_free (selected_dives); } selected_dives = gtk_tree_selection_get_selected_rows(selection, NULL); + selectiontracker = realloc(selectiontracker, nr_selected * sizeof(int)); switch (nr_selected) { - case 0: /* keep showing the last selected dive */ + case 0: /* there is no clear way to figure out which dive to show */ + amount_selected = 0; + selected_dive = -1; return; case 1: /* just pick that dive as selected */ amount_selected = 1; path = g_list_nth_data(selected_dives, 0); - if (gtk_tree_model_get_iter(model, &iter, path)) { - gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value); - selected_dive = g_value_get_int(&value); + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) { + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); + /* due to the way this callback gets invoked it is possible that + in the process of unselecting a summary dive we get here with + just one summary dive selected - ignore that case */ + if (selected_dive < 0) { + amount_selected = 0; + return; + } + selectiontracker[0] = selected_dive; repaint_dive(); } return; - default: /* multiple selections - what now? At this point I - * don't want to change the selected dive unless - * there is exactly one dive selected; not sure this + default: /* multiple selections - what now? + * We don't change the selected dive unless there is exactly one dive selected; not sure this * is the most intuitive solution. - * I do however want to keep around which dives have - * been selected */ + * The dives that have been selected are processed */ amount_selected = g_list_length(selected_dives); - process_selected_dives(selected_dives, model); + process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model)); repaint_dive(); return; } @@ -111,13 +208,17 @@ static void star_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int nr_stars; + int nr_stars, idx; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_RATING, &nr_stars, -1); - if (nr_stars < 0 || nr_stars > 5) - nr_stars = 0; - snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_RATING, &nr_stars, -1); + if (idx < 0) { + *buffer = '\0'; + } else { + if (nr_stars < 0 || nr_stars > 5) + nr_stars = 0; + snprintf(buffer, sizeof(buffer), "%s", star_strings[nr_stars]); + } g_object_set(renderer, "text", buffer, NULL); } @@ -127,23 +228,33 @@ static void date_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int val; + int val, idx; struct tm *tm; time_t when; 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 */ when = val; tm = gmtime(&when); - snprintf(buffer, sizeof(buffer), - "%s, %s %d, %d %02d:%02d", - weekday(tm->tm_wday), - monthname(tm->tm_mon), - tm->tm_mday, tm->tm_year + 1900, - tm->tm_hour, tm->tm_min); + switch(idx) { + case -NEW_TRIP: + snprintf(buffer, sizeof(buffer), + "Trip %s, %s %d, %d", + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900); + break; + default: + snprintf(buffer, sizeof(buffer), + "%s, %s %d, %d %02d:%02d", + weekday(tm->tm_wday), + monthname(tm->tm_mon), + tm->tm_mday, tm->tm_year + 1900, + tm->tm_hour, tm->tm_min); + } g_object_set(renderer, "text", buffer, NULL); } @@ -153,34 +264,37 @@ static void depth_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int depth, integer, frac, len; + int depth, integer, frac, len, idx; 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); - switch (output_units.length) { - case METERS: - /* To tenths of meters */ - depth = (depth + 49) / 100; - integer = depth / 10; - frac = depth % 10; - if (integer < 20) + if (idx < 0) { + *buffer = '\0'; + } else { + switch (output_units.length) { + case METERS: + /* To tenths of meters */ + depth = (depth + 49) / 100; + integer = depth / 10; + frac = depth % 10; + if (integer < 20) + break; + if (frac >= 5) + integer++; + frac = -1; break; - if (frac >= 5) - integer++; - frac = -1; - break; - case FEET: - integer = mm_to_feet(depth) + 0.5; - frac = -1; - break; - default: - return; + case FEET: + integer = mm_to_feet(depth) + 0.5; + frac = -1; + break; + default: + return; + } + len = snprintf(buffer, sizeof(buffer), "%d", integer); + if (frac >= 0) + len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); } - len = snprintf(buffer, sizeof(buffer), "%d", integer); - if (frac >= 0) - len += snprintf(buffer+len, sizeof(buffer)-len, ".%d", frac); - g_object_set(renderer, "text", buffer, NULL); } @@ -191,10 +305,14 @@ static void duration_data_func(GtkTreeViewColumn *col, gpointer data) { unsigned int sec; + int idx; char buffer[16]; - gtk_tree_model_get(model, iter, DIVE_DURATION, &sec, -1); - snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DURATION, &sec, -1); + if (idx < 0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d:%02d", sec / 60, sec % 60); g_object_set(renderer, "text", buffer, NULL); } @@ -205,13 +323,13 @@ static void temperature_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; 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; - if (value) { + if (idx >= 0 && value) { double deg; switch (output_units.temperature) { case CELSIUS: @@ -229,6 +347,23 @@ static void temperature_data_func(GtkTreeViewColumn *col, 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 < 0) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", nr); + g_object_set(renderer, "text", buffer, NULL); +} + /* * Get "maximal" dive gas for a dive. * Rules: @@ -338,12 +473,16 @@ static void nitrox_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int index, o2, he, o2low; + int idx, o2, he, o2low; char buffer[80]; struct dive *dive; - gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1); - dive = get_dive(index); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) { + *buffer = '\0'; + goto exit; + } + dive = get_dive(idx); get_dive_gas(dive, &o2, &he, &o2low); o2 = (o2 + 5) / 10; he = (he + 5) / 10; @@ -358,7 +497,7 @@ static void nitrox_data_func(GtkTreeViewColumn *col, snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2); else strcpy(buffer, "air"); - +exit: g_object_set(renderer, "text", buffer, NULL); } @@ -369,16 +508,16 @@ static void sac_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; const char *fmt; char buffer[16]; 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) { - g_object_set(renderer, "text", "", NULL); - return; + if (idx < 0 || !value) { + *buffer = '\0'; + goto exit; } sac = value / 1000.0; @@ -392,7 +531,7 @@ static void sac_data_func(GtkTreeViewColumn *col, break; } snprintf(buffer, sizeof(buffer), fmt, sac); - +exit: g_object_set(renderer, "text", buffer, NULL); } @@ -403,17 +542,15 @@ static void otu_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int value; + int value, idx; 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; - } - - snprintf(buffer, sizeof(buffer), "%d", value); + if (idx < 0 || !value) + *buffer = '\0'; + else + snprintf(buffer, sizeof(buffer), "%d", value); g_object_set(renderer, "text", buffer, NULL); } @@ -542,19 +679,27 @@ static void get_suit(struct dive *dive, char **str) /* * Set up anything that could have changed due to editing - * of dive information + * of dive information; we need to do this for both models, + * so we simply call set_one_dive again with the non-current model */ +/* forward declaration for recursion */ +static gboolean set_one_dive(GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + static void fill_one_dive(struct dive *dive, GtkTreeModel *model, GtkTreeIter *iter) { char *location, *cylinder, *suit; + GtkTreeStore *othermodel; get_cylinder(dive, &cylinder); get_location(dive, &location); get_suit(dive, &suit); - gtk_list_store_set(GTK_LIST_STORE(model), iter, + gtk_tree_store_set(GTK_TREE_STORE(model), iter, DIVE_NR, dive->number, DIVE_LOCATION, location, DIVE_CYLINDER, cylinder, @@ -568,6 +713,14 @@ static void fill_one_dive(struct dive *dive, free(location); free(cylinder); free(suit); + + if (model == GTK_TREE_MODEL(dive_list.treemodel)) + othermodel = dive_list.listmodel; + else + othermodel = dive_list.treemodel; + if (othermodel != dive_list.model) + /* recursive call */ + gtk_tree_model_foreach(GTK_TREE_MODEL(othermodel), set_one_dive, dive); } static gboolean set_one_dive(GtkTreeModel *model, @@ -575,12 +728,14 @@ static gboolean set_one_dive(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - GValue value = {0, }; + int idx; struct dive *dive; /* Get the dive number */ - gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value); - dive = get_dive(g_value_get_int(&value)); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + if (idx < 0) + return FALSE; + dive = get_dive(idx); if (!dive) return TRUE; if (data && dive != data) @@ -633,27 +788,90 @@ void update_dive_list_col_visibility(void) return; } +/* random heuristic - not diving in three days implies new dive trip */ +#define TRIP_THRESHOLD 3600*24*3 +static int new_group(struct dive *dive, struct dive **last_dive, time_t *tm_date) +{ + if (!last_dive) + return TRUE; + if (*last_dive) { + struct dive *ldive = *last_dive; + if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) { + *last_dive = dive; + return FALSE; + } + } + *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; +} + static void fill_dive_list(void) { int i; - GtkTreeIter iter; - GtkListStore *store; + GtkTreeIter iter, parent_iter; + GtkTreeStore *liststore, *treestore; + struct dive *last_dive = NULL; + struct dive *first_trip_dive = NULL; + struct dive *last_trip_dive = NULL; + time_t dive_date; - store = GTK_LIST_STORE(dive_list.model); + treestore = GTK_TREE_STORE(dive_list.treemodel); + liststore = GTK_TREE_STORE(dive_list.listmodel); i = dive_table.nr; while (--i >= 0) { struct dive *dive = dive_table.dives[i]; + if (new_group(dive, &last_dive, &dive_date)) + { + /* make sure we display the first date of the trip in previous summary */ + if (first_trip_dive && last_trip_dive && last_trip_dive->when < first_trip_dive->when) + gtk_tree_store_set(treestore, &parent_iter, + DIVE_DATE, last_trip_dive->when, + DIVE_LOCATION, last_trip_dive->location, + -1); + first_trip_dive = dive; + + gtk_tree_store_append(treestore, &parent_iter, NULL); + gtk_tree_store_set(treestore, &parent_iter, + DIVE_INDEX, -NEW_TRIP, + DIVE_NR, -NEW_TRIP, + DIVE_DATE, dive_date, + DIVE_LOCATION, dive->location, + DIVE_TEMPERATURE, 0, + DIVE_SAC, 0, + -1); + } + last_trip_dive = dive; update_cylinder_related_info(dive); - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, + gtk_tree_store_append(treestore, &iter, &parent_iter); + gtk_tree_store_set(treestore, &iter, DIVE_INDEX, i, DIVE_NR, dive->number, DIVE_DATE, dive->when, DIVE_DEPTH, dive->maxdepth, DIVE_DURATION, dive->duration.seconds, - DIVE_LOCATION, "location", + DIVE_LOCATION, dive->location, + DIVE_RATING, dive->rating, + DIVE_TEMPERATURE, dive->watertemp.mkelvin, + DIVE_SAC, 0, + -1); + gtk_tree_store_append(liststore, &iter, NULL); + gtk_tree_store_set(liststore, &iter, + DIVE_INDEX, i, + DIVE_NR, dive->number, + DIVE_DATE, dive->when, + DIVE_DEPTH, dive->maxdepth, + DIVE_DURATION, dive->duration.seconds, + DIVE_LOCATION, dive->location, + DIVE_RATING, dive->rating, DIVE_TEMPERATURE, dive->watertemp.mkelvin, DIVE_TOTALWEIGHT, 0, DIVE_SUIT, dive->suit, @@ -664,14 +882,21 @@ static void fill_dive_list(void) update_dive_list_units(); if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dive_list.model), &iter)) { GtkTreeSelection *selection; + + /* select the last dive (and make sure it's an actual dive that is selected) */ + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &selected_dive, -1); + first_leaf(GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); gtk_tree_selection_select_iter(selection, &iter); + selectiontracker = realloc(selectiontracker, sizeof(int)); + *selectiontracker = selected_dive; } } 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.treemodel)); + gtk_tree_store_clear(GTK_TREE_STORE(dive_list.listmodel)); fill_dive_list(); repaint_dive(); } @@ -683,7 +908,7 @@ static struct divelist_column { unsigned int flags; int *visible; } 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_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT }, [DIVE_DEPTH] = { "ft", depth_data_func, NULL, ALIGN_RIGHT }, @@ -708,14 +933,20 @@ static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_c unsigned int flags = col->flags; int *visible = col->visible; GtkWidget *tree_view = dl->tree_view; - GtkListStore *model = dl->model; + GtkTreeStore *treemodel = dl->treemodel; + GtkTreeStore *listmodel = dl->listmodel; GtkTreeViewColumn *ret; if (visible && !*visible) flags |= INVISIBLE; 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); + if (sort_func) { + /* the sort functions are needed in the corresponding models */ + if (index == DIVE_DATE) + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treemodel), index, sort_func, NULL, NULL); + else + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(listmodel), index, sort_func, NULL, NULL); + } return ret; } @@ -731,15 +962,17 @@ static void realize_cb(GtkWidget *tree_view, gpointer userdata) static void row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + gpointer userdata) { int index; GtkTreeIter iter; - if (!gtk_tree_model_get_iter(model, &iter, path)) + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dive_list.model), &iter, path)) return; - gtk_tree_model_get(model, &iter, DIVE_INDEX, &index, -1); - edit_dive_info(get_dive(index)); + gtk_tree_model_get(GTK_TREE_MODEL(dive_list.model), &iter, DIVE_INDEX, &index, -1); + /* a negative index is special for the "group by date" entries */ + if (index >= 0) + edit_dive_info(get_dive(index)); } void add_dive_cb(GtkWidget *menuitem, gpointer data) @@ -755,41 +988,170 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data) free(dive); } +void edit_dive_cb(GtkWidget *menuitem, gpointer data) +{ + edit_multi_dive_info(amount_selected, selectiontracker); +} + static void popup_divelist_menu(GtkTreeView *tree_view, GtkTreeModel *model, int button) { GtkWidget *menu, *menuitem; + char editlabel[] = "Edit dives"; menu = gtk_menu_new(); menuitem = gtk_menu_item_new_with_label("Add dive"); - g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), model); + g_signal_connect(menuitem, "activate", G_CALLBACK(add_dive_cb), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + if (amount_selected) { + if (amount_selected == 1) + editlabel[strlen(editlabel) - 1] = '\0'; + menuitem = gtk_menu_item_new_with_label(editlabel); + g_signal_connect(menuitem, "activate", G_CALLBACK(edit_dive_cb), model); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + } gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button, gtk_get_current_event_time()); } -static void popup_menu_cb(GtkTreeView *tree_view, - GtkTreeModel *model) +static void popup_menu_cb(GtkTreeView *tree_view, gpointer userdata) { - popup_divelist_menu(tree_view, model, 0); + popup_divelist_menu(tree_view, GTK_TREE_MODEL(dive_list.model), 0); } -static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, GtkTreeModel *model) +static gboolean button_press_cb(GtkWidget *treeview, GdkEventButton *event, gpointer userdata) { /* Right-click? Bring up the menu */ if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - popup_divelist_menu(GTK_TREE_VIEW(treeview), model, 3); + popup_divelist_menu(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(dive_list.model), 3); return TRUE; } return FALSE; } +/* we need to have a temporary copy of the selected dives while + switching model as the selection_cb function keeps getting called + when gtk_tree_selection_select_path is called. We also need to + keep copies of the sort order so we can restore that as well after + switching models. */ +static int *oldselection; +static int old_nr_selected; +static gboolean second_call = FALSE; +static GtkSortType sortorder[] = { [0 ... DIVELIST_COLUMNS - 1] = GTK_SORT_DESCENDING, }; +static int lastcol = DIVE_DATE; + +/* Check if this dive was selected previously and select it again in the new model; + * This is used after we switch models to maintain consistent selections. + * We always return FALSE to iterate through all dives */ +static gboolean select_selected(GtkTreeModel *model, GtkTreePath *path, + GtkTreeIter *iter, gpointer data) +{ + int i, idx; + GtkTreeSelection *selection = GTK_TREE_SELECTION(data); + + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1); + for (i = 0; i < old_nr_selected; i++) + if (oldselection[i] == idx) { + gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), path); + gtk_tree_selection_select_path(selection, path); + + return FALSE; + } + return FALSE; + +} + +static void update_column_and_order(int colid) +{ + /* Careful: the index into treecolumns is off by one as we don't have a + tree_view column for DIVE_INDEX */ + GtkTreeViewColumn **treecolumns = &dive_list.nr; + + /* this will trigger a second call into sort_column_change_cb, + so make sure we don't start an infinite recursion... */ + second_call = TRUE; + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(dive_list.model), colid, sortorder[colid]); + gtk_tree_view_column_set_sort_order(treecolumns[colid - 1], sortorder[colid]); + second_call = FALSE; +} + +/* If the sort column is date (default), show the tree model. + For every other sort column only show the list model. + If the model changed, inform the new model of the chosen sort column and make + sure the same dives are still selected. + + The challenge with this function is that once we change the model + we also need to change the sort column again (as it was changed in + the other model) and that causes this function to be called + recursively - so we need to catch that. +*/ +static void sort_column_change_cb(GtkTreeSortable *treeview, gpointer data) +{ + int colid; + GtkSortType order; + GtkTreeStore *currentmodel = dive_list.model; + + if (second_call) + return; + + gtk_tree_sortable_get_sort_column_id(treeview, &colid, &order); + if(colid == lastcol) { + /* we just changed sort order */ + sortorder[colid] = order; + return; + } else { + lastcol = colid; + } + if(colid == DIVE_DATE) + dive_list.model = dive_list.treemodel; + else + dive_list.model = dive_list.listmodel; + if (dive_list.model != currentmodel) { + GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view)); + + /* remember what is currently selected, switch models and reselect the selected rows */ + old_nr_selected = amount_selected; + oldselection = malloc(old_nr_selected * sizeof(int)); + if (amount_selected) + memcpy(oldselection, selectiontracker, amount_selected * sizeof(int)); + gtk_tree_view_set_model(GTK_TREE_VIEW(dive_list.tree_view), GTK_TREE_MODEL(dive_list.model)); + + update_column_and_order(colid); + + if (old_nr_selected) { + /* we need to select all the dives that were selected */ + /* this is fundamentally an n^2 algorithm as implemented - YUCK */ + gtk_tree_model_foreach(GTK_TREE_MODEL(dive_list.model), select_selected, selection); + } + } else { + if (order != sortorder[colid]) { + update_column_and_order(colid); + } + } +} + GtkWidget *dive_list_create(void) { GtkTreeSelection *selection; - dive_list.model = gtk_list_store_new(DIVELIST_COLUMNS, + dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS, + G_TYPE_INT, /* index */ + G_TYPE_INT, /* nr */ + G_TYPE_INT, /* Date */ + G_TYPE_INT, /* Star rating */ + G_TYPE_INT, /* Depth */ + G_TYPE_INT, /* Duration */ + G_TYPE_INT, /* Temperature */ + G_TYPE_INT, /* Total weight */ + G_TYPE_STRING, /* Suit */ + G_TYPE_STRING, /* Cylinder */ + G_TYPE_INT, /* Nitrox */ + G_TYPE_INT, /* SAC */ + G_TYPE_INT, /* OTU */ + G_TYPE_STRING /* Location */ + ); + dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS, G_TYPE_INT, /* index */ G_TYPE_INT, /* nr */ G_TYPE_INT, /* Date */ @@ -805,6 +1167,7 @@ GtkWidget *dive_list_create(void) G_TYPE_INT, /* OTU */ G_TYPE_STRING /* Location */ ); + dive_list.model = dive_list.treemodel; dive_list.tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dive_list.model)); set_divelist_font(divelist_font); @@ -835,10 +1198,14 @@ GtkWidget *dive_list_create(void) NULL); g_signal_connect_after(dive_list.tree_view, "realize", G_CALLBACK(realize_cb), NULL); - g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), dive_list.model); - g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), dive_list.model); - g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), dive_list.model); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), dive_list.model); + g_signal_connect(dive_list.tree_view, "row-activated", G_CALLBACK(row_activated_cb), NULL); + g_signal_connect(dive_list.tree_view, "button-press-event", G_CALLBACK(button_press_cb), NULL); + g_signal_connect(dive_list.tree_view, "popup-menu", G_CALLBACK(popup_menu_cb), NULL); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), NULL); + g_signal_connect(dive_list.listmodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + g_signal_connect(dive_list.treemodel, "sort-column-changed", G_CALLBACK(sort_column_change_cb), NULL); + + gtk_tree_selection_set_select_function(selection, modify_selection_cb, NULL, NULL); dive_list.container_widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dive_list.container_widget), diff --git a/equipment.c b/equipment.c index 165b9d77c..71639c9d1 100644 --- a/equipment.c +++ b/equipment.c @@ -2,10 +2,10 @@ /* creates the UI for the equipment page - * controlled through the following interfaces: * - * void show_dive_equipment(struct dive *dive) + * void show_dive_equipment(struct dive *dive, int w_idx) * * called from gtk-ui: - * GtkWidget *equipment_widget(void) + * GtkWidget *equipment_widget(int w_idx) */ #include <stdio.h> #include <string.h> @@ -40,10 +40,11 @@ enum { struct equipment_list { int max_index; GtkListStore *model; + GtkTreeView *tree_view; GtkWidget *edit, *add, *del; }; -static struct equipment_list cylinder_list, weightsystem_list; +static struct equipment_list cylinder_list[2], weightsystem_list[2]; struct cylinder_widget { @@ -521,11 +522,11 @@ static void show_equipment(struct dive *dive, int max, } } -void show_dive_equipment(struct dive *dive) +void show_dive_equipment(struct dive *dive, int w_idx) { - show_equipment(dive, MAX_CYLINDERS, &cylinder_list, + show_equipment(dive, MAX_CYLINDERS, &cylinder_list[w_idx], &cyl_ptr, &cylinder_none, &set_one_cylinder); - show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list, + show_equipment(dive, MAX_WEIGHTSYSTEMS, &weightsystem_list[w_idx], &ws_ptr, &weightsystem_none, &set_one_weightsystem); } @@ -1063,11 +1064,12 @@ static int get_model_index(GtkListStore *model, GtkTreeIter *iter) return index; } -static void edit_cb(GtkButton *button, GtkTreeView *tree_view) +static void edit_cb(GtkButton *button, int w_idx) { int index; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; cylinder_t cyl; @@ -1085,11 +1087,12 @@ static void edit_cb(GtkButton *button, GtkTreeView *tree_view) repaint_dive(); } -static void add_cb(GtkButton *button, GtkTreeView *tree_view) +static void add_cb(GtkButton *button, int w_idx) { - int index = cylinder_list.max_index; + int index = cylinder_list[w_idx].max_index; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; cylinder_t cyl; @@ -1102,15 +1105,16 @@ static void add_cb(GtkButton *button, GtkTreeView *tree_view) selection = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_select_iter(selection, &iter); - cylinder_list.max_index++; - gtk_widget_set_sensitive(cylinder_list.add, cylinder_list.max_index < MAX_CYLINDERS); + cylinder_list[w_idx].max_index++; + gtk_widget_set_sensitive(cylinder_list[w_idx].add, cylinder_list[w_idx].max_index < MAX_CYLINDERS); } -static void del_cb(GtkButton *button, GtkTreeView *tree_view) +static void del_cb(GtkButton *button, int w_idx) { int index, nr; GtkTreeIter iter; - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; + GtkTreeView *tree_view = cylinder_list[w_idx].tree_view; GtkTreeSelection *selection; struct dive *dive; cylinder_t *cyl; @@ -1127,27 +1131,28 @@ static void del_cb(GtkButton *button, GtkTreeView *tree_view) if (!dive) return; cyl = dive->cylinder + index; - nr = cylinder_list.max_index - index - 1; + nr = cylinder_list[w_idx].max_index - index - 1; gtk_list_store_remove(model, &iter); - cylinder_list.max_index--; + cylinder_list[w_idx].max_index--; memmove(cyl, cyl+1, nr*sizeof(*cyl)); memset(cyl+nr, 0, sizeof(*cyl)); mark_divelist_changed(TRUE); flush_divelist(dive); - gtk_widget_set_sensitive(cylinder_list.edit, 0); - gtk_widget_set_sensitive(cylinder_list.del, 0); - gtk_widget_set_sensitive(cylinder_list.add, 1); + gtk_widget_set_sensitive(cylinder_list[w_idx].edit, 0); + gtk_widget_set_sensitive(cylinder_list[w_idx].del, 0); + gtk_widget_set_sensitive(cylinder_list[w_idx].add, 1); } -static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_edit_cb(GtkButton *button, int w_idx) { int index; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; weightsystem_t ws; @@ -1165,11 +1170,12 @@ static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view) repaint_dive(); } -static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_add_cb(GtkButton *button, int w_idx) { - int index = weightsystem_list.max_index; + int index = weightsystem_list[w_idx].max_index; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; weightsystem_t ws; @@ -1182,15 +1188,16 @@ static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view) selection = gtk_tree_view_get_selection(tree_view); gtk_tree_selection_select_iter(selection, &iter); - weightsystem_list.max_index++; - gtk_widget_set_sensitive(weightsystem_list.add, weightsystem_list.max_index < MAX_WEIGHTSYSTEMS); + weightsystem_list[w_idx].max_index++; + gtk_widget_set_sensitive(weightsystem_list[w_idx].add, weightsystem_list[w_idx].max_index < MAX_WEIGHTSYSTEMS); } -static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view) +static void ws_del_cb(GtkButton *button, int w_idx) { int index, nr; GtkTreeIter iter; - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; + GtkTreeView *tree_view = weightsystem_list[w_idx].tree_view; GtkTreeSelection *selection; struct dive *dive; weightsystem_t *ws; @@ -1207,20 +1214,20 @@ static void ws_del_cb(GtkButton *button, GtkTreeView *tree_view) if (!dive) return; ws = dive->weightsystem + index; - nr = weightsystem_list.max_index - index - 1; + nr = weightsystem_list[w_idx].max_index - index - 1; gtk_list_store_remove(model, &iter); - weightsystem_list.max_index--; + weightsystem_list[w_idx].max_index--; memmove(ws, ws+1, nr*sizeof(*ws)); memset(ws+nr, 0, sizeof(*ws)); mark_divelist_changed(TRUE); flush_divelist(dive); - gtk_widget_set_sensitive(weightsystem_list.edit, 0); - gtk_widget_set_sensitive(weightsystem_list.del, 0); - gtk_widget_set_sensitive(weightsystem_list.add, 1); + gtk_widget_set_sensitive(weightsystem_list[w_idx].edit, 0); + gtk_widget_set_sensitive(weightsystem_list[w_idx].del, 0); + gtk_widget_set_sensitive(weightsystem_list[w_idx].add, 1); } static GtkListStore *create_tank_size_model(void) @@ -1340,33 +1347,33 @@ static void selection_cb(GtkTreeSelection *selection, struct equipment_list *lis static void row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + int w_idx) { - edit_cb(NULL, tree_view); + edit_cb(NULL, w_idx); } static void ws_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, - GtkTreeModel *model) + int w_idx) { - ws_edit_cb(NULL, tree_view); + ws_edit_cb(NULL, w_idx); } -GtkWidget *cylinder_list_widget(void) +GtkWidget *cylinder_list_widget(int w_idx) { - GtkListStore *model = cylinder_list.model; + GtkListStore *model = cylinder_list[w_idx].model; GtkWidget *tree_view; GtkTreeSelection *selection; tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_widget_set_can_focus(tree_view, FALSE); - g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), model); + g_signal_connect(tree_view, "row-activated", G_CALLBACK(row_activated_cb), GINT_TO_POINTER(w_idx)); selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &cylinder_list[w_idx]); g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, @@ -1382,9 +1389,9 @@ GtkWidget *cylinder_list_widget(void) return tree_view; } -GtkWidget *weightsystem_list_widget(void) +GtkWidget *weightsystem_list_widget(int w_idx) { - GtkListStore *model = weightsystem_list.model; + GtkListStore *model = weightsystem_list[w_idx].model; GtkWidget *tree_view; GtkTreeSelection *selection; @@ -1394,7 +1401,7 @@ GtkWidget *weightsystem_list_widget(void) selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_BROWSE); - g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list); + g_signal_connect(selection, "changed", G_CALLBACK(selection_cb), &weightsystem_list[w_idx]); g_object_set(G_OBJECT(tree_view), "headers-visible", TRUE, "enable-grid-lines", GTK_TREE_VIEW_GRID_LINES_BOTH, @@ -1407,7 +1414,7 @@ GtkWidget *weightsystem_list_widget(void) return tree_view; } -static GtkWidget *cylinder_list_create(void) +static GtkWidget *cylinder_list_create(int w_idx) { GtkListStore *model; @@ -1420,11 +1427,11 @@ static GtkWidget *cylinder_list_create(void) G_TYPE_INT, /* CYL_O2: permille */ G_TYPE_INT /* CYL_HE: permille */ ); - cylinder_list.model = model; - return cylinder_list_widget(); + cylinder_list[w_idx].model = model; + return cylinder_list_widget(w_idx); } -static GtkWidget *weightsystem_list_create(void) +static GtkWidget *weightsystem_list_create(int w_idx) { GtkListStore *model; @@ -1432,11 +1439,11 @@ static GtkWidget *weightsystem_list_create(void) G_TYPE_STRING, /* WS_DESC: utf8 */ G_TYPE_INT /* WS_WEIGHT: grams */ ); - weightsystem_list.model = model; - return weightsystem_list_widget(); + weightsystem_list[w_idx].model = model; + return weightsystem_list_widget(w_idx); } -GtkWidget *equipment_widget(void) +GtkWidget *equipment_widget(int w_idx) { GtkWidget *vbox, *hbox, *frame, *framebox, *tree_view; GtkWidget *add, *del, *edit; @@ -1444,14 +1451,17 @@ GtkWidget *equipment_widget(void) vbox = gtk_vbox_new(FALSE, 3); /* - * We create the cylinder size model at startup, since - * we're going to share it across all cylinders and all - * dives. So if you add a new cylinder type in one dive, - * it will show up when you edit the cylinder types for - * another dive. + * We create the cylinder size (and weightsystem) models + * at startup for the primary cylinder / weightsystem widget, + * since we're going to share it across all cylinders and all + * dives. So if you add a new cylinder type or weightsystem in + * one dive, it will show up when you edit the cylinder types + * or weightsystems for another dive. */ - cylinder_model = create_tank_size_model(); - tree_view = cylinder_list_create(); + if (w_idx == W_IDX_PRIMARY) + cylinder_model = create_tank_size_model(); + tree_view = cylinder_list_create(w_idx); + cylinder_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view); hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); @@ -1477,19 +1487,21 @@ GtkWidget *equipment_widget(void) gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0); - cylinder_list.edit = edit; - cylinder_list.add = add; - cylinder_list.del = del; + cylinder_list[w_idx].edit = edit; + cylinder_list[w_idx].add = add; + cylinder_list[w_idx].del = del; - g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), tree_view); - g_signal_connect(add, "clicked", G_CALLBACK(add_cb), tree_view); - g_signal_connect(del, "clicked", G_CALLBACK(del_cb), tree_view); + g_signal_connect(edit, "clicked", G_CALLBACK(edit_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(add, "clicked", G_CALLBACK(add_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(del, "clicked", G_CALLBACK(del_cb), GINT_TO_POINTER(w_idx)); hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); - weightsystem_model = create_weightsystem_model(); - tree_view = weightsystem_list_create(); + if (w_idx == W_IDX_PRIMARY) + weightsystem_model = create_weightsystem_model(); + tree_view = weightsystem_list_create(w_idx); + weightsystem_list[w_idx].tree_view = GTK_TREE_VIEW(tree_view); frame = gtk_frame_new("Weight"); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, FALSE, 3); @@ -1512,13 +1524,13 @@ GtkWidget *equipment_widget(void) gtk_box_pack_start(GTK_BOX(hbox), add, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), del, FALSE, FALSE, 0); - weightsystem_list.edit = edit; - weightsystem_list.add = add; - weightsystem_list.del = del; + weightsystem_list[w_idx].edit = edit; + weightsystem_list[w_idx].add = add; + weightsystem_list[w_idx].del = del; - g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), tree_view); - g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), tree_view); - g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), tree_view); + g_signal_connect(edit, "clicked", G_CALLBACK(ws_edit_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(add, "clicked", G_CALLBACK(ws_add_cb), GINT_TO_POINTER(w_idx)); + g_signal_connect(del, "clicked", G_CALLBACK(ws_del_cb), GINT_TO_POINTER(w_idx)); return vbox; } @@ -810,7 +810,7 @@ void init_ui(int *argcp, char ***argvp) gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes")); /* Frame for dive equipment */ - nb_page = equipment_widget(); + nb_page = equipment_widget(W_IDX_PRIMARY); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment")); /* Frame for single dive statistics */ @@ -358,25 +358,27 @@ static void save_dive_info_changes(struct dive *dive, struct dive_info *info) changed =1; } - old_text = dive->notes; - dive->notes = get_text(info->notes); - if (text_changed(old_text,dive->notes)) - changed = 1; - if (old_text) - g_free(old_text); - + if (info->notes) { + old_text = dive->notes; + dive->notes = get_text(info->notes); + if (text_changed(old_text,dive->notes)) + changed = 1; + if (old_text) + g_free(old_text); + } if (changed) { mark_divelist_changed(TRUE); update_dive(dive); } } -static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info) +static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info *info, gboolean multi) { - GtkWidget *hbox, *label, *cylinder, *frame; - char buffer[80]; + GtkWidget *hbox, *label, *frame, *equipment; + char buffer[80] = "Edit multiple dives"; - divename(buffer, sizeof(buffer), dive); + if (!multi) + divename(buffer, sizeof(buffer), dive); label = gtk_label_new(buffer); gtk_box_pack_start(GTK_BOX(box), label, FALSE, TRUE, 0); @@ -394,28 +396,57 @@ static void dive_info_widget(GtkWidget *box, struct dive *dive, struct dive_info info->rating = text_entry(hbox, "Rating", star_list, star_strings[dive->rating]); info->suit = text_entry(hbox, "Suit", suit_list, dive->suit); - info->notes = text_view(box, "Notes", READ_WRITE); - if (dive->notes && *dive->notes) - gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); - + /* only show notes if editing a single dive */ + if (multi) { + info->notes = NULL; + } else { + info->notes = text_view(box, "Notes", READ_WRITE); + if (dive->notes && *dive->notes) + gtk_text_buffer_set_text(gtk_text_view_get_buffer(info->notes), dive->notes, -1); + } hbox = gtk_hbox_new(FALSE, 3); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0); - frame = gtk_frame_new("Cylinder"); - cylinder = cylinder_list_widget(); - gtk_container_add(GTK_CONTAINER(frame), cylinder); + /* create a secondary Equipment widget */ + frame = gtk_frame_new("Equipment"); + equipment = equipment_widget(W_IDX_SECONDARY); + gtk_container_add(GTK_CONTAINER(frame), equipment); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); } -int edit_dive_info(struct dive *dive) +/* we use these to find out if we edited the cylinder or weightsystem entries */ +static cylinder_t remember_cyl[MAX_CYLINDERS]; +static weightsystem_t remember_ws[MAX_WEIGHTSYSTEMS]; + +void save_equipment_data(struct dive *dive) { - int success; + if (dive) { + memcpy(remember_cyl, dive->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS); + memcpy(remember_ws, dive->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS); + } +} + +void update_equipment_data(struct dive *dive, struct dive *master) +{ + if (dive == master) + return; + if (memcmp(remember_cyl, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS)) { + memcpy(dive->cylinder, master->cylinder, sizeof(cylinder_t) * MAX_CYLINDERS); + } + if (memcmp(remember_ws, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS)) { + memcpy(dive->weightsystem, master->weightsystem, sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS); + } +} + +int edit_multi_dive_info(int nr, int *indices) +{ + int success, i; GtkWidget *dialog, *vbox; struct dive_info info; + struct dive *dive; - if (!dive) + if (!nr) return 0; - dialog = gtk_dialog_new_with_buttons("Dive Info", GTK_WINDOW(main_window), GTK_DIALOG_DESTROY_WITH_PARENT, @@ -424,18 +455,44 @@ int edit_dive_info(struct dive *dive) NULL); vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); - dive_info_widget(vbox, dive, &info); - + /* SCARY STUFF - IS THIS THE BEST WAY TO DO THIS??? + * + * current_dive is one of our selected dives - and that is + * the one that is used to pre-fill the edit widget. Its + * data is used as the starting point for all selected dives + * I think it would be better to somehow collect and combine + * info from all the selected dives */ + dive = current_dive; + dive_info_widget(vbox, dive, &info, (nr > 1)); + show_dive_equipment(dive, W_IDX_SECONDARY); + save_equipment_data(dive); gtk_widget_show_all(dialog); success = gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT; if (success) - save_dive_info_changes(dive, &info); - + for (i = 0; i < nr; i++) { + /* copy all "info" fields */ + save_dive_info_changes(get_dive(indices[i]), &info); + /* copy the cylinders / weightsystems */ + update_equipment_data(get_dive(indices[i]), dive); + /* this is extremely inefficient... it loops through all + dives to find the right one - but we KNOW the index already */ + flush_divelist(get_dive(indices[i])); + } gtk_widget_destroy(dialog); return success; } +int edit_dive_info(struct dive *dive) +{ + int idx; + + if (!dive) + return 0; + idx = dive->number; + return edit_multi_dive_info(1, &idx); +} + static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...) { va_list ap; @@ -190,7 +190,7 @@ void update_dive(struct dive *new_dive) } if (new_dive) { show_dive_info(new_dive); - show_dive_equipment(new_dive); + show_dive_equipment(new_dive, W_IDX_PRIMARY); show_dive_stats(new_dive); } buffered_dive = new_dive; diff --git a/statistics.c b/statistics.c index 19105653c..3a260066a 100644 --- a/statistics.c +++ b/statistics.c @@ -142,25 +142,39 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive) } } -void process_selected_dives(GList *selected_dives, GtkTreeModel *model) +/* make sure we skip the selected summary entries */ +void process_selected_dives(GList *selected_dives, int *selectiontracker, GtkTreeModel *model) { struct dive *dp; - unsigned int i; + unsigned int i, j; + int idx; GtkTreeIter iter; GtkTreePath *path; memset(&stats_selection, 0, sizeof(stats_selection)); - stats_selection.selection_size = amount_selected; - for (i = 0; i < amount_selected; ++i) { + /* adjust amount_selected and remove negative index entries from list */ + for (i = 0, j = 0; j < amount_selected; ++i) { GValue value = {0, }; path = g_list_nth_data(selected_dives, i); if (gtk_tree_model_get_iter(model, &iter, path)) { gtk_tree_model_get_value(model, &iter, 0, &value); - dp = get_dive(g_value_get_int(&value)); + idx = g_value_get_int(&value); + if (idx > 0) { + dp = get_dive(idx); + if (dp) { + selectiontracker[j] = idx; + process_dive(dp, &stats_selection); + j++; + continue; + } + } } - process_dive(dp, &stats_selection); + /* we didn't process it, so shorten the list */ + amount_selected--; } + /* record the actual number of dives selected */ + stats_selection.selection_size = amount_selected; } static void set_label(GtkWidget *w, const char *fmt, ...) |