#include <gtk/gtk.h>

GtkTreeModel *model, *sort , *filter;

/* Let's remove bottommost child node from tree. In this
   example this causes filter and sort model caches
   to go out of sync... */
static void clicked(GtkWidget *widget, gpointer data)
{
  GtkTreeIter iter, iter2, root;
  gint n;
  GtkTreeModel *model = GTK_TREE_MODEL(data);

  gtk_tree_model_get_iter_first(model, &root);
  n = gtk_tree_model_iter_n_children(model, &root);
  
  if (gtk_tree_model_iter_nth_child(model, &iter, &root, n - 2))
  {
    if (gtk_tree_model_iter_children(model, &iter2, &iter))
      gtk_tree_store_remove(GTK_TREE_STORE(data), &iter2);
  }
  else g_assert_not_reached();
}

/* Carefully constructed model. This bug is hard to get appear... */
static gboolean fill_model(gpointer data)
{
  GtkTreeIter root, iter, child;
  GtkTreeStore *store;

  static int i = 0;
  i++;

  store = GTK_TREE_STORE(data);
  gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &root);

  gtk_tree_store_append(store, &iter, &root);

  if (i < 3)
    gtk_tree_store_set(store, &iter, 0, i, -1);
  
  if (i%2 == 0)
  {
    gtk_tree_store_append(store, &child, &iter);
    gtk_tree_store_set(store, &child, 0, 10, -1);
  }

  return i < 3;
}

static GtkTreeModel *create_model(void)
{
  GtkTreeIter iter, child, root;
  GtkTreeStore *store = gtk_tree_store_new(1, G_TYPE_INT);

  gtk_tree_store_append(store, &root, NULL);
  gtk_tree_store_set(store, &root, 0, 33, -1);

  gtk_tree_store_append(store, &iter, &root);
  gtk_tree_store_set(store, &iter, 0, 50, -1);

  gtk_tree_store_append(store, &iter, NULL);
  gtk_tree_store_set(store, &iter, 0, 22, -1);

  return GTK_TREE_MODEL(store);
}

/* We filter the last node out. This causes sort model to clear
   it's level cache, but filter model still believes that node is there... */
static gboolean filter_func(GtkTreeModel *model, 
  GtkTreeIter *iter, gpointer data)
{
  gint value;

  gtk_tree_model_get(model, iter, 0, &value, -1);

  return (value != 0);
}

int main(int argc, char *argv[])
{
  GtkWidget *window, *vbox, *button, *tree;
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;

  gtk_init(&argc, &argv);

  model = create_model();
  sort = gtk_tree_model_sort_new_with_model(model);
  filter = gtk_tree_model_filter_new(sort, NULL);

  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), 
    filter_func, NULL, NULL);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  vbox = gtk_vbox_new(FALSE, 0);
  button = gtk_button_new_with_label("Remove");
  tree = gtk_tree_view_new_with_model(filter);
  g_object_unref(model); 

  col = gtk_tree_view_column_new();
  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer, "text", 0);

  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), col);

  gtk_box_pack_start_defaults(GTK_BOX(vbox), tree);
  gtk_box_pack_start_defaults(GTK_BOX(vbox), button);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  g_signal_connect(window, "destroy", gtk_main_quit, NULL);
  g_signal_connect(button, "clicked", G_CALLBACK(clicked), model);
  gtk_widget_show_all(window);
  gtk_tree_view_expand_all(GTK_TREE_VIEW(tree));

  g_timeout_add(2000, fill_model, model);

  gtk_main();

  return 0;
}
