comparison src/cagou/plugins/plugin_wid_chat.py @ 109:7631325e11f4

chat: added completion of header's input, any entity in cache containing the entered text is shown
author Goffi <goffi@goffi.org>
date Sun, 15 Jan 2017 21:21:29 +0100
parents 9909ed7a7a20
children c3952922ca56
comparison
equal deleted inserted replaced
108:953ddf817b8a 109:7631325e11f4
30 from sat_frontends.quick_frontend import quick_widgets 30 from sat_frontends.quick_frontend import quick_widgets
31 from sat_frontends.quick_frontend import quick_chat 31 from sat_frontends.quick_frontend import quick_chat
32 from sat_frontends.tools import jid 32 from sat_frontends.tools import jid
33 from cagou.core import cagou_widget 33 from cagou.core import cagou_widget
34 from cagou.core.image import Image 34 from cagou.core.image import Image
35 from cagou.core.common import JidWidget
36 from kivy.uix.dropdown import DropDown
35 from cagou import G 37 from cagou import G
36 38
37 39
38 PLUGIN_INFO = { 40 PLUGIN_INFO = {
39 "name": _(u"chat"), 41 "name": _(u"chat"),
129 self.header_input.hint_text = u"{}".format(target) 131 self.header_input.hint_text = u"{}".format(target)
130 self.host.addListener('progressError', self.onProgressError, profiles) 132 self.host.addListener('progressError', self.onProgressError, profiles)
131 self.host.addListener('progressFinished', self.onProgressFinished, profiles) 133 self.host.addListener('progressFinished', self.onProgressFinished, profiles)
132 self._waiting_pids = {} # waiting progress ids 134 self._waiting_pids = {} # waiting progress ids
133 self.postInit() 135 self.postInit()
136 # completion attribtues
137 self._hi_comp_data = None
138 self._hi_comp_last = None
139 self._hi_comp_dropdown = DropDown()
140 self._hi_comp_allowed = True
134 141
135 @classmethod 142 @classmethod
136 def factory(cls, plugin_info, target, profiles): 143 def factory(cls, plugin_info, target, profiles):
137 profiles = list(profiles) 144 profiles = list(profiles)
138 if len(profiles) > 1: 145 if len(profiles) > 1:
139 raise NotImplementedError(u"Multi-profiles is not available yet for chat") 146 raise NotImplementedError(u"Multi-profiles is not available yet for chat")
140 if target is None: 147 if target is None:
141 target = G.host.profiles[profiles[0]].whoami 148 target = G.host.profiles[profiles[0]].whoami
142 return G.host.widgets.getOrCreateWidget(cls, target, on_new_widget=None, on_existing_widget=C.WIDGET_RECREATE, profiles=profiles) 149 return G.host.widgets.getOrCreateWidget(cls, target, on_new_widget=None, on_existing_widget=C.WIDGET_RECREATE, profiles=profiles)
150
151 ## header ##
152
153 def changeWidget(self, jid_):
154 """change current widget for a new one with given jid
155
156 @param jid_(jid.JID): jid of the widget to create
157 """
158 plugin_info = G.host.getPluginInfo(main=Chat)
159 factory = plugin_info['factory']
160 G.host.switchWidget(self, factory(plugin_info, jid_, profiles=[self.profile]))
161 self.header_input.text = ''
162
163 def onHeaderInput(self):
164 text = self.header_input.text.strip()
165 try:
166 if text.count(u'@') != 1 or text.count(u' '):
167 raise ValueError
168 jid_ = jid.JID(text)
169 except ValueError:
170 log.info(u"entered text is not a jid")
171 return
172
173 def discoCb(disco):
174 # TODO: check if plugin XEP-0045 is activated
175 if "conference" in [i[0] for i in disco[1]]:
176 G.host.bridge.mucJoin(unicode(jid_), "", "", self.profile, callback=self._mucJoinCb, errback=self._mucJoinEb)
177 else:
178 self.changeWidget(jid_)
179
180 def discoEb(failure):
181 log.warning(u"Disco failure, ignore this text: {}".format(failure))
182
183 G.host.bridge.discoInfos(jid_.domain, self.profile, callback=discoCb, errback=discoEb)
184
185 def onHeaderInputCompleted(self, input_wid, completed_text):
186 self._hi_comp_allowed = False
187 input_wid.text = completed_text
188 self._hi_comp_allowed = True
189 self._hi_comp_dropdown.dismiss()
190 self.onHeaderInput()
191
192 def onHeaderInputComplete(self, wid, text):
193 if not self._hi_comp_allowed:
194 return
195 text = text.lstrip()
196 if not text:
197 self._hi_comp_data = None
198 self._hi_comp_last = None
199 return
200
201 profile = list(self.profiles)[0]
202
203 if self._hi_comp_data is None:
204 # first completion, we build the initial list
205 comp_data = self._hi_comp_data = []
206 self._hi_comp_last = ''
207 for jid_, jid_data in G.host.contact_lists[profile].all_iter:
208 comp_data.append((jid_, jid_data))
209 comp_data.sort(key=lambda datum: datum[0])
210 else:
211 comp_data = self._hi_comp_data
212
213 # XXX: dropdown is rebuilt each time backspace is pressed or if the text is changed,
214 # it works OK, but some optimisation may be done here
215 dropdown = self._hi_comp_dropdown
216
217 if not text.startswith(self._hi_comp_last) or not self._hi_comp_last:
218 # text has changed or backspace has been pressed, we restart
219 dropdown.clear_widgets()
220
221 for jid_, jid_data in comp_data:
222 nick = jid_data.get(u'nick', u'')
223 if text in jid_.bare or text in nick.lower():
224 btn = JidWidget(
225 jid = jid_.bare,
226 profile = profile,
227 size_hint = (0.5, None),
228 nick = nick,
229 on_release=lambda dummy, txt=jid_.bare: self.onHeaderInputCompleted(wid, txt)
230 )
231 dropdown.add_widget(btn)
232 else:
233 # more chars, we continue completion by removing unwanted widgets
234 to_remove = []
235 for c in dropdown.children[0].children:
236 if text not in c.jid and text not in (c.nick or ''):
237 to_remove.append(c)
238 for c in to_remove:
239 dropdown.remove_widget(c)
240
241 dropdown.open(wid)
242 self._hi_comp_last = text
143 243
144 def messageDataConverter(self, idx, mess_id): 244 def messageDataConverter(self, idx, mess_id):
145 return {"mess_data": self.messages[mess_id]} 245 return {"mess_data": self.messages[mess_id]}
146 246
147 def _onHistoryPrinted(self): 247 def _onHistoryPrinted(self):
236 self.changeWidget(jid_) 336 self.changeWidget(jid_)
237 337
238 def _mucJoinEb(self, failure): 338 def _mucJoinEb(self, failure):
239 log.warning(u"Can't join room: {}".format(failure)) 339 log.warning(u"Can't join room: {}".format(failure))
240 340
241 def changeWidget(self, jid_):
242 """change current widget for a new one with given jid
243
244 @param jid_(jid.JID): jid of the widget to create
245 """
246 plugin_info = G.host.getPluginInfo(main=Chat)
247 factory = plugin_info['factory']
248 G.host.switchWidget(self, factory(plugin_info, jid_, profiles=[self.profile]))
249 self.header_input.text = ''
250
251 def onHeaderInput(self):
252 text = self.header_input.text.strip()
253 try:
254 if text.count(u'@') != 1 or text.count(u' '):
255 raise ValueError
256 jid_ = jid.JID(text)
257 except ValueError:
258 log.info(u"entered text is not a jid")
259 return
260
261 def discoCb(disco):
262 # TODO: check if plugin XEP-0045 is activated
263 if "conference" in [i[0] for i in disco[1]]:
264 G.host.bridge.mucJoin(unicode(jid_), "", "", self.profile, callback=self._mucJoinCb, errback=self._mucJoinEb)
265 else:
266 self.changeWidget(jid_)
267
268 def discoEb(failure):
269 log.warning(u"Disco failure, ignore this text: {}".format(failure))
270
271 G.host.bridge.discoInfos(jid_.domain, self.profile, callback=discoCb, errback=discoEb)
272
273 def _onDelete(self): 341 def _onDelete(self):
274 self.host.removeListener('progressFinished', self.onProgressFinished) 342 self.host.removeListener('progressFinished', self.onProgressFinished)
275 self.host.removeListener('progressError', self.onProgressError) 343 self.host.removeListener('progressError', self.onProgressError)
276 return super(Chat, self).onDelete() 344 return super(Chat, self).onDelete()
277 345