3.7. Removing Multiple Rows

Removing multiple rows at once can be a bit tricky at times, and requires some thought on how to do this best. For example, it is not possible to traverse a store with foreach method, check in the callback function whether the given row should be removed and then just remove it by calling one of the stores' remove functions. This will not work, because the model is changed from within the foreach loop, which might suddenly invalidate formerly valid tree iters in the foreach function, and thus lead to unpredictable results.

You could traverse the store in a while loop of course, and call list_store#remove or tree_store#remove whenever you want to remove a row, and then just continue if the remove functions returns true (meaning that the iter is still valid and now points to the row after the row that was removed). However, this approach will only work with Gtk+-2.2 or later and will not work if you want your programs to compile and work with Gtk+-2.0 as well, for the reasons outlined above (in Gtk+-2.0 the remove functions did not set the passed iter to the next valid row). Also, while this approach might be feasable for a list store, it gets a bit awkward for a tree store. (The current LablGTK2 supports Gtk+-2.4 and remove method returns true or false.)

Here is an example for an alternative approach to removing multiple rows in one go (here we want to remove all rows from the store that contain persons that have been born after 1980, but it could just as well be all selected rows or some other criterion):


 (******************************************************************
  *
  *  Removing multiple rows in one go
  *
  ******************************************************************)

  ...

  let filter store pred =
    let rr_list = ref [] in (* list of GTree.row_reference to remove *)
    let foreach_func path iter =
      if pred iter
      then rr_list := store#get_row_reference path :: !rr_list;
      false	(* do not stop walking the store, call us with next row *)
    in
    store#foreach foreach_func;
    !rr_list

  let remove_people_born_after_1980 store =
    let born_after_1980 iter =
      let year_of_birth = store#get iter col_year_born in
      year_of_birth > 1980
    in
    let r_list = filter store born_after_1980 in
    let remove reference =
      let iter = reference#iter in
      store#remove iter;	(* This returns bool *)
      ()
    in
    List.iter remove r_list

  ...

GTree.list_store#clear (gtk_list_store_clear) and GTree.tree_store#clear (gtk_tree_store_clear) come in handy if you want to remove all rows.