Hi everyone! After hesitating for months already, I’ve finally decided to set up my own development blog, in the hope that it creates more awareness of FLOSS software (and other related projects I work on). For starters, let’s continue Philip Withnall’s excellent effort in making a GLib API mini-series of blog posts.

TL;DR: Starting from GLib 2.64, you can use g_list_store_find() and g_list_store_find_with_equal_func().

GListStore

A common pattern in GTK to use display a list of items is by creating a GListModel —which holds the the items— and then binding that to a GtkListBox using gtk_list_box_bind_model() so that you can cleanly separate UI logic from updates to your data model. Since GListModel is just an interface, you have to make sure you either implement it, or just use the simple implementation provided by GLib: GListStore which often works good enough for most use cases.

As a side note: the reason why you can’t just bind to a GList or a GPtrArray (two commonly used data structures) is because they don’t signal the consumer if items got added/removed. Adding these is of course a possibility, but that would come with a performance hit (and an ABI break, since the structs are public).

Something which always mildly frustrated me about GListStore is that it doesn’t provide a quick one-liner to check whether it contains a certain element (and if so, at what index). This meant that a lot of projects using it have to provide their own helper, or repeatedly have to write for-loops.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
static void
foo (GListStore *store, MyElement *item)
{
  gboolean found = FALSE;
  guint i, index = 0;

  for (i = 0; i < g_list_store_n_elements (store); i++) {
    if (g_list_store_get_item (store, i) == item) {
      found = TRUE;
      index = i;
      break;
    }
  }

  if (found) {
    g_debug ("Element found at position %u", index);
  } else {
    g_debug ("Item wasn't found, maybe it needs to be added?");
  }
}

The new API

However, starting from version 2.63.3, GLib now provides two convenience functions for you: g_list_store_find() and g_list_store_find_with_equal_func(). These functions return a boolean to know whether the element was found, and provide an optional out-argument if you’re interested in the position of the element.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static void
foo (GListStore *store, MyElement *item)
{
  guint index;

  /* Or set the last argument to NULL if you don't care about the index */
  if (g_list_store_find (store, item, &index)) {
    g_debug ("Element found at position %u", index);
  } else {
    g_debug ("Item wasn't found, maybe it needs to be added?");
  }
}

If you want to have a custom equality function (rather than comparing raw pointers), then you can use the latter addition:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
static gboolean
my_elements_equal (gconstpointer a, gconstpointer b)
{
  MyElement *el_a = (MyElement *) a;
  MyElement *el_b = (MyElement *) b;

  return g_strcmp0 (el_a->name, el_b->name) == 0;
}

static void
foo (GListStore *store, MyElement *item)
{
  guint index;

  /* Or set the last argument to NULL if you don't care about the index */
  if (g_list_store_find_with_equal_func (store, item, my_elements_equal, &index)) {
    g_debug ("Element found at position %u", index);
  } else {
    g_debug ("Item wasn't found, maybe it needs to be added?");
  }
}

The next stable release that will include them will be GLib 2.64. Enjoy!

Thanks

I wanted to say thanks to Sam and Philip for giving the final pushes that I needed to start this blog. Also to Carlos for documenting on how to set up a blog using GitLab CI. Finally, thanks to Nirbheek and Philip for taking the time to review the code.