diff frontends/src/quick_frontend/quick_chat.py @ 1367:f71a0fc26886

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 10:52:28 +0100
parents ba87b940f07a
children 017270e6eea4
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_chat.py	Thu Feb 05 11:59:26 2015 +0100
+++ b/frontends/src/quick_frontend/quick_chat.py	Wed Mar 18 10:52:28 2015 +0100
@@ -20,41 +20,92 @@
 from sat.core.i18n import _
 from sat.core.log import getLogger
 log = getLogger(__name__)
-from sat_frontends.tools.jid  import JID
-from sat_frontends.quick_frontend.quick_utils import unescapePrivate
+from sat_frontends.tools import jid
+from sat_frontends.quick_frontend import quick_widgets
 from sat_frontends.quick_frontend.constants import Const as C
 
 
-class QuickChat(object):
+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 = lambda x: str(x)
+
+
+class QuickChat(quick_widgets.QuickWidget):
 
-    def __init__(self, target, host, type_='one2one'):
-        self.target = target
-        self.host = host
+    def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
+        """
+        @param type_: can be C.CHAT_ONE2ONE for single conversation or C.CHAT_GROUP for chat à la IRC
+        """
+        quick_widgets.QuickWidget.__init__(self, host, target, profiles=profiles)
+        assert type_ in (C.CHAT_ONE2ONE, C.CHAT_GROUP)
+        if type_ == C.CHAT_GROUP and target.resource:
+            raise ValueError("A group chat entity can't have a resource")
+        self.current_target = target
         self.type = type_
-        self.id = ""
+        self.id = "" # FIXME: to be removed
         self.nick = None
         self.occupants = set()
+        self.games = {}
 
-    def setType(self, type_):
-        """Set the type of the chat
-        @param type: can be 'one2one' for single conversation or 'group' for chat à la IRC
+    def __str__(self):
+        return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile)
+
+    @staticmethod
+    def getWidgetHash(target, profile):
+        return (unicode(profile), target.bare)
+
+    @staticmethod
+    def getPrivateHash(target, profile):
+        """Get unique hash for private conversations
+
+        This method should be used with force_hash to get unique widget for private MUC conversations
         """
-        self.type = type_
+        return (unicode(profile), target)
+
+
+    def addTarget(self, target):
+        super(QuickChat, self).addTarget(target)
+        if target.resource:
+            self.current_target = target # FIXME: tmp, must use resource priority throught contactList instead
+
+    @property
+    def target(self):
+        if self.type == C.CHAT_GROUP:
+            return self.current_target.bare
+        return self.current_target
+
+    def manageMessage(self, entity, mess_type):
+        """Tell if this chat widget manage an entity and message type couple
+
+        @param entity (jid.JID): (full) jid of the sending entity
+        @param mess_type (str): message type as given by newMessage
+        @return (bool): True if this Chat Widget manage this couple
+        """
+        if self.type == C.CHAT_GROUP:
+            if mess_type == C.MESS_TYPE_GROUPCHAT and self.target == entity.bare:
+                return True
+        else:
+            if mess_type != C.MESS_TYPE_GROUPCHAT and entity in self.targets:
+                return True
+        return False
 
     def setPresents(self, nicks):
-        """Set the users presents in the contact list for a group chat
-        @param nicks: list of nicknames
+        """Set the occupants of a group chat.
+
+        @param nicks (list[unicode]): sorted list of nicknames
         """
-        log.debug (_("Adding users %s to room") % nicks)
-        if self.type != "group":
-            log.error (_("[INTERNAL] trying to set presents nicks for a non group chat window"))
-            raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
+        log.debug(_("Adding users %s to room") % nicks)
+        if self.type != C.CHAT_GROUP:
+            log.error(_("[INTERNAL] trying to set presents nicks for a non group chat window"))
+            raise Exception("INTERNAL ERROR")  # TODO: raise proper Exception here
         self.occupants.update(nicks)
 
     def replaceUser(self, nick, show_info=True):
         """Add user if it is not in the group list"""
         log.debug (_("Replacing user %s") % nick)
-        if self.type != "group":
+        if self.type != C.CHAT_GROUP:
             log.error (_("[INTERNAL] trying to replace user for a non group chat window"))
             raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
         len_before = len(self.occupants)
@@ -65,7 +116,7 @@
     def removeUser(self, nick, show_info=True):
         """Remove a user from the group list"""
         log.debug(_("Removing user %s") % nick)
-        if self.type != "group":
+        if self.type != C.CHAT_GROUP:
             log.error (_("[INTERNAL] trying to remove user for a non group chat window"))
             raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
         self.occupants.remove(nick)
@@ -82,7 +133,7 @@
     def changeUserNick(self, old_nick, new_nick):
         """Change nick of a user in group list"""
         log.debug(_("Changing nick of user %(old_nick)s to %(new_nick)s") % {"old_nick": old_nick, "new_nick": new_nick})
-        if self.type != "group":
+        if self.type != C.CHAT_GROUP:
             log.error (_("[INTERNAL] trying to change user nick for a non group chat window"))
             raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
         self.removeUser(old_nick, show_info=False)
@@ -92,7 +143,7 @@
     def setSubject(self, subject):
         """Set title for a group chat"""
         log.debug(_("Setting subject to %s") % subject)
-        if self.type != "group":
+        if self.type != C.CHAT_GROUP:
             log.error (_("[INTERNAL] trying to set subject for a non group chat window"))
             raise Exception("INTERNAL ERROR") #TODO: raise proper Exception here
 
@@ -106,71 +157,97 @@
         @param search (str): pattern to filter the history results
         @param profile (str): %(doc_profile)s
         """
-        log.debug(_("now we print the history (%d messages)") % size)
+        log_msg = _(u"now we print the history")
+        if size != C.HISTORY_LIMIT_DEFAULT:
+            log_msg += _(u" (%d messages)" % size)
+        log.debug(log_msg)
 
         def onHistory(history):
             for line in history:
-                timestamp, from_jid, to_jid, message, _type, extra = line
-                if ((self.type == 'group' and _type != 'groupchat') or
-                   (self.type == 'one2one' and _type == 'groupchat')):
+                timestamp, from_jid, to_jid, message, type_, extra = line # FIXME: extra is unused !
+                if ((self.type == C.CHAT_GROUP and type_ != C.MESS_TYPE_GROUPCHAT) or
+                   (self.type == C.CHAT_ONE2ONE and type_ == C.MESS_TYPE_GROUPCHAT)):
                     continue
-                self.printMessage(JID(from_jid), message, profile, timestamp)
+                self.printMessage(jid.JID(from_jid), message, {'timestamp':timestamp}, profile)
             self.afterHistoryPrint()
 
         def onHistoryError(err):
             log.error(_("Can't get history"))
 
-        if self.target.startswith(C.PRIVATE_PREFIX):
-            target = unescapePrivate(self.target)
-        else:
-            target = self.target.bare
+        target = self.target.bare
+
+        self.host.bridge.getHistory(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, search, profile, callback=onHistory, errback=onHistoryError)
 
-        return self.host.bridge.getHistory(self.host.profiles[profile]['whoami'].bare, target, size, search=search, profile=profile, callback=onHistory, errback=onHistoryError)
+    def _get_nick(self, entity):
+        """Return nick of this entity when possible"""
+        if self.type == C.CHAT_GROUP:
+            return entity.resource
+        contact_list = self.host.contact_lists[self.profile]
+        if entity.bare in contact_list:
+            return contact_list.getCache(entity,'nick') or contact_list.getCache(entity,'name') or entity.node or entity
+        return entity.node or entity
+
+    def onPrivateCreated(self, widget):
+        """Method called when a new widget for private conversation (MUC) is created"""
+        raise NotImplementedError
+
+    def getOrCreatePrivateWidget(self, entity):
+        """Create a widget for private conversation, or get it if it already exists
 
-    def _get_nick(self, jid):
-        """Return nick of this jid when possible"""
-        if self.target.startswith(C.PRIVATE_PREFIX):
-            unescaped = unescapePrivate(self.target)
-            if jid.startswith(C.PRIVATE_PREFIX) or unescaped.bare == jid.bare:
-                return unescaped.resource
-        return jid.resource if self.type == "group" else (self.host.contact_list.getCache(jid,'nick') or self.host.contact_list.getCache(jid,'name') or jid.node)
+        @param entity: full jid of the target
+        """
+        return self.host.widgets.getOrCreateWidget(QuickChat, entity, type_=C.CHAT_ONE2ONE, force_hash=self.getPrivateHash(self.profile, entity), on_new_widget=self.onPrivateCreated, profile=self.profile) # we force hash to have a new widget, not this one again
 
-    def printMessage(self, from_jid, msg, profile, timestamp=None):
+    def newMessage(self, from_jid, target, msg, type_, extra, profile):
+        if self.type == C.CHAT_GROUP and target.resource and type_ != C.MESS_TYPE_GROUPCHAT:
+            # we have a private message, we forward it to a private conversation widget
+            chat_widget = self.getOrCreatePrivateWidget(target)
+            chat_widget.newMessage(from_jid, target, msg, type_, extra, profile)
+        else:
+            if type_ == C.MESS_TYPE_INFO:
+                self.printInfo(msg, extra=extra)
+            else:
+                self.printMessage(from_jid, msg, extra, profile)
+
+    def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
         """Print message in chat window. Must be implemented by child class"""
-        jid = JID(from_jid)
-        nick = self._get_nick(jid)
-        mymess = (jid.resource == self.nick) if self.type == "group" else (jid.bare == self.host.profiles[profile]['whoami'].bare) #mymess = True if message comes from local user
+        nick = self._get_nick(from_jid)
+        mymess = (from_jid.resource == self.nick) if self.type == C.CHAT_GROUP else (from_jid.bare == self.host.profiles[profile].whoami.bare) #mymess = True if message comes from local user
         if msg.startswith('/me '):
-            self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', timestamp=timestamp)
+            self.printInfo('* %s %s' % (nick, msg[4:]), type_='me', extra=extra)
             return
-        return jid, nick, mymess
+        return nick, mymess
 
-    def printInfo(self, msg, type_='normal', timestamp=None):
+    def printInfo(self, msg, type_='normal', extra=None):
         """Print general info
         @param msg: message to print
         @type_: one of:
             normal: general info like "toto has joined the room"
             me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
-        @param timestamp (float): number of seconds since epoch
+        @param extra (dict): message data
+        """
+        raise NotImplementedError
+
+    def updateChatState(self, from_jid, state):
+        """Set the chat state (XEP-0085) of the contact.
+
+        @param state: the new chat state
         """
         raise NotImplementedError
 
-    def startGame(self, game_type, referee, players):
-        """Configure the chat window to start a game"""
-        #No need to raise an error as game are not mandatory
-        log.warning(_('startGame is not implemented in this frontend'))
+    def addGamePanel(self, widget):
+        """Insert a game panel to this Chat dialog.
 
-    def getGame(self, game_type):
-        """Return class managing the game type"""
-        #No need to raise an error as game are not mandatory
-        log.warning(_('getGame is not implemented in this frontend'))
-
-    def updateChatState(self, state, nick=None):
-        """Set the chat state (XEP-0085) of the contact. Leave nick to None
-        to set the state for a one2one conversation, or give a nickname or
-        C.ALL_OCCUPANTS to set the state of a participant within a MUC.
-        @param state: the new chat state
-        @param nick: None for one2one, the MUC user nick or ALL_OCCUPANTS
+        @param widget (Widget): the game panel
         """
         raise NotImplementedError
 
+    def removeGamePanel(self, widget):
+        """Remove the game panel from this Chat dialog.
+
+        @param widget (Widget): the game panel
+        """
+        raise NotImplementedError
+
+
+quick_widgets.register(QuickChat)