changeset 1360:8ea8fa13c351 frontends_multi_profiles

frontends (quick_frontend, primitivus): fixes room games: - add quick_frontend.quick_games for registering the signals and registering the UI classes - rename the signals handlers to fit the convention (e.g.: tarotGameScoreHandler) - rename card_game to game_tarot, quick_card_game to quick_game_tarot, CardGame to TarotGame
author souliane <souliane@mailoo.org>
date Wed, 11 Mar 2015 12:43:48 +0100
parents 83127a4c89ce
children d3e9848b9574
files frontends/src/primitivus/card_game.py frontends/src/primitivus/chat.py frontends/src/primitivus/game_tarot.py frontends/src/primitivus/primitivus frontends/src/quick_frontend/quick_app.py frontends/src/quick_frontend/quick_card_game.py frontends/src/quick_frontend/quick_chat.py frontends/src/quick_frontend/quick_game_tarot.py frontends/src/quick_frontend/quick_games.py
diffstat 9 files changed, 650 insertions(+), 665 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/primitivus/card_game.py	Wed Mar 11 12:36:22 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,354 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# Primitivus: a SAT frontend
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-
-# 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 urwid
-from urwid_satext import sat_widgets
-from sat_frontends.tools.games import TarotCard
-from sat_frontends.quick_frontend.quick_card_game import QuickCardGame
-from sat_frontends.primitivus import xmlui
-from sat_frontends.primitivus.keys import action_key_map as a_key
-
-
-class CardDisplayer(urwid.Text):
-    """Show a card"""
-    signals = ['click']
-
-    def __init__(self, card):
-        self.__selected = False
-        self.card = card
-        urwid.Text.__init__(self, card.getAttrText())
-
-    def selectable(self):
-        return True
-
-    def keypress(self, size, key):
-        if key == a_key['CARD_SELECT']:
-            self.select(not self.__selected)
-            self._emit('click')
-        return key
-
-    def mouse_event(self, size, event, button, x, y, focus):
-        if urwid.is_mouse_event(event) and button == 1:
-            self.select(not self.__selected)
-            self._emit('click')
-            return True
-
-        return False
-
-    def select(self, state=True):
-        self.__selected = state
-        attr, txt = self.card.getAttrText()
-        if self.__selected:
-            attr += '_selected'
-        self.set_text((attr, txt))
-        self._invalidate()
-
-    def isSelected(self):
-        return self.__selected
-
-    def getCard(self):
-        return self.card
-
-    def render(self, size, focus=False):
-        canvas = urwid.CompositeCanvas(urwid.Text.render(self, size, focus))
-        if focus:
-            canvas.set_cursor((0, 0))
-        return canvas
-
-
-class Hand(urwid.WidgetWrap):
-    """Used to display several cards, and manage a hand"""
-    signals = ['click']
-
-    def __init__(self, hand=[], selectable=False, on_click=None, user_data=None):
-        """@param hand: list of Card"""
-        self.__selectable = selectable
-        self.columns = urwid.Columns([], dividechars=1)
-        if on_click:
-            urwid.connect_signal(self, 'click', on_click, user_data)
-        if hand:
-            self.update(hand)
-        urwid.WidgetWrap.__init__(self, self.columns)
-
-    def selectable(self):
-        return self.__selectable
-
-    def keypress(self, size, key):
-
-        if CardDisplayer in [wid.__class__ for wid in self.columns.widget_list]:
-            return self.columns.keypress(size, key)
-        else:
-            #No card displayed, we still have to manage the clicks
-            if key == a_key['CARD_SELECT']:
-                self._emit('click', None)
-            return key
-
-    def getSelected(self):
-        """Return a list of selected cards"""
-        _selected = []
-        for wid in self.columns.widget_list:
-            if isinstance(wid, CardDisplayer) and wid.isSelected():
-                _selected.append(wid.getCard())
-        return _selected
-
-    def update(self, hand):
-        """Update the hand displayed in this widget
-        @param hand: list of Card"""
-        try:
-            del self.columns.widget_list[:]
-            del self.columns.column_types[:]
-        except IndexError:
-            pass
-        self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
-        for card in hand:
-            widget = CardDisplayer(card)
-            self.columns.widget_list.append(widget)
-            self.columns.column_types.append(('fixed', 3))
-            urwid.connect_signal(widget, 'click', self.__onClick)
-        self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
-        self.columns.focus_position = 1
-
-    def __onClick(self, card_wid):
-        self._emit('click', card_wid)
-
-
-class Card(TarotCard):
-    """This class is used to represent a card, logically
-    and give a text representation with attributes"""
-    SIZE = 3  # size of a displayed card
-
-    def __init__(self, suit, value):
-        """@param file: path of the PNG file"""
-        TarotCard.__init__(self, (suit, value))
-
-    def getAttrText(self):
-        """return text representation of the card with attributes"""
-        try:
-            value = "%02i" % int(self.value)
-        except ValueError:
-            value = self.value[0].upper() + self.value[1]
-        if self.suit == "atout":
-            if self.value == "excuse":
-                suit = 'c'
-            else:
-                suit = 'A'
-            color = 'neutral'
-        elif self.suit == "pique":
-            suit = u'♠'
-            color = 'black'
-        elif self.suit == "trefle":
-            suit = u'♣'
-            color = 'black'
-        elif self.suit == "coeur":
-            suit = u'♥'
-            color = 'red'
-        elif self.suit == "carreau":
-            suit = u'♦'
-            color = 'red'
-        if self.bout:
-            color = 'special'
-        return ('card_%s' % color, u"%s%s" % (value, suit))
-
-    def getWidget(self):
-        """Return a widget representing the card"""
-        return CardDisplayer(self)
-
-
-class Table(urwid.FlowWidget):
-    """Represent the cards currently on the table"""
-
-    def __init__(self):
-        self.top = self.left = self.bottom = self.right = None
-
-    def putCard(self, location, card):
-        """Put a card on the table
-        @param location: where to put the card (top, left, bottom or right)
-        @param card: Card to play or None"""
-        assert location in ['top', 'left', 'bottom', 'right']
-        assert isinstance(card, Card) or card == None
-        if [getattr(self, place) for place in ['top', 'left', 'bottom', 'right']].count(None) == 0:
-            #If the table is full of card, we remove them
-            self.top = self.left = self.bottom = self.right = None
-        setattr(self, location, card)
-        self._invalidate()
-
-    def rows(self, size, focus=False):
-        return self.display_widget(size, focus).rows(size, focus)
-
-    def render(self, size, focus=False):
-        return self.display_widget(size, focus).render(size, focus)
-
-    def display_widget(self, size, focus):
-        cards = {}
-        max_col, = size
-        separator = " - "
-        margin = max((max_col - Card.SIZE) / 2, 0) * ' '
-        margin_center = max((max_col - Card.SIZE * 2 - len(separator)) / 2, 0) * ' '
-        for location in ['top', 'left', 'bottom', 'right']:
-            card = getattr(self, location)
-            cards[location] = card.getAttrText() if card else Card.SIZE * ' '
-        render_wid = [urwid.Text([margin, cards['top']]),
-                      urwid.Text([margin_center, cards['left'], separator, cards['right']]),
-                      urwid.Text([margin, cards['bottom']])]
-        return urwid.Pile(render_wid)
-
-
-class CardGame(QuickCardGame, urwid.WidgetWrap):
-    """Widget for card games"""
-
-    def __init__(self, parent, referee, players, player_nick):
-        QuickCardGame.__init__(self, parent, referee, players, player_nick)
-        self.loadCards()
-        self.top = urwid.Pile([urwid.Padding(urwid.Text(self.top_nick), 'center')])
-        #self.parent.host.debug()
-        self.table = Table()
-        self.center = urwid.Columns([('fixed', len(self.left_nick), urwid.Filler(urwid.Text(self.left_nick))),
-                                urwid.Filler(self.table),
-                                ('fixed', len(self.right_nick), urwid.Filler(urwid.Text(self.right_nick)))
-                               ])
-        """urwid.Pile([urwid.Padding(self.top_card_wid,'center'),
-                             urwid.Columns([('fixed',len(self.left_nick),urwid.Text(self.left_nick)),
-                                            urwid.Padding(self.center_cards_wid,'center'),
-                                            ('fixed',len(self.right_nick),urwid.Text(self.right_nick))
-                                           ]),
-                             urwid.Padding(self.bottom_card_wid,'center')
-                             ])"""
-        self.hand_wid = Hand(selectable=True, on_click=self.onClick)
-        self.main_frame = urwid.Frame(self.center, header=self.top, footer=self.hand_wid, focus_part='footer')
-        urwid.WidgetWrap.__init__(self, self.main_frame)
-        self.parent.host.bridge.tarotGameReady(player_nick, referee, self.parent.host.profile)
-
-    def loadCards(self):
-        """Load all the cards in memory"""
-        QuickCardGame.loadCards(self)
-        for value in map(str, range(1, 22)) + ['excuse']:
-            card = Card('atout', value)
-            self.cards[card.suit, card.value] = card
-            self.deck.append(card)
-        for suit in ["pique", "coeur", "carreau", "trefle"]:
-            for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
-                card = Card(suit, value)
-                self.cards[card.suit, card.value] = card
-                self.deck.append(card)
-
-    def newGame(self, hand):
-        """Start a new game, with given hand"""
-        if hand is []:  # reset the display after the scores have been showed
-            self.resetRound()
-            for location in ['top', 'left', 'bottom', 'right']:
-                self.table.putCard(location, None)
-            self.parent.host.redraw()
-            self.parent.host.bridge.tarotGameReady(self.player_nick, self.referee, self.parent.host.profile)
-            return
-        QuickCardGame.newGame(self, hand)
-        self.hand_wid.update(self.hand)
-        self.parent.host.redraw()
-
-    def contratSelected(self, data):
-        """Called when the contrat has been choosed
-        @param data: form result"""
-        contrat = data[0][1]
-        QuickCardGame.contratSelected(self, contrat)
-
-    def chooseContrat(self, xml_data):
-        """Called when the player has to select his contrat
-        @param xml_data: SàT xml representation of the form"""
-        form = xmlui.create(self.parent.host, xml_data, title=_('Please choose your contrat'), flags=['NO_CANCEL'])
-        form.show(valign='top')
-
-    def showCards(self, game_stage, cards, data):
-        """Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
-        QuickCardGame.showCards(self, game_stage, cards, data)
-        self.center.widget_list[1] = urwid.Filler(Hand(self.to_show))
-        self.parent.host.redraw()
-
-    def myTurn(self):
-        QuickCardGame.myTurn(self)
-
-    def showScores(self, xml_data, winners, loosers):
-        """Called when the round is over, display the scores
-        @param xml_data: SàT xml representation of the form"""
-        if not winners and not loosers:
-            title = _("Draw game")
-        else:
-            title = _('You win \o/') if self.player_nick in winners else _('You loose :(')
-        form = xmlui.create(self.parent.host, xml_data, title=title, flags=['NO_CANCEL'])
-        form.show()
-
-    def invalidCards(self, phase, played_cards, invalid_cards):
-        """Invalid cards have been played
-        @param phase: phase of the game
-        @param played_cards: all the cards played
-        @param invalid_cards: cards which are invalid"""
-        QuickCardGame.invalidCards(self, phase, played_cards, invalid_cards)
-        self.hand_wid.update(self.hand)
-        if self._autoplay == None:  # No dialog if there is autoplay
-            self.parent.host.notify(_('Cards played are invalid !'))
-        self.parent.host.redraw()
-
-    def cardsPlayed(self, player, cards):
-        """A card has been played by player"""
-        QuickCardGame.cardsPlayed(self, player, cards)
-        self.table.putCard(self.getPlayerLocation(player), self.played[player])
-        self._checkState()
-        self.parent.host.redraw()
-
-    def _checkState(self):
-        if isinstance(self.center.widget_list[1].original_widget, Hand):  # if we have a hand displayed
-            self.center.widget_list[1] = urwid.Filler(self.table)  # we show again the table
-            if self.state == "chien":
-                self.to_show = []
-                self.state = "wait"
-            elif self.state == "wait_for_ecart":
-                self.state = "ecart"
-                self.hand.extend(self.to_show)
-                self.hand.sort()
-                self.to_show = []
-                self.hand_wid.update(self.hand)
-
-    ##EVENTS##
-    def onClick(self, hand, card_wid):
-        """Called when user do an action on the hand"""
-        if not self.state in ['play', 'ecart', 'wait_for_ecart']:
-            #it's not our turn, we ignore the click
-            card_wid.select(False)
-            return
-        self._checkState()
-        if self.state == "ecart":
-            if len(self.hand_wid.getSelected()) == 6:
-                pop_up_widget = sat_widgets.ConfirmDialog(_("Do you put these cards in chien ?"), yes_cb=self.onEcartDone, no_cb=self.parent.host.removePopUp)
-                self.parent.host.showPopUp(pop_up_widget)
-        elif self.state == "play":
-            card = card_wid.getCard()
-            self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.host.profile)
-            self.hand.remove(card)
-            self.hand_wid.update(self.hand)
-            self.state = "wait"
-
-    def onEcartDone(self, button):
-        """Called when player has finished his écart"""
-        ecart = []
-        for card in self.hand_wid.getSelected():
-            ecart.append((card.suit, card.value))
-            self.hand.remove(card)
-        self.hand_wid.update(self.hand)
-        self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, ecart, self.parent.host.profile)
-        self.state = "wait"
-        self.parent.host.removePopUp()
--- a/frontends/src/primitivus/chat.py	Wed Mar 11 12:36:22 2015 +0100
+++ b/frontends/src/primitivus/chat.py	Wed Mar 11 12:43:48 2015 +0100
@@ -25,7 +25,8 @@
 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 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
@@ -197,13 +198,21 @@
                 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()
@@ -333,18 +342,6 @@
             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
@@ -379,3 +376,4 @@
 
 
 quick_widgets.register(QuickChat, Chat)
+quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/src/primitivus/game_tarot.py	Wed Mar 11 12:43:48 2015 +0100
@@ -0,0 +1,348 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Primitivus: a SAT frontend
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# 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 urwid
+from urwid_satext import sat_widgets
+from sat_frontends.tools.games import TarotCard
+from sat_frontends.quick_frontend.quick_game_tarot import QuickTarotGame
+from sat_frontends.primitivus import xmlui
+from sat_frontends.primitivus.keys import action_key_map as a_key
+
+
+class CardDisplayer(urwid.Text):
+    """Show a card"""
+    signals = ['click']
+
+    def __init__(self, card):
+        self.__selected = False
+        self.card = card
+        urwid.Text.__init__(self, card.getAttrText())
+
+    def selectable(self):
+        return True
+
+    def keypress(self, size, key):
+        if key == a_key['CARD_SELECT']:
+            self.select(not self.__selected)
+            self._emit('click')
+        return key
+
+    def mouse_event(self, size, event, button, x, y, focus):
+        if urwid.is_mouse_event(event) and button == 1:
+            self.select(not self.__selected)
+            self._emit('click')
+            return True
+
+        return False
+
+    def select(self, state=True):
+        self.__selected = state
+        attr, txt = self.card.getAttrText()
+        if self.__selected:
+            attr += '_selected'
+        self.set_text((attr, txt))
+        self._invalidate()
+
+    def isSelected(self):
+        return self.__selected
+
+    def getCard(self):
+        return self.card
+
+    def render(self, size, focus=False):
+        canvas = urwid.CompositeCanvas(urwid.Text.render(self, size, focus))
+        if focus:
+            canvas.set_cursor((0, 0))
+        return canvas
+
+
+class Hand(urwid.WidgetWrap):
+    """Used to display several cards, and manage a hand"""
+    signals = ['click']
+
+    def __init__(self, hand=[], selectable=False, on_click=None, user_data=None):
+        """@param hand: list of Card"""
+        self.__selectable = selectable
+        self.columns = urwid.Columns([], dividechars=1)
+        if on_click:
+            urwid.connect_signal(self, 'click', on_click, user_data)
+        if hand:
+            self.update(hand)
+        urwid.WidgetWrap.__init__(self, self.columns)
+
+    def selectable(self):
+        return self.__selectable
+
+    def keypress(self, size, key):
+
+        if CardDisplayer in [wid.__class__ for wid in self.columns.widget_list]:
+            return self.columns.keypress(size, key)
+        else:
+            #No card displayed, we still have to manage the clicks
+            if key == a_key['CARD_SELECT']:
+                self._emit('click', None)
+            return key
+
+    def getSelected(self):
+        """Return a list of selected cards"""
+        _selected = []
+        for wid in self.columns.widget_list:
+            if isinstance(wid, CardDisplayer) and wid.isSelected():
+                _selected.append(wid.getCard())
+        return _selected
+
+    def update(self, hand):
+        """Update the hand displayed in this widget
+        @param hand: list of Card"""
+        try:
+            del self.columns.widget_list[:]
+            del self.columns.column_types[:]
+        except IndexError:
+            pass
+        self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
+        for card in hand:
+            widget = CardDisplayer(card)
+            self.columns.widget_list.append(widget)
+            self.columns.column_types.append(('fixed', 3))
+            urwid.connect_signal(widget, 'click', self.__onClick)
+        self.columns.contents.append((urwid.Text(''), ('weight', 1, False)))
+        self.columns.focus_position = 1
+
+    def __onClick(self, card_wid):
+        self._emit('click', card_wid)
+
+
+class Card(TarotCard):
+    """This class is used to represent a card, logically
+    and give a text representation with attributes"""
+    SIZE = 3  # size of a displayed card
+
+    def __init__(self, suit, value):
+        """@param file: path of the PNG file"""
+        TarotCard.__init__(self, (suit, value))
+
+    def getAttrText(self):
+        """return text representation of the card with attributes"""
+        try:
+            value = "%02i" % int(self.value)
+        except ValueError:
+            value = self.value[0].upper() + self.value[1]
+        if self.suit == "atout":
+            if self.value == "excuse":
+                suit = 'c'
+            else:
+                suit = 'A'
+            color = 'neutral'
+        elif self.suit == "pique":
+            suit = u'♠'
+            color = 'black'
+        elif self.suit == "trefle":
+            suit = u'♣'
+            color = 'black'
+        elif self.suit == "coeur":
+            suit = u'♥'
+            color = 'red'
+        elif self.suit == "carreau":
+            suit = u'♦'
+            color = 'red'
+        if self.bout:
+            color = 'special'
+        return ('card_%s' % color, u"%s%s" % (value, suit))
+
+    def getWidget(self):
+        """Return a widget representing the card"""
+        return CardDisplayer(self)
+
+
+class Table(urwid.FlowWidget):
+    """Represent the cards currently on the table"""
+
+    def __init__(self):
+        self.top = self.left = self.bottom = self.right = None
+
+    def putCard(self, location, card):
+        """Put a card on the table
+        @param location: where to put the card (top, left, bottom or right)
+        @param card: Card to play or None"""
+        assert location in ['top', 'left', 'bottom', 'right']
+        assert isinstance(card, Card) or card == None
+        if [getattr(self, place) for place in ['top', 'left', 'bottom', 'right']].count(None) == 0:
+            #If the table is full of card, we remove them
+            self.top = self.left = self.bottom = self.right = None
+        setattr(self, location, card)
+        self._invalidate()
+
+    def rows(self, size, focus=False):
+        return self.display_widget(size, focus).rows(size, focus)
+
+    def render(self, size, focus=False):
+        return self.display_widget(size, focus).render(size, focus)
+
+    def display_widget(self, size, focus):
+        cards = {}
+        max_col, = size
+        separator = " - "
+        margin = max((max_col - Card.SIZE) / 2, 0) * ' '
+        margin_center = max((max_col - Card.SIZE * 2 - len(separator)) / 2, 0) * ' '
+        for location in ['top', 'left', 'bottom', 'right']:
+            card = getattr(self, location)
+            cards[location] = card.getAttrText() if card else Card.SIZE * ' '
+        render_wid = [urwid.Text([margin, cards['top']]),
+                      urwid.Text([margin_center, cards['left'], separator, cards['right']]),
+                      urwid.Text([margin, cards['bottom']])]
+        return urwid.Pile(render_wid)
+
+
+class TarotGame(QuickTarotGame, urwid.WidgetWrap):
+    """Widget for card games"""
+
+    def __init__(self, parent, referee, players):
+        QuickTarotGame.__init__(self, parent, referee, players)
+        self.loadCards()
+        self.top = urwid.Pile([urwid.Padding(urwid.Text(self.top_nick), 'center')])
+        #self.parent.host.debug()
+        self.table = Table()
+        self.center = urwid.Columns([('fixed', len(self.left_nick), urwid.Filler(urwid.Text(self.left_nick))),
+                                urwid.Filler(self.table),
+                                ('fixed', len(self.right_nick), urwid.Filler(urwid.Text(self.right_nick)))
+                               ])
+        """urwid.Pile([urwid.Padding(self.top_card_wid,'center'),
+                             urwid.Columns([('fixed',len(self.left_nick),urwid.Text(self.left_nick)),
+                                            urwid.Padding(self.center_cards_wid,'center'),
+                                            ('fixed',len(self.right_nick),urwid.Text(self.right_nick))
+                                           ]),
+                             urwid.Padding(self.bottom_card_wid,'center')
+                             ])"""
+        self.hand_wid = Hand(selectable=True, on_click=self.onClick)
+        self.main_frame = urwid.Frame(self.center, header=self.top, footer=self.hand_wid, focus_part='footer')
+        urwid.WidgetWrap.__init__(self, self.main_frame)
+        self.parent.host.bridge.tarotGameReady(self.player_nick, referee, self.parent.profile)
+
+    def loadCards(self):
+        """Load all the cards in memory"""
+        QuickTarotGame.loadCards(self)
+        for value in map(str, range(1, 22)) + ['excuse']:
+            card = Card('atout', value)
+            self.cards[card.suit, card.value] = card
+            self.deck.append(card)
+        for suit in ["pique", "coeur", "carreau", "trefle"]:
+            for value in map(str, range(1, 11)) + ["valet", "cavalier", "dame", "roi"]:
+                card = Card(suit, value)
+                self.cards[card.suit, card.value] = card
+                self.deck.append(card)
+
+    def tarotGameNewHandler(self, hand):
+        """Start a new game, with given hand"""
+        if hand is []:  # reset the display after the scores have been showed
+            self.resetRound()
+            for location in ['top', 'left', 'bottom', 'right']:
+                self.table.putCard(location, None)
+            self.parent.host.redraw()
+            self.parent.host.bridge.tarotGameReady(self.player_nick, self.referee, self.parent.profile)
+            return
+        QuickTarotGame.tarotGameNewHandler(self, hand)
+        self.hand_wid.update(self.hand)
+        self.parent.host.redraw()
+
+    def tarotGameChooseContratHandler(self, xml_data):
+        """Called when the player has to select his contrat
+        @param xml_data: SàT xml representation of the form"""
+        form = xmlui.create(self.parent.host, xml_data, title=_('Please choose your contrat'), flags=['NO_CANCEL'], profile=self.parent.profile)
+        form.show(valign='top')
+
+    def tarotGameShowCardsHandler(self, game_stage, cards, data):
+        """Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
+        QuickTarotGame.tarotGameShowCardsHandler(self, game_stage, cards, data)
+        self.center.widget_list[1] = urwid.Filler(Hand(self.to_show))
+        self.parent.host.redraw()
+
+    def tarotGameYourTurnHandler(self):
+        QuickTarotGame.tarotGameYourTurnHandler(self)
+
+    def tarotGameScoreHandler(self, xml_data, winners, loosers):
+        """Called when the round is over, display the scores
+        @param xml_data: SàT xml representation of the form"""
+        if not winners and not loosers:
+            title = _("Draw game")
+        else:
+            title = _('You win \o/') if self.player_nick in winners else _('You loose :(')
+        form = xmlui.create(self.parent.host, xml_data, title=title, flags=['NO_CANCEL'], profile=self.parent.profile)
+        form.show()
+
+    def tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards):
+        """Invalid cards have been played
+        @param phase: phase of the game
+        @param played_cards: all the cards played
+        @param invalid_cards: cards which are invalid"""
+        QuickTarotGame.tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards)
+        self.hand_wid.update(self.hand)
+        if self._autoplay == None:  # No dialog if there is autoplay
+            self.parent.host.notify(_('Cards played are invalid !'))
+        self.parent.host.redraw()
+
+    def tarotGameCardsPlayedHandler(self, player, cards):
+        """A card has been played by player"""
+        QuickTarotGame.tarotGameCardsPlayedHandler(self, player, cards)
+        self.table.putCard(self.getPlayerLocation(player), self.played[player])
+        self._checkState()
+        self.parent.host.redraw()
+
+    def _checkState(self):
+        if isinstance(self.center.widget_list[1].original_widget, Hand):  # if we have a hand displayed
+            self.center.widget_list[1] = urwid.Filler(self.table)  # we show again the table
+            if self.state == "chien":
+                self.to_show = []
+                self.state = "wait"
+            elif self.state == "wait_for_ecart":
+                self.state = "ecart"
+                self.hand.extend(self.to_show)
+                self.hand.sort()
+                self.to_show = []
+                self.hand_wid.update(self.hand)
+
+    ##EVENTS##
+    def onClick(self, hand, card_wid):
+        """Called when user do an action on the hand"""
+        if not self.state in ['play', 'ecart', 'wait_for_ecart']:
+            #it's not our turn, we ignore the click
+            card_wid.select(False)
+            return
+        self._checkState()
+        if self.state == "ecart":
+            if len(self.hand_wid.getSelected()) == 6:
+                pop_up_widget = sat_widgets.ConfirmDialog(_("Do you put these cards in chien ?"), yes_cb=self.onEcartDone, no_cb=self.parent.host.removePopUp)
+                self.parent.host.showPopUp(pop_up_widget)
+        elif self.state == "play":
+            card = card_wid.getCard()
+            self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.profile)
+            self.hand.remove(card)
+            self.hand_wid.update(self.hand)
+            self.state = "wait"
+
+    def onEcartDone(self, button):
+        """Called when player has finished his écart"""
+        ecart = []
+        for card in self.hand_wid.getSelected():
+            ecart.append((card.suit, card.value))
+            self.hand.remove(card)
+        self.hand_wid.update(self.hand)
+        self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, ecart, self.parent.profile)
+        self.state = "wait"
+        self.parent.host.removePopUp()
--- a/frontends/src/primitivus/primitivus	Wed Mar 11 12:36:22 2015 +0100
+++ b/frontends/src/primitivus/primitivus	Wed Mar 11 12:43:48 2015 +0100
@@ -45,7 +45,6 @@
 import signal
 
 
-
 class EditBar(sat_widgets.ModalEdit):
     """
     The modal edit bar where you would enter messages and commands.
@@ -676,7 +675,7 @@
                 title = _('Registration')
                 misc['target'] = data['target']
                 misc['action_back'] = self.bridge.gatewayRegister
-            ui = xmlui.create(self, title=title, xml_data = data['xml'], misc = misc)
+            ui = xmlui.create(self, title=title, xml_data=data['xml'], misc=misc, profile=profile)
             if data['type'] == 'registration':
                 ui.show('popup')
             else:
--- a/frontends/src/quick_frontend/quick_app.py	Wed Mar 11 12:36:22 2015 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Wed Mar 11 12:43:48 2015 +0100
@@ -26,7 +26,7 @@
 
 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 import quick_chat, quick_games
 from sat_frontends.quick_frontend.constants import Const as C
 
 import sys
@@ -252,25 +252,14 @@
         self.registerSignal("roomUserLeft", iface="plugin")
         self.registerSignal("roomUserChangedNick", iface="plugin")
         self.registerSignal("roomNewSubject", iface="plugin")
-        self.registerSignal("tarotGameStarted", iface="plugin")
-        self.registerSignal("tarotGameNew", iface="plugin")
-        self.registerSignal("tarotGameChooseContrat", iface="plugin")
-        self.registerSignal("tarotGameShowCards", iface="plugin")
-        self.registerSignal("tarotGameYourTurn", iface="plugin")
-        self.registerSignal("tarotGameScore", iface="plugin")
-        self.registerSignal("tarotGameCardsPlayed", iface="plugin")
-        self.registerSignal("tarotGameInvalidCards", iface="plugin")
-        self.registerSignal("quizGameStarted", iface="plugin")
-        self.registerSignal("quizGameNew", iface="plugin")
-        self.registerSignal("quizGameQuestion", iface="plugin")
-        self.registerSignal("quizGamePlayerBuzzed", iface="plugin")
-        self.registerSignal("quizGamePlayerSays", iface="plugin")
-        self.registerSignal("quizGameAnswerResult", iface="plugin")
-        self.registerSignal("quizGameTimerExpired", iface="plugin")
-        self.registerSignal("quizGameTimerRestarted", iface="plugin")
         self.registerSignal("chatStateReceived", iface="plugin")
         self.registerSignal("personalEvent", iface="plugin")
 
+        # FIXME: do it dynamically
+        quick_games.Tarot.registerSignals(self)
+        quick_games.Quiz.registerSignals(self)
+        quick_games.Radiocol.registerSignals(self)
+
         self.current_action_ids = set() # FIXME: to be removed
         self.current_action_ids_cb = {} # FIXME: to be removed
         self.media_dir = self.bridge.getConfig('', 'media_dir')
@@ -576,107 +565,6 @@
         chat_widget.setSubject(subject)
         log.debug("new subject for room [%(room_jid)s]: %(subject)s" % {'room_jid': room_jid, "subject": subject})
 
-    def tarotGameStartedHandler(self, room_jid_s, referee, players, profile):
-        log.debug(_("Tarot Game Started \o/"))
-        room_jid = jid.JID(room_jid_s)
-        chat_widget = self.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile)
-        chat_widget.startGame("Tarot", referee, players)
-        log.debug("new Tarot game started by [%(referee)s] in room [%(room_jid)s] with %(players)s" % {'referee': referee, 'room_jid': room_jid, 'players': [str(player) for player in players]})
-
-    def tarotGameNewHandler(self, room_jid_s, hand, profile):
-        log.debug(_("New Tarot Game"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").newGame(hand)
-
-    def tarotGameChooseContratHandler(self, room_jid_s, xml_data, profile):
-        """Called when the player has to select his contrat"""
-        log.debug(_("Tarot: need to select a contrat"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").chooseContrat(xml_data)
-
-    def tarotGameShowCardsHandler(self, room_jid_s, game_stage, cards, data, profile):
-        log.debug(_("Show cards"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").showCards(game_stage, cards, data)
-
-    def tarotGameYourTurnHandler(self, room_jid_s, profile):
-        log.debug(_("My turn to play"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").myTurn()
-
-    def tarotGameScoreHandler(self, room_jid_s, xml_data, winners, loosers, profile):
-        """Called when the game is finished and the score are updated"""
-        log.debug(_("Tarot: score received"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").showScores(xml_data, winners, loosers)
-
-    def tarotGameCardsPlayedHandler(self, room_jid_s, player, cards, profile):
-        log.debug(_("Card(s) played (%(player)s): %(cards)s") % {"player": player, "cards": cards})
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").cardsPlayed(player, cards)
-
-    def tarotGameInvalidCardsHandler(self, room_jid_s, phase, played_cards, invalid_cards, profile):
-        log.debug(_("Cards played are not valid: %s") % invalid_cards)
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Tarot").invalidCards(phase, played_cards, invalid_cards)
-
-    def quizGameStartedHandler(self, room_jid_s, referee, players, profile):
-        log.debug(_("Quiz Game Started \o/"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.startGame("Quiz", referee, players)
-            log.debug(_("new Quiz game started by [%(referee)s] in room [%(room_jid)s] with %(players)s") % {'referee': referee, 'room_jid': room_jid_s, 'players': [str(player) for player in players]})
-
-    def quizGameNewHandler(self, room_jid_s, data, profile):
-        log.debug(_("New Quiz Game"))
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGameNewHandler(data)
-
-    def quizGameQuestionHandler(self, room_jid_s, question_id, question, timer, profile):
-        """Called when a new question is asked"""
-        log.debug(_(u"Quiz: new question: %s") % question)
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGameQuestionHandler(question_id, question, timer)
-
-    def quizGamePlayerBuzzedHandler(self, room_jid_s, player, pause, profile):
-        """Called when a player pushed the buzzer"""
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGamePlayerBuzzedHandler(player, pause)
-
-    def quizGamePlayerSaysHandler(self, room_jid_s, player, text, delay, profile):
-        """Called when a player say something"""
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGamePlayerSaysHandler(player, text, delay)
-
-    def quizGameAnswerResultHandler(self, room_jid_s, player, good_answer, score, profile):
-        """Called when a player say something"""
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGameAnswerResultHandler(player, good_answer, score)
-
-    def quizGameTimerExpiredHandler(self, room_jid_s, profile):
-        """Called when nobody answered the question in time"""
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGameTimerExpiredHandler()
-
-    def quizGameTimerRestartedHandler(self, room_jid_s, time_left, profile):
-        """Called when the question is not answered, and we still have time"""
-        chat_widget = self.widgets.getWidget(quick_chat.QuickChat, jid.JID(room_jid_s), profile)
-        if chat_widget:
-            chat_widget.getGame("Quiz").quizGameTimerRestartedHandler(time_left)
-
     def chatStateReceivedHandler(self, from_jid_s, state, profile):
         """Called when a new chat state is received.
 
--- a/frontends/src/quick_frontend/quick_card_game.py	Wed Mar 11 12:36:22 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-# helper class for making a SAT frontend
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Affero General Public License for more details.
-
-# 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.log import getLogger
-log = getLogger(__name__)
-from sat_frontends.tools.jid  import JID
-
-
-
-class QuickCardGame(object):
-
-    def __init__(self, parent, referee, players, player_nick):
-        self._autoplay = None #XXX: use 0 to activate fake play, None else
-        self.parent = parent
-        self.referee = referee
-        self.players = players
-        self.played = {}
-        for player in players:
-            self.played[player] = None
-        self.player_nick = player_nick
-        self.bottom_nick = unicode(self.player_nick)
-        idx = self.players.index(self.player_nick)
-        idx = (idx + 1) % len(self.players)
-        self.right_nick = unicode(self.players[idx])
-        idx = (idx + 1) % len(self.players)
-        self.top_nick = unicode(self.players[idx])
-        idx = (idx + 1) % len(self.players)
-        self.left_nick = unicode(self.players[idx])
-        self.bottom_nick = unicode(player_nick)
-        self.selected = [] #Card choosed by the player (e.g. during ecart)
-        self.hand_size = 13 #number of cards in a hand
-        self.hand = []
-        self.to_show = []
-        self.state = None
-
-    def resetRound(self):
-        """Reset the game's variables to be reatty to start the next round"""
-        del self.selected[:]
-        del self.hand[:]
-        del self.to_show[:]
-        self.state = None
-        for pl in self.played:
-            self.played[pl] = None
-
-    def getPlayerLocation(self, nick):
-        """return player location (top,bottom,left or right)"""
-        for location in ['top','left','bottom','right']:
-            if getattr(self,'%s_nick' % location) == nick:
-                return location
-        assert(False)
-
-    def loadCards(self):
-        """Load all the cards in memory
-        @param dir: directory where the PNG files are"""
-        self.cards={}
-        self.deck=[]
-        self.cards["atout"]={} #As Tarot is a french game, it's more handy & logical to keep french names
-        self.cards["pique"]={} #spade
-        self.cards["coeur"]={} #heart
-        self.cards["carreau"]={} #diamond
-        self.cards["trefle"]={} #club
-
-    def newGame(self, hand):
-        """Start a new game, with given hand"""
-        assert (len(self.hand) == 0)
-        for suit, value in hand:
-            self.hand.append(self.cards[suit, value])
-        self.hand.sort()
-        self.state = "init"
-
-    def contratSelected(self, contrat):
-        """Called when the contrat has been choosed
-        @param data: form result"""
-        self.parent.host.bridge.tarotGameContratChoosed(self.player_nick, self.referee, contrat or 'Passe', self.parent.host.profile)
-
-    def chooseContrat(self, xml_data):
-        """Called when the player as to select his contrat
-        @param xml_data: SàT xml representation of the form"""
-        raise NotImplementedError
-
-    def showCards(self, game_stage, cards, data):
-        """Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
-        self.to_show = []
-        for suit, value in cards:
-            self.to_show.append(self.cards[suit, value])
-        if game_stage == "chien" and data['attaquant'] == self.player_nick:
-            self.state = "wait_for_ecart"
-        else:
-            self.state = "chien"
-
-    def myTurn(self):
-        """Called when we have to play :)"""
-        if self.state == "chien":
-            self.to_show = []
-        self.state = "play"
-        self.__fakePlay()
-
-    def __fakePlay(self):
-        """Convenience method for stupid autoplay
-        /!\ don't forgot to comment any interactive dialog for invalid card"""
-        if self._autoplay == None:
-            return
-        if self._autoplay >= len(self.hand):
-            self._autoplay = 0
-        card = self.hand[self._autoplay]
-        self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.host.profile)
-        del self.hand[self._autoplay]
-        self.state = "wait"
-        self._autoplay+=1
-
-    def showScores(self, xml_data, winners, loosers):
-        """Called at the end of a game
-        @param xml_data: SàT xml representation of the scores
-        @param winners: list of winners' nicks
-        @param loosers: list of loosers' nicks"""
-        raise NotImplementedError
-
-    def cardsPlayed(self, player, cards):
-        """A card has been played by player"""
-        if self.to_show:
-            self.to_show = []
-        pl_cards = []
-        if self.played[player] != None: #FIXME
-            for pl in self.played:
-                self.played[pl] = None
-        for suit, value in cards:
-            pl_cards.append(self.cards[suit, value])
-        self.played[player] = pl_cards[0]
-
-    def invalidCards(self, phase, played_cards, invalid_cards):
-        """Invalid cards have been played
-        @param phase: phase of the game
-        @param played_cards: all the cards played
-        @param invalid_cards: cards which are invalid"""
-
-        if phase == "play":
-            self.state = "play"
-        elif phase == "ecart":
-            self.state = "ecart"
-        else:
-            log.error ('INTERNAL ERROR: unmanaged game phase')
-
-        for suit, value in played_cards:
-            self.hand.append(self.cards[suit, value])
-
-        self.hand.sort()
-        self.__fakePlay()
-
--- a/frontends/src/quick_frontend/quick_chat.py	Wed Mar 11 12:36:22 2015 +0100
+++ b/frontends/src/quick_frontend/quick_chat.py	Wed Mar 11 12:43:48 2015 +0100
@@ -48,6 +48,7 @@
         self.id = "" # FIXME: to be removed
         self.nick = None
         self.occupants = set()
+        self.games = {}
 
     def __str__(self):
         return u"Chat Widget [target: {}, type: {}, profile: {}]".format(self.target, self.type, self.profile)
@@ -227,16 +228,6 @@
         """
         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 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, from_jid, state):
         """Set the chat state (XEP-0085) of the contact.
 
@@ -244,4 +235,19 @@
         """
         raise NotImplementedError
 
+    def addGamePanel(self, widget):
+        """Insert a game panel to this Chat dialog.
+
+        @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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/src/quick_frontend/quick_game_tarot.py	Wed Mar 11 12:43:48 2015 +0100
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# helper class for making a SAT frontend
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# 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.log import getLogger
+log = getLogger(__name__)
+from sat_frontends.tools.jid import JID
+
+
+class QuickTarotGame(object):
+
+    def __init__(self, parent, referee, players):
+        self._autoplay = None  #XXX: use 0 to activate fake play, None else
+        self.parent = parent
+        self.referee = referee
+        self.players = players
+        self.played = {}
+        for player in players:
+            self.played[player] = None
+        self.player_nick = parent.nick
+        self.bottom_nick = unicode(self.player_nick)
+        idx = self.players.index(self.player_nick)
+        idx = (idx + 1) % len(self.players)
+        self.right_nick = unicode(self.players[idx])
+        idx = (idx + 1) % len(self.players)
+        self.top_nick = unicode(self.players[idx])
+        idx = (idx + 1) % len(self.players)
+        self.left_nick = unicode(self.players[idx])
+        self.bottom_nick = unicode(self.player_nick)
+        self.selected = []  #Card choosed by the player (e.g. during ecart)
+        self.hand_size = 13  #number of cards in a hand
+        self.hand = []
+        self.to_show = []
+        self.state = None
+
+    def resetRound(self):
+        """Reset the game's variables to be reatty to start the next round"""
+        del self.selected[:]
+        del self.hand[:]
+        del self.to_show[:]
+        self.state = None
+        for pl in self.played:
+            self.played[pl] = None
+
+    def getPlayerLocation(self, nick):
+        """return player location (top,bottom,left or right)"""
+        for location in ['top','left','bottom','right']:
+            if getattr(self,'%s_nick' % location) == nick:
+                return location
+        assert(False)
+
+    def loadCards(self):
+        """Load all the cards in memory
+        @param dir: directory where the PNG files are"""
+        self.cards={}
+        self.deck=[]
+        self.cards["atout"]={} #As Tarot is a french game, it's more handy & logical to keep french names
+        self.cards["pique"]={} #spade
+        self.cards["coeur"]={} #heart
+        self.cards["carreau"]={} #diamond
+        self.cards["trefle"]={} #club
+
+    def tarotGameNewHandler(self, hand):
+        """Start a new game, with given hand"""
+        assert (len(self.hand) == 0)
+        for suit, value in hand:
+            self.hand.append(self.cards[suit, value])
+        self.hand.sort()
+        self.state = "init"
+
+    def tarotGameChooseContratHandler(self, xml_data):
+        """Called when the player as to select his contrat
+        @param xml_data: SàT xml representation of the form"""
+        raise NotImplementedError
+
+    def tarotGameShowCardsHandler(self, game_stage, cards, data):
+        """Display cards in the middle of the game (to show for e.g. chien ou poignée)"""
+        self.to_show = []
+        for suit, value in cards:
+            self.to_show.append(self.cards[suit, value])
+        if game_stage == "chien" and data['attaquant'] == self.player_nick:
+            self.state = "wait_for_ecart"
+        else:
+            self.state = "chien"
+
+    def tarotGameYourTurnHandler(self):
+        """Called when we have to play :)"""
+        if self.state == "chien":
+            self.to_show = []
+        self.state = "play"
+        self.__fakePlay()
+
+    def __fakePlay(self):
+        """Convenience method for stupid autoplay
+        /!\ don't forgot to comment any interactive dialog for invalid card"""
+        if self._autoplay == None:
+            return
+        if self._autoplay >= len(self.hand):
+            self._autoplay = 0
+        card = self.hand[self._autoplay]
+        self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], self.parent.profile)
+        del self.hand[self._autoplay]
+        self.state = "wait"
+        self._autoplay+=1
+
+    def tarotGameScoreHandler(self, xml_data, winners, loosers):
+        """Called at the end of a game
+        @param xml_data: SàT xml representation of the scores
+        @param winners: list of winners' nicks
+        @param loosers: list of loosers' nicks"""
+        raise NotImplementedError
+
+    def tarotGameCardsPlayedHandler(self, player, cards):
+        """A card has been played by player"""
+        if self.to_show:
+            self.to_show = []
+        pl_cards = []
+        if self.played[player] != None: #FIXME
+            for pl in self.played:
+                self.played[pl] = None
+        for suit, value in cards:
+            pl_cards.append(self.cards[suit, value])
+        self.played[player] = pl_cards[0]
+
+    def tarotGameInvalidCardsHandler(self, phase, played_cards, invalid_cards):
+        """Invalid cards have been played
+        @param phase: phase of the game
+        @param played_cards: all the cards played
+        @param invalid_cards: cards which are invalid"""
+
+        if phase == "play":
+            self.state = "play"
+        elif phase == "ecart":
+            self.state = "ecart"
+        else:
+            log.error ('INTERNAL ERROR: unmanaged game phase')
+
+        for suit, value in played_cards:
+            self.hand.append(self.cards[suit, value])
+
+        self.hand.sort()
+        self.__fakePlay()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/src/quick_frontend/quick_games.py	Wed Mar 11 12:43:48 2015 +0100
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# helper class for making a SAT frontend
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# 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.log import getLogger
+log = getLogger(__name__)
+
+from sat.core.i18n import _
+
+from sat_frontends.tools import jid
+from sat_frontends.quick_frontend.constants import Const as C
+
+import quick_chat
+
+
+class RoomGame(object):
+    _game_name = None
+    _signal_prefix = None
+    _signal_suffixes = None
+
+    @classmethod
+    def registerSignals(cls, host):
+
+        def make_handler(suffix, signal):
+            def handler(*args):
+                if suffix in ("Started", "Players"):
+                    return cls.startedHandler(host, suffix, *args)
+                return cls.genericHandler(host, signal, *args)
+            return handler
+
+        for suffix in cls._signal_suffixes:
+            signal = cls._signal_prefix + suffix
+            host.registerSignal(signal, handler=make_handler(suffix, signal), iface="plugin")
+
+    @classmethod
+    def startedHandler(cls, host, suffix, *args):
+        room_jid, args, profile = jid.JID(args[0]), args[1:-1], args[-1]
+        referee, players, args = args[0], args[1], args[2:]
+        chat_widget = host.widgets.getOrCreateWidget(quick_chat.QuickChat, room_jid, type_=C.CHAT_GROUP, profile=profile)
+
+        # self.occupants_panel.updateSpecials(players, SYMBOLS[self._game_name.lower()]) # FIXME
+        if suffix == "Players" or chat_widget.nick not in players:
+            return  # waiting for other players to join, or not playing
+        if cls._game_name in chat_widget.games:
+            return  # game panel is already there
+        real_class = host.widgets.getRealClass(cls)
+        if real_class == cls:
+            host.showDialog(_(u"A {game} activity between {players} has been started, but you couldn't take part because your client doesn't support it.").format(game=cls._game_name, players=', '.join(players)),
+                            _(u"{game} Game").format(game=cls._game_name))
+            return
+        panel = real_class(chat_widget, referee, players, *args)
+        chat_widget.games[cls._game_name] = panel
+        chat_widget.addGamePanel(panel)
+
+    @classmethod
+    def genericHandler(cls, host, signal, *args):
+        room_jid, args, profile = jid.JID(args[0]), args[1:-1], args[-1]
+        chat_widget = host.widgets.getWidget(quick_chat.QuickChat, room_jid, profile)
+        if chat_widget:
+            try:
+                game_panel = chat_widget.games[cls._game_name]
+            except KeyError:
+                log.error("TODO: better game synchronisation - received signal %s but no panel is found" % signal)
+                return
+            else:
+                getattr(game_panel, "%sHandler" % signal)(*args)
+
+
+class Tarot(RoomGame):
+    _game_name = "Tarot"
+    _signal_prefix = "tarotGame"
+    _signal_suffixes = ("Started", "Players", "New", "ChooseContrat",
+                        "ShowCards", "YourTurn", "Score", "CardsPlayed",
+                        "InvalidCards",
+                        )
+
+
+class Quiz(RoomGame):
+    _game_name = "Quiz"
+    _signal_prefix = "quizGame"
+    _signal_suffixes = ("Started", "New", "Question", "PlayerBuzzed",
+                        "PlayerSays", "AnswerResult", "TimerExpired",
+                        "TimerRestarted",
+                        )
+
+
+class Radiocol(RoomGame):
+    _game_name = "Radiocol"
+    _signal_prefix = "radiocol"
+    _signal_suffixes = ("Started", "Players", "SongRejected", "Preload",
+                        "Play", "NoUpload", "UploadOk")