From b632a8d2e16be4344db31ecadbf2a0b42abf1caa Mon Sep 17 00:00:00 2001
From: Yann Leboulanger <asterix@lagaule.org>
Date: Wed, 5 Nov 2008 20:26:58 +0000
Subject: [PATCH] [thorstenp] improve advanced configuration editor speed. see
 #4457

---
 src/advanced.py      | 125 ++++++++++++++++++++++---------------------
 src/common/config.py |  19 +++++++
 2 files changed, 84 insertions(+), 60 deletions(-)

diff --git a/src/advanced.py b/src/advanced.py
index 8c33aef440..618675a541 100644
--- a/src/advanced.py
+++ b/src/advanced.py
@@ -25,6 +25,7 @@
 
 import gtk
 import gtkgui_helpers
+import gobject
 
 from common import gajim
 
@@ -41,6 +42,42 @@
 
 GTKGUI_GLADE = 'manage_accounts_window.glade'
 
+def rate_limit(rate):
+	''' call func at most *rate* times per second '''
+	def decorator(func):
+		timeout = [None]
+		def f(*args, **kwargs):
+			if timeout[0] is not None:
+				gobject.source_remove(timeout[0])
+				timeout[0] = None
+			def timeout_func():
+				func(*args, **kwargs)
+				timeout[0] = None
+			timeout[0] = gobject.timeout_add(int(1000.0 / rate), timeout_func)
+		return f
+	return decorator
+
+def tree_model_iter_children(model, treeiter):
+	it = model.iter_children(treeiter)
+	while it:
+		yield it
+		it = model.iter_next(it)
+
+def tree_model_pre_order(model, treeiter):
+	yield treeiter
+	for childiter in tree_model_iter_children(model, treeiter):
+		for it in tree_model_pre_order(model, childiter):
+			yield it
+
+try:
+	any(()) # builtin since python 2.5
+except Exception:
+	def any(iterable):
+		for element in iterable:
+			if element:
+				return True
+		return False
+
 class AdvancedConfigurationWindow(object):
 	def __init__(self):
 		self.xml = gtkgui_helpers.get_glade('advanced_configuration_window.glade')
@@ -65,7 +102,9 @@ def __init__(self):
 			'color': _('Color')} 
 
 		treeview = self.xml.get_widget('advanced_treeview')
+		self.treeview = treeview
 		self.model = gtk.TreeStore(str, str, str)
+		self.fill_model()
 		self.model.set_sort_column_id(0, gtk.SORT_ASCENDING)
 		self.modelfilter = self.model.filter_new()
 		self.modelfilter.set_visible_func(self.visible_func)
@@ -88,9 +127,6 @@ def __init__(self):
 		treeview.insert_column_with_attributes(-1, _('Type'),
 			renderer_text, text = 2)
 
-		# add data to model
-		gajim.config.foreach(self.fill, self.model)
-
 		treeview.set_model(self.modelfilter)
 
 		# connect signal for selection change
@@ -215,65 +251,34 @@ def on_advanced_configuration_window_destroy(self, widget):
 	def on_advanced_close_button_clicked(self, widget):
 		self.window.destroy()
 
-	def find_iter(self, model, parent_iter, name):
-		if not parent_iter:
-			iter = model.get_iter_root()
-		else:
-			iter = model.iter_children(parent_iter)
-		while iter:
-			if model[iter][C_PREFNAME].decode('utf-8') == name:
-				break
-			iter = model.iter_next(iter)
-		return iter
-
-	def fill(self, model, name, parents, val):
-		iter = None
-		if parents:
-			for p in parents:
-				iter2 = self.find_iter(model, iter, p)
-				if iter2:
-					iter = iter2
-
-		if not val:
-			model.append(iter, [name, '', ''])
-			return
-		type = ''
-		if val[OPT_TYPE]:
-			type = val[OPT_TYPE][0]
-			type = self.types[type] # i18n
-		value = val[OPT_VAL]
-		if name == 'password':
-			#we talk about password
-			value = _('Hidden') # override passwords with this string
-		if value in self.right_true_dict:
-			value = self.right_true_dict[value]
-		model.append(iter, [name, value, type])
-
-	def visible_func(self, model, iter_):
-		str = self.entry.get_text().decode('utf-8')
-		if str in (None, ''):
-			return True # show all
-		name = model[iter_][C_PREFNAME].decode('utf-8')
-		# If a child of the iter matches, we return True
-		if model.iter_has_child(iter_):
-			iterC = model.iter_children(iter_)
-			while iterC:
-				nameC = model[iterC][C_PREFNAME].decode('utf-8')
-				if model.iter_has_child(iterC):
-					iterCC = model.iter_children(iterC)
-					while iterCC:
-						nameCC = model[iterCC][C_PREFNAME].decode('utf-8')
-						if nameCC.find(str) != -1:
-							return True
-						iterCC = model.iter_next(iterCC)
-				elif nameC.find(str) != -1:
-					return True
-				iterC = model.iter_next(iterC)
-		elif name.find(str) != -1:
-			return True
-		return False
+	def fill_model(self, node=None, parent=None):
+		for item, option in gajim.config.get_children(node):
+			name = item[-1]
+			if option is None: # Node
+				newparent = self.model.append(parent, [name, '', ''])
+				self.fill_model(item, newparent)
+			else: # Leaf
+				type_ = self.types[option[OPT_TYPE][0]]
+				if name == 'password':
+					value = _('Hidden')
+				else:
+					value = self.right_true_dict.get(option[OPT_VAL],
+						option[OPT_VAL])
+				self.model.append(parent, [name, value, type_])
 
+	def visible_func(self, model, treeiter):
+		search_string  = self.entry.get_text()
+		return any(search_string in model[it][C_PREFNAME] for it in
+			tree_model_pre_order(model, treeiter) if model[it][C_VALUE] != '')
+
+	@rate_limit(3)
 	def on_advanced_entry_changed(self, widget):
 		self.modelfilter.refilter()
+		if not widget.get_text():
+			# Maybe the expanded rows should be remembered here ...
+			self.treeview.collapse_all()
+		else:
+			# ... and be restored correctly here
+			self.treeview.expand_all()	
 
 # vim: se ts=3:
diff --git a/src/common/config.py b/src/common/config.py
index 21b5aabc0c..b7bbdb4eb1 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -477,6 +477,25 @@ def foreach(self, cb, data = None):
 				for opt3 in dict[opt2]:
 					cb(data, opt3, [opt, opt2], dict[opt2][opt3])
 
+	def get_children(self, node=None):
+		''' Tree-like interface '''
+		if node is None:
+			for child, option in self.__options.iteritems():
+				yield (child, ), option
+			for grandparent in self.__options_per_key:
+				yield (grandparent, ), None
+		elif len(node) == 1:
+			grandparent, = node
+			for parent in self.__options_per_key[grandparent][1]:
+				yield (grandparent, parent), None
+		elif len(node) == 2:
+			grandparent, parent = node
+			children = self.__options_per_key[grandparent][1][parent]
+			for child, option in children.iteritems():
+				yield (grandparent, parent, child), option
+		else:
+			raise ValueError('Invalid node')
+
 	def is_valid_int(self, val):
 		try:
 			ival = int(val)
-- 
GitLab