comparison frontends/src/primitivus/chat.py @ 1290:faa1129559b8 frontends_multi_profiles

core, frontends: refactoring to base Libervia on QuickFrontend (big mixed commit): /!\ not finished, everything is still instable ! - bridge: DBus bridge has been modified to allow blocking call to be called in the same way as asynchronous calls - bridge: calls with a callback and no errback are now possible, default errback log the error - constants: removed hack to manage presence without OrderedDict, as an OrderedDict like class has been implemented in Libervia - core: getLastResource has been removed and replaced by getMainResource (there is a global better management of resources) - various style improvments: use of constants when possible, fixed variable overlaps, import of module instead of direct class import - frontends: printInfo and printMessage methods in (Quick)Chat are more generic (use of extra instead of timestamp) - frontends: bridge creation and option parsing (command line arguments) are now specified by the frontend in QuickApp __init__ - frontends: ProfileManager manage a more complete plug sequence (some stuff formerly manage in contact_list have moved to ProfileManager) - quick_frontend (quick_widgets): QuickWidgetsManager is now iterable (all widgets are then returned), or can return an iterator on a specific class (return all widgets of this class) with getWidgets - frontends: tools.jid can now be used in Pyjamas, with some care - frontends (XMLUI): profile is now managed - core (memory): big improvment on entities cache management (and specially resource management) - core (params/exceptions): added PermissionError - various fixes and improvments, check diff for more details
author Goffi <goffi@goffi.org>
date Sat, 24 Jan 2015 01:00:29 +0100
parents e3a9ea76de35
children 8ea8fa13c351
comparison
equal deleted inserted replaced
1289:653f2e2eea31 1290:faa1129559b8
28 from sat_frontends.primitivus.card_game import CardGame 28 from sat_frontends.primitivus.card_game import CardGame
29 from sat_frontends.primitivus.constants import Const as C 29 from sat_frontends.primitivus.constants import Const as C
30 from sat_frontends.primitivus.keys import action_key_map as a_key 30 from sat_frontends.primitivus.keys import action_key_map as a_key
31 from sat_frontends.primitivus.widget import PrimitivusWidget 31 from sat_frontends.primitivus.widget import PrimitivusWidget
32 import time 32 import time
33 from sat_frontends.tools.jid import JID 33 from sat_frontends.tools import jid
34 34
35 35
36 class ChatText(urwid.FlowWidget): 36 class ChatText(urwid.FlowWidget):
37 """Manage the printing of chat message""" 37 """Manage the printing of chat message"""
38 38
81 return txt_widget 81 return txt_widget
82 82
83 83
84 class Chat(PrimitivusWidget, QuickChat): 84 class Chat(PrimitivusWidget, QuickChat):
85 85
86 def __init__(self, host, target, type_='one2one', profiles=None): 86 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None):
87 QuickChat.__init__(self, host, target, type_, profiles=profiles) 87 QuickChat.__init__(self, host, target, type_, profiles=profiles)
88 self.content = urwid.SimpleListWalker([]) 88 self.content = urwid.SimpleListWalker([])
89 self.text_list = urwid.ListBox(self.content) 89 self.text_list = urwid.ListBox(self.content)
90 self.chat_widget = urwid.Frame(self.text_list) 90 self.chat_widget = urwid.Frame(self.text_list)
91 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)]) 91 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
92 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)]) 92 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
93 self.pile = urwid.Pile([self.chat_colums]) 93 self.pile = urwid.Pile([self.chat_colums])
94 PrimitivusWidget.__init__(self, self.pile, self.target) 94 PrimitivusWidget.__init__(self, self.pile, self.target)
95 95
96 # we must adapt the behavious with the type 96 # we must adapt the behaviour with the type
97 if type_ == 'one2one': 97 if type_ == C.CHAT_ONE2ONE:
98 self.historyPrint(profile=self.profile) 98 self.historyPrint(profile=self.profile)
99 elif type_ == 'group': 99 elif type_ == C.CHAT_GROUP:
100 if len(self.chat_colums.contents) == 1: 100 if len(self.chat_colums.contents) == 1:
101 present_widget = self._buildPresentList() 101 present_widget = self._buildPresentList()
102 self.present_panel = sat_widgets.VerticalSeparator(present_widget) 102 self.present_panel = sat_widgets.VerticalSeparator(present_widget)
103 self._appendPresentPanel() 103 self._appendPresentPanel()
104 104
108 self.show_title = 1 #0: clip title; 1: full title; 2: no title 108 self.show_title = 1 #0: clip title; 1: full title; 2: no title
109 self.subject = None 109 self.subject = None
110 110
111 def keypress(self, size, key): 111 def keypress(self, size, key):
112 if key == a_key['OCCUPANTS_HIDE']: #user wants to (un)hide the presents panel 112 if key == a_key['OCCUPANTS_HIDE']: #user wants to (un)hide the presents panel
113 if self.type == 'group': 113 if self.type == C.CHAT_GROUP:
114 widgets = [widget for (widget, options) in self.chat_colums.contents] 114 widgets = [widget for (widget, options) in self.chat_colums.contents]
115 if self.present_panel in widgets: 115 if self.present_panel in widgets:
116 self._removePresentPanel() 116 self._removePresentPanel()
117 else: 117 else:
118 self._appendPresentPanel() 118 self._appendPresentPanel()
138 return super(Chat, self).keypress(size, key) 138 return super(Chat, self).keypress(size, key)
139 139
140 def getMenu(self): 140 def getMenu(self):
141 """Return Menu bar""" 141 """Return Menu bar"""
142 menu = sat_widgets.Menu(self.host.loop) 142 menu = sat_widgets.Menu(self.host.loop)
143 if self.type == 'group': 143 if self.type == C.CHAT_GROUP:
144 self.host.addMenus(menu, C.MENU_ROOM, {'room_jid': self.target.bare}) 144 self.host.addMenus(menu, C.MENU_ROOM, {'room_jid': self.target.bare})
145 game = _("Game") 145 game = _("Game")
146 menu.addMenu(game, "Tarot", self.onTarotRequest) 146 menu.addMenu(game, "Tarot", self.onTarotRequest)
147 elif self.type == 'one2one': 147 elif self.type == C.CHAT_ONE2ONE:
148 self.host.addMenus(menu, C.MENU_SINGLE, {'jid': self.target}) 148 self.host.addMenus(menu, C.MENU_SINGLE, {'jid': self.target})
149 menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest) 149 menu.addMenu(_("Action"), _("Send file"), self.onSendFileRequest)
150 return menu 150 return menu
151 151
152 def updateChatState(self, from_jid, state): 152 def updateChatState(self, from_jid, state):
168 self.host.redraw() 168 self.host.redraw()
169 else: 169 else:
170 self.title_dynamic = '({})'.format(state) 170 self.title_dynamic = '({})'.format(state)
171 171
172 def _presentClicked(self, list_wid, clicked_wid): 172 def _presentClicked(self, list_wid, clicked_wid):
173 assert self.type == 'group' 173 assert self.type == C.CHAT_GROUP
174 nick = clicked_wid.getValue().value 174 nick = clicked_wid.getValue().value
175 if nick == self.getUserNick(): 175 if nick == self.getUserNick():
176 #We ignore clicks on our own nick 176 #We ignore clicks on our own nick
177 return 177 return
178 contact_list = self.host.contact_lists[self.profile] 178 contact_list = self.host.contact_lists[self.profile]
179 full_jid = JID("%s/%s" % (self.target.bare, nick)) 179 full_jid = jid.JID("%s/%s" % (self.target.bare, nick))
180 180
181 #we have a click on a nick, we need to create the widget if it doesn't exists 181 #we have a click on a nick, we need to create the widget if it doesn't exists
182 self.getOrCreatePrivateWidget(full_jid) 182 self.getOrCreatePrivateWidget(full_jid)
183 183
184 #now we select the new window 184 #now we select the new window
256 self.host.redraw() 256 self.host.redraw()
257 257
258 def onPrivateCreated(self, widget): 258 def onPrivateCreated(self, widget):
259 self.host.contact_lists[widget.profile].specialResourceVisible(widget.target) 259 self.host.contact_lists[widget.profile].specialResourceVisible(widget.target)
260 260
261 def printMessage(self, from_jid, msg, profile, timestamp=None): 261 def printMessage(self, from_jid, msg, extra=None, profile=C.PROF_KEY_NONE):
262 assert isinstance(from_jid, JID) 262 assert isinstance(from_jid, jid.JID)
263 if extra is None:
264 extra = {}
263 try: 265 try:
264 jid, nick, mymess = QuickChat.printMessage(self, from_jid, msg, profile, timestamp) 266 timestamp = float(extra['timestamp'])
267 except KeyError:
268 timestamp=None
269 try:
270 nick, mymess = QuickChat.printMessage(self, from_jid, msg, extra, profile)
265 except TypeError: 271 except TypeError:
266 # None is returned, the message is managed 272 # None is returned, the message is managed
267 return 273 return
268 new_text = ChatText(self, timestamp, nick, mymess, msg) 274 new_text = ChatText(self, timestamp, nick, mymess, msg)
269 275
289 # XXX: do not send notifications for each line of the history being displayed 295 # XXX: do not send notifications for each line of the history being displayed
290 # FIXME: this must be changed in the future if the timestamp is passed with 296 # FIXME: this must be changed in the future if the timestamp is passed with
291 # all messages and not only with the messages coming from the history. 297 # all messages and not only with the messages coming from the history.
292 self._notify(from_jid, msg) 298 self._notify(from_jid, msg)
293 299
294 def printInfo(self, msg, type_='normal', timestamp=None): 300 def printInfo(self, msg, type_='normal', extra=None):
295 """Print general info 301 """Print general info
296 @param msg: message to print 302 @param msg: message to print
297 @type_: one of: 303 @type_: one of:
298 normal: general info like "toto has joined the room" 304 normal: general info like "toto has joined the room"
299 me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist" 305 me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
300 @param timestamp (float): number of seconds since epoch 306 @param timestamp (float): number of seconds since epoch
301 """ 307 """
308 if extra is None:
309 extra = {}
310 try:
311 timestamp = float(extra['timestamp'])
312 except KeyError:
313 timestamp=None
302 _widget = ChatText(self, timestamp, None, False, msg, is_info=True) 314 _widget = ChatText(self, timestamp, None, False, msg, is_info=True)
303 self.content.append(_widget) 315 self.content.append(_widget)
304 self._notify(msg=msg) 316 self._notify(msg=msg)
305 317
306 def _notify(self, from_jid="somebody", msg=""): 318 def _notify(self, from_jid="somebody", msg=""):
314 #we don't change focus if user is not at the bottom 326 #we don't change focus if user is not at the bottom
315 #as that mean that he is probably watching discussion history 327 #as that mean that he is probably watching discussion history
316 self.text_list.focus_position = len(self.content) - 1 328 self.text_list.focus_position = len(self.content) - 1
317 self.host.redraw() 329 self.host.redraw()
318 if not self.host.x_notify.hasFocus(): 330 if not self.host.x_notify.hasFocus():
319 if self.type == "one2one": 331 if self.type == C.CHAT_ONE2ONE:
320 self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid) 332 self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % from_jid)
321 elif self.getUserNick().lower() in msg.lower(): 333 elif self.getUserNick().lower() in msg.lower():
322 self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target}) 334 self.host.x_notify.sendNotification(_("Primitivus: %(user)s mentioned you in room '%(room)s'") % {'user': from_jid, 'room': self.target})
323 335
324 def startGame(self, game_type, referee, players): 336 def startGame(self, game_type, referee, players):
354 except UnicodeError: 366 except UnicodeError:
355 log.error("FIXME: filepath with unicode error are not managed yet") 367 log.error("FIXME: filepath with unicode error are not managed yet")
356 self.host.showDialog(_(u"File has a unicode error in its name, it's not yet managed by SàT"), title=_("Can't send file"), type_="error") 368 self.host.showDialog(_(u"File has a unicode error in its name, it's not yet managed by SàT"), title=_("Can't send file"), type_="error")
357 return 369 return
358 #FIXME: check last_resource: what if self.target.resource exists ? 370 #FIXME: check last_resource: what if self.target.resource exists ?
359 last_resource = self.host.bridge.getLastResource(unicode(self.target.bare), self.profile) 371 last_resource = self.host.bridge.getMainResource(unicode(self.target.bare), self.profile)
360 if last_resource: 372 if last_resource:
361 full_jid = JID("%s/%s" % (self.target.bare, last_resource)) 373 full_jid = jid.JID("%s/%s" % (self.target.bare, last_resource))
362 else: 374 else:
363 full_jid = self.target 375 full_jid = self.target
364 progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.profile) 376 progress_id = self.host.bridge.sendFile(full_jid, filepath, {}, self.profile)
365 self.host.addProgress(progress_id,filepath) 377 self.host.addProgress(progress_id,filepath)
366 self.host.showDialog(_(u"You file request has been sent, we are waiting for your contact answer"), title=_("File request sent")) 378 self.host.showDialog(_(u"You file request has been sent, we are waiting for your contact answer"), title=_("File request sent"))