I have a Gtk::TreeView with a Gtk::TreeModel and a Gtk::TreeModelFilter. The Tree Model is like this:
category1
--> actual row of data
category2
--> actual row of data
I want to filter on the contents of @search_entry, but I want category1 to be displayed if a row under it is still visible, and category2 to be hidden if there are no rows under it still visible. My understanding of Gtk::TreeModelFilter#set_visible_func is that you get the model and iter from the "child model", so that you can check whether to display the child iter. This function gets called on every iter in the model every time I call Gtk::TreeModelFilter#refilter.Therefore I'm saying: if the iter you just gave me is on the first level, get the path, step down one, convert to the same path on the filter's model and use whether the new path exists to test visibility.
@store = Gtk::TreeStore.new(Gdk::Pixbuf, String, String, Menagerie::Program, TrueClass)
@tree_filter = Gtk::TreeModelFilter.new(@store)
@treeview.model = @tree_filter
# @first_time gets set to false after the model is loaded the first time
@first_time = true
@tree_filter.set_visible_func do |model, iter|
has_visible_children = true
begin
iter_path = iter.path
if iter_path.depth == 1 && @first_time != true
iter_path.down!
has_visible_children = @tree_filter.convert_child_path_to_path(iter_path) ? true : false
end
rescue => e
puts "THIS ERROR: " + e.message
end
unless @search_entry.text == ""
if [1,2].collect {|i| iter[i] =~ /#{@search_entry.text}/i }.any?
true
elsif iter[4] == true and has_visible_children
true
else
false
end
else
true
end
end
The line
has_visible_children = @tree_filter.convert_child_path_to_path(iter_path) ? true : false
is causing a "THIS ERROR: stack level too deep" output for each iter.
There's an infinite recursion going on here, but I don't see where it's happening or how I can avoid it. I'm sure I'm thinking about this the wrong way, but I've been hacking on this a couple days without a breakthrough.
refilter
calls the block on every node. The return value isn't saved with the node, though, so no matter how you do it, if you have to look down the tree, you'll be repeating calculations.
# Simplified version - returns true if search_text found in iter or any of its
# first-level children.
# Let's assume you add a method to GTK::TreeIter:
# def has_text? search_text
# self[1] =~ /#{search_text}/i or self[2] =~ /#{search_text}/i
# end
@tree_filter.set_visible_func do |model, iter|
next true if @search_entry.text.empty? # No filtering if no search text
next true if iter.path.depth == 0 # Always show root node
next true if iter.has_text? @search_entry.text
if child_iter = iter.first_child # Then we have children to check
has_visible_children = false
loop do
has_visible_children ||= child_iter.has_text? @search_entry.text
break unless child_iter.next! # returns false if no more children
end
next has_visible_children
end
next false # Not root, doesn't contain search_text, has no children
end