comparison frontends/src/primitivus/chat.py @ 1963:a2bc5089c2eb

backend, frontends: message refactoring (huge commit): /!\ several features are temporarily disabled, like notifications in frontends next step in refactoring, with the following changes: - jp: updated jp message to follow changes in backend/bridge - jp: added --lang, --subject, --subject_lang, and --type options to jp message + fixed unicode handling for jid - quick_frontend (QuickApp, QuickChat): - follow backend changes - refactored chat, message are now handled in OrderedDict and uid are kept so they can be updated - Message and Occupant classes handle metadata, so frontend just have to display them - Primitivus (Chat): - follow backend/QuickFrontend changes - info & standard messages are handled in the same MessageWidget class - improved/simplified handling of messages, removed update() method - user joined/left messages are merged when next to each other - a separator is shown when message is received while widget is out of focus, so user can quickly see the new messages - affiliation/role are shown (in a basic way for now) in occupants panel - removed "/me" messages handling, as it will be done by a backend plugin - message language is displayed when available (only one language per message for now) - fixed :history and :search commands - core (constants): new constants for messages type, XML namespace, entity type - core: *Message methods renamed to follow new code sytle (e.g. sendMessageToBridge => messageSendToBridge) - core (messages handling): fixed handling of language - core (messages handling): mes_data['from'] and ['to'] are now jid.JID - core (core.xmpp): reorganised message methods, added getNick() method to client.roster - plugin text commands: fixed plugin and adapted to new messages behaviour. client is now used in arguments instead of profile - plugins: added information for cancellation reason in CancelError calls - plugin XEP-0045: various improvments, but this plugin still need work: - trigger is used to avoid message already handled by the plugin to be handled a second time - changed the way to handle history, the last message from DB is checked and we request only messages since this one, in seconds (thanks Poezio folks :)) - subject reception is waited before sending the roomJoined signal, this way we are sure that everything including history is ready - cmd_* method now follow the new convention with client instead of profile - roomUserJoined and roomUserLeft messages are removed, the events are now handled with info message with a "ROOM_USER_JOINED" info subtype - probably other forgotten stuffs :p
author Goffi <goffi@goffi.org>
date Mon, 20 Jun 2016 18:41:53 +0200
parents 633b5c21aefd
children d727aab9a80e
comparison
equal deleted inserted replaced
1962:a45235d8dc93 1963:a2bc5089c2eb
21 from sat.core import log as logging 21 from sat.core import log as logging
22 log = logging.getLogger(__name__) 22 log = logging.getLogger(__name__)
23 import urwid 23 import urwid
24 from urwid_satext import sat_widgets 24 from urwid_satext import sat_widgets
25 from sat_frontends.quick_frontend import quick_widgets 25 from sat_frontends.quick_frontend import quick_widgets
26 from sat_frontends.quick_frontend.quick_chat import QuickChat 26 from sat_frontends.quick_frontend import quick_chat
27 from sat_frontends.quick_frontend import quick_games 27 from sat_frontends.quick_frontend import quick_games
28 from sat_frontends.primitivus import game_tarot 28 from sat_frontends.primitivus import game_tarot
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 import jid 33 from sat_frontends.tools import jid
34 34 from functools import total_ordering
35 35 import bisect
36 class ChatText(urwid.FlowWidget): 36
37 """Manage the printing of chat message""" 37
38 38 class MessageWidget(urwid.WidgetWrap):
39 def __init__(self, parent, timestamp, nick, my_mess, message, align='left', is_info=False): 39
40 self.parent = parent 40 def __init__(self, mess_data):
41 self.timestamp = time.localtime(timestamp) 41 """
42 self.nick = nick 42 @param mess_data(quick_chat.Message, None): message data
43 self.my_mess = my_mess 43 None: used only for non text widgets (e.g.: focus separator)
44 self.message = unicode(message) 44 """
45 self.align = align 45 self.mess_data = mess_data
46 self.is_info = is_info 46 mess_data.widgets.add(self)
47 self.timestamp = time.localtime(mess_data.timestamp)
48 super(MessageWidget, self).__init__(urwid.Text(self.markup))
49
50 @property
51 def markup(self):
52 return self._generateInfoMarkup() if self.mess_data.type == C.MESS_TYPE_INFO else self._generateMarkup()
53
54 @property
55 def info_type(self):
56 return self.mess_data.info_type
57
58 @property
59 def parent(self):
60 return self.mess_data.parent
61
62 @property
63 def message(self):
64 """Return currently displayed message"""
65 message = self.mess_data.message
66 if self.parent.lang in message:
67 self.selected_lang = self.parent.lang
68 return message[self.parent.lang]
69 try:
70 self.selected_lang = ''
71 return message['']
72 except KeyError:
73 try:
74 lang, mess = message.iteritems().next()
75 self.selected_lang = lang
76 return mess
77 except StopIteration:
78 log.error(u"Can't find message for uid {}".format(self.mess_data.uid))
79
80 @message.setter
81 def message(self, value):
82 self.mess_data.message = {'':value}
83 self._w.set_text(self.markup)
84
85 @property
86 def type(self):
87 try:
88 return self.mess_data.type
89 except AttributeError:
90 return C.MESS_TYPE_INFO
47 91
48 def selectable(self): 92 def selectable(self):
49 return True 93 return True
50 94
51 def keypress(self, size, key): 95 def keypress(self, size, key):
52 return key 96 return key
53 97
54 def rows(self, size, focus=False): 98 def get_cursor_coords(self, size):
55 return self.display_widget(size, focus).rows(size, focus) 99 return 0, 0
56 100
57 def render(self, size, focus=False): 101 def render(self, size, focus=False):
58 canvas = urwid.CompositeCanvas(self.display_widget(size, focus).render(size, focus)) 102 # Text widget doesn't render cursor, but we want one
103 # so we add it here
104 canvas = urwid.CompositeCanvas(self._w.render(size, focus))
59 if focus: 105 if focus:
60 canvas.set_cursor(self.get_cursor_coords(size)) 106 canvas.set_cursor(self.get_cursor_coords(size))
61 return canvas 107 return canvas
62 108
109 def _generateInfoMarkup(self):
110 return ('info_msg', self.message)
111
112 def _generateMarkup(self):
113 """Generate text markup according to message data and Widget options"""
114 markup = []
115 d = self.mess_data
116
117 # timestamp
118 if self.parent.show_timestamp:
119 # if the message was sent before today, we print the full date
120 time_format = "%c" if self.timestamp < self.parent.day_change else "%H:%M"
121 markup.append(('date', "[{}]".format(time.strftime(time_format, self.timestamp).decode('utf-8'))))
122
123 # nickname
124 if self.parent.show_short_nick:
125 markup.append(('my_nick' if d.own_mess else 'other_nick', "**" if d.own_mess else "*"))
126 else:
127 markup.append(('my_nick' if d.own_mess else 'other_nick', u"[{}] ".format(d.nick or '')))
128
129 msg = self.message # needed to generate self.selected_lang
130
131 if self.selected_lang:
132 markup.append(("msg_lang", u"[{}] ".format(self.selected_lang)))
133
134 # message body
135 markup.append(msg)
136
137 return markup
138
139 @total_ordering
140 class OccupantWidget(urwid.WidgetWrap):
141
142 def __init__(self, occupant_data):
143 self.occupant_data = occupant_data
144 occupant_data.widgets.add(self)
145 markup = self._generateMarkup()
146 super(OccupantWidget, self).__init__(urwid.Text(markup))
147
148 def __eq__(self, other):
149 return self.occupant_data.nick == other.occupant_data.nick
150
151 def __lt__(self, other):
152 return self.occupant_data.nick.lower() < other.occupant_data.nick.lower()
153
154 @property
155 def parent(self):
156 return self.mess_data.parent
157
158 def selectable(self):
159 return True
160
161 def keypress(self, size, key):
162 return key
163
63 def get_cursor_coords(self, size): 164 def get_cursor_coords(self, size):
64 return 0, 0 165 return 0, 0
65 166
66 def display_widget(self, size, focus): 167 def render(self, size, focus=False):
67 render_txt = [] 168 # Text widget doesn't render cursor, but we want one
68 if not self.is_info: 169 # so we add it here
69 if self.parent.show_timestamp: 170 canvas = urwid.CompositeCanvas(self._w.render(size, focus))
70 time_format = "%c" if self.timestamp < self.parent.day_change else "%H:%M" # if the message was sent before today, we print the full date 171 if focus:
71 render_txt.append(('date', "[%s]" % time.strftime(time_format, self.timestamp).decode('utf-8'))) 172 canvas.set_cursor(self.get_cursor_coords(size))
72 if self.parent.show_short_nick: 173 return canvas
73 render_txt.append(('my_nick' if self.my_mess else 'other_nick', "**" if self.my_mess else "*")) 174
74 else: 175 def _generateMarkup(self):
75 render_txt.append(('my_nick' if self.my_mess else 'other_nick', "[%s] " % (self.nick or ''))) 176 # TODO: role and affiliation are shown in a Q&D way
76 render_txt.append(self.message) 177 # should be more intuitive and themable
77 txt_widget = urwid.Text(render_txt, align=self.align) 178 o = self.occupant_data
78 if self.is_info: 179 markup = []
79 return urwid.AttrMap(txt_widget, 'info_msg') 180 markup.append(('info_msg', '{}{} '.format(
80 return txt_widget 181 o.role[0].upper(),
81 182 o.affiliation[0].upper(),
82 183 )))
83 class Chat(PrimitivusWidget, QuickChat): 184 markup.append(o.nick)
84 185 return markup
85 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, profiles=None): 186
86 QuickChat.__init__(self, host, target, type_, profiles=profiles) 187
87 self.content = urwid.SimpleListWalker([]) 188 class Chat(PrimitivusWidget, quick_chat.QuickChat):
88 self.text_list = urwid.ListBox(self.content) 189
89 self.chat_widget = urwid.Frame(self.text_list) 190 def __init__(self, host, target, type_=C.CHAT_ONE2ONE, occupants=None, subject=None, profiles=None):
191 quick_chat.QuickChat.__init__(self, host, target, type_, occupants, subject, profiles=profiles)
192 self.mess_walker = urwid.SimpleListWalker([])
193 self.mess_widgets = urwid.ListBox(self.mess_walker)
194 self.chat_widget = urwid.Frame(self.mess_widgets)
90 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)]) 195 self.chat_colums = urwid.Columns([('weight', 8, self.chat_widget)])
91 self.pile = urwid.Pile([self.chat_colums]) 196 self.pile = urwid.Pile([self.chat_colums])
92 PrimitivusWidget.__init__(self, self.pile, self.target) 197 PrimitivusWidget.__init__(self, self.pile, self.target)
93 198
94 # we must adapt the behaviour with the type 199 # we must adapt the behaviour with the type
95 if type_ == C.CHAT_GROUP: 200 if type_ == C.CHAT_GROUP:
96 if len(self.chat_colums.contents) == 1: 201 if len(self.chat_colums.contents) == 1:
97 self.occupants_list = sat_widgets.GenericList([], option_type=sat_widgets.ClickableText, on_click=self._occupantsClicked) 202 self.occupants_walker = urwid.SimpleListWalker([])
98 self.occupants_panel = sat_widgets.VerticalSeparator(self.occupants_list) 203 # TODO: put a real ContactPanel class here, based on FocusWidget ?
204 self.occupants_widgets = urwid.ListBox(self.occupants_walker)
205 # FIXME
206 # , option_type=sat_widgets.ClickableText, on_click=self._occupantsClicked)
207 self.occupants_panel = sat_widgets.VerticalSeparator(self.occupants_widgets)
99 self._appendOccupantsPanel() 208 self._appendOccupantsPanel()
209 occupants_list = sorted(self.occupants.keys(), key=lambda o:o.lower())
210 for occupant in occupants_list:
211 occupant_data = self.occupants[occupant]
212 self.occupants_walker.append(OccupantWidget(occupant_data))
213
100 self.host.addListener('presence', self.presenceListener, [profiles]) 214 self.host.addListener('presence', self.presenceListener, [profiles])
101 215
216 # focus marker is a separator indicated last visible message before focus was lost
217 self.focus_marker = None # link to current marker
218 self.focus_marker_set = None # True if a new marker has been inserted
102 self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00 %Y")) # struct_time of day changing time 219 self.day_change = time.strptime(time.strftime("%a %b %d 00:00:00 %Y")) # struct_time of day changing time
103 self.show_timestamp = True 220 self.show_timestamp = True
104 self.show_short_nick = False 221 self.show_short_nick = False
105 self.show_title = 1 # 0: clip title; 1: full title; 2: no title 222 self.show_title = 1 # 0: clip title; 1: full title; 2: no title
106 self.subject = None 223 self.postInit()
107 224
108 def keypress(self, size, key): 225 def keypress(self, size, key):
109 if key == a_key['OCCUPANTS_HIDE']: # user wants to (un)hide the occupants panel 226 if key == a_key['OCCUPANTS_HIDE']: # user wants to (un)hide the occupants panel
110 if self.type == C.CHAT_GROUP: 227 if self.type == C.CHAT_GROUP:
111 widgets = [widget for (widget, options) in self.chat_colums.contents] 228 widgets = [widget for (widget, options) in self.chat_colums.contents]
113 self._removeOccupantsPanel() 230 self._removeOccupantsPanel()
114 else: 231 else:
115 self._appendOccupantsPanel() 232 self._appendOccupantsPanel()
116 elif key == a_key['TIMESTAMP_HIDE']: # user wants to (un)hide timestamp 233 elif key == a_key['TIMESTAMP_HIDE']: # user wants to (un)hide timestamp
117 self.show_timestamp = not self.show_timestamp 234 self.show_timestamp = not self.show_timestamp
118 for wid in self.content: 235 for wid in self.mess_walker:
119 wid._invalidate() 236 wid._invalidate()
120 elif key == a_key['SHORT_NICKNAME']: # user wants to (not) use short nick 237 elif key == a_key['SHORT_NICKNAME']: # user wants to (not) use short nick
121 self.show_short_nick = not self.show_short_nick 238 self.show_short_nick = not self.show_short_nick
122 for wid in self.content: 239 for wid in self.mess_walker:
123 wid._invalidate() 240 wid._invalidate()
124 elif key == a_key['SUBJECT_SWITCH']: # user wants to (un)hide group's subject or change its apperance 241 elif key == a_key['SUBJECT_SWITCH']: # user wants to (un)hide group's subject or change its apperance
125 if self.subject: 242 if self.subject:
126 self.show_title = (self.show_title + 1) % 3 243 self.show_title = (self.show_title + 1) % 3
127 if self.show_title == 0: 244 if self.show_title == 0:
158 @param show: availability 275 @param show: availability
159 @param priority: resource's priority 276 @param priority: resource's priority
160 @param statuses: dict of statuses 277 @param statuses: dict of statuses
161 @param profile: %(doc_profile)s 278 @param profile: %(doc_profile)s
162 """ 279 """
163 assert self.type == C.CHAT_GROUP 280 # FIXME: disable for refactoring, need to be checked and re-enabled
164 if entity.bare != self.target: 281 return
165 return 282 # assert self.type == C.CHAT_GROUP
166 self.update(entity) 283 # if entity.bare != self.target:
167 284 # return
168 def update(self, entity=None): 285 # self.update(entity)
169 """Update one or all entities. 286
170 287 def createMessage(self, message):
171 @param entity (jid.JID): entity to update 288 self.appendMessage(message)
172 """ 289
173 contact_list = self.host.contact_lists[self.profile] 290 def _user_moved(self, message):
174 291 """return true if message is a user left/joined message
175 if self.type == C.CHAT_ONE2ONE: # only update the chat title 292
176 states = self.getEntityStates(self.target) 293 @param message(quick_chat.Message): message to add
177 self.title_dynamic = ' '.join([u'({})'.format(state) for state in states.values()]) 294 """
178 self.host.redraw() 295 if message.type != C.MESS_TYPE_INFO:
179 return 296 return False
180 297 try:
181 nicks = list(self.occupants) 298 info_type = message.extra['info_type']
182 if entity is None: # rebuild all the occupants list 299 except KeyError:
183 values = [] 300 return False
184 nicks.sort() 301 else:
185 for nick in nicks: 302 return info_type in quick_chat.ROOM_USER_MOVED
186 values.append(self._buildOccupantMarkup(jid.newResource(self.target, nick))) 303
187 self.occupants_list.changeValues(values) 304 def appendMessage(self, message):
188 else: # add, remove or update only one occupant 305 """Create a MessageWidget and append it
189 nick = entity.resource 306
190 show = contact_list.getCache(entity, C.PRESENCE_SHOW) 307 Can merge messages together is desirable (e.g.: multiple joined/leave)
191 if show == C.PRESENCE_UNAVAILABLE or show is None: 308 @param message(quick_chat.Message): message to add
192 try: 309 """
193 self.occupants_list.deleteValue(nick) 310 if self._user_moved(message):
194 except ValueError: 311 for wid in reversed(self.mess_walker):
195 pass 312 # we merge in/out messages if no message was sent meanwhile
196 else: 313 if not isinstance(wid, MessageWidget):
197 values = self.occupants_list.getAllValues() 314 continue
198 markup = self._buildOccupantMarkup(entity) 315 if wid.mess_data.type != C.MESS_TYPE_INFO:
199 if not values: # room has just been created 316 break
200 values = [markup] 317 if wid.info_type in quick_chat.ROOM_USER_MOVED and wid.mess_data.nick == message.nick:
201 else: # add or update the occupant, keep the list sorted 318 try:
202 index = 0 319 count = wid.reentered_count
203 for entry in values: 320 except AttributeError:
204 order = cmp(entry.value if hasattr(entry, 'value') else entry, nick) 321 count = wid.reentered_count = 1
205 if order < 0: 322 nick = wid.mess_data.nick
206 index += 1 323 if message.info_type == quick_chat.ROOM_USER_LEFT:
207 continue 324 wid.message = _(u"<= {nick} has left the room ({count})").format(nick=nick, count=count)
208 if order > 0: # insert the occupant 325 else:
209 values.insert(index, markup) 326 wid.message = _(u"<=> {nick} re-entered the room ({count})") .format(nick=nick, count=count)
210 else: # update an existing occupant 327 wid.reentered_count+=1
211 values[index] = markup 328 return
212 break 329
213 if index == len(values): # add to the end of the list 330 if ((self.host.selected_widget != self or not self.host.x_notify.hasFocus())
214 values.append(markup) 331 and self.focus_marker_set is not None):
215 self.occupants_list.changeValues(values) 332 if not self.focus_marker_set and not self._locked and self.mess_walker:
216 self.host.redraw() 333 if self.focus_marker is not None:
217 334 self.mess_walker.remove(self.focus_marker)
218 def _buildOccupantMarkup(self, entity): 335 self.focus_marker = urwid.Divider('—')
219 """Return the option attributes for a MUC occupant. 336 self.mess_walker.append(self.focus_marker)
220 337 self.focus_marker_set = True
221 @param nick (unicode): occupant nickname 338 else:
222 """ 339 if self.focus_marker_set:
223 # TODO: for now it's not a markup but a simple text, the problem is that ListOption is unicode and not urwid.Text 340 self.focus_marker_set = False
224 contact_list = self.host.contact_lists[self.profile] 341
225 show = contact_list.getCache(entity, C.PRESENCE_SHOW) 342 if not message.message:
226 states = self.getEntityStates(entity) 343 log.error(u"Received an empty message for uid {}".format(message.uid))
227 nick = entity.resource 344 else:
228 show_icon, entity_attr = C.PRESENCE.get(show, (u'', u'default')) # TODO: use entity_attr and return (nick, markup) 345 self.mess_walker.append(MessageWidget(message))
229 text = "%s%s %s" % (u''.join(states.values()), show_icon, nick) 346 self.mess_widgets.focus_position = len(self.mess_walker) - 1 # scroll down
230 return (nick, text) 347 self.host.redraw() # FIXME: should not be necessary
348
349 def addUser(self, nick):
350 occupant = super(Chat, self).addUser(nick)
351 bisect.insort(self.occupants_walker, OccupantWidget(occupant))
352
353 def removeUser(self, occupant_data):
354 occupant = super(Chat, self).removeUser(occupant_data)
355 if occupant is not None:
356 for widget in occupant.widgets:
357 self.occupants_walker.remove(widget)
231 358
232 def _occupantsClicked(self, list_wid, clicked_wid): 359 def _occupantsClicked(self, list_wid, clicked_wid):
360 # FIXME: not called anymore after refactoring
233 assert self.type == C.CHAT_GROUP 361 assert self.type == C.CHAT_GROUP
234 nick = clicked_wid.getValue().value 362 nick = clicked_wid.getValue().value
235 if nick == self.nick: 363 if nick == self.nick:
236 # We ignore clicks on our own nick 364 # We ignore clicks on our own nick
237 return 365 return
272 del self.pile.contents[0] 400 del self.pile.contents[0]
273 self.host.redraw() 401 self.host.redraw()
274 402
275 def setSubject(self, subject, wrap='space'): 403 def setSubject(self, subject, wrap='space'):
276 """Set title for a group chat""" 404 """Set title for a group chat"""
277 QuickChat.setSubject(self, subject) 405 quick_chat.QuickChat.setSubject(self, subject)
278 self.subject = subject
279 self.subj_wid = urwid.Text(unicode(subject.replace('\n', '|') if wrap == 'clip' else subject), 406 self.subj_wid = urwid.Text(unicode(subject.replace('\n', '|') if wrap == 'clip' else subject),
280 align='left' if wrap == 'clip' else 'center', wrap=wrap) 407 align='left' if wrap == 'clip' else 'center', wrap=wrap)
281 self.chat_widget.header = urwid.AttrMap(self.subj_wid, 'title') 408 self.chat_widget.header = urwid.AttrMap(self.subj_wid, 'title')
282 self.host.redraw() 409 self.host.redraw()
283 410
284 def clearHistory(self): 411 ## Messages
285 """Clear the content of this chat.""" 412
286 del self.content[:] 413 def updateHistory(self, size=C.HISTORY_LIMIT_DEFAULT, search='', profile='@NONE@'):
287 414 del self.mess_walker[:]
288 def afterHistoryPrint(self): 415 if search:
416 self.mess_walker.append(urwid.Text(_(u"Results for searching the globbing pattern: {}").format(search)))
417 self.mess_walker.append(urwid.Text(_(u"Type ':history <lines>' to reset the chat history").format(search)))
418 super(Chat, self).updateHistory(size, search, profile)
419
420 def _onHistoryPrinted(self):
289 """Refresh or scroll down the focus after the history is printed""" 421 """Refresh or scroll down the focus after the history is printed"""
290 if len(self.content): 422 for message in self.messages.itervalues():
291 self.text_list.focus_position = len(self.content) - 1 # scroll down 423 self.appendMessage(message)
292 self.host.redraw() 424 super(Chat, self)._onHistoryPrinted()
293 425
294 def onPrivateCreated(self, widget): 426 def onPrivateCreated(self, widget):
295 self.host.contact_lists[widget.profile].specialResourceVisible(widget.target) 427 self.host.contact_lists[widget.profile].specialResourceVisible(widget.target)
296 428
297 def printMessage(self, nick, my_message, message, timestamp, extra=None, profile=C.PROF_KEY_NONE): 429 def onSelected(self):
298 """Print message in chat window. 430 self.focus_marker_set = False
299
300 @param nick (unicode): author nick
301 @param my_message (boolean): True if profile is the author
302 @param message (unicode): message content
303 @param extra (dict): extra data
304 """
305 new_text = ChatText(self, timestamp, nick, my_message, message)
306 self.content.append(new_text)
307 QuickChat.printMessage(self, nick, my_message, message, timestamp, extra, profile)
308
309 def printInfo(self, msg, type_='normal', extra=None):
310 """Print general info
311 @param msg: message to print
312 @type_: one of:
313 normal: general info like "toto has joined the room"
314 me: "/me" information like "/me clenches his fist" ==> "toto clenches his fist"
315 @param timestamp (float): number of seconds since epoch
316 """
317 if extra is None:
318 extra = {}
319 try:
320 timestamp = float(extra['timestamp'])
321 except KeyError:
322 timestamp = None
323 _widget = ChatText(self, timestamp, None, False, msg, is_info=True)
324 self.content.append(_widget)
325 QuickChat.printInfo(self, msg, type_, extra)
326 431
327 def notify(self, contact="somebody", msg=""): 432 def notify(self, contact="somebody", msg=""):
328 """Notify the user of a new message if primitivus doesn't have the focus. 433 """Notify the user of a new message if primitivus doesn't have the focus.
329 434
330 @param contact (unicode): contact who wrote to the users 435 @param contact (unicode): contact who wrote to the users
331 @param msg (unicode): the message that has been received 436 @param msg (unicode): the message that has been received
332 """ 437 """
438 # FIXME: not called anymore after refactoring
333 if msg == "": 439 if msg == "":
334 return 440 return
335 if self.text_list.get_focus()[1] == len(self.content) - 2: 441 if self.mess_widgets.get_focus()[1] == len(self.mess_walker) - 2:
336 # we don't change focus if user is not at the bottom 442 # we don't change focus if user is not at the bottom
337 # as that mean that he is probably watching discussion history 443 # as that mean that he is probably watching discussion history
338 self.text_list.focus_position = len(self.content) - 1 444 self.mess_widgets.focus_position = len(self.mess_walker) - 1
339 self.host.redraw() 445 self.host.redraw()
340 if not self.host.x_notify.hasFocus(): 446 if not self.host.x_notify.hasFocus():
341 if self.type == C.CHAT_ONE2ONE: 447 if self.type == C.CHAT_ONE2ONE:
342 self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % contact) 448 self.host.x_notify.sendNotification(_("Primitivus: %s is talking to you") % contact)
343 elif self.nick is not None and self.nick.lower() in msg.lower(): 449 elif self.nick is not None and self.nick.lower() in msg.lower():
352 self.host.bridge.tarotGameCreate(self.target, list(self.occupants), self.profile) 458 self.host.bridge.tarotGameCreate(self.target, list(self.occupants), self.profile)
353 459
354 # MISC EVENTS # 460 # MISC EVENTS #
355 461
356 def onDelete(self): 462 def onDelete(self):
357 QuickChat.onDelete(self) 463 # FIXME: to be checked after refactoring
464 quick_chat.QuickChat.onDelete(self)
358 if self.type == C.CHAT_GROUP: 465 if self.type == C.CHAT_GROUP:
359 self.host.removeListener('presence', self.presenceListener) 466 self.host.removeListener('presence', self.presenceListener)
360 467
361 468
362 quick_widgets.register(QuickChat, Chat) 469 quick_widgets.register(quick_chat.QuickChat, Chat)
363 quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame) 470 quick_widgets.register(quick_games.Tarot, game_tarot.TarotGame)