};
/* magic numbers that indicate (as negative values) model entries that
- * are summary entries for day / month / year */
-#define NEW_DAY 1
-#define NEW_MON 2
-#define NEW_YR 3
+ * are summary entries for a divetrip */
+#define NEW_TRIP 1
#ifdef DEBUG_MODEL
static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
static GList *selected_dives;
static int *selectiontracker;
-/* if we are sorting by date and are using a tree model, we don't want the selection
- to be a summary entry, but instead the first child below that entry. So we descend
- down the tree until we find a leaf (entry with non-negative index)
- */
+/* 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;
if (!gtk_tree_model_iter_children(model, iter, &parent))
/* we should never have a parent without child */
return;
- /* clicking on a parent should toggle expand status */
- if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(dive_list.tree_view), tpath))
- gtk_tree_view_collapse_row(GTK_TREE_VIEW(dive_list.tree_view), tpath);
- else
+ 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;
+
+ 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;
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 */
path = g_list_nth_data(selected_dives, 0);
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);
- /* make sure we're not on a summary entry */
- first_leaf (GTK_TREE_MODEL(dive_list.model), &iter, &selected_dive);
+ /* 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 */
- /* TODO:
- this also does not handle the case if a summary row is selected;
- We should iterate and select all dives under that row */
+ * The dives that have been selected are processed */
amount_selected = g_list_length(selected_dives);
process_selected_dives(selected_dives, selectiontracker, GTK_TREE_MODEL(dive_list.model));
repaint_dive();
tm = gmtime(&when);
switch(idx) {
- case -NEW_DAY:
+ case -NEW_TRIP:
snprintf(buffer, sizeof(buffer),
- "%s, %s %d, %d",
+ "Trip %s, %s %d, %d",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900);
break;
- case -NEW_MON:
- snprintf(buffer, sizeof(buffer),
- "%s %d",
- monthname(tm->tm_mon),
- tm->tm_year + 1900);
- break;
- case -NEW_YR:
- snprintf(buffer, sizeof(buffer),
- "%d", tm->tm_year + 1900);
- break;
default:
snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %02d:%02d",
return;
}
-static int new_date(struct dive *dive, struct dive **last_dive, const int flag, time_t *tm_date)
+/* 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;
- 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 || flag > NEW_MON) &&
- (tm1.tm_mday == tm2.tm_mday || flag > NEW_DAY))
+ if (abs(dive->when - ldive->when) < TRIP_THRESHOLD) {
+ *last_dive = dive;
return FALSE;
+ }
}
- if (flag == NEW_DAY)
- *last_dive = dive;
+ *last_dive = dive;
if (tm_date) {
struct tm *tm1 = gmtime(&dive->when);
tm1->tm_sec = 0;
static void fill_dive_list(void)
{
- int i, j;
- GtkTreeIter iter, parent_iter[NEW_YR + 2], *parents[NEW_YR + 2] = {NULL, };
+ int i;
+ 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;
treestore = GTK_TREE_STORE(dive_list.treemodel);
while (--i >= 0) {
struct dive *dive = dive_table.dives[i];
- for (j = NEW_YR; j >= NEW_DAY; j--) {
- if (new_date(dive, &last_dive, j, &dive_date))
- {
- gtk_tree_store_append(treestore, &parent_iter[j], parents[j+1]);
- parents[j] = &parent_iter[j];
- gtk_tree_store_set(treestore, parents[j],
- DIVE_INDEX, -j,
- DIVE_NR, -j,
- DIVE_DATE, dive_date,
- DIVE_LOCATION, "",
- DIVE_TEMPERATURE, 0,
- DIVE_SAC, 0,
+ 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_tree_store_append(treestore, &iter, parents[NEW_DAY]);
+ gtk_tree_store_append(treestore, &iter, &parent_iter);
gtk_tree_store_set(treestore, &iter,
DIVE_INDEX, i,
DIVE_NR, dive->number,
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);
- /* make sure it's an actual dive that is selected */
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);
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), 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,
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),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);