tImplement sorting of Win32 GtkTreeView - vaccinewars - be a doctor and try to vaccinate the world
 (HTM) git clone git://src.adamsgaard.dk/vaccinewars
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 63b61df04aa175dd7db2f71a556548ee6cc3b7ed
 (DIR) parent 86342a09e48c57a65ded56c7a7631104a55d39bb
 (HTM) Author: Ben Webb <ben@salilab.org>
       Date:   Sun, 29 Nov 2020 00:00:23 -0800
       
       Implement sorting of Win32 GtkTreeView
       
       Add a basic implementation of sorting for our
       Win32 GtkTreeView, including handling
       changing the sort order when column headers
       are clicked.
       
       Diffstat:
         M src/gtkport/treeview.c              |     110 ++++++++++++++++++++++++++++++-
         M src/gtkport/treeview.h              |       3 +++
         M src/gui_client/gtk_client.c         |       8 ++++++++
       
       3 files changed, 118 insertions(+), 3 deletions(-)
       ---
 (DIR) diff --git a/src/gtkport/treeview.c b/src/gtkport/treeview.c
       t@@ -62,13 +62,14 @@ static void gtk_tree_view_set_column_width(GtkTreeView *tv, gint column,
        static void gtk_tree_view_set_column_width_full(GtkTreeView *tv, gint column,
                                                        gint width,
                                                        gboolean ResizeHeader);
       +static void gtk_tree_view_click_column(GtkWidget *widget, gint column);
        
        static GtkSignalType GtkTreeViewSignals[] = {
          {"size_request", gtk_marshal_VOID__GPOIN, gtk_tree_view_size_request},
          {"set_size", gtk_marshal_VOID__GPOIN, gtk_tree_view_set_size},
          {"realize", gtk_marshal_VOID__VOID, gtk_tree_view_realize},
          {"destroy", gtk_marshal_VOID__VOID, gtk_tree_view_destroy},
       -  {"click-column", gtk_marshal_VOID__GINT, NULL},
       +  {"click-column", gtk_marshal_VOID__GINT, gtk_tree_view_click_column},
          {"changed", gtk_marshal_VOID__VOID, NULL},
          {"show", gtk_marshal_VOID__VOID, gtk_tree_view_show},
          {"hide", gtk_marshal_VOID__VOID, gtk_tree_view_hide},
       t@@ -506,6 +507,28 @@ void gtk_tree_view_destroy(GtkWidget *widget)
          view->model = NULL;
        }
        
       +void gtk_tree_view_click_column(GtkWidget *widget, gint column)
       +{
       +  GtkTreeView *view = GTK_TREE_VIEW(widget);
       +  GtkTreeViewColumn *col = g_slist_nth_data(view->columns, column);
       +  GtkListStore *model = view->model;
       +  if (!model || !view->headers_clickable) return;
       +
       +  if (col->sort_column_id == model->sort_column_id) {
       +    /* toggle order */
       +    if (model->sort_order == GTK_SORT_ASCENDING) {
       +      model->sort_order = GTK_SORT_DESCENDING;
       +    } else {
       +      model->sort_order = GTK_SORT_ASCENDING;
       +    }
       +  } else {
       +    model->sort_column_id = col->sort_column_id;
       +    model->sort_order = GTK_SORT_ASCENDING;
       +  }
       +  model->need_sort = TRUE;
       +  gtk_tree_view_sort(view);
       +}
       +
        void gtk_tree_view_show(GtkWidget *widget)
        {
          if (GTK_WIDGET_REALIZED(widget)) {
       t@@ -820,7 +843,7 @@ gint *gtk_tree_path_get_indices_with_depth(GtkTreePath *path, gint *depth)
        {
          /* Only one level; path *is* the row index */
          *depth = 1;
       -  return path;
       +  return (gint *)path;
        }
        
        void gtk_tree_selection_unselect_path(GtkTreeSelection *selection,
       t@@ -1086,10 +1109,91 @@ void gtk_tree_view_set_model(GtkTreeView *tree_view, GtkTreeModel *model)
          if (model) {
            tree_view->model = model;
            model->view = tree_view;
       -    /* todo: update view if necessary */
          }
        }
        
       +struct ListStoreSortData {
       +  GtkTreeIterCompareFunc sort_func;
       +  GtkListStore *store;
       +  gboolean reversed;
       +};
       +
       +static gint tree_view_sort_func(gconstpointer a, gconstpointer b, gpointer data){
       +  /* Map from sorting an array of guint indices into sorting a GtkListStore */
       +  struct ListStoreSortData *d = data;
       +  const guint *inda = a, *indb = b;
       +  if (d->reversed) {
       +    return d->sort_func(d->store, (guint*)indb, (guint*)inda, NULL);
       +  } else {
       +    return d->sort_func(d->store, (guint*)inda, (guint*)indb, NULL);
       +  }
       +}
       +
       +void gtk_tree_view_sort(GtkTreeView *tv)
       +{
       +  GtkListStore *model = tv->model;
       +  struct ListStoreSortData data;
       +  HWND hWnd;
       +  GArray *inds, *revinds;
       +  guint i;
       +  GArray *newrows;
       +  if (!model || !model->need_sort
       +      || model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) {
       +    return;
       +  }
       +
       +  /* Before sort, inds[row] = row */
       +  inds = g_array_sized_new(FALSE, FALSE, sizeof(guint), model->rows->len);
       +  for (i = 0; i < model->rows->len; ++i) {
       +    g_array_append_val(inds, i);
       +  }
       +  data.sort_func = g_array_index(model->sort_func, GtkTreeIterCompareFunc,
       +                                 model->sort_column_id);
       +  data.store = model;
       +  data.reversed = model->sort_order == GTK_SORT_DESCENDING;
       +  g_array_sort_with_data(inds, tree_view_sort_func, &data);
       +
       +  /* After sort, inds[newrow] = oldrow */
       +  /* Now we can reconstruct model->rows in the new order */
       +  newrows = g_array_sized_new(FALSE, FALSE, sizeof(GtkListStoreRow),
       +                              model->rows->len);
       +  for (i = 0; i < model->rows->len; ++i) {
       +    guint oldrow = g_array_index(inds, guint, i);
       +    g_array_append_val(newrows,
       +                       g_array_index(model->rows, GtkListStoreRow, oldrow));
       +  }
       +
       +  /* Make revinds[oldrow] = newrow */
       +  revinds = g_array_sized_new(FALSE, FALSE, sizeof(guint), model->rows->len);
       +  g_array_set_size(revinds, model->rows->len);
       +  for (i = 0; i < model->rows->len; ++i) {
       +    guint oldrow = g_array_index(inds, guint, i);
       +    g_array_index(revinds, guint, oldrow) = i;
       +  }
       +
       +  /* Update selection (only works for a single selection currently) */
       +  if (tv->selection) {
       +    guint oldrow = GPOINTER_TO_UINT(tv->selection->data);
       +    guint newrow = g_array_index(revinds, guint, oldrow);
       +    if (oldrow != newrow) {
       +      gtk_tree_selection_unselect_path(tv, &oldrow);
       +      gtk_tree_selection_select_path(tv, &newrow);
       +    }
       +  }
       +
       +  /* No need to free the old row data since the new structure takes ownership */
       +  g_array_free(model->rows, TRUE);
       +  model->rows = newrows;
       +
       +  g_array_free(inds, TRUE);
       +  g_array_free(revinds, TRUE);
       +  model->need_sort = FALSE;
       +
       +  hWnd = GTK_WIDGET(tv)->hWnd;
       +  if (hWnd)
       +    InvalidateRect(hWnd, NULL, FALSE);
       +}
       +
        GtkTreeModel *gtk_tree_view_get_model(GtkTreeView *tree_view)
        {
          return tree_view->model;
 (DIR) diff --git a/src/gtkport/treeview.h b/src/gtkport/treeview.h
       t@@ -181,6 +181,9 @@ gint gtk_tree_view_insert_column(GtkTreeView *tree_view,
                                         gint position);
        GtkTreeViewColumn *gtk_tree_view_get_column(GtkTreeView *tree_view, gint n);
        
       +/* Force a sort of the view */
       +void gtk_tree_view_sort(GtkTreeView *tv);
       +
        void gtk_tree_sortable_set_sort_func(GtkTreeSortable *sortable,
                                             gint sort_column_id,
                                             GtkTreeIterCompareFunc sort_func,
 (DIR) diff --git a/src/gui_client/gtk_client.c b/src/gui_client/gtk_client.c
       t@@ -1352,6 +1352,7 @@ void UpdateInventory(struct InventoryWidgets *Inven,
            herestore = GTK_LIST_STORE(gtk_tree_view_get_model(tv[1]));
          } else {
            tv[1] = NULL;
       +    herestore = NULL;
          }
        
          for (i = 0; i < numlist; i++) {
       t@@ -1413,6 +1414,13 @@ void UpdateInventory(struct InventoryWidgets *Inven,
            g_free(titles[0]);
          }
        
       +#ifdef CYGWIN
       +  /* Our Win32 GtkTreeView implementation doesn't auto-sort, so force it */
       +  if (herelist) {
       +    gtk_tree_view_sort(GTK_TREE_VIEW(herelist));
       +  }
       +#endif
       +
          /* Scroll so that selection is visible */
          for (i = 0; i < numlist; i++) {
            gtk_tree_selection_selected_foreach(gtk_tree_view_get_selection(tv[i]),