diff frontends/src/quick_frontend/quick_app.py @ 1337:f29beedb33b0 frontends_multi_profiles

merged souliane changes
author Goffi <goffi@goffi.org>
date Mon, 23 Feb 2015 18:08:22 +0100
parents 2ecc07a8f91b 0f92b6a150ff
children 139263ee85c5
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_app.py	Mon Feb 23 18:04:25 2015 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Mon Feb 23 18:08:22 2015 +0100
@@ -17,16 +17,25 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from sat.core.i18n import _
-import sys
 from sat.core.log import getLogger
 log = getLogger(__name__)
+
+from sat.core.i18n import _
 from sat.core import exceptions
+
 from sat_frontends.tools import jid
 from sat_frontends.quick_frontend.quick_widgets import QuickWidgetsManager
 from sat_frontends.quick_frontend import quick_chat
+from sat_frontends.quick_frontend.constants import Const as C
 
-from sat_frontends.quick_frontend.constants import Const as C
+import sys
+from collections import OrderedDict
+
+try:
+    # FIXME: to be removed when an acceptable solution is here
+    unicode('')  # XXX: unicode doesn't exist in pyjamas
+except (TypeError, AttributeError):  # Error raised is not the same depending on pyjsbuild options
+    unicode = str
 
 
 class ProfileManager(object):
@@ -75,11 +84,14 @@
 
     def _plug_profile_gotCachedValues(self, cached_values):
         # TODO: watched plugin
+
+        # add the contact list and its listener
         contact_list = self.host.addContactList(self.profile)
+        self.host.contact_lists[self.profile] = contact_list
 
         for entity, data in cached_values.iteritems():
             for key, value in data.iteritems():
-                self.host.contact_lists[self.profile].setCache(jid.JID(entity), key, value)
+                contact_list.setCache(jid.JID(entity), key, value)
 
         if not self.bridge.isConnected(self.profile):
             self.host.setStatusOnline(False, profile=self.profile)
@@ -169,11 +181,18 @@
     def unplug(self, profile):
         if profile not in self._profiles:
             raise ValueError('The profile [{}] is not plugged'.format(profile))
+
+        # remove the contact list and its listener
+        host = self._profiles[profile].host
+        host.contact_lists[profile].onDelete()
+        del host.contact_lists[profile]
+
         del self._profiles[profile]
 
     def chooseOneProfile(self):
         return self._profiles.keys()[0]
 
+
 class QuickApp(object):
     """This class contain the main methods needed for the frontend"""
 
@@ -196,7 +215,7 @@
         self.selected_widget = None # widget currently selected (must be filled by frontend)
 
         # listeners
-        self._listeners = {} # key: listerner type ("avatar", "selected", etc), value: list of callbacks
+        self._listeners = {} # key: listener type ("avatar", "selected", etc), value: list of callbacks
 
         ## bridge ##
         try:
@@ -287,31 +306,38 @@
             handler(*args, **kwargs)
         self.bridge.register(functionName, signalReceived, iface)
 
-    def addListerner(self, type_, callback):
-        """Add a listerner for an event
+    def addListener(self, type_, callback, profiles_filter=None):
+        """Add a listener for an event
 
         /!\ don't forget to remove listener when not used anymore (e.g. if you delete a widget)
         @param type_: type of event, can be:
             - avatar: called when avatar data is updated
-                      args: (entity, avatar file, profile)
+                      args: (entity, avatar file)
+            - presence: called when a presence is received
+                         args: (entity, show, priority, statuses)
         @param callback: method to call on event
+        @param profiles_filter (set[unicode]): if set and not empty, the
+            listener will be callable only by one of the given profiles.
         """
         assert type_ in C.LISTENERS
-        self._listeners.setdefault(type_, []).append(callback)
+        self._listeners.setdefault(type_, OrderedDict())[callback] = profiles_filter
 
     def removeListener(self, type_, callback):
         """Remove a callback from listeners
 
-        @param type_: same as for [addListerner]
+        @param type_: same as for [addListener]
         @param callback: callback to remove
         """
         assert type_ in C.LISTENERS
-        self._listeners[type_].remove(callback)
+        self._listeners[type_].pop(callback)
 
-    def callListeners(self, type_, *args):
-        """Call all methods which listen of type_ event
+    def callListeners(self, type_, profile, *args):
+        """Call the methods which listen type_ event. If a profiles filter has
+        been register with a listener and profile argument is not None, the
+        listener will be called only if profile is in the profiles filter list.
 
-        @param type_: same as for [addListerner]
+        @param type_: same as for [addListener]
+        @param profile (unicode): %(doc_profile)s
         @param *args: arguments sent to callback
         """
         assert type_ in C.LISTENERS
@@ -320,8 +346,9 @@
         except KeyError:
             pass
         else:
-            for listener in listeners:
-                listener(*args)
+            for listener, profiles_filter in listeners.iteritems():
+                if profile is None or not profiles_filter or profile in profiles_filter:
+                    listener(*args)
 
     def check_profile(self, profile):
         """Tell if the profile is currently followed by the application"""
@@ -374,7 +401,7 @@
         """Tell the application to not follow anymore the profile"""
         if not profile in self.profiles:
             raise ValueError("The profile [{}] is not plugged".format(profile))
-        self.profiles.remove(profile)
+        self.profiles.unplug(profile)
 
     def clear_profile(self):
         self.profiles.clear()
@@ -468,7 +495,7 @@
         # if entity.bare in self.profiles[profile].data.get('watched',[]) and not entity.bare in self.profiles[profile]['onlineContact']:
         #     self.showAlert(_("Watched jid [%s] is connected !") % entity.bare)
 
-        self.contact_lists[profile].updatePresence(entity, show, priority, statuses)
+        self.callListeners('presence', profile, entity, show, priority, statuses)
 
     def roomJoinedHandler(self, room_jid_s, room_nicks, user_nick, profile):
         """Called when a MUC room is joined"""
@@ -614,10 +641,8 @@
 
     def _subscribe_cb(self, answer, data):
         entity, profile = data
-        if answer:
-            self.bridge.subscription("subscribed", entity.bare, profile_key=profile)
-        else:
-            self.bridge.subscription("unsubscribed", entity.bare, profile_key=profile)
+        type_ = "subscribed" if answer else "unsubscribed"
+        self.bridge.subscription(type_, unicode(entity.bare), profile_key=profile)
 
     def subscribeHandler(self, type, raw_jid, profile):
         """Called when a subsciption management signal is received"""
@@ -632,7 +657,7 @@
             # this is a subscriptionn request, we have to ask for user confirmation
             self.showDialog(_("The contact %s wants to subscribe to your presence.\nDo you accept ?") % entity.bare, _('Subscription confirmation'), 'yes/no', answer_cb=self._subscribe_cb, answer_data=(entity, profile))
 
-    def showDialog(self, message, title, type="info", answer_cb=None):
+    def showDialog(self, message, title, type="info", answer_cb=None, answer_data=None):
         raise NotImplementedError
 
     def showAlert(self, message):
@@ -650,8 +675,8 @@
         elif (namespace, name) == ('General', C.SHOW_EMPTY_GROUPS):
             self.contact_lists[profile].showEmptyGroups(C.bool(value))
 
-    def contactDeletedHandler(self, jid, profile):
-        target = jid.JID(jid)
+    def contactDeletedHandler(self, jid_s, profile):
+        target = jid.JID(jid_s)
         self.contact_lists[profile].remove(target)
 
     def entityDataUpdatedHandler(self, entity_s, key, value, profile):
@@ -663,7 +688,7 @@
             if entity in self.contact_lists[profile]:
                 def gotFilename(filename):
                     self.contact_lists[profile].setCache(entity, 'avatar', filename)
-                    self.callListeners('avatar', entity, filename, profile)
+                    self.callListeners('avatar', profile, entity, filename)
                 self.bridge.getAvatarFile(value, callback=gotFilename)
 
     def askConfirmationHandler(self, confirm_id, confirm_type, data, profile):
@@ -687,8 +712,12 @@
 
     def onExit(self):
         """Must be called when the frontend is terminating"""
+        to_unplug = []
         for profile in self.profiles:
             if self.bridge.isConnected(profile):
                 if C.bool(self.bridge.getParamA("autodisconnect", "Connection", profile_key=profile)):
                     #The user wants autodisconnection
                     self.bridge.disconnect(profile)
+            to_unplug.append(profile)
+        for profile in to_unplug:
+            self.unplug_profile(profile)