Mercurial > libervia-backend
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")) |