struct DiveList {
GtkWidget *tree_view;
GtkWidget *container_widget;
- GtkListStore *model;
+ GtkTreeStore *model, *listmodel, *treemodel;
GtkTreeViewColumn *nr, *date, *stars, *depth, *duration, *location;
- GtkTreeViewColumn *temperature, *cylinder, *nitrox, *sac, *otu;
+ GtkTreeViewColumn *temperature, *cylinder, *totalweight, *suit, *nitrox, *sac, *otu;
int changed;
};
DIVE_DEPTH, /* int: dive->maxdepth in mm */
DIVE_DURATION, /* int: in seconds */
DIVE_TEMPERATURE, /* int: in mkelvin */
+ DIVE_TOTALWEIGHT, /* int: in grams */
+ DIVE_SUIT, /* "wet, 3mm" */
DIVE_CYLINDER,
DIVE_NITROX, /* int: dummy */
DIVE_SAC, /* int: in ml/min */
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);
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;
}
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);
}
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);
}
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);
}
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);
}
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:
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:
*o2low_p = mino2;
}
+static int total_weight(struct dive *dive)
+{
+ int i, total_grams = 0;
+
+ if (dive)
+ for (i=0; i< MAX_WEIGHTSYSTEMS; i++)
+ total_grams += dive->weightsystem[i].weight.grams;
+ return total_grams;
+}
+
+static void weight_data_func(GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int indx, decimals;
+ double value;
+ char buffer[80];
+ struct dive *dive;
+
+ gtk_tree_model_get(model, iter, DIVE_INDEX, &indx, -1);
+ dive = get_dive(indx);
+ value = get_weight_units(total_weight(dive), &decimals, NULL);
+ if (value == 0.0)
+ *buffer = '\0';
+ else
+ snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
+
+ g_object_set(renderer, "text", buffer, NULL);
+}
+
static gint nitrox_sort_func(GtkTreeModel *model,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b,
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;
snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
else
strcpy(buffer, "air");
-
+ exit:
g_object_set(renderer, "text", buffer, NULL);
}
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;
break;
}
snprintf(buffer, sizeof(buffer), fmt, sac);
-
+ exit:
g_object_set(renderer, "text", buffer, NULL);
}
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);
}
get_string(str, dive->cylinder[0].type.description);
}
+static void get_suit(struct dive *dive, char **str)
+{
+ get_string(str, dive->suit);
+}
+
/*
* 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;
+ 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,
DIVE_RATING, dive->rating,
DIVE_SAC, dive->sac,
DIVE_OTU, dive->otu,
+ DIVE_TOTALWEIGHT, total_weight(dive),
+ DIVE_SUIT, suit,
-1);
+
+ 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,
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)
(void) get_temp_units(0, &unit);
gtk_tree_view_column_set_title(dive_list.temperature, unit);
+ (void) get_weight_units(0, NULL, &unit);
+ gtk_tree_view_column_set_title(dive_list.totalweight, unit);
+
gtk_tree_model_foreach(model, set_one_dive, NULL);
}
{
gtk_tree_view_column_set_visible(dive_list.cylinder, visible_cols.cylinder);
gtk_tree_view_column_set_visible(dive_list.temperature, visible_cols.temperature);
+ gtk_tree_view_column_set_visible(dive_list.totalweight, visible_cols.totalweight);
+ gtk_tree_view_column_set_visible(dive_list.suit, visible_cols.suit);
gtk_tree_view_column_set_visible(dive_list.nitrox, visible_cols.nitrox);
gtk_tree_view_column_set_visible(dive_list.sac, visible_cols.sac);
gtk_tree_view_column_set_visible(dive_list.otu, visible_cols.otu);
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,
DIVE_SAC, 0,
-1);
}
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();
}
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 },
[DIVE_DURATION] = { "min", duration_data_func, NULL, ALIGN_RIGHT },
[DIVE_TEMPERATURE] = { UTF8_DEGREE "F", temperature_data_func, NULL, ALIGN_RIGHT, &visible_cols.temperature },
+ [DIVE_TOTALWEIGHT] = { "lbs", weight_data_func, NULL, ALIGN_RIGHT, &visible_cols.totalweight },
+ [DIVE_SUIT] = { "Suit", NULL, NULL, ALIGN_LEFT, &visible_cols.suit },
[DIVE_CYLINDER] = { "Cyl", NULL, NULL, 0, &visible_cols.cylinder },
[DIVE_NITROX] = { "O" UTF8_SUBSCRIPT_2 "%", nitrox_data_func, nitrox_sort_func, 0, &visible_cols.nitrox },
[DIVE_SAC] = { "SAC", sac_data_func, NULL, 0, &visible_cols.sac },
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;
}
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)
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 */
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.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);
dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH);
dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION);
dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE);
+ dive_list.totalweight = divelist_column(&dive_list, dl_column + DIVE_TOTALWEIGHT);
+ dive_list.suit = divelist_column(&dive_list, dl_column + DIVE_SUIT);
dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER);
dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX);
dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC);
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),
/* 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>
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 {
model = GTK_TREE_MODEL(weightsystem_model);
gtk_tree_model_foreach(model, match_desc, (void *)desc);
- if (!found_match) {
- GtkListStore *store = GTK_LIST_STORE(model);
-
- gtk_list_store_append(store, iter);
- gtk_list_store_set(store, iter,
- 0, desc,
- 1, weight,
+ if (found_match) {
+ gtk_list_store_set(GTK_LIST_STORE(model), found_match,
+ WS_WEIGHT, weight,
+ -1);
+ } else if (desc && desc[0]) {
+ gtk_list_store_append(GTK_LIST_STORE(model), iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), iter,
+ WS_DESC, desc,
+ WS_WEIGHT, weight,
-1);
return iter;
}
}
}
- 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);
}
GtkComboBox *box;
int grams;
double value;
+ GtkTreeIter iter;
/* Ignore uninitialized cylinder widgets */
box = weightsystem_widget->description;
grams = value * 1000;
ws->weight.grams = grams;
ws->description = desc;
+ add_weightsystem_type(desc, grams, &iter);
}
/*
const char *name;
int grams;
} ws_info[100] = {
- /* Need an empty entry for the no weight system case */
- { "", },
{ "integrated", 0 },
{ "belt", 0 },
{ "ankle", 0 },
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;
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;
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;
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;
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;
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;
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)
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,
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;
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,
return tree_view;
}
- static GtkWidget *cylinder_list_create(void)
+ static GtkWidget *cylinder_list_create(int w_idx)
{
GtkListStore *model;
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;
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;
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);
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);
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;
}
#include "display-gtk.h"
#include "divelist.h"
-static GtkEntry *location, *buddy, *divemaster, *rating;
+static GtkEntry *location, *buddy, *divemaster, *rating, *suit;
static GtkTextView *notes;
-static GtkListStore *location_list, *people_list, *star_list;
+static GtkListStore *location_list, *people_list, *star_list, *suit_list;
static char *get_text(GtkTextView *view)
{
SET_TEXT_VALUE(divemaster);
SET_TEXT_VALUE(buddy);
SET_TEXT_VALUE(location);
+ SET_TEXT_VALUE(suit);
gtk_entry_set_text(rating, star_strings[dive->rating]);
gtk_text_buffer_set_text(gtk_text_view_get_buffer(notes),
dive && dive->notes ? dive->notes : "", -1);
gtk_tree_model_get(model, iter, 0, &entry, -1);
cmp = strcmp(entry, string);
+ if (entry)
+ free(entry);
/* Stop. The entry is bigger than the new one */
if (cmp > 0)
add_string_list_entry(string, location_list);
}
+void add_suit(const char *string)
+{
+ add_string_list_entry(string, suit_list);
+}
+
static int get_rating(const char *string)
{
int rating_val = 0;
}
struct dive_info {
- GtkComboBoxEntry *location, *divemaster, *buddy, *rating;
+ GtkComboBoxEntry *location, *divemaster, *buddy, *rating, *suit;
GtkTextView *notes;
};
changed = 1;
}
+ new_text = get_combo_box_entry_text(info->suit, &dive->suit);
+ if (new_text) {
+ add_suit(new_text);
+ changed = 1;
+ }
+
rating_string = strdup(star_strings[dive->rating]);
new_text = get_combo_box_entry_text(info->rating, &rating_string);
if (new_text) {
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);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
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,
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;
add_string_list_entry(THREE_STARS, star_list);
add_string_list_entry(FOUR_STARS, star_list);
add_string_list_entry(FIVE_STARS, star_list);
+ suit_list = gtk_list_store_new(1, G_TYPE_STRING);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
location = text_value(vbox, "Location");
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
rating = text_value(hbox, "Rating");
+ suit = text_value(hbox, "Suit");
notes = text_view(vbox, "Notes", READ_ONLY);
return vbox;