Search code examples
rubygnomeruby-gnome2

Stack level too deep error Ruby-Gnome2


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.


Solution

  • 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