Chapter 7. Sorting

Lists and trees are meant to be sorted. This is done using the GTree.tree_sortable (GtkTreeSortable) interface. GTree.list_store and GTree.tree_store inherit from GTree.tree_sortable which in turn inherits from GTree.model.

The most straight forward way to sort a list store or tree store is to directly use the tree sortable interface on them. This will sort the store in place, meaning that rows will actually be reordered in the store if required. This has the advantage that the position of a row in the tree view will always be the same as the position of a row in the model, in other words: a tree path refering to a row in the view will always refer to the same row in the model, so you can get a row's iter easily with Gtree.model.get_iter using a tree path supplied by the tree view. This is not only convenient, but also sufficient for most scenarios.

7.1. GTree.tree_sortable

The tree sortable interface is fairly simple and should be easy to use. Basically you define a 'sort column ID' integer for every criterion you might want to sort by and tell the tree sortable which function should be called to compare two rows (represented by two tree iters) for every sort ID with GTree.tree_sortable#set_sort_func method (gtk_tree_sortable_set_sort_func). Then you sort the model by setting the sort column ID and sort order with GTree.tree_sortable#set_sort_column_id method (gtk_tree_sortable_set_sort_column_id), and the model will be re-sorted using the compare function you have set up. Your sort column IDs can correspond to your model columns, but they do not have to (you might want to sort according to a criterion that is not directly represented by the data in one single model column, for example). Some code to illustrate this:


(* file: sortable.ml *)

open GTree

let cols = new GTree.column_list
let col_name = cols#add Gobject.Data.string
let col_year_born = cols#add Gobject.Data.uint

...

let compare a b =
  if a < b then -1
  else if a > b then 1
  else 0

let sort_by_name (model:#GTree.model) row1 row2 =
  let name1 = model#get ~row:row1 ~column:col_name in
  let name2 = model#get ~row:row2 ~column:col_name in
  compare name1 name2

...
 
let create_list_and_view ~packing () =
  let liststore = create_and_fill_model () in

  liststore#set_sort_func col_name.index sort_by_name;
  liststore#set_sort_func col_year_born.index sort_by_year_born;

  (* set initial sort order *)
  liststore#set_sort_column_id col_name.index `ASCENDING;

  let view = GTree.view ~model:liststore ~packing () in

  ...

Usually things are a bit easier if you make use of the tree view column headers for sorting, in which case you only need to assign sort column IDs and your compare functions, but do not need to set the current sort column ID or order yourself (see below).

Your tree iter compare function should return a negative value if the row specified by iter a comes before the row specified by iter b, and a positive value if row b comes before row a. It should return 0 if both rows are equal according to your sorting criterion (you might want to use a second sort criterion though to avoid 'jumping' of equal rows when the store gets resorted). Your tree iter compare function should not take the sort order into account, but assume an ascending sort order (otherwise bad things will happen).