views:

402

answers:

2

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.

+1  A: 

I know nothing about Ruby, but this error clearly points towards too many recursion iterations. The context needs to be stored on the stack for each call, causing - hurray - a

stack overflow

:-) Add a variable to track your levels of iterations and print it out with the error. There is either something wrong with your data or the recursion logic, or both.

cdonner
+1  A: 

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
Sarah Mei
Okay, I see. We can figure everything out from the TreeModel, without worrying about the TreeModelFilter. And you cleaned up my code! Thanks, Sarah.
method