Files reorganisation
0
frontends/src/wix/__init__.py
Normal file
254
frontends/src/wix/card_game.py
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
wix: a SAT frontend
|
||||
Copyright (C) 2009, 2010 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 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import wx
|
||||
import os.path, glob
|
||||
import pdb
|
||||
from logging import debug, info, error
|
||||
from tools.jid import JID
|
||||
from tools.games import TarotCard
|
||||
from quick_frontend.quick_card_game import QuickCardGame
|
||||
from xmlui import XMLUI
|
||||
|
||||
CARD_WIDTH = 74
|
||||
CARD_HEIGHT = 136
|
||||
MIN_WIDTH = 950 #Minimum size of the panel
|
||||
MIN_HEIGHT = 500
|
||||
|
||||
class WxCard(TarotCard):
|
||||
"""This class is used to represent a card, graphically and logically"""
|
||||
|
||||
def __init__(self, file):
|
||||
"""@param file: path of the PNG file"""
|
||||
self.bitmap = wx.Image(file).ConvertToBitmap()
|
||||
root_name = os.path.splitext(os.path.basename(file))[0]
|
||||
suit,value = root_name.split('_')
|
||||
TarotCard.__init__(self, (suit, value))
|
||||
print "Carte:",suit, value #, self.bout
|
||||
|
||||
def draw(self, dc, x, y):
|
||||
"""Draw the card on the device context
|
||||
@param dc: device context
|
||||
@param x: abscissa
|
||||
@param y: ordinate"""
|
||||
dc.DrawBitmap(self.bitmap, x, y, True)
|
||||
|
||||
|
||||
class CardPanel(QuickCardGame,wx.Panel):
|
||||
"""This class is used to display the cards"""
|
||||
|
||||
def __init__(self, parent, referee, players, player_nick):
|
||||
QuickCardGame.__init__(self, parent, referee, players, player_nick)
|
||||
wx.Panel.__init__(self, parent)
|
||||
self.SetMinSize(wx.Size(MIN_WIDTH, MIN_HEIGHT))
|
||||
self.loadCards("images/cards/")
|
||||
self.mouse_over_card = None #contain the card to highlight
|
||||
self.visible_size = CARD_WIDTH/2 #number of pixels visible for cards
|
||||
self.hand = []
|
||||
self.to_show = []
|
||||
self.state = None
|
||||
self.SetBackgroundColour(wx.GREEN)
|
||||
self.Bind(wx.EVT_SIZE, self.onResize)
|
||||
self.Bind(wx.EVT_PAINT, self.onPaint)
|
||||
self.Bind(wx.EVT_MOTION, self.onMouseMove)
|
||||
self.Bind(wx.EVT_LEFT_UP, self.onMouseClick)
|
||||
self.parent.host.bridge.tarotGameReady(player_nick, referee, profile_key = self.parent.host.profile)
|
||||
|
||||
def loadCards(self, dir):
|
||||
"""Load all the cards in memory
|
||||
@param dir: directory where the PNG files are"""
|
||||
QuickCardGame.loadCards(self)
|
||||
for file in glob.glob(dir+'/*_*.png'):
|
||||
card = WxCard(file)
|
||||
self.cards[card.suit, card.value]=card
|
||||
self.deck.append(card)
|
||||
|
||||
def newGame(self, hand):
|
||||
"""Start a new game, with given hand"""
|
||||
QuickCardGame.newGame(self, hand)
|
||||
self._recalc_ori()
|
||||
self.Refresh()
|
||||
|
||||
def contratSelected(self, data):
|
||||
"""Called when the contrat has been choosed
|
||||
@param data: form result"""
|
||||
debug (_("Contrat choosed"))
|
||||
contrat = data[0][1]
|
||||
QuickCardGame.contratSelected(self, contrat)
|
||||
|
||||
def chooseContrat(self, xml_data):
|
||||
"""Called when the player as to select his contrat
|
||||
@param xml_data: SàT xml representation of the form"""
|
||||
misc = {'callback': self.contratSelected}
|
||||
form = XMLUI(self.parent.host, xml_data, title = _('Please choose your contrat'), options = ['NO_CANCEL'], misc = misc)
|
||||
|
||||
def showScores(self, xml_data, winners, loosers):
|
||||
"""Called when the player as to select hist contrat
|
||||
@param xml_data: SàT xml representation of the form"""
|
||||
form = XMLUI(self.parent.host, xml_data, title = _('You win \o/') if self.player_nick in winners else _('You loose :('), options = ['NO_CANCEL'])
|
||||
|
||||
def cardsPlayed(self, player, cards):
|
||||
"""A card has been played by player"""
|
||||
QuickCardGame.cardsPlayed(self, player, cards)
|
||||
self.Refresh()
|
||||
|
||||
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._recalc_ori()
|
||||
self.Refresh()
|
||||
if self._autoplay==None: #No dialog if there is autoplay
|
||||
wx.MessageDialog(self, _("Cards played are invalid !"), _("Error"), wx.OK | wx.ICON_ERROR).ShowModal()
|
||||
|
||||
def _is_on_hand(self, pos_x, pos_y):
|
||||
"""Return True if the coordinate are on the hand cards"""
|
||||
if pos_x > self.orig_x and pos_y > self.orig_y \
|
||||
and pos_x < self.orig_x + (len(self.hand)+1) * self.visible_size \
|
||||
and pos_y < self.end_y:
|
||||
return True
|
||||
return False
|
||||
|
||||
def onResize(self, event):
|
||||
self._recalc_ori()
|
||||
|
||||
def _recalc_ori(self):
|
||||
"""Recalculate origins of hand, must be call when hand size change"""
|
||||
self.orig_x = (self.GetSizeTuple()[0]-(len(self.hand)+1)*self.visible_size)/2 #where we start to draw cards
|
||||
self.orig_y = self.GetSizeTuple()[1] - CARD_HEIGHT - 20
|
||||
self.end_y = self.orig_y + CARD_HEIGHT
|
||||
|
||||
def onPaint(self, event):
|
||||
dc = wx.PaintDC(self)
|
||||
|
||||
#We print the names to know who play where TODO: print avatars when available
|
||||
max_x, max_y = self.GetSize()
|
||||
border = 10 #border between nick and end of panel
|
||||
right_y = left_y = 200
|
||||
right_width, right_height = dc.GetTextExtent(self.right_nick)
|
||||
right_x = max_x - right_width - border
|
||||
left_x = border
|
||||
top_width, top_height = dc.GetTextExtent(self.top_nick)
|
||||
top_x = (max_x - top_width) / 2
|
||||
top_y = border
|
||||
dc.DrawText(self.right_nick, right_x, right_y)
|
||||
dc.DrawText(self.top_nick, top_x, top_y)
|
||||
dc.DrawText(self.left_nick, left_x, left_y)
|
||||
|
||||
#We draw the played cards:
|
||||
center_y = 200 #ordinate used as center point
|
||||
left_x = (max_x - CARD_WIDTH)/2 - CARD_WIDTH - 5
|
||||
right_x = (max_x/2) + (CARD_WIDTH/2) + 5
|
||||
left_y = right_y = center_y - CARD_HEIGHT/2
|
||||
top_x = bottom_x = (max_x - CARD_WIDTH)/2
|
||||
top_y = center_y - CARD_HEIGHT - 5
|
||||
bottom_y = center_y + 5
|
||||
for side in ['left', 'top', 'right', 'bottom']:
|
||||
card = self.played[getattr(self, side+'_nick')]
|
||||
if card != None:
|
||||
card.draw(dc,locals()[side+'_x'], locals()[side+'_y'])
|
||||
|
||||
x=self.orig_x
|
||||
for card in self.hand:
|
||||
if (self.state == "play" or self.state == "ecart") and card == self.mouse_over_card \
|
||||
or self.state == "ecart" and card in self.selected:
|
||||
y = self.orig_y - 30
|
||||
else:
|
||||
y = self.orig_y
|
||||
|
||||
card.draw(dc,x,y)
|
||||
x+=self.visible_size
|
||||
|
||||
if self.to_show:
|
||||
"""There are cards to display in the middle"""
|
||||
size = len(self.to_show)*(CARD_WIDTH+10)-10
|
||||
x = (max_x - size)/2
|
||||
for card in self.to_show:
|
||||
card.draw(dc, x, 150)
|
||||
x+=CARD_WIDTH+10
|
||||
|
||||
def onMouseMove(self, event):
|
||||
pos_x,pos_y = event.GetPosition()
|
||||
if self._is_on_hand(pos_x, pos_y):
|
||||
try:
|
||||
self.mouse_over_card = self.hand[(pos_x-self.orig_x)/self.visible_size]
|
||||
except IndexError:
|
||||
self.mouse_over_card = self.hand[-1]
|
||||
self.Refresh()
|
||||
else:
|
||||
self.mouse_over_card = None
|
||||
self.Refresh()
|
||||
|
||||
def onMouseClick(self, event):
|
||||
print "mouse click:",event.GetPosition()
|
||||
pos_x,pos_y = event.GetPosition()
|
||||
|
||||
if self.state == "chien":
|
||||
self.to_show = []
|
||||
self.state = "wait"
|
||||
return
|
||||
elif self.state == "wait_for_ecart":
|
||||
self.state = "ecart"
|
||||
self.hand.extend(self.to_show)
|
||||
self.hand.sort()
|
||||
self.to_show = []
|
||||
self._recalc_ori()
|
||||
self.Refresh()
|
||||
return
|
||||
|
||||
if self._is_on_hand(pos_x, pos_y):
|
||||
idx = (pos_x-self.orig_x)/self.visible_size
|
||||
if idx == len(self.hand):
|
||||
idx-=1
|
||||
if self.hand[idx] == self.mouse_over_card:
|
||||
if self.state == "ecart":
|
||||
if self.hand[idx] in self.selected:
|
||||
self.selected.remove(self.hand[idx])
|
||||
else:
|
||||
self.selected.append(self.hand[idx])
|
||||
if len(self.selected) == 6: #TODO: use variable here, as chien len can change with variants
|
||||
dlg = wx.MessageDialog(self, _("Do you put these cards in chien ?"), _(u"Écart"), wx.YES_NO | wx.ICON_QUESTION)
|
||||
answer = dlg.ShowModal()
|
||||
if answer == wx.ID_YES:
|
||||
ecart = []
|
||||
for card in self.selected:
|
||||
ecart.append((card.suit, card.value))
|
||||
self.hand.remove(card)
|
||||
del self.selected[:]
|
||||
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, ecart, profile_key = self.parent.host.profile)
|
||||
self.state = "wait"
|
||||
|
||||
self._recalc_ori()
|
||||
self.Refresh()
|
||||
if self.state == "play":
|
||||
card = self.hand[idx]
|
||||
self.parent.host.bridge.tarotGamePlayCards(self.player_nick, self.referee, [(card.suit, card.value)], profile_key = self.parent.host.profile)
|
||||
del self.hand[idx]
|
||||
self.state = "wait"
|
||||
self._recalc_ori()
|
||||
self.Refresh()
|
||||
|
||||
|
||||
263
frontends/src/wix/chat.py
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
wix: a SAT frontend
|
||||
Copyright (C) 2009, 2010 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 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import wx
|
||||
import os.path
|
||||
import time
|
||||
import pdb
|
||||
from logging import debug, info, error, warning
|
||||
from tools.jid import JID
|
||||
from quick_frontend.quick_chat import QuickChat
|
||||
from contact_list import ContactList
|
||||
from card_game import CardPanel
|
||||
|
||||
|
||||
idSEND = 1
|
||||
idTAROT = 2
|
||||
|
||||
class Chat(wx.Frame, QuickChat):
|
||||
"""The chat Window for one to one conversations"""
|
||||
|
||||
def __init__(self, target, host, type='one2one'):
|
||||
wx.Frame.__init__(self, None, title=target, pos=(0,0), size=(400,200))
|
||||
QuickChat.__init__(self, target, host, type)
|
||||
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.SetSizer(self.sizer)
|
||||
|
||||
self.splitter = wx.SplitterWindow(self, -1)
|
||||
self.sizer.Add(self.splitter, 1, flag = wx.EXPAND)
|
||||
|
||||
self.conv_panel = wx.Panel(self.splitter)
|
||||
self.conv_panel.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.subjectBox = wx.TextCtrl(self.conv_panel, -1, style = wx.TE_READONLY)
|
||||
self.chatWindow = wx.TextCtrl(self.conv_panel, -1, style = wx.TE_MULTILINE | wx.TE_RICH | wx.TE_READONLY)
|
||||
self.textBox = wx.TextCtrl(self.conv_panel, -1, style = wx.TE_PROCESS_ENTER)
|
||||
self.conv_panel.sizer.Add(self.subjectBox, flag=wx.EXPAND)
|
||||
self.conv_panel.sizer.Add(self.chatWindow, 1, flag=wx.EXPAND)
|
||||
self.conv_panel.sizer.Add(self.textBox, 0, flag=wx.EXPAND)
|
||||
self.conv_panel.SetSizer(self.conv_panel.sizer)
|
||||
self.splitter.Initialize(self.conv_panel)
|
||||
self.SetMenuBar(wx.MenuBar())
|
||||
|
||||
#events
|
||||
self.Bind(wx.EVT_CLOSE, self.onClose, self)
|
||||
self.Bind(wx.EVT_TEXT_ENTER, self.onEnterPressed, self.textBox)
|
||||
|
||||
#fonts
|
||||
self.font={}
|
||||
self.font["points"] = self.chatWindow.GetFont().GetPointSize()
|
||||
self.font["family"] = self.chatWindow.GetFont().GetFamily()
|
||||
|
||||
|
||||
#misc
|
||||
self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00 %Y")) #struct_time of day changing time
|
||||
self.setType(self.type)
|
||||
self.textBox.SetFocus()
|
||||
self.Hide() #We hide because of the show toggle
|
||||
|
||||
def __createPresents(self):
|
||||
"""Create a list of present people in a group chat"""
|
||||
self.present_panel = wx.Panel(self.splitter)
|
||||
self.present_panel.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.present_panel.SetBackgroundColour(wx.BLUE)
|
||||
self.present_panel.presents = ContactList(self.present_panel, self.host, type='nicks')
|
||||
self.present_panel.presents.SetMinSize(wx.Size(80,20))
|
||||
self.present_panel.sizer.Add(self.present_panel.presents, 1, wx.EXPAND)
|
||||
self.present_panel.SetSizer(self.present_panel.sizer)
|
||||
self.splitter.SplitVertically(self.present_panel, self.conv_panel, 80)
|
||||
|
||||
def setType(self, type):
|
||||
QuickChat.setType(self, type)
|
||||
if type is 'group' and not self.splitter.IsSplit():
|
||||
self.__createPresents()
|
||||
self.subjectBox.Show()
|
||||
self.__eraseMenus()
|
||||
self.__createMenus_group()
|
||||
self.sizer.Layout()
|
||||
elif type is 'one2one' and self.splitter.IsSplit():
|
||||
self.splitter.Unsplit(self.present_panel)
|
||||
del self.present_panel
|
||||
self.GetMenuBar().Show()
|
||||
self.subjectBox.Hide()
|
||||
self.__eraseMenus()
|
||||
self.__createMenus_O2O()
|
||||
self.nick = None
|
||||
else:
|
||||
self.subjectBox.Hide()
|
||||
self.__eraseMenus()
|
||||
self.__createMenus_O2O()
|
||||
self.historyPrint(profile=self.host.profile)
|
||||
|
||||
def startGame(self, game_type, referee, players):
|
||||
"""Configure the chat window to start a game"""
|
||||
if game_type=="Tarot":
|
||||
debug (_("configure chat window for Tarot game"))
|
||||
self.tarot_panel = CardPanel(self, referee, players, self.nick)
|
||||
self.sizer.Prepend(self.tarot_panel, 0, flag=wx.EXPAND)
|
||||
self.sizer.Layout()
|
||||
self.Fit()
|
||||
|
||||
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_panel
|
||||
|
||||
|
||||
def setPresents(self, nicks):
|
||||
"""Set the users presents in the contact list for a group chat
|
||||
@param nicks: list of nicknames
|
||||
"""
|
||||
QuickChat.setPresents(self, nicks)
|
||||
for nick in nicks:
|
||||
self.present_panel.presents.replace(nick)
|
||||
|
||||
def replaceUser(self, nick):
|
||||
"""Add user if it is not in the group list"""
|
||||
debug (_("Replacing user %s") % nick)
|
||||
if self.type != "group":
|
||||
error (_("[INTERNAL] trying to replace user for a non group chat window"))
|
||||
return
|
||||
QuickChat.replaceUser(self, nick)
|
||||
self.present_panel.presents.replace(nick)
|
||||
|
||||
def removeUser(self, nick):
|
||||
"""Remove a user from the group list"""
|
||||
QuickChat.removeUser(self, nick)
|
||||
self.present_panel.presents.remove(nick)
|
||||
|
||||
def setSubject(self, subject):
|
||||
"""Set title for a group chat"""
|
||||
QuickChat.setSubject(self, subject)
|
||||
self.subjectBox.SetValue(subject)
|
||||
|
||||
def __eraseMenus(self):
|
||||
"""erase all menus"""
|
||||
menuBar = self.GetMenuBar()
|
||||
for i in range(menuBar.GetMenuCount()):
|
||||
menuBar.Remove(i)
|
||||
|
||||
def __createMenus_O2O(self):
|
||||
"""create menu bar for one 2 one chat"""
|
||||
info("Creating menus")
|
||||
self.__eraseMenus()
|
||||
menuBar = self.GetMenuBar()
|
||||
actionMenu = wx.Menu()
|
||||
actionMenu.Append(idSEND, _("&SendFile CTRL-s"),_(" Send a file to contact"))
|
||||
menuBar.Append(actionMenu,_("&Action"))
|
||||
|
||||
#events
|
||||
wx.EVT_MENU(self, idSEND, self.onSendFile)
|
||||
|
||||
def __createMenus_group(self):
|
||||
"""create menu bar for group chat"""
|
||||
info("Creating menus")
|
||||
self.__eraseMenus()
|
||||
menuBar = self.GetMenuBar()
|
||||
actionMenu = wx.Menu()
|
||||
actionMenu.Append(idTAROT, _("Start &Tarot game CTRL-t"),_(" Start a Tarot card game")) #tmp
|
||||
menuBar.Append(actionMenu,_("&Games"))
|
||||
|
||||
#events
|
||||
wx.EVT_MENU(self, idTAROT, self.onStartTarot)
|
||||
|
||||
def __del__(self):
|
||||
wx.Frame.__del__(self)
|
||||
|
||||
def onClose(self, event):
|
||||
"""Close event: we only hide the frame."""
|
||||
event.Veto()
|
||||
self.Hide()
|
||||
|
||||
def onEnterPressed(self, event):
|
||||
"""Behaviour when enter pressed in send line."""
|
||||
self.host.bridge.sendMessage(self.target.short if self.type=='group' else self.target,
|
||||
event.GetString(),
|
||||
type = "groupchat" if self.type=='group' else "chat",
|
||||
profile_key=self.host.profile)
|
||||
self.textBox.Clear()
|
||||
|
||||
def __blink(self):
|
||||
"""Do wizzz and buzzz to show window to user or
|
||||
at least inform him of something new"""
|
||||
#TODO: use notification system
|
||||
if not self.IsActive():
|
||||
self.RequestUserAttention()
|
||||
if not self.IsShown():
|
||||
self.Show()
|
||||
|
||||
def printMessage(self, from_jid, msg, profile, timestamp=""):
|
||||
"""Print the message with differents colors depending on where it comes from."""
|
||||
try:
|
||||
jid,nick,mymess = QuickChat.printMessage(self, from_jid, msg, profile, timestamp)
|
||||
except TypeError:
|
||||
return
|
||||
print "printMessage, jid=",jid,"type=",self.type
|
||||
_font_bold = wx.Font(self.font["points"], self.font["family"], wx.NORMAL, wx.BOLD)
|
||||
_font_normal = wx.Font(self.font["points"], self.font["family"], wx.NORMAL, wx.NORMAL)
|
||||
_font_italic = wx.Font(self.font["points"], self.font["family"], wx.ITALIC if mymess else wx.NORMAL, wx.NORMAL)
|
||||
self.chatWindow.SetDefaultStyle(wx.TextAttr("GREY", font=_font_normal))
|
||||
msg_time = time.localtime(timestamp or None)
|
||||
time_format = "%c" if msg_time < self.day_change else "%H:%M" #if the message was sent before today, we print the full date
|
||||
self.chatWindow.AppendText("[%s]" % time.strftime(time_format, msg_time ))
|
||||
self.chatWindow.SetDefaultStyle(wx.TextAttr( "BLACK" if mymess else "BLUE", font=_font_bold))
|
||||
self.chatWindow.AppendText("[%s] " % nick)
|
||||
self.chatWindow.SetDefaultStyle(wx.TextAttr("BLACK", font=_font_italic))
|
||||
self.chatWindow.AppendText("%s\n" % msg)
|
||||
if not mymess:
|
||||
self.__blink()
|
||||
|
||||
def printInfo(self, msg, type='normal'):
|
||||
"""Print general info
|
||||
@param msg: message to print
|
||||
@type: one of:
|
||||
normal: general info like "toto has joined the room"
|
||||
me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
|
||||
"""
|
||||
_font_bold = wx.Font(self.font["points"], self.font["family"], wx.NORMAL, wx.BOLD)
|
||||
_font_normal = wx.Font(self.font["points"], self.font["family"], wx.NORMAL, wx.NORMAL)
|
||||
self.chatWindow.SetDefaultStyle(wx.TextAttr("BLACK", font=_font_bold if type == 'normal' else _font_normal))
|
||||
self.chatWindow.AppendText("%s\n" % msg)
|
||||
if type=="me":
|
||||
self.__blink()
|
||||
|
||||
### events ###
|
||||
|
||||
def onSendFile(self, e):
|
||||
debug(_("Send File"))
|
||||
filename = wx.FileSelector(_("Choose a file to send"), flags = wx.FD_FILE_MUST_EXIST)
|
||||
if filename:
|
||||
debug(_("filename: %s"),filename)
|
||||
full_jid = self.host.CM.get_full(self.target)
|
||||
id = self.host.bridge.sendFile(full_jid, filename)
|
||||
self.host.waitProgress(id, _("File Transfer"), _("Copying %s") % os.path.basename(filename))
|
||||
|
||||
def onStartTarot(self, e):
|
||||
debug (_("Starting Tarot game"))
|
||||
warning (_("FIXME: temporary menu, must be changed"))
|
||||
if len(self.occupants) != 4:
|
||||
err_dlg = wx.MessageDialog(self, _("You need to be exactly 4 peoples in the room to start a Tarot game"), _("Can't start game"), style = wx.OK | wx.ICON_ERROR) #FIXME: gof: temporary only, need to choose the people with who the game has to be started
|
||||
err_dlg.ShowModal()
|
||||
else:
|
||||
self.host.bridge.tarotGameCreate(self.id, list(self.occupants), self.host.profile)
|
||||
15
frontends/src/wix/constants.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import sys
|
||||
import __builtin__
|
||||
|
||||
__builtin__.__dict__['APP_NAME'] = "Wix"
|
||||
__builtin__.__dict__['LICENCE_PATH'] = "../../COPYING"
|
||||
__builtin__.__dict__['IMAGE_DIR'] = sys.path[0]+'/images'
|
||||
|
||||
__builtin__.__dict__['msgOFFLINE'] = _("offline")
|
||||
__builtin__.__dict__['msgONLINE'] = _("online")
|
||||
__builtin__.__dict__['const_DEFAULT_GROUP'] = "Unclassed"
|
||||
__builtin__.__dict__['const_STATUS'] = [("", _("Online"), None),
|
||||
("chat", _("Free for chat"), "green"),
|
||||
("away", _("AFK"), "brown"),
|
||||
("dnd", _("DND"), "red"),
|
||||
("xa", _("Away"), "red")]
|
||||
182
frontends/src/wix/contact_list.py
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
import wx
|
||||
from quick_frontend.quick_contact_list import QuickContactList
|
||||
from logging import debug, info, error
|
||||
from cgi import escape
|
||||
from tools.jid import JID
|
||||
|
||||
|
||||
class Group(unicode):
|
||||
"""Class used to recognize groups"""
|
||||
|
||||
class Contact(unicode):
|
||||
"""Class used to recognize groups"""
|
||||
|
||||
class ContactList(wx.SimpleHtmlListBox, QuickContactList):
|
||||
"""Customized control to manage contacts."""
|
||||
|
||||
def __init__(self, parent, host, type="JID"):
|
||||
"""init the contact list
|
||||
@param parent: WxWidgets parent of the widget
|
||||
@param host: wix main app class
|
||||
@param type: type of contact list: "JID" for the usual big jid contact list
|
||||
"CUSTOM" for a customized contact list (self.__presentItem must then be overrided)
|
||||
"""
|
||||
wx.SimpleHtmlListBox.__init__(self, parent, -1)
|
||||
QuickContactList.__init__(self, host.CM)
|
||||
self.host = host
|
||||
self.type = type
|
||||
self.__typeSwitch()
|
||||
self.groups = {} #list contacts in each groups, key = group
|
||||
self.Bind(wx.EVT_LISTBOX, self.onSelected)
|
||||
self.Bind(wx.EVT_LISTBOX_DCLICK, self.onActivated)
|
||||
|
||||
def __contains__(self, jid):
|
||||
return bool(self.__find_idx(jid))
|
||||
|
||||
def __typeSwitch(self):
|
||||
if self.type == "JID":
|
||||
self.__presentItem = self.__presentItemJID
|
||||
elif type != "CUSTOM":
|
||||
self.__presentItem = self.__presentItemDefault
|
||||
|
||||
def __find_idx(self, entity):
|
||||
"""Find indexes of given contact (or groups) in contact list, manage jid
|
||||
@return: list of indexes"""
|
||||
result=[]
|
||||
for i in range(self.GetCount()):
|
||||
if (type(entity) == JID and type(self.GetClientData(i)) == JID and self.GetClientData(i).short == entity.short) or\
|
||||
self.GetClientData(i) == entity:
|
||||
result.append(i)
|
||||
return result
|
||||
|
||||
def replace(self, contact, groups=None):
|
||||
debug(_("update %s") % contact)
|
||||
if not self.__find_idx(contact):
|
||||
self.add(contact, groups)
|
||||
else:
|
||||
for i in self.__find_idx(contact):
|
||||
self.SetString(i, self.__presentItem(contact))
|
||||
|
||||
def disconnect(self, contact):
|
||||
self.remove(contact) #for now, we only show online contacts
|
||||
|
||||
def __eraseGroup(self, group):
|
||||
"""Erase all contacts in group
|
||||
@param group: group to erase
|
||||
@return: True if something as been erased"""
|
||||
erased = False
|
||||
indexes = self.__find_idx(group)
|
||||
for idx in indexes:
|
||||
while idx<self.GetCount()-1 and type(self.GetClientData(idx+1)) != Group:
|
||||
erased = True
|
||||
self.Delete(idx+1)
|
||||
return erased
|
||||
|
||||
|
||||
def __presentGroup(self, group):
|
||||
"""Make a nice presentation for the contact groups"""
|
||||
html = u"""-- [%s] --""" % group
|
||||
|
||||
return html
|
||||
|
||||
def __presentItemDefault(self, contact):
|
||||
"""Make a basic presentation of string contacts in the list."""
|
||||
return contact
|
||||
|
||||
def __presentItemJID(self, jid):
|
||||
"""Make a nice presentation of the contact in the list for JID contacts."""
|
||||
name = self.CM.getAttr(jid,'name')
|
||||
nick = self.CM.getAttr(jid,'nick')
|
||||
show = filter(lambda x:x[0]==self.CM.getAttr(jid,'show'), const_STATUS)[0]
|
||||
#show[0]==shortcut
|
||||
#show[1]==human readable
|
||||
#show[2]==color (or None)
|
||||
show_html = "<font color='%s'>[%s]</font>" % (show[2], show[1]) if show[2] else ""
|
||||
status = self.CM.getAttr(jid,'status') or ''
|
||||
avatar = self.CM.getAttr(jid,'avatar') or IMAGE_DIR+'/empty_avatar.png'
|
||||
|
||||
html = """
|
||||
<table border='0'>
|
||||
<td>
|
||||
<img height='64' width='64' src='%s' />
|
||||
</td>
|
||||
<td>
|
||||
<b>%s</b> %s<br />
|
||||
<i>%s</i>
|
||||
</td>
|
||||
</table>
|
||||
""" % (avatar,
|
||||
escape(nick or name or jid.node or jid.short),
|
||||
show_html,
|
||||
escape(status))
|
||||
|
||||
return html
|
||||
|
||||
def clear_contacts(self):
|
||||
"""Clear all the contact list"""
|
||||
self.Clear()
|
||||
|
||||
def add(self, contact, groups = None):
|
||||
"""add a contact to the list"""
|
||||
debug (_("adding %s"),contact)
|
||||
if not groups:
|
||||
idx = self.Insert(self.__presentItem(contact), 0, contact)
|
||||
else:
|
||||
for group in groups:
|
||||
indexes = self.__find_idx(group)
|
||||
gp_idx = 0
|
||||
if not indexes: #this is a new group, we have to create it
|
||||
gp_idx = self.Append(self.__presentGroup(group), Group(group))
|
||||
else:
|
||||
gp_idx = indexes[0]
|
||||
|
||||
self.Insert(self.__presentItem(contact), gp_idx+1, contact)
|
||||
|
||||
|
||||
|
||||
def remove(self, contact):
|
||||
"""remove a contact from the list"""
|
||||
debug (_("removing %s"), contact)
|
||||
list_idx = self.__find_idx(contact)
|
||||
list_idx.reverse() #as we make some deletions, we have to reverse the order
|
||||
for i in list_idx:
|
||||
self.Delete(i)
|
||||
|
||||
def onSelected(self, event):
|
||||
"""Called when a contact is selected."""
|
||||
data = self.getSelection()
|
||||
if data == None: #we have a group
|
||||
first_visible = self.GetVisibleBegin()
|
||||
group = self.GetClientData(self.GetSelection())
|
||||
erased = self.__eraseGroup(group)
|
||||
if not erased: #the group was already erased, we can add again the contacts
|
||||
contacts = self.CM.getContFromGroup(group)
|
||||
contacts.sort()
|
||||
id_insert = self.GetSelection()+1
|
||||
for contact in contacts:
|
||||
self.Insert(self.__presentItem(contact), id_insert, contact)
|
||||
self.SetSelection(wx.NOT_FOUND)
|
||||
self.ScrollToLine(first_visible)
|
||||
event.Skip(False)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
def onActivated(self, event):
|
||||
"""Called when a contact is clicked or activated with keyboard."""
|
||||
data = self.getSelection()
|
||||
self.onActivatedCB(data)
|
||||
event.Skip()
|
||||
|
||||
def getSelection(self):
|
||||
"""Return the selected contact, or an empty string if there is not"""
|
||||
if self.GetSelection() == wx.NOT_FOUND:
|
||||
return None
|
||||
data = self.GetClientData(self.GetSelection())
|
||||
if type(data) == Group:
|
||||
return None
|
||||
return data
|
||||
|
||||
def registerActivatedCB(self, cb):
|
||||
"""Register a callback with manage contact activation."""
|
||||
self.onActivatedCB=cb
|
||||
|
||||
169
frontends/src/wix/gateways.py
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
wix: a SAT frontend
|
||||
Copyright (C) 2009, 2010 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 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import wx
|
||||
import pdb
|
||||
from xml.dom import minidom
|
||||
from logging import debug, info, error
|
||||
from tools.jid import JID
|
||||
from quick_frontend.quick_gateways import QuickGatewaysManager
|
||||
|
||||
class GatewaysManager(wx.Frame,QuickGatewaysManager):
|
||||
|
||||
def __init__(self, host, gateways, title=_("Gateways manager"), server=None):
|
||||
wx.Frame.__init__(self, None, title=title)
|
||||
QuickGatewaysManager.__init__(self, host, gateways, server)
|
||||
|
||||
if server:
|
||||
self.SetTitle(title+" (%s)" % server)
|
||||
|
||||
#Fonts
|
||||
self.normal_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
|
||||
self.bold_font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.BOLD)
|
||||
self.italic_font = wx.Font(8, wx.DEFAULT, wx.FONTSTYLE_ITALIC, wx.NORMAL)
|
||||
self.button_font = wx.Font(6, wx.DEFAULT, wx.NORMAL, wx.BOLD)
|
||||
|
||||
|
||||
self.modified = {} # dict of modified data (i.e. what we have to save)
|
||||
self.ctl_list = {} # usefull to access ctrl, key = (name, category)
|
||||
|
||||
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
warning = wx.TextCtrl(self, -1, value=self.WARNING_MSG, style = wx.TE_MULTILINE |
|
||||
wx.TE_READONLY |
|
||||
wx.TE_LEFT)
|
||||
warning.SetFont(self.bold_font)
|
||||
self.sizer.Add(warning, 0, wx.EXPAND)
|
||||
warning.ShowPosition(0)
|
||||
self.panel = wx.Panel(self)
|
||||
self.panel.sizer = wx.FlexGridSizer(cols=5)
|
||||
self.panel.SetSizer(self.panel.sizer)
|
||||
self.panel.SetAutoLayout(True)
|
||||
self.sizer.Add(self.panel, 1, flag=wx.EXPAND)
|
||||
self.SetSizer(self.sizer)
|
||||
self.SetAutoLayout(True)
|
||||
|
||||
#events
|
||||
self.Bind(wx.EVT_CLOSE, self.onClose, self)
|
||||
|
||||
self.MakeModal()
|
||||
self.panel.sizer.Add(wx.Window(self.panel, -1))
|
||||
title_name = wx.StaticText(self.panel, -1, "Name")
|
||||
title_name.SetFont(self.bold_font)
|
||||
title_type = wx.StaticText(self.panel, -1, "Type")
|
||||
title_type.SetFont(self.bold_font)
|
||||
self.panel.sizer.Add(title_name)
|
||||
self.panel.sizer.Add(title_type)
|
||||
self.panel.sizer.Add(wx.Window(self.panel, -1))
|
||||
self.panel.sizer.Add(wx.Window(self.panel, -1))
|
||||
|
||||
for gateway in gateways:
|
||||
self.addGateway(gateway, gateways[gateway])
|
||||
|
||||
self.ext_server_panel = wx.Panel(self)
|
||||
self.ext_server_panel.sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.ext_server_panel.SetSizer(self.ext_server_panel.sizer)
|
||||
self.ext_server_panel.SetAutoLayout(True)
|
||||
self.sizer.Add(self.ext_server_panel, 0, flag=wx.EXPAND)
|
||||
|
||||
ext_server_label = wx.StaticText(self.ext_server_panel, -1, _("Use external XMPP server: "))
|
||||
ext_server_label.SetFont(wx.ITALIC_FONT)
|
||||
self.ext_server_text = wx.TextCtrl(self.ext_server_panel, -1)
|
||||
ext_server_button = wx.Button(self.ext_server_panel, -1, _("GO !"))
|
||||
self.ext_server_panel.Bind(wx.EVT_BUTTON, self.browseExternalServer, ext_server_button)
|
||||
|
||||
self.ext_server_panel.sizer.Add(ext_server_label)
|
||||
self.ext_server_panel.sizer.Add(self.ext_server_text, 1, flag=wx.EXPAND)
|
||||
self.ext_server_panel.sizer.Add(ext_server_button)
|
||||
|
||||
#self.panel.sizer.Fit(self)
|
||||
self.sizer.Fit(self)
|
||||
|
||||
self.Show()
|
||||
|
||||
def browseExternalServer(self, event):
|
||||
"""Open the gateway manager on given server"""
|
||||
server = self.ext_server_text.GetValue()
|
||||
debug(_("Opening gateways manager on [%s]") % server)
|
||||
id = self.host.bridge.findGateways(server, self.host.profile)
|
||||
self.host.current_action_ids.add(id)
|
||||
self.host.current_action_ids_cb[id] = self.host.onGatewaysFound
|
||||
self.MakeModal(False)
|
||||
self.Destroy()
|
||||
|
||||
|
||||
def addGateway(self, gateway, param):
|
||||
|
||||
#First The icon
|
||||
isz = (16,16)
|
||||
im_icon = wx.StaticBitmap(self.panel, -1, wx.ArtProvider.GetBitmap(wx.ART_GO_FORWARD, wx.ART_TOOLBAR, isz))
|
||||
|
||||
#Then the name
|
||||
|
||||
label=wx.StaticText(self.panel, -1, param['name'])
|
||||
label.SetFont(self.normal_font)
|
||||
|
||||
#Then the transport type message
|
||||
type_label_txt = self.getGatewayDesc(param['type'])
|
||||
|
||||
type_label = wx.StaticText(self.panel, -1, type_label_txt)
|
||||
type_label.SetFont(self.italic_font)
|
||||
|
||||
#The buttons
|
||||
def register_cb(event):
|
||||
"""Called when register button is clicked"""
|
||||
gateway_jid = event.GetEventObject().gateway_jid
|
||||
id = self.host.bridge.in_band_register(gateway_jid, self.host.profile)
|
||||
self.host.current_action_ids.add(id)
|
||||
self.MakeModal(False)
|
||||
self.Destroy()
|
||||
|
||||
def unregister_cb(event):
|
||||
"""Called when unregister button is clicked"""
|
||||
gateway_jid = event.GetEventObject().gateway_jid
|
||||
id = self.host.bridge.gatewayRegister("CANCEL",gateway_jid, None, self.host.profile)
|
||||
self.host.current_action_ids.add(id)
|
||||
self.MakeModal(False)
|
||||
self.Destroy()
|
||||
|
||||
reg_button = wx.Button(self.panel, -1, _("Register"), size=wx.Size(-1, 8))
|
||||
reg_button.SetFont(self.button_font)
|
||||
reg_button.gateway_jid = JID(gateway)
|
||||
self.panel.Bind(wx.EVT_BUTTON, register_cb, reg_button)
|
||||
unreg_button = wx.Button(self.panel, -1, _("Unregister"), size=wx.Size(-1, 8))
|
||||
unreg_button.SetFont(self.button_font)
|
||||
unreg_button.gateway_jid = JID(gateway)
|
||||
self.panel.Bind(wx.EVT_BUTTON, unregister_cb, unreg_button)
|
||||
|
||||
self.panel.sizer.Add(im_icon)
|
||||
self.panel.sizer.Add(label)
|
||||
self.panel.sizer.Add(type_label)
|
||||
self.panel.sizer.Add(reg_button, 1, wx.EXPAND)
|
||||
self.panel.sizer.Add(unreg_button, 1, wx.EXPAND)
|
||||
|
||||
|
||||
def onClose(self, event):
|
||||
"""Close event"""
|
||||
debug(_("close"))
|
||||
self.MakeModal(False)
|
||||
event.Skip()
|
||||
|
||||
1
frontends/src/wix/images/cards/.size
Normal file
|
|
@ -0,0 +1 @@
|
|||
Biggest size: 74X136
|
||||
1
frontends/src/wix/images/cards/COPYING
Normal file
|
|
@ -0,0 +1 @@
|
|||
The work in this directory is licensed under the Creative Commons BY-SA License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/; or, (b) send a letter to Creative Commons, 171 2nd Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
3
frontends/src/wix/images/cards/README
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
The Tarot cards pictures come from the deck found on wikicommons (the script used to cut them is available at frontend/wix/images/split_card.sh), and available at http://upload.wikimedia.org/wikipedia/commons/8/8d/Tarotcards.jpg under Creative Commons BY-SA. The authors shown on the site are Piast and Svick. The splitted cards are, according to the license, available under the same conditions.
|
||||
Notice that the Creative Common BY-SA apply only to this directory
|
||||
|
||||
BIN
frontends/src/wix/images/cards/atout_1.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
frontends/src/wix/images/cards/atout_10.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_11.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_12.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
frontends/src/wix/images/cards/atout_13.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_14.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_15.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_16.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
frontends/src/wix/images/cards/atout_17.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
frontends/src/wix/images/cards/atout_18.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
frontends/src/wix/images/cards/atout_19.png
Normal file
|
After Width: | Height: | Size: 26 KiB |