diff frontends/src/primitivus/chat.py @ 1367:f71a0fc26886

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 10:52:28 +0100
parents d3e9848b9574
children 017270e6eea4
line wrap: on
line diff
--- a/frontends/src/primitivus/chat.py	Thu Feb 05 11:59:26 2015 +0100
+++ b/frontends/src/primitivus/chat.py	Wed Mar 18 10:52:28 2015 +0100
@@ -23,13 +23,15 @@
 import urwid
 from urwid_satext import sat_widgets
 from urwid_satext.files_management import FileDialog
+from sat_frontends.quick_frontend import quick_widgets
 from sat_frontends.quick_frontend.quick_chat import QuickChat
-from sat_frontends.primitivus.card_game import CardGame
-from sat_frontends.quick_frontend.quick_utils import escapePrivate, unescapePrivate
+from sat_frontends.quick_frontend import quick_games
+from sat_frontends.primitivus import game_tarot
 from sat_frontends.primitivus.constants import Const as C
 from sat_frontends.primitivus.keys import action_key_map as a_key
+from sat_frontends.primitivus.widget import PrimitivusWidget
 import time
-from sat_frontends.tools.jid  import JID
+from sat_frontends.tools import jid
 
 
 class ChatText(urwid.FlowWidget):
@@ -80,19 +82,27 @@
         return txt_widget
 
 
-class Chat(urwid.WidgetWrap, QuickChat):
+class Chat(PrimitivusWidget, QuickChat):
 
-    def __init__(self, target, host, type_='one2one'):
-        self.target = target
-        QuickChat.__init__(self, target, host, type_)
+    def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
+        QuickChat.__init__(self, host, target, type_, profiles=profiles)
         self.content = urwid.SimpleListWalker([])
         self.text_list = urwid.ListBox(self.content)
         self.chat_widget = urwid.Frame(self.text_list)
         self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
         self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
         self.pile = urwid.Pile([self.chat_colums])
-        urwid.WidgetWrap.__init__(self, self.__getDecoration(self.pile))
-        self.setType(type_)
+        PrimitivusWidget.__init__(self, self.pile, self.target)
+
+        # we must adapt the behaviour with the type
+        if type_ == C.CHAT_ONE2ONE:
+            self.historyPrint(profile=self.profile)
+        elif type_ == C.CHAT_GROUP:
+            if len(self.chat_colums.contents) == 1:
+                present_widget = self._buildPresentList()
+                self.present_panel = sat_widgets.VerticalSeparator(present_widget)
+                self._appendPresentPanel()
+
         self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00  %Y")) #struct_time of day changing time
         self.show_timestamp = True
         self.show_short_nick = False
@@ -101,12 +111,12 @@
 
     def keypress(self, size, key):
         if key == a_key['OCCUPANTS_HIDE']: #user wants to (un)hide the presents panel
-            if self.type == 'group':
+            if self.type == C.CHAT_GROUP:
                 widgets = [widget for (widget, options) in self.chat_colums.contents]
                 if self.present_panel in widgets:
-                    self.__removePresentPanel()
+                    self._removePresentPanel()
                 else:
-                    self.__appendPresentPanel()
+                    self._appendPresentPanel()
         elif key == a_key['TIMESTAMP_HIDE']: #user wants to (un)hide timestamp
             self.show_timestamp = not self.show_timestamp
             for wid in self.content:
@@ -115,10 +125,6 @@
             self.show_short_nick = not self.show_short_nick
             for wid in self.content:
                 wid._invalidate()
-        elif key == a_key['DECORATION_HIDE']: #user wants to (un)hide widget decoration
-            show = not isinstance(self._w, sat_widgets.LabelLine)
-            self.showDecoration(show)
-            self._invalidate()
         elif key == a_key['SUBJECT_SWITCH']: #user wants to (un)hide group's subject or change its apperance
             if self.subject:
                 self.show_title = (self.show_title + 1) % 3
@@ -130,67 +136,30 @@
                     self.chat_widget.header = None
                 self._invalidate()
 
-
         return super(Chat, self).keypress(size, key)
 
     def getMenu(self):
         """Return Menu bar"""
         menu = sat_widgets.Menu(self.host.loop)
-        if self.type == 'group':
+        if self.type == C.CHAT_GROUP:
             self.host.addMenus(menu, C.MENU_ROOM, {'room_jid': self.target.bare})
             game = _("Game")
             menu.addMenu(game, "Tarot", self.onTarotRequest)
-        elif self.type == 'one2one':
-            self.host.addMenus(menu, C.MENU_SINGLE, {'jid': unescapePrivate(self.target)})
+        elif self.type == C.CHAT_ONE2ONE:
+            self.host.addMenus(menu, C.MENU_SINGLE, {'jid': self.target})
             menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest)
         return menu
 
-    def setType(self, type_):
-        QuickChat.setType(self, type_)
-        if type_ == 'one2one':
-            self.historyPrint(profile=self.host.profile)
-        elif type_ == 'group':
-            if len(self.chat_colums.contents) == 1:
-                present_widget = self.__buildPresentList()
-                self.present_panel = sat_widgets.VerticalSeparator(present_widget)
-                self.__appendPresentPanel()
-
-    def __getDecoration(self, widget):
-        return sat_widgets.LabelLine(widget, self.__getSurrendedText())
-
-    def __getSurrendedText(self):
-        """Get the text to be displayed as the dialog title."""
-        if not hasattr(self, "surrended_text"):
-            self.__setSurrendedText()
-        return self.surrended_text
-
-    def __setSurrendedText(self, state=None):
-        """Set the text to be displayed as the dialog title
-        @param stat: chat state of the contact
-        """
-        text = unicode(unescapePrivate(self.target))
-        if state:
-            text += " (" + state + ")"
-        self.surrended_text = sat_widgets.SurroundedText(text)
-
-    def showDecoration(self, show=True):
-        """Show/Hide the decoration around the chat window"""
-        if show:
-            main_widget = self.__getDecoration(self.pile)
-        else:
-            main_widget = self.pile
-        self._w = main_widget
-
-    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 C.ALL_OCCUPANTS
-        """
-        if nick:
-            assert(self.type == 'group')
-            occupants = self.occupants if nick == C.ALL_OCCUPANTS else [nick]
+    def updateChatState(self, from_jid, state):
+        if self.type == C.CHAT_GROUP:
+            if from_jid == C.ENTITY_ALL:
+                occupants = self.occupants
+            else:
+                nick = from_jid.resource
+                if not nick:
+                    log.debug("no nick found for chatstate")
+                    return
+                occupants = [nick]
             options = self.present_wid.getAllValues()
             for index in xrange(0, len(options)):
                 nick = options[index].value
@@ -199,46 +168,51 @@
             self.present_wid.changeValues(options)
             self.host.redraw()
         else:
-            assert(self.type == 'one2one')
-            self.__setSurrendedText(state)
-            self.showDecoration()
-            self.host.redraw()
+            self.title_dynamic = '({})'.format(state)
 
     def _presentClicked(self, list_wid, clicked_wid):
-        assert(self.type == 'group')
+        assert self.type == C.CHAT_GROUP
         nick = clicked_wid.getValue().value
         if nick == self.getUserNick():
-            #We ignore click on our own nick
+            #We ignore clicks on our own nick
             return
-        #we have a click on a nick, we add the private conversation to the contact_list
-        full_jid = JID("%s/%s" % (self.target.bare, nick))
-        new_jid = escapePrivate(full_jid)
-        if new_jid not in self.host.contact_list:
-            self.host.contact_list.add(new_jid, [C.GROUP_NOT_IN_ROSTER])
+        contact_list = self.host.contact_lists[self.profile]
+        full_jid = jid.JID("%s/%s" % (self.target.bare, nick))
+
+        #we have a click on a nick, we need to create the widget if it doesn't exists
+        self.getOrCreatePrivateWidget(full_jid)
 
         #now we select the new window
-        self.host.contact_list.setFocus(full_jid, True)
+        contact_list.setFocus(full_jid, True)
 
-    def __buildPresentList(self):
+    def _buildPresentList(self):
         self.present_wid = sat_widgets.GenericList([],option_type = sat_widgets.ClickableText, on_click=self._presentClicked)
         return self.present_wid
 
-    def __appendPresentPanel(self):
+    def _appendPresentPanel(self):
         self.chat_colums.contents.append((self.present_panel,('weight', 2, False)))
 
-    def __removePresentPanel(self):
+    def _removePresentPanel(self):
         for widget, options in self.chat_colums.contents:
             if widget is self.present_panel:
                 self.chat_colums.contents.remove((widget, options))
                 break
 
-    def __appendGamePanel(self, widget):
+    def addGamePanel(self, widget):
+        """Insert a game panel to this Chat dialog.
+
+        @param widget (Widget): the game panel
+        """
         assert (len(self.pile.contents) == 1)
-        self.pile.contents.insert(0,(widget,('weight', 1)))
-        self.pile.contents.insert(1,(urwid.Filler(urwid.Divider('-'),('fixed', 1))))
+        self.pile.contents.insert(0, (widget, ('weight', 1)))
+        self.pile.contents.insert(1, (urwid.Filler(urwid.Divider('-'), ('fixed', 1))))
         self.host.redraw()
 
-    def __removeGamePanel(self):
+    def removeGamePanel(self, widget):
+        """Remove the game panel from this Chat dialog.
+
+        @param widget (Widget): the game panel
+        """
         assert (len(self.pile.contents) == 3)
         del self.pile.contents[0]
         self.host.redraw()
@@ -252,12 +226,11 @@
         self.chat_widget.header = urwid.AttrMap(self.subj_wid,'title')
         self.host.redraw()
 
-    def setPresents(self, param_nicks):
-        """Set the users presents in the contact list for a group chat
-        @param nicks: list of nicknames
+    def setPresents(self, nicks):
+        """Set the occupants of a group chat.
+
+        @param nicks (list[unicode]): sorted list of nicknames
         """
-        nicks = [unicode(nick) for nick in param_nicks] #FIXME: should be done in DBus bridge
-        nicks.sort()
         QuickChat.setPresents(self, nicks)
         self.present_wid.changeValues(nicks)
         self.host.redraw()
@@ -290,13 +263,22 @@
             self.text_list.focus_position = len(self.content) - 1  # scroll down
         self.host.redraw()
 
-    def printMessage(self, from_jid, msg, profile, timestamp=None):
-        assert isinstance(from_jid, JID)
+    def onPrivateCreated(self, widget):
+        self.host.contact_lists[widget.profile].specialResourceVisible(widget.target)
+
+    def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
+        assert isinstance(from_jid, jid.JID)
+        if extra is None:
+            extra = {}
         try:
-            jid, nick, mymess = QuickChat.printMessage(self, from_jid, msg, profile, timestamp)
+            timestamp = float(extra['timestamp'])
+        except KeyError:
+            timestamp=None
+        try:
+            nick, mymess = QuickChat.printMessage(self, from_jid, msg, extra, profile)
         except TypeError:
+            # None is returned, the message is managed
             return
-
         new_text = ChatText(self, timestamp, nick, mymess, msg)
 
         if timestamp and self.content:
@@ -323,7 +305,7 @@
             # all messages and not only with the messages coming from the history.
             self._notify(from_jid, msg)
 
-    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:
@@ -331,6 +313,12 @@
             me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
         @param timestamp (float): number of seconds since epoch
         """
+        if extra is None:
+            extra = {}
+        try:
+            timestamp = float(extra['timestamp'])
+        except KeyError:
+            timestamp=None
         _widget = ChatText(self, timestamp, None, False, msg, is_info=True)
         self.content.append(_widget)
         self._notify(msg=msg)
@@ -348,30 +336,18 @@
             self.text_list.focus_position = len(self.content) - 1
         self.host.redraw()
         if not self.host.x_notify.hasFocus():
-            if self.type == "one2one":
+            if self.type == C.CHAT_ONE2ONE:
                 self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid)
             elif self.getUserNick().lower() in msg.lower():
                 self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target})
 
-    def startGame(self, game_type, referee, players):
-        """Configure the chat window to start a game"""
-        if game_type=="Tarot":
-            self.tarot_wid = CardGame(self, referee, players, self.nick)
-            self.__appendGamePanel(self.tarot_wid)
-
-    def getGame(self, game_type):
-        """Return class managing the game type"""
-        #TODO: check that the game is launched, and manage errors
-        if game_type=="Tarot":
-            return self.tarot_wid
-
     #MENU EVENTS#
     def onTarotRequest(self, menu):
         # TODO: move this to plugin_misc_tarot with dynamic menu
         if len(self.occupants) != 4:
             self.host.showPopUp(sat_widgets.Alert(_("Can't start game"), _("You need to be exactly 4 peoples in the room to start a Tarot game"), ok_cb=self.host.removePopUp))
         else:
-            self.host.bridge.tarotGameCreate(self.id, list(self.occupants), self.host.profile)
+            self.host.bridge.tarotGameCreate(self.id, list(self.occupants), self.profile)
 
     def onSendFileRequest(self, menu):
         # TODO: move this to core with dynamic menus
@@ -388,11 +364,15 @@
             self.host.showDialog(_(u"File has a unicode error in its name, it's not yet managed by SàT"), title=_("Can't send file"), type_="error")
             return
         #FIXME: check last_resource: what if self.target.resource exists ?
-        last_resource = self.host.bridge.getLastResource(unicode(self.target.bare), self.host.profile)
+        last_resource = self.host.bridge.getMainResource(unicode(self.target.bare), self.profile)
         if last_resource:
-            full_jid = JID("%s/%s" % (self.target.bare, last_resource))
+            full_jid = jid.JID("%s/%s" % (self.target.bare, last_resource))
         else:
             full_jid = self.target
-        progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.host.profile)
+        progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.profile)
         self.host.addProgress(progress_id,filepath)
         self.host.showDialog(_(u"You file request has been sent, we are waiting for your contact answer"), title=_("File request sent"))
+
+
+quick_widgets.register(QuickChat, Chat)
+quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)