[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdegames
From:       Wolfgang Rohdewald <wolfgang () rohdewald ! de>
Date:       2012-03-23 8:05:53
Message-ID: 20120323080553.7DB7FAC899 () svn ! kde ! org
[Download RAW message or body]

SVN commit 1286780 by wrohdewald:

players can now chat with each other

FEATURE:

 AM            doc/kajongg/chat.png  
 M  +31 -0     doc/kajongg/index.docbook  
 M  +1 -0      kajongg/CMakeLists.txt  
 A             kajongg/src/chat.py  
 M  +1 -0      kajongg/src/client.py  
 M  +1 -0      kajongg/src/common.py  
 M  +27 -1     kajongg/src/humanclient.py  
 M  +1 -0      kajongg/src/kajonggui.rc  
 M  +43 -1     kajongg/src/message.py  
 M  +12 -0     kajongg/src/playfield.py  
 M  +25 -1     kajongg/src/server.py  
 M  +27 -0     kajongg/src/tables.py  


--- trunk/KDE/kdegames/doc/kajongg/index.docbook #1286779:1286780
@@ -35,6 +35,8 @@
   <!ENTITY lastsource "lastsource">
 ]>
 
+<!-- TODO: describe the window with the list of tables -->
+
 <book lang="&language;"> <!-- do not change this! -->
 <bookinfo>
 	<title>The &kajongg; Handbook</title> <!-- This is the title of this docbook. -->
@@ -475,6 +477,26 @@
 				Explains how the score for the current hand was computed.
 				</para></listitem>
 			</varlistentry>
+			<varlistentry id="view-chat">
+				<term><menuchoice>
+					<guimenu>View</guimenu>
+					<guimenuitem>Chat</guimenuitem>
+				</menuchoice></term>
+				<listitem><para><action>Chat</action>
+				<screenshot>
+					<screeninfo>Chat Window</screeninfo>
+					<mediaobject>
+						<imageobject>
+						<imagedata fileref="chat.png" format="PNG"/>
+						</imageobject>
+						<textobject>
+						<phrase>Chat Window</phrase>
+						</textobject>
+					</mediaobject>
+				</screenshot>
+				Lets you chat with the other players.
+				</para></listitem>
+			</varlistentry>
 		</variablelist>
 </sect1>
 <sect1 id="settings-menu">
@@ -882,6 +904,15 @@
 </varlistentry>
 
 <varlistentry>
+<term><keycombo action="simul">&Ctrl;<keycap>H</keycap></keycombo></term>
+<listitem>
+<para>
+Show a chat window.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
 <term><keycombo action="simul">&Ctrl;<keycap>G</keycap></keycombo></term>
 <listitem>
 <para>
--- trunk/KDE/kdegames/kajongg/CMakeLists.txt #1286779:1286780
@@ -24,6 +24,7 @@
 src/background.py
 src/backgroundselector.py
 src/board.py
+src/chat.py
 src/handboard.py
 src/message.py
 src/client.py
--- trunk/KDE/kdegames/kajongg/src/client.py #1286779:1286780
@@ -56,6 +56,7 @@
             if myRuleset == self.ruleset:
                 self.myRuleset = myRuleset
                 break
+        self.chatWindow = None
 
     def __str__(self):
         return 'Table %d %s gameid=%s rules %s players %s online %s' % (self.tableid or 0,
--- trunk/KDE/kdegames/kajongg/src/common.py #1286779:1286780
@@ -45,6 +45,7 @@
     robbingKong = False
     mahJongg = False
     sound = False
+    chat = False
     argString = None
 
     def __init__(self):
--- trunk/KDE/kdegames/kajongg/src/humanclient.py #1286779:1286780
@@ -36,7 +36,8 @@
 from util import m18n, m18nc, logWarning, logException, socketName, english, \
     appdataDir, logInfo, logDebug, removeIfExists
 from util import SERVERMARK, isAlive
-from message import Message
+from message import Message, ChatMessage
+from chat import ChatWindow
 from common import InternalParameters, PREF, Debug
 from game import Players
 from query import Transaction, Query
@@ -752,6 +753,24 @@
         Client.remote_tablesChanged(self, tables)
         self.tableList.loadTables(self.tables)
 
+    def remote_chat(self, data):
+        """others chat to me"""
+        chatLine = ChatMessage(data)
+        if Debug.chat:
+            logDebug('got chatLine: %s' % chatLine)
+        if self.table:
+            table = self.table
+        else:
+            table = None
+            for _ in self.tableList.view.model().tables:
+                if _.tableid == chatLine.tableid:
+                    table = _
+            assert table.tableid == chatLine.tableid
+        if not chatLine.isStatusMessage:
+            ChatWindow.createFor(table)
+        if table.chatWindow:
+            table.chatWindow.receiveLine(chatLine)
+
     def readyForGameStart(self, tableid, gameid, wantedGame, playerNames, shouldSave=True):
         """playerNames are in wind order ESWN"""
         self.tableList.hideForever = True
@@ -1095,6 +1114,9 @@
             self.readyHandQuestion.hide()
         if field.clientDialog:
             field.clientDialog.hide()
+        if self.table.chatWindow:
+            self.table.chatWindow.hide()
+            self.table.chatWindow = None
 
     def callServer(self, *args):
         """if we are online, call server"""
@@ -1112,3 +1134,7 @@
                 self.perspective = None
                 logWarning(m18n('The connection to the server %1 broke, please try again later.',
                                   self.url))
+
+    def sendChat(self, chatLine):
+        """send chat message to server"""
+        self.callServer('chat', chatLine.serialize())
--- trunk/KDE/kdegames/kajongg/src/kajonggui.rc #1286779:1286780
@@ -14,6 +14,7 @@
    <Action name="scoring" />
    <Action name="scoreTable" />
    <Action name="explain" />
+   <Action name="chat" />
   </Menu>
   <Menu name="settings" >
    <Action name="players" />
--- trunk/KDE/kdegames/kajongg/src/message.py #1286779:1286780
@@ -18,7 +18,9 @@
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 """
 
-from util import m18n, m18nc, m18ncE, logWarning, logException, logDebug
+import datetime
+
+from util import m18n, m18nc, m18ncE, logWarning, logException, logDebug, SERVERMARK
 from sound import Voice, Sound
 from meld import Meld
 from common import InternalParameters, Debug
@@ -559,4 +561,44 @@
                         msg = glob()
                         type.__setattr__(Message, msg.name.replace(' ', ''), msg)
 
+class ChatMessage:
+    """holds relevant info about a chat message"""
+    def __init__(self, tableid, fromUser=None, message=None, isStatusMessage=False):
+        if isinstance(tableid, basestring) and SERVERMARK in tableid:
+            parts = tableid.split(SERVERMARK)
+            self.tableid = int(parts[0])
+            self.timestamp = datetime.time(hour=int(parts[1]), minute=int(parts[2]), second=int(parts[3]))
+            self.fromUser = parts[4]
+            self.message = parts[5]
+            self.isStatusMessage = bool(int(parts[6]))
+        else:
+            self.tableid = tableid
+            self.fromUser = fromUser
+            self.message = message
+            self.isStatusMessage = isStatusMessage
+            self.timestamp = datetime.datetime.utcnow().time()
+
+    def __unicode__(self):
+        return 'statusMessage=%s %02d:%02d:%02d %s: %s' % (
+            str(self.isStatusMessage),
+            self.timestamp.hour,
+            self.timestamp.minute,
+            self.timestamp.second,
+            self.fromUser,
+            self.message)
+
+    def __repr__(self):
+        return unicode(self)
+
+    def serialize(self):
+        """encode me in a string for network transfer"""
+        return SERVERMARK.join([
+            str(self.tableid),
+            str(self.timestamp.hour),
+            str(self.timestamp.minute),
+            str(self.timestamp.second),
+            self.fromUser,
+            self.message,
+            str(int(self.isStatusMessage))])
+
 __scanSelf()
--- trunk/KDE/kdegames/kajongg/src/playfield.py #1286779:1286780
@@ -81,6 +81,7 @@
     from animation import animate, afterCurrentAnimationDo, Animated
     from player import Player, Players
     from game import Game, ScoringGame
+    from chat import ChatWindow
 
 except ImportError, e:
     NOTFOUND.append('kajongg is not correctly installed: modules: %s' % e)
@@ -548,6 +549,11 @@
         self.actionAbortGame.setEnabled(False)
         self.actionQuit = self.kajonggAction("quit", "application-exit", self.quit, Qt.Key_Q)
         self.actionPlayers = self.kajonggAction("players", "im-user", self.slotPlayers)
+        self.actionChat = self.kajonggToggleAction("chat", "call-start",
+            shortcut=Qt.Key_H, actionData=ChatWindow)
+        game = self.game
+        self.actionChat.setEnabled(bool(game) and bool(game.client))
+        self.actionChat.setChecked(bool(game) and bool(game.client) and bool(game.client.table.chatWindow))
         self.actionScoring = self.kajonggToggleAction("scoring", "draw-freehand",
             shortcut=Qt.Key_S, actionData=ScoringDialog)
         self.actionScoring.setEnabled(False)
@@ -671,6 +677,7 @@
         self.actionScoreTable.setText(m18nc('kajongg', "&Score Table"))
         self.actionExplain.setText(m18n("&Explain Scores"))
         self.actionAutoPlay.setText(m18n("&Demo Mode"))
+        self.actionChat.setText(m18n("C&hat"))
 
     def changeEvent(self, event):
         """when the applicationwide language changes, recreate GUI"""
@@ -827,6 +834,9 @@
         actionData = action.data().toPyObject()
         if checked:
             if isinstance(actionData, type):
+                if actionData == ChatWindow:
+                    actionData = ChatWindow.createFor(self.game.client.table)
+                else:
                 actionData = actionData(self.game)
                 action.setData(QVariant(actionData))
                 if isinstance(actionData, ScoringDialog):
@@ -891,6 +901,8 @@
         self.discardBoard.setVisible(bool(game) and not scoring)
         self.actionScoring.setEnabled(scoring and not game.finished())
         self.actionAutoPlay.setEnabled(not self.startingGame)
+        self.actionChat.setEnabled(bool(game) and not self.startingGame)
+        self.actionChat.setChecked(bool(game) and bool(game.client) and bool(game.client.table.chatWindow))
         if self.actionScoring.isChecked():
             self.actionScoring.setChecked(scoring and not game.finished())
         for view in [self.scoringDialog, self.explainView, self.scoreTable]:
--- trunk/KDE/kdegames/kajongg/src/server.py #1286779:1286780
@@ -54,7 +54,7 @@
 from scoringengine import Ruleset
 from util import m18n, m18nE, m18ncE, logInfo, logDebug, logWarning, SERVERMARK, \
     Duration, socketName, logError
-from message import Message
+from message import Message, ChatMessage
 from common import elements, Debug
 from sound import Voice
 from deferredutil import DeferredBlock
@@ -126,6 +126,8 @@
     """a table on the game server"""
     # pylint: disable=R0902
     # pylint we need more than 10 instance attributes
+    # pylint: disable=R0904
+    # pylint we have too many public methods
 
     def __init__(self, server, owner, rulesetStr, playOpen, autoPlay, wantedGame):
         self.server = server
@@ -176,6 +178,13 @@
             result -= sum (x.name.startswith('ROBOT') for x in self.preparedGame.players)
         return result
 
+    def sendChatMessage(self, chatLine):
+        """sends a chat messages to all clients"""
+        if Debug.chat:
+            logDebug('server sends chat msg %s' % chatLine)
+        for other in self.users:
+            self.server.callRemote(other, 'chat', chatLine.serialize())
+
     def addUser(self, user):
         """add user to this table"""
         if user.name in list(x.name for x in self.users):
@@ -183,6 +192,9 @@
         if len(self.users) == self.maxSeats():
             raise srvError(pb.Error, m18nE('All seats are already taken'))
         self.users.append(user)
+        self.sendChatMessage(ChatMessage(self.tableid, user.name,
+            m18nE('takes a seat'), isStatusMessage=True))
+
         if len(self.users) == self.maxSeats():
             self.readyForGameStart(self.owner)
 
@@ -191,6 +203,8 @@
         if user in self.users:
             self.game = None
             self.users.remove(user)
+            self.sendChatMessage(ChatMessage(self.tableid, user.name,
+                m18nE('leaves the table'), isStatusMessage=True))
             if user is self.owner:
                 # silently pass ownership
                 if self.users:
@@ -743,6 +757,13 @@
         self.users = list()
         Players.load()
 
+    def chat(self, chatString):
+        """a client sent us a chat message"""
+        chatLine = ChatMessage(chatString)
+        if Debug.chat:
+            logDebug('server got chat message %s' % chatLine)
+        self.tables[chatLine.tableid].sendChatMessage(chatLine)
+
     def login(self, user):
         """accept a new user"""
         if not user in self.users:
@@ -988,6 +1009,9 @@
     def perspective_logout(self):
         """perspective_* methods are to be called remotely"""
         self.detached(None)
+    def perspective_chat(self, chatString):
+        """perspective_* methods are to be called remotely"""
+        return self.server.chat(chatString)
     def __str__(self):
         return '%d:%s' % (id(self) % 10000, self.name)
 
--- trunk/KDE/kdegames/kajongg/src/tables.py #1286779:1286780
@@ -41,6 +41,7 @@
 from sound import Voice
 from common import InternalParameters, Debug, PREF
 from client import ClientTable
+from chat import ChatWindow
 from modeltest import ModelTest
 
 class TablesModel(QAbstractTableModel):
@@ -182,6 +183,9 @@
         self.compareButton.clicked.connect(self.compareRuleset)
         self.compareButton.setIcon(KIcon("preferences-plugin-script"))
         self.compareButton.setToolTip(m18n('Compare the rules of this table with my own rulesets'))
+        self.chatButton = buttonBox.addButton(m18n('&Chat'), QDialogButtonBox.AcceptRole)
+        self.chatButton.setIcon(KIcon("call-start"))
+        self.chatButton.clicked.connect(self.chat)
         self.startButton = buttonBox.addButton(m18n('&Start'), QDialogButtonBox.AcceptRole)
         self.startButton.clicked.connect(self.startGame)
         self.startButton.setIcon(KIcon("arrow-right"))
@@ -199,6 +203,10 @@
         StateSaver(self, self.view.horizontalHeader())
         self.login()
 
+    def chat(self):
+        """chat"""
+        ChatWindow.createFor(self.selectedTable())
+
     @apply
     def hideForever(): # pylint: disable=E0202
         """we never want to see this table list for local games,
@@ -313,6 +321,12 @@
         self.startButton.setEnabled(not suspendedLocalGame and hasTable \
             and self.client.username == table.playerNames[0])
         self.compareButton.setEnabled(hasTable and table.myRuleset is None)
+        self.chatButton.setVisible(not self.client.hasLocalServer())
+        self.chatButton.setEnabled(self.leaveButton.isEnabled())
+        if self.chatButton.isEnabled():
+            self.chatButton.setToolTip(m18n("Chat with others on this table"))
+        else:
+            self.chatButton.setToolTip(m18n("For chatting with others on this table, please first take a seat"))
 
     def selectionChanged(self, selected, dummyDeselected):
         """update button states according to selection"""
@@ -405,6 +419,18 @@
         """leave a table"""
         self.client.callServer('leaveTable', self.selectedTable().tableid)
 
+    def __keepChatWindows(self, tables):
+        """copy chatWindows from the old table list which will be thrown away"""
+        if self.view.model():
+            chatWindows = dict((x.tableid, x.chatWindow) for x in self.view.model().tables)
+            unusedWindows = set(x.chatWindow for x in self.view.model().tables)
+            for table in tables:
+                table.chatWindow = chatWindows.get(table.tableid, None)
+                unusedWindows -= set([table.chatWindow])
+            for unusedWindow in unusedWindows:
+                if unusedWindow:
+                    unusedWindow.hide()
+
     def loadTables(self, tables):
         """build and use a model around the tables.
         Show all new tables (no gameid given yet) and all suspended
@@ -418,6 +444,7 @@
                 elif not table.gameExistsLocally():
                     logDebug('%s does not exist locally' % table)
         tables = [x for x in tables if not x.gameid or x.gameExistsLocally()]
+        self.__keepChatWindows(tables)
         model = TablesModel(tables)
         self.view.setModel(model)
         if Debug.modelTest:
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic