From 49f2095d94eb551ea56c34ac2c5525af559a0b08 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Thu, 16 Aug 2018 23:07:21 +1200 Subject: [PATCH 1/4] unix: add MultiSelect flag and OnSelectionChange callback --- ui.h | 3 +++ unix/table.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/ui.h b/ui.h index 40aea949..b6be8358 100644 --- a/ui.h +++ b/ui.h @@ -1383,6 +1383,9 @@ struct uiTableParams { // If CellValue() for this column for any row returns NULL, that // row will also use the default background color. int RowBackgroundColorModelColumn; + // MultiSelect sets selection mode for the table. + // 0=single selection, 1=allow multiple rows to be selected + int MultiSelect; }; // uiTable is a uiControl that shows tabular data, allowing users to diff --git a/unix/table.c b/unix/table.c index d1e6fe60..f6b2328f 100644 --- a/unix/table.c +++ b/unix/table.c @@ -18,6 +18,8 @@ struct uiTable { // TODO document this properly GHashTable *indeterminatePositions; guint indeterminateTimer; + void (*onSelectionChanged)(uiTable *, void *); + void *onSelectionChangedData; }; // use the same size as GtkFileChooserWidget's treeview @@ -492,9 +494,28 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } +static void onChanged(GtkTreeSelection *sel, gpointer data) +{ + uiTable* t = uiTable(data); + (*(t->onSelectionChanged))(t, t->onSelectionChangedData); +} + +static void defaultOnSelectionChanged(uiTable *t, void *data) +{ + // do nothing +} + +void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *, void *), void *data) +{ + t->onSelectionChanged = f; + t->onSelectionChangedData = data; +} + uiTable *uiNewTable(uiTableParams *p) { uiTable *t; + GtkTreeSelection* sel; + GtkSelectionMode selMode; uiUnixNewControl(uiTable, t); @@ -518,5 +539,14 @@ uiTable *uiNewTable(uiTableParams *p) t->indeterminatePositions = g_hash_table_new_full(rowcolHash, rowcolEqual, uiprivFree, uiprivFree); + sel = gtk_tree_view_get_selection(t->tv); + selMode = GTK_SELECTION_SINGLE; + if (p->MultiSelect == 1) { + selMode = GTK_SELECTION_MULTIPLE; + } + gtk_tree_selection_set_mode(sel, selMode); + g_signal_connect(sel, "changed", G_CALLBACK(onChanged), t); + uiTableOnSelectionChanged(t, defaultOnSelectionChanged, NULL); + return t; } From c148ee79daefce5ca32b5f5c184c81667077b0c9 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Sat, 18 Aug 2018 10:04:19 +1200 Subject: [PATCH 2/4] unix: add uiTableSelection to get table selection --- ui.h | 23 +++++++++++++++++++++ unix/table.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/ui.h b/ui.h index b6be8358..1064e2aa 100644 --- a/ui.h +++ b/ui.h @@ -1458,6 +1458,29 @@ _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, int buttonModelColumn, int buttonClickableModelColumn); +// uiTableOnSelectionChanged sets a callback to invoke upon changes +// in the set of selected items in the table. +_UI_EXTERN void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *t, void *data), void *data); + +// uiTableSelection holds an array of row indexes for a table. +typedef struct uiTableSelection uiTableSelection; +struct uiTableSelection +{ + int NumItems; + int* Items; +}; + +// uiTableCurrentSelection returns a uiTableSelection containing +// an array of all the selected rows in the table. +// If no rows are selected, a uiTableSelection will still be +// returned, with NumItems set to 0. +// The caller is responsible for calling uiFreeTableSelection() +// when finished with the uiTableSelection. +_UI_EXTERN uiTableSelection* uiTableCurrentSelection(uiTable* t); + +// uiFreeTableSelection frees any memory allocated to a uiTableSelection +_UI_EXTERN void uiFreeTableSelection(uiTableSelection* sel); + // uiNewTable() creates a new uiTable with the specified parameters. _UI_EXTERN uiTable *uiNewTable(uiTableParams *params); diff --git a/unix/table.c b/unix/table.c index f6b2328f..67d18343 100644 --- a/unix/table.c +++ b/unix/table.c @@ -511,6 +511,62 @@ void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *, void *), void *d t->onSelectionChangedData = data; } +// internal implementation of uiTableSelection (growable, because we +// don't know the size of the selection in advance under Gtk+) +// So we have extra stuff hanging on to the end of the public +// uiTableSelection we return, but callers don't have to care. +// Yay C! +struct growableTableSelection +{ + uiTableSelection sel; + int cap; + // TODO: could have an int here for single-selection case to avoid extra alloc... +}; + +static void collectSelection( GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + struct growableTableSelection* it = (struct growableTableSelection*)data; + int depth = gtk_tree_path_get_depth(path); + gint* indices = gtk_tree_path_get_indices(path); + if (depth < 1) { + return; + } + + // append to collection + if (it->sel.NumItems == it->cap) { + // initial alloc or grow + if (it->cap == 0) { + it->cap = 16; + } else { + it->cap *= 2; + } + it->sel.Items = uiprivRealloc(it->sel.Items, sizeof(int) * it->cap, "int[]"); + } + it->sel.Items[it->sel.NumItems++] = (int)indices[0]; +} + +uiTableSelection* uiTableCurrentSelection(uiTable* t) +{ + struct growableTableSelection* g = uiprivAlloc(sizeof(struct growableTableSelection), "uiTableSelection"); + g->sel.NumItems = 0; + g->sel.Items = NULL; + g->cap = 0; + gtk_tree_selection_selected_foreach( gtk_tree_view_get_selection(t->tv), collectSelection, (gpointer)g); + return (uiTableSelection*)g; +} + +void uiFreeTableSelection(uiTableSelection* sel) +{ + struct growableTableSelection* g = (struct growableTableSelection*)sel; + if (g->sel.Items) { + uiprivFree(g->sel.Items); + } + uiprivFree(g); +} + uiTable *uiNewTable(uiTableParams *p) { uiTable *t; From ff3be31b9e47a65b96f305af07aca2866f0c01cd Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Sat, 18 Aug 2018 10:49:34 +1200 Subject: [PATCH 3/4] add some notes to uiTableSelection --- ui.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui.h b/ui.h index 1064e2aa..23405b2d 100644 --- a/ui.h +++ b/ui.h @@ -1463,6 +1463,9 @@ _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, _UI_EXTERN void uiTableOnSelectionChanged(uiTable *t, void (*f)(uiTable *t, void *data), void *data); // uiTableSelection holds an array of row indexes for a table. +// it's safe to fiddle with the Items data in place (eg a caller +// might want to sort them - that's ok). But probably best not to +// change NumItems or try to reallocate Items. typedef struct uiTableSelection uiTableSelection; struct uiTableSelection { From 27664db5e454ef7e280a8df5b1d1dc8396f58767 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Sat, 18 Aug 2018 10:57:38 +1200 Subject: [PATCH 4/4] style tweakage: remove {} about single-line ifs --- unix/table.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/unix/table.c b/unix/table.c index 67d18343..528d6c27 100644 --- a/unix/table.c +++ b/unix/table.c @@ -531,9 +531,8 @@ static void collectSelection( GtkTreeModel *model, struct growableTableSelection* it = (struct growableTableSelection*)data; int depth = gtk_tree_path_get_depth(path); gint* indices = gtk_tree_path_get_indices(path); - if (depth < 1) { + if (depth < 1) return; - } // append to collection if (it->sel.NumItems == it->cap) { @@ -561,9 +560,8 @@ uiTableSelection* uiTableCurrentSelection(uiTable* t) void uiFreeTableSelection(uiTableSelection* sel) { struct growableTableSelection* g = (struct growableTableSelection*)sel; - if (g->sel.Items) { + if (g->sel.Items) uiprivFree(g->sel.Items); - } uiprivFree(g); } @@ -597,9 +595,8 @@ uiTable *uiNewTable(uiTableParams *p) sel = gtk_tree_view_get_selection(t->tv); selMode = GTK_SELECTION_SINGLE; - if (p->MultiSelect == 1) { + if (p->MultiSelect == 1) selMode = GTK_SELECTION_MULTIPLE; - } gtk_tree_selection_set_mode(sel, selMode); g_signal_connect(sel, "changed", G_CALLBACK(onChanged), t); uiTableOnSelectionChanged(t, defaultOnSelectionChanged, NULL);