From 7378efcb21c0669aa757181199e27c08889c212e Mon Sep 17 00:00:00 2001
From: Benjamin Richter <br@waldteufel-online.net>
Date: Sat, 26 Sep 2009 20:23:59 +0200
Subject: [PATCH] Improve group renaming efficiency. Fixes #4212

---
 src/common/connection.py                   |  5 ++
 src/common/xmpp/roster_nb.py               | 10 ++++
 src/common/zeroconf/connection_zeroconf.py |  5 ++
 src/common/zeroconf/roster_zeroconf.py     |  4 ++
 src/roster_window.py                       | 63 ++++++++++++++++------
 5 files changed, 70 insertions(+), 17 deletions(-)

diff --git a/src/common/connection.py b/src/common/connection.py
index 27b840f7b7..bcbdc459af 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -1487,6 +1487,11 @@ class Connection(ConnectionHandlers):
 			self.connection.getRoster().setItem(jid = jid, name = name,
 				groups = groups)
 
+	def update_contacts(self, contacts):
+		'''update multiple roster items on jabber server'''
+		if self.connection:
+			self.connection.getRoster().setItemMulti(contacts)
+
 	def send_new_account_infos(self, form, is_form):
 		if is_form:
 			# Get username and password and put them in new_account_info
diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py
index a34e6ced1c..715476b9ed 100644
--- a/src/common/xmpp/roster_nb.py
+++ b/src/common/xmpp/roster_nb.py
@@ -174,6 +174,16 @@ class NonBlockingRoster(PlugIn):
 		item=query.setTag('item',attrs)
 		for group in groups: item.addChild(node=Node('group',payload=[group]))
 		self._owner.send(iq)
+	def setItemMulti(self,items):
+		''' Renames multiple contacts and sets their group lists.'''
+		iq=Iq('set',NS_ROSTER)
+		query=iq.getTag('query')
+		for i in items:
+			attrs={'jid':i['jid']}
+			if i['name']: attrs['name']=i['name']
+			item=query.setTag('item',attrs)
+			for group in i['groups']: item.addChild(node=Node('group',payload=[group]))
+		self._owner.send(iq)
 	def getItems(self):
 		''' Return list of all [bare] JIDs that the roster is currently tracks.'''
 		return self._data.keys()
diff --git a/src/common/zeroconf/connection_zeroconf.py b/src/common/zeroconf/connection_zeroconf.py
index e832378bc3..c23f4a125e 100644
--- a/src/common/zeroconf/connection_zeroconf.py
+++ b/src/common/zeroconf/connection_zeroconf.py
@@ -526,6 +526,11 @@ class ConnectionZeroconf(ConnectionHandlersZeroconf):
 			self.connection.getRoster().setItem(jid = jid, name = name,
 				groups = groups)
 
+	def update_contacts(self, contacts):
+		'''update multiple roster items'''
+		if self.connection:
+			self.connection.getRoster().setItemMulti(contacts)
+
 	def new_account(self, name, config, sync = False):
 		gajim.log.debug('This should not happen (new_account)')
 
diff --git a/src/common/zeroconf/roster_zeroconf.py b/src/common/zeroconf/roster_zeroconf.py
index 38808b06bc..42fe0edd6c 100644
--- a/src/common/zeroconf/roster_zeroconf.py
+++ b/src/common/zeroconf/roster_zeroconf.py
@@ -88,6 +88,10 @@ class Roster:
 		self._data[jid]['status'] = status
 		self._data[jid]['show'] = status
 
+	def setItemMulti(self, items):
+		for i in items:
+			self.setItem(jid=i['jid'], name=i['name'], groups=i['groups'])
+
 	def delItem(self, jid):
 		#print 'roster_zeroconf.py: delItem %s' % jid
 		if jid in self._data:
diff --git a/src/roster_window.py b/src/roster_window.py
index 7bc4c1cf1c..c5eb1474e7 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -848,6 +848,51 @@ class RosterWindow:
 		'''Remove transport from roster and redraw account and group.'''
 		self.remove_contact(jid, account, force=True, backend=True)
 		return True
+		
+	def rename_group(self, old_name, new_name):
+		"""
+		rename a roster group
+		"""
+		if old_name == new_name:
+			return
+		
+		# Groups may not change name from or to a special groups
+		for g in helpers.special_groups:
+			if g in (new_name, old_name):
+				return
+		
+		# update all contacts in the given group
+		if self.regroup:
+			accounts = gajim.connections.keys()
+		else:
+			accounts = [account,]
+		
+		for acc in accounts:
+			changed_contacts = []
+			for jid in gajim.contacts.get_jid_list(acc):
+				contact = gajim.contacts.get_first_contact_from_jid(acc, jid)
+				if old_name not in contact.groups:
+					continue
+				
+				self.remove_contact(jid, acc, force=True)
+				
+				contact.groups.remove(old_name)
+				if new_name not in contact.groups:
+					contact.groups.append(new_name)
+			
+				changed_contacts.append({'jid':jid, 'name':contact.name, 
+					'groups':contact.groups})
+			
+			gajim.connections[acc].update_contacts(changed_contacts)				
+			
+			for c in changed_contacts:
+				self.add_contact(c['jid'], acc)
+				
+			self._adjust_group_expand_collapse_state(new_name, acc)
+			
+			self.draw_group(old_name, acc)
+			self.draw_group(new_name, acc)
+					
 
 	def add_contact_to_groups(self, jid, account, groups, update=True):
 		'''Add contact to given groups and redraw them.
@@ -2711,23 +2756,7 @@ class RosterWindow:
 					win.show_title()
 			elif row_type == 'group':
 				# in C_JID column, we hold the group name (which is not escaped)
-				if old_text == new_text:
-					return
-				# Groups may not change name from or to a special groups
-				for g in helpers.special_groups:
-					if g in (new_text, old_text):
-						return
-				# update all contacts in the given group
-				if self.regroup:
-					accounts = gajim.connections.keys()
-				else:
-					accounts = [account,]
-				for acc in accounts:
-					for jid in gajim.contacts.get_jid_list(acc):
-						contact = gajim.contacts.get_first_contact_from_jid(acc, jid)
-						if old_text in contact.groups:
-							self.add_contact_to_groups(jid, acc, [new_text,])
-							self.remove_contact_from_groups(jid, acc, [old_text,])
+				self.rename_group(old_text, new_text)
 
 		def on_canceled():
 			if 'rename' in gajim.interface.instances:
-- 
GitLab