[kupfer: 16/22] plugin.pidgin: Listen to Sign Off signal and rework signals



commit 9b910045c1fea01f8ec793d96067febd35dd67f8
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Thu Oct 29 18:37:04 2009 +0100

    plugin.pidgin: Listen to Sign Off signal and rework signals
    
    Listen to SigningOff signal to remove contacts as whole accounts sign
    off. This signal is also called when Pidgin quits normally.
    
    Implement this with _remove_buddies_not_connected, which removes all
    buddies whole account is not connected right now.
    
    We also listen to Buddy Connected and Status Changed signals to get all
    buddy updates. Single buddy updates are added to a queue and scheduled
    a short time in the future. This has two main benefits:
    
    * Many buddy updates per connection is more effective, and we only
      process each buddy at most once.
    * Buddy update happens outside the D-Bus signal notification, so we
      are safer during extreme events (signals during Pidgin teardown)
    
    Insert some debug printouts so that we see where we do (too)
    frequent updates or double updates.
    
    Use the helplib's convenience function for connecting to dbus signals.

 kupfer/plugin/pidgin.py |   92 +++++++++++++++++++++++++++++-----------------
 1 files changed, 58 insertions(+), 34 deletions(-)
---
diff --git a/kupfer/plugin/pidgin.py b/kupfer/plugin/pidgin.py
index 7643a02..018eb2a 100644
--- a/kupfer/plugin/pidgin.py
+++ b/kupfer/plugin/pidgin.py
@@ -4,9 +4,9 @@ import dbus
 
 from kupfer.objects import (Leaf, Action, Source, AppLeafContentMixin,
 		TextLeaf, TextSource)
-from kupfer import pretty
+from kupfer import pretty, scheduler
 from kupfer import icons
-from kupfer.helplib import DbusWeakCallback, PicklingHelperMixin
+from kupfer.helplib import dbus_signal_connect_weakly, PicklingHelperMixin
 
 __kupfer_name__ = _("Pidgin")
 __kupfer_sources__ = ("ContactsSource", )
@@ -139,10 +139,14 @@ class ContactsSource(AppLeafContentMixin, Source, PicklingHelperMixin):
 		self.mark_for_update()
 		self.all_buddies = {}
 		self._install_dbus_signal()
+		self._buddy_update_timer = scheduler.Timer()
+		self._buddy_update_queue = set()
 
 	def pickle_prepare(self):
 		# delete data that we do not want to save to next session
 		self.all_buddies = {}
+		self._buddy_update_timer = None
+		self._buddy_update_queue = None
 
 	def _get_pidgin_contact(self, interface, buddy, account=None, protocol=None):
 		if not account:
@@ -192,11 +196,46 @@ class ContactsSource(AppLeafContentMixin, Source, PicklingHelperMixin):
 										   protocol=protocol,
 										   account=account)
 
-	def _buddy_signed_on(self, buddy):
+	def _remove_buddies_not_connected(self):
+		""" Remove buddies that belong to accounts no longer connected """
+		if not self.all_buddies:
+			return
 		interface = _create_dbus_connection()
-		if not buddy in self.all_buddies:
-			self.all_buddies[buddy] = self._get_pidgin_contact(interface, buddy)
-			self.mark_for_update()
+		if interface is None:
+			return
+
+		accounts = interface.PurpleAccountsGetAllActive()
+		is_disconnected = interface.PurpleAccountIsDisconnected
+		conn_accounts = set(a for a in accounts if not is_disconnected(a))
+		for buddy, pcontact in self.all_buddies.items():
+			if pcontact.account not in conn_accounts:
+				del self.all_buddies[buddy]
+
+	def _signing_off(self, conn):
+		self.output_debug("Pidgin Signing Off", conn)
+		self._remove_buddies_not_connected()
+		self.mark_for_update()
+
+	def _update_pending(self):
+		"""Update all buddies in the update queue"""
+		interface = _create_dbus_connection()
+		if interface is None:
+			self._buddy_update_queue.clear()
+			return
+		for buddy in self._buddy_update_queue:
+			if interface.PurpleBuddyIsOnline(buddy):
+				self.output_debug("updating buddy", buddy)
+				pcontact = self._get_pidgin_contact(interface, buddy)
+				self.all_buddies[buddy] = pcontact
+			else:
+				self.all_buddies.pop(buddy, None)
+		self._buddy_update_queue.clear()
+		self.mark_for_update()
+
+	def _buddy_signed_on(self, buddy):
+		if buddy not in self.all_buddies:
+			self._buddy_update_queue.add(buddy)
+			self._buddy_update_timer.set(1, self._update_pending)
 
 	def _buddy_signed_off(self, buddy):
 		if buddy in self.all_buddies:
@@ -206,14 +245,8 @@ class ContactsSource(AppLeafContentMixin, Source, PicklingHelperMixin):
 	def _buddy_status_changed(self, buddy, old, new):
 		'''Callback when status is changed reload the entry
 		which get the new status'''
-		interface = _create_dbus_connection()
-		status_message = interface.PurpleStatusGetAttrString(old, "message")
-
-		if buddy in self.all_buddies:
-			del self.all_buddies[buddy]
-
-		self.all_buddies[buddy] = self._get_pidgin_contact(interface, buddy)
-		self.mark_for_update()
+		self._buddy_update_queue.add(buddy)
+		self._buddy_update_timer.set(1, self._update_pending)
 
 	def _install_dbus_signal(self):
 		'''Add signals to pidgin when buddy goes offline or
@@ -222,27 +255,18 @@ class ContactsSource(AppLeafContentMixin, Source, PicklingHelperMixin):
 			session_bus = dbus.Bus()
 		except dbus.DBusException:
 			return
-		buddy_sign_on_cb = DbusWeakCallback(self._buddy_signed_on)
-		buddy_sign_on_cb.token = session_bus.add_signal_receiver(
-				buddy_sign_on_cb,
-				"BuddySignedOn",
-				dbus_interface="im.pidgin.purple.PurpleInterface",
-				byte_arrays=True)
-
-		buddy_status_changed_cb = DbusWeakCallback(self._buddy_status_changed)
-		buddy_status_changed_cb.token = session_bus.add_signal_receiver(
-				buddy_status_changed_cb,
-				"BuddyStatusChanged",
-				dbus_interface="im.pidgin.purple.PurpleInterface",
-				byte_arrays=True)
-
-		buddy_sign_off_cb = DbusWeakCallback(self._buddy_signed_off)
-		buddy_sign_off_cb.token = session_bus.add_signal_receiver(
-				buddy_sign_off_cb,
-				"BuddySignedOff",
-				dbus_interface="im.pidgin.purple.PurpleInterface",
-				byte_arrays=True)
 
+		dbus_signal_connect_weakly(session_bus, "SigningOff",
+				self._signing_off, dbus_interface=IFACE_NAME)
+
+		dbus_signal_connect_weakly(session_bus, "BuddySignedOn",
+				self._buddy_signed_on, dbus_interface=IFACE_NAME)
+
+		dbus_signal_connect_weakly(session_bus, "BuddyStatusChanged",
+				self._buddy_status_changed, dbus_interface=IFACE_NAME)
+
+		dbus_signal_connect_weakly(session_bus, "BuddySignedOff",
+				self._buddy_signed_off, dbus_interface=IFACE_NAME)
 
 	def get_items(self):
 		if not self.all_buddies:



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]