# HG changeset patch # User Goffi # Date 1348527514 -7200 # Node ID e9634d2e7b38ae2f04044e1188483caf6a1ca0b3 # Parent 00d3679976abcce99e4fbc124a5b9d1fe52dbd4d core, quick_frontend, primitivus, wix: Contacts List refactoring phase 1: - QuickContactManagement is not used anymore and will be removed, ContactList + Core are used instead - disconnected contacts are now displayed in Primitivus (M-d to show/hide them) - avatars are temporary unavailable in wix - new bridge method: getContactsFromGroup diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/bridge/DBus.py --- a/frontends/src/bridge/DBus.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/bridge/DBus.py Tue Sep 25 00:58:34 2012 +0200 @@ -96,6 +96,9 @@ def getContacts(self, profile_key="@DEFAULT@"): return self.db_core_iface.getContacts(profile_key) + def getContactsFromGroup(self, group, profile_key="@DEFAULT@"): + return self.db_core_iface.getContactsFromGroup(group, profile_key) + def getHistory(self, from_jid, to_jid, limit, between=True, callback=None, errback=None): return self.db_core_iface.getHistory(from_jid, to_jid, limit, between, reply_handler=callback, error_handler=lambda err:errback(err._dbus_error_name[len(const_ERROR_PREFIX)+1:])) diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/primitivus/chat.py --- a/frontends/src/primitivus/chat.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/primitivus/chat.py Tue Sep 25 00:58:34 2012 +0200 @@ -262,11 +262,8 @@ def startGame(self, game_type, referee, players): """Configure the chat window to start a game""" if game_type=="Tarot": - try: - self.tarot_wid = CardGame(self, referee, players, self.nick) - self.__appendGamePanel(self.tarot_wid) - except e: - self.host.debug() + self.tarot_wid = CardGame(self, referee, players, self.nick) + self.__appendGamePanel(self.tarot_wid) def getGame(self, game_type): """Return class managing the game type""" @@ -288,6 +285,11 @@ #MISC EVENTS# def onFileSelected(self, filepath): self.host.removePopUp() - full_jid = self.host.CM.get_full(self.target) + #FIXME: check last_resource: what if self.target.resource exists ? + last_resource = self.host.bridge.getLastResource(unicode(self.target.short), self.host.profile) + if last_resource: + full_jid = JID("%s/%s" % (self.target.short, last_resource)) + else: + full_jid = self.target id = self.host.bridge.sendFile(full_jid, filepath, {}, self.host.profile) self.host.addProgress(id,filepath) diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/primitivus/constants.py --- a/frontends/src/primitivus/constants.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/primitivus/constants.py Tue Sep 25 00:58:34 2012 +0200 @@ -54,6 +54,7 @@ ('warning', 'light red', 'default'), ('progress_normal', 'default', 'black'), ('progress_complete', 'default', 'light red'), + ('show_disconnected', 'dark gray', 'default'), ('show_normal', 'default', 'default'), ('show_normal_focus', 'default, bold', 'default'), ('show_chat', 'dark green', 'default'), @@ -67,7 +68,8 @@ ('status', 'yellow', 'default'), ('status_focus', 'yellow, bold', 'default'), ] -__builtin__.__dict__['const_SHOW_ICON'] = {"": (u'✔', "show_normal"), +__builtin__.__dict__['const_SHOW_ICON'] = {"unavailable": (u'⨯', "show_disconnected"), + "": (u'✔', "show_normal"), "chat": (u'✆', "show_chat"), "away": (u'✈', "show_away"), "dnd": (u'✖', "show_dnd"), diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/primitivus/contact_list.py --- a/frontends/src/primitivus/contact_list.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/primitivus/contact_list.py Tue Sep 25 00:58:34 2012 +0200 @@ -28,12 +28,13 @@ class ContactList(urwid.WidgetWrap, QuickContactList): signals = ['click','change'] - def __init__(self, host, CM, on_click=None, on_change=None, user_data=None): + def __init__(self, host, on_click=None, on_change=None, user_data=None): self.host = host self.selected = None self.groups={} self.alert_jid=set() self.show_status = False + self.show_disconnected = False #we now build the widget self.frame = urwid.Frame(self.__buildList()) @@ -43,9 +44,9 @@ urwid.connect_signal(self, 'click', on_click, user_data) if on_change: urwid.connect_signal(self, 'change', on_change, user_data) - QuickContactList.__init__(self, CM) + QuickContactList.__init__(self) - def _update(self): + def update(self): """Update display, keep focus""" widget, position = self.frame.body.get_focus() self.frame.body = self.__buildList() @@ -53,10 +54,16 @@ self.frame.body.set_focus(position) self.host.redraw() + def update_jid(self, jid): + self.update() + def keypress(self, size, key): if key == "meta s": #user wants to (un)hide contacts' statuses self.show_status = not self.show_status - self._update() + self.update() + elif key == "meta d": #user wants to (un)hide disconnected contacts + self.show_disconnected = not self.show_disconnected + self.update() return super(ContactList, self).keypress(size, key) def __contains__(self, jid): @@ -77,12 +84,12 @@ def putAlert(self, jid): """Put an alert on the jid to get attention from user (e.g. for new message)""" self.alert_jid.add(jid.short) - self._update() + self.update() def __groupClicked(self, group_wid): group = self.groups[group_wid.getValue()] group[0] = not group[0] - self._update() + self.update() self.setFocus(group_wid.getValue()) def __contactClicked(self, contact_wid, selected): @@ -92,7 +99,7 @@ widget.setState(widget.data == self.selected, invisible=True) if self.selected in self.alert_jid: self.alert_jid.remove(self.selected) - self._update() + self.update() self._emit('click') def __buildContact(self, content, param_contacts): @@ -100,13 +107,20 @@ @param content: widget list, e.g. SimpleListWalker @param contacts: list of JID""" contacts = list(param_contacts) - contacts.sort() + + widgets = [] #list of built widgets + for contact in contacts: jid=JID(contact) - name = self.CM.getAttr(jid,'name') - nick = self.CM.getAttr(jid,'nick') - status = self.CM.getAttr(jid, 'status') - show = self.CM.getAttr(jid, 'show') + name = self.getCache(jid, 'name') + nick = self.getCache(jid, 'nick') + status = self.getCache(jid, 'status') + show = self.getCache(jid, 'show') + if show == None: + show = "unavailable" + if (not self.show_disconnected and show == "unavailable" + and not contact in self.alert_jid and contact != self.selected): + continue show_icon, show_attr = const_SHOW_ICON.get(show,('','default')) contact_disp = ('alert' if contact in self.alert_jid else show_attr, nick or name or jid.node or jid.short) display = [ show_icon + " " , contact_disp] @@ -118,6 +132,12 @@ selected = contact==self.selected, header=header) widget.data = contact + widget.comp = contact_disp[1].lower() #value to use for sorting + widgets.append(widget) + + widgets.sort(key=lambda widget: widget.comp) + + for widget in widgets: content.append(widget) urwid.connect_signal(widget, 'change', self.__contactClicked) @@ -125,7 +145,7 @@ """Build the main contact list widget""" content = urwid.SimpleListWalker([]) group_keys = self.groups.keys() - group_keys.sort() + group_keys.sort(key = lambda x: x.lower() if x else x) for key in group_keys: unfolded = self.groups[key][0] if key!=None: @@ -145,28 +165,30 @@ widget.setState(False, invisible=True) - def get_contact(self): + def getContact(self): """Return contact currently selected""" return self.selected - def clear_contacts(self): + def clearContacts(self): """clear all the contact list""" self.groups={} self.selected = None self.unselectAll() - self._update() + self.update() - def replace(self, jid, groups=[None]): + def replace(self, jid, groups=None, attributes=None): """add a contact to the list if doesn't exist, else update it""" + if not groups: + groups = [None] + if not attributes: + attributes={} assert isinstance(groups, list) assert isinstance(jid, JID) - if not groups: - groups=[None] for group in groups: if not self.groups.has_key(group): self.groups[group] = [True,set()] #[unfold,list_of_contacts] self.groups[group][1].add(jid.short) - self._update() + self.update() """contacts = self.list_wid.getAllValues() @@ -176,10 +198,6 @@ self.list_wid.changeValues(contacts) self._emit('change')""" - def disconnect(self, jid): - """mark a contact disconnected""" - self.remove(jid.short) - def remove(self, param_jid): """remove a contact from the list""" groups_to_remove = [] @@ -192,7 +210,7 @@ groups_to_remove.append(group) for group in groups_to_remove: del self.groups[group] - self._update() + self.update() def add(self, jid, param_groups=[None]): """add a contact to the list""" diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/primitivus/primitivus --- a/frontends/src/primitivus/primitivus Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/primitivus/primitivus Tue Sep 25 00:58:34 2012 +0200 @@ -56,7 +56,6 @@ class PrimitivusApp(QuickApp): def __init__(self): - self.CM = QuickContactManagement() #FIXME: not the best place QuickApp.__init__(self) ## main loop setup ## @@ -141,12 +140,12 @@ elif input == 'f2': #user wants to (un)hide the contact_list try: center_widgets = self.center_part.widget_list - if self.contactList in center_widgets: + if self.contact_list in center_widgets: self.center_part.set_focus(0) #necessary as the focus change to the next object, we can go out of range if we are on the last object of self.center_part - center_widgets.remove(self.contactList) + center_widgets.remove(self.contact_list) del self.center_part.column_types[0] else: - center_widgets.insert(0, self.contactList) + center_widgets.insert(0, self.contact_list) self.center_part.column_types.insert(0, ('weight', 2)) except AttributeError: #The main widget is not built (probably in Profile Manager) @@ -196,9 +195,9 @@ return menu_roller def __buildMainWidget(self): - self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw()) - #self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))]) - self.center_part = urwid.Columns([('weight',2,self.contactList), ('weight',8,urwid.Filler(urwid.Text('')))]) + self.contact_list = ContactList(self, on_click = self.contactSelected, on_change=lambda w: self.redraw()) + #self.center_part = urwid.Columns([('weight',2,self.contact_list),('weight',8,Chat('',self))]) + self.center_part = urwid.Columns([('weight',2,self.contact_list), ('weight',8,urwid.Filler(urwid.Text('')))]) self.editBar = sat_widgets.AdvancedEdit(u'> ') self.editBar.setCompletionMethod(self._nick_completion) urwid.connect_signal(self.editBar,'click',self.onTextEntered) @@ -209,7 +208,7 @@ def _nick_completion(self, text, completion_data): """Completion method which complete pseudo in group chat for params, see AdvancedEdit""" - contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectable at once + contact = self.contact_list.getContact() ###Based on the fact that there is currently only one contact selectable at once if contact: chat = self.chat_wins[contact] if chat.type != "group": @@ -266,7 +265,7 @@ wid_idx = len(self.center_part.widget_list)-1 self.center_part.widget_list[wid_idx] = widget self.menu_roller.removeMenu(_('Chat menu')) - self.contactList.unselectAll() + self.contact_list.unselectAll() self.redraw() def removeWindow(self): @@ -289,7 +288,7 @@ self.notBar.setProgress(percentage) def contactSelected(self, contact_list): - contact = contact_list.get_contact() + contact = contact_list.getContact() if contact: assert(len(self.center_part.widget_list)==2) self.center_part.widget_list[1] = self.chat_wins[contact] @@ -297,7 +296,7 @@ def onTextEntered(self, editBar): """Called when text is entered in the main edit bar""" - contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectableat once + contact = self.contact_list.getContact() ###Based on the fact that there is currently only one contact selectableat once if contact: chat = self.chat_wins[contact] self.bridge.sendMessage(contact, @@ -311,8 +310,8 @@ return QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) sender = JID(from_jid) - if JID(self.contactList.selected).short != sender.short: - self.contactList.putAlert(sender) + if JID(self.contact_list.selected).short != sender.short: + self.contact_list.putAlert(sender) def _dialogOkCb(self, widget, data): self.removePopUp() @@ -422,8 +421,8 @@ def onRemoveContact(self, button): self.removePopUp() - info(_("Unsubscribing %s presence"),self.contactList.get_contact()) - self.bridge.delContact(self.contactList.get_contact(), profile_key=self.profile) + info(_("Unsubscribing %s presence"),self.contact_list.getContact()) + self.bridge.delContact(self.contact_list.getContact(), profile_key=self.profile) #MENU EVENTS# def onConnectRequest(self, menu): @@ -460,7 +459,7 @@ self.showPopUp(pop_up_widget) def onRemoveContactRequest(self, menu): - contact = self.contactList.get_contact() + contact = self.contact_list.getContact() if not contact: self.showPopUp(sat_widgets.Alert(_("Error"), _("You have not selected any contact to delete !"), ok_cb=self.removePopUp)) else: diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/quick_frontend/quick_app.py --- a/frontends/src/quick_frontend/quick_app.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/quick_frontend/quick_app.py Tue Sep 25 00:58:34 2012 +0200 @@ -19,7 +19,7 @@ along with this program. If not, see . """ -from logging import debug, info, error +from logging import debug, info, warning, error from sat.tools.jid import JID from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService from optparse import OptionParser @@ -32,7 +32,6 @@ """This class contain the main methods needed for the frontend""" def __init__(self, single_profile=True): - self.rosterList = {} self.profiles = {} self.single_profile = single_profile self.check_options() @@ -157,7 +156,7 @@ ### now we fill the contact list ### for contact in self.bridge.getContacts(profile): - self.newContact(contact[0], contact[1], contact[2], profile) + self.newContact(*contact, profile=profile) presences = self.bridge.getPresenceStatus(profile) for contact in presences: @@ -207,8 +206,7 @@ if not self.check_profile(profile): return debug(_("Disconnected")) - self.CM.clear() - self.contactList.clear_contacts() + self.contactList.clearContacts() self.setStatusOnline(False) def connectionError(self, error_type, profile): @@ -227,10 +225,7 @@ return entity=JID(JabberId) _groups = list(groups) - self.rosterList[entity.short]=(dict(attributes), _groups) - if entity in self.CM: - self.CM.update(entity, 'groups', _groups) - self.contactList.replace(entity, self.CM.getAttr(entity, 'groups')) + self.contact_list._replace(entity, _groups, attributes) def newMessage(self, from_jid, msg, type, to_jid, profile): if not self.check_profile(profile): @@ -266,36 +261,20 @@ self.setStatusOnline(False) return + self.contact_list.updatePresence(from_jid, show, priority, statuses) + if show != 'unavailable': - name="" - groups = [] - if self.rosterList.has_key(from_jid.short): - if self.rosterList[from_jid.short][0].has_key("name"): - name=self.rosterList[from_jid.short][0]["name"] - groups=self.rosterList[from_jid.short][1] - + #FIXME: must be moved in a plugin if from_jid.short in self.profiles[profile]['watched'] and not from_jid.short in self.profiles[profile]['onlineContact']: self.showAlert(_("Watched jid [%s] is connected !") % from_jid.short) self.profiles[profile]['onlineContact'].add(from_jid) #FIXME onlineContact is useless with CM, must be removed - self.CM.add(from_jid) - self.CM.update(from_jid, 'name', unicode(name)) - self.CM.update(from_jid, 'show', show) - self.CM.update(from_jid, 'statuses', statuses) - self.CM.update(from_jid, 'groups', groups) - cache = self.bridge.getCardCache(from_jid, profile) - if cache.has_key('nick'): - self.CM.update(from_jid, 'nick', unicode(cache['nick'])) - if cache.has_key('avatar'): - self.CM.update(from_jid, 'avatar', self.bridge.getAvatarFile(cache['avatar'])) - self.contactList.replace(from_jid, self.CM.getAttr(from_jid, 'groups')) + + #TODO: vcard data (avatar) if show=="unavailable" and from_jid in self.profiles[profile]['onlineContact']: self.profiles[profile]['onlineContact'].remove(from_jid) - self.CM.remove(from_jid) - if not self.CM.isConnected(from_jid): - self.contactList.disconnect(from_jid) def roomJoined(self, room_jid, room_nicks, user_nick, profile): """Called when a MUC room is joined""" @@ -491,27 +470,27 @@ if not self.check_profile(profile): return target = JID(jid) - self.contactList.remove(self.CM.get_full(target)) - self.CM.remove(target) + self.contactList.remove(target) try: self.profiles[profile]['onlineContact'].remove(target.short) except KeyError: pass def updatedValue(self, name, data, profile): + #FIXME: to be removed if not self.check_profile(profile): return if name == "card_nick": target = JID(data['jid']) if target in self.contactList: - self.CM.update(target, 'nick', unicode(data['nick'])) - self.contactList.replace(target) + #self.CM.update(target, 'nick', unicode(data['nick'])) + self.contact_list._replace(target) elif name == "card_avatar": target = JID(data['jid']) if target in self.contactList: filename = self.bridge.getAvatarFile(data['avatar']) - self.CM.update(target, 'avatar', filename) - self.contactList.replace(target) + #self.CM.update(target, 'avatar', filename) + self.contact_list._replace(target) def askConfirmation(self, type, id, data): raise NotImplementedError diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/quick_frontend/quick_chat.py --- a/frontends/src/quick_frontend/quick_chat.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/quick_frontend/quick_chat.py Tue Sep 25 00:58:34 2012 +0200 @@ -99,7 +99,7 @@ def _get_nick(self, jid): """Return nick of this jid when possible""" - return jid.resource if self.type == "group" else (self.host.CM.getAttr(jid,'nick') or self.host.CM.getAttr(jid,'name') or jid.node) + return jid.resource if self.type == "group" else (self.host.contact_list.getCache(jid,'nick') or self.host.contact_list.getCache(jid,'name') or jid.node) def printMessage(self, from_jid, msg, profile, timestamp): """Print message in chat window. Must be implemented by child class""" diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/quick_frontend/quick_contact_list.py --- a/frontends/src/quick_frontend/quick_contact_list.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/quick_frontend/quick_contact_list.py Tue Sep 25 00:58:34 2012 +0200 @@ -19,35 +19,49 @@ along with this program. If not, see . """ -from logging import debug, info, error -from sat.tools.jid import JID +from logging import debug class QuickContactList(): """This class manage the visual representation of contacts""" - def __init__(self, CM): - """ - @param CM: instance of QuickContactManagement - """ + def __init__(self): debug(_("Contact List init")) - self.CM = CM + self._cache = {} + + def update_jid(self, jid): + """Update the jid in the list when something changed""" + raise NotImplementedError + + def getCache(self, jid, name): + try: + jid_cache = self._cache[jid.short] + if name == 'status': #XXX: we get the first status for 'status' key + return jid_cache['statuses'].get('default','') + return jid_cache[name] + except (KeyError, IndexError): + return None + + def setCache(self, jid, name, value): + jid_cache = self._cache.setdefault(jid.short, {}) + jid_cache[name] = value def __contains__(self, jid): raise NotImplementedError - def clear_contacts(self, jid): + def clearContacts(self, jid): """Clear all the contact list""" raise NotImplementedError - def replace(self, jid, groups=None): + def _replace(self, jid, groups=None, attributes=None): + if 'name' in attributes: + self.setCache(jid, 'name', attributes['name']) + self.replace(jid, groups, attributes) + + def replace(self, jid, groups, attributes): """add a contact to the list if doesn't exist, else update it""" raise NotImplementedError - def disconnect(self, jid): - """mark a contact disconnected""" - raise NotImplementedError - def remove(self, jid): """remove a contact from the list""" raise NotImplementedError @@ -55,3 +69,14 @@ def add(self, jid, param_groups=None): """add a contact to the list""" raise NotImplementedError + + def updatePresence(self, jid, show, priority, statuses): + """Update entity's presence status + @param jid: entity to update's jid + @param show: availability + @parap priority: resource's priority + @param statuses: dict of statuses""" + self.setCache(jid, 'show', show) + self.setCache(jid, 'prority', priority) + self.setCache(jid, 'statuses', statuses) + self.update_jid(jid) diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/wix/chat.py --- a/frontends/src/wix/chat.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/wix/chat.py Tue Sep 25 00:58:34 2012 +0200 @@ -258,7 +258,12 @@ 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) + #FIXME: check last_resource: what if self.target.resource exists ? + last_resource = self.host.bridge.getLastResource(unicode(self.target.short), self.host.profile) + if last_resource: + full_jid = JID("%s/%s" % (self.target.short, last_resource)) + else: + full_jid = self.target id = self.host.bridge.sendFile(full_jid, filename, {}, self.host.profile) self.host.waitProgress(id, _("File Transfer"), _("Copying %s") % os.path.basename(filename)) diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/wix/contact_list.py --- a/frontends/src/wix/contact_list.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/wix/contact_list.py Tue Sep 25 00:58:34 2012 +0200 @@ -44,7 +44,7 @@ "CUSTOM" for a customized contact list (self.__presentItem must then be overrided) """ wx.SimpleHtmlListBox.__init__(self, parent, -1) - QuickContactList.__init__(self, host.CM) + QuickContactList.__init__(self) self.host = host self.type = type self.__typeSwitch() @@ -72,17 +72,19 @@ result.append(i) return result - def replace(self, contact, groups=None): + def update_jid(self, jid): + self.replace(jid) + + def replace(self, contact, groups=None, attributes=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)) + _present = self.__presentItem(contact) + if _present != None: + self.SetString(i, _present) - 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 @@ -108,15 +110,19 @@ 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] + name = self.getCache(jid,'name') + nick = self.getCache(jid,'nick') + _show = self.getCache(jid,'show') + if _show == None or _show == 'unavailable': + return None + show = filter(lambda x : x[0] == _show, const_STATUS)[0] + #show[0]==shortcut #show[1]==human readable #show[2]==color (or None) show_html = "[%s]" % (show[2], show[1]) if show[2] else "" - status = self.CM.getAttr(jid,'status') or '' - avatar = self.CM.getAttr(jid,'avatar') or self.empty_avatar #XXX: there is a weird bug here: if the image has an extension (i.e. empty_avatar.png), + status = self.getCache(jid,'status') or '' + avatar = self.getCache(jid,'avatar') or self.empty_avatar #XXX: there is a weird bug here: if the image has an extension (i.e. empty_avatar.png), #WxPython segfault, and it doesn't without nothing. I couldn't reproduce the case with a basic test script, so it need further investigation before reporting it #to WxPython dev. Anyway, the program crash with a segfault, not a python exception, so there is definitely something wrong with WxPython. #The case seems to happen when SimpleHtmlListBox parse the HTML with the tag @@ -138,7 +144,7 @@ return html - def clear_contacts(self): + def clearContacts(self): """Clear all the contact list""" self.Clear() @@ -146,7 +152,9 @@ """add a contact to the list""" debug (_("adding %s"),contact) if not groups: - idx = self.Insert(self.__presentItem(contact), 0, contact) + _present = self.__presentItem(contact) + if _present: + idx = self.Insert(_present, 0, contact) else: for group in groups: indexes = self.__find_idx(group) @@ -156,7 +164,9 @@ else: gp_idx = indexes[0] - self.Insert(self.__presentItem(contact), gp_idx+1, contact) + _present = self.__presentItem(contact) + if _present: + self.Insert(_present, gp_idx+1, contact) @@ -176,11 +186,13 @@ 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 = [JID(contact) for contact in self.host.bridge.getContactsFromGroup(group, self.host.profile)] contacts.sort() id_insert = self.GetSelection()+1 for contact in contacts: - self.Insert(self.__presentItem(contact), id_insert, contact) + _present = self.__presentItem(contact) + if _present: + self.Insert(_present, id_insert, contact) self.SetSelection(wx.NOT_FOUND) self.ScrollToLine(first_visible) event.Skip(False) diff -r 00d3679976ab -r e9634d2e7b38 frontends/src/wix/main_window.py --- a/frontends/src/wix/main_window.py Wed Sep 05 00:19:32 2012 +0200 +++ b/frontends/src/wix/main_window.py Tue Sep 25 00:58:34 2012 +0200 @@ -64,17 +64,16 @@ def __init__(self): QuickApp.__init__(self) wx.Frame.__init__(self,None, title="SàT Wix", size=(300,500)) - self.CM = QuickContactManagement() #FIXME: not the best place #sizer self.sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(self.sizer) #Frame elements - self.contactList = ContactList(self, self) - self.contactList.registerActivatedCB(self.onContactActivated) - self.contactList.Hide() - self.sizer.Add(self.contactList, 1, flag=wx.EXPAND) + self.contact_list = ContactList(self, self) + self.contact_list.registerActivatedCB(self.onContactActivated) + self.contact_list.Hide() + self.sizer.Add(self.contact_list, 1, flag=wx.EXPAND) self.chat_wins=ChatList(self) self.CreateStatusBar() @@ -120,7 +119,7 @@ """Hide profile panel then plug profile""" debug (_('plugin profile %s' % profile_key)) self.profile_pan.Hide() - self.contactList.Show() + self.contact_list.Show() self.sizer.Layout() for i in range(self.menuBar.GetMenuCount()): self.menuBar.EnableTop(i, True) @@ -410,7 +409,7 @@ def onRemoveContact(self, e): debug(_("Remove contact request")) - target = self.contactList.getSelection() + target = self.contact_list.getSelection() if not target: dlg = wx.MessageDialog(self, _("You haven't selected any contact !"), _('Error'), @@ -433,7 +432,7 @@ def onShowProfile(self, e): debug(_("Show contact's profile request")) - target = self.contactList.getSelection() + target = self.contact_list.getSelection() if not target: dlg = wx.MessageDialog(self, _("You haven't selected any contact !"), _('Error'), diff -r 00d3679976ab -r e9634d2e7b38 src/bridge/DBus.py --- a/src/bridge/DBus.py Wed Sep 05 00:19:32 2012 +0200 +++ b/src/bridge/DBus.py Tue Sep 25 00:58:34 2012 +0200 @@ -256,6 +256,12 @@ return self._callback("getContacts", unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, + in_signature='ss', out_signature='as', + async_callbacks=None) + def getContactsFromGroup(self, group, profile_key="@DEFAULT@"): + return self._callback("getContactsFromGroup", unicode(group), unicode(profile_key)) + + @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='ssib', out_signature='a(dsss)', async_callbacks=('callback', 'errback')) def getHistory(self, from_jid, to_jid, limit, between=True, callback=None, errback=None): @@ -366,7 +372,7 @@ @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, in_signature='sssss', out_signature='', async_callbacks=None) - def sendMessage(self, to_jid, message, subject='', mess_type="chat", profile_key="@DEFAULT@"): + def sendMessage(self, to_jid, message, subject='', mess_type="auto", profile_key="@DEFAULT@"): return self._callback("sendMessage", unicode(to_jid), unicode(message), unicode(subject), unicode(mess_type), unicode(profile_key)) @dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX, diff -r 00d3679976ab -r e9634d2e7b38 src/bridge/bridge_constructor/bridge_template.ini --- a/src/bridge/bridge_constructor/bridge_template.ini Wed Sep 05 00:19:32 2012 +0200 +++ b/src/bridge/bridge_constructor/bridge_template.ini Tue Sep 25 00:58:34 2012 +0200 @@ -271,6 +271,17 @@ - list of attributes as in [newContact] - groups where the contact is +[getContactsFromGroup] +type=method +category=core +sig_in=ss +sig_out=as +param_1_default="@DEFAULT@" +doc=Return informations about all contacts +doc_param_0=group: name of the group to check +doc_param_1=%(doc_profile_key)s +doc_return=array of jids + [getLastResource] type=method category=core diff -r 00d3679976ab -r e9634d2e7b38 src/core/exceptions.py --- a/src/core/exceptions.py Wed Sep 05 00:19:32 2012 +0200 +++ b/src/core/exceptions.py Tue Sep 25 00:58:34 2012 +0200 @@ -30,3 +30,6 @@ class UnknownEntityError(Exception): pass + +class UnknownGroupError(Exception): + pass diff -r 00d3679976ab -r e9634d2e7b38 src/core/sat_main.py --- a/src/core/sat_main.py Wed Sep 05 00:19:32 2012 +0200 +++ b/src/core/sat_main.py Tue Sep 25 00:58:34 2012 +0200 @@ -126,6 +126,7 @@ self.bridge.register("asyncConnect", self.asyncConnect) self.bridge.register("disconnect", self.disconnect) self.bridge.register("getContacts", self.getContacts) + self.bridge.register("getContactsFromGroup", self.getContactsFromGroup) self.bridge.register("getLastResource", self.memory.getLastResource) self.bridge.register("getPresenceStatus", self.memory.getPresenceStatus) self.bridge.register("getWaitingSub", self.memory.getWaitingSub) @@ -276,8 +277,7 @@ def getContacts(self, profile_key): client = self.getClient(profile_key) if not client: - error(_('Asking contacts for a non-existant profile')) - return [] + raise ProfileUnknownError(_('Asking contacts for a non-existant profile')) ret = [] for item in client.roster.getItems(): #we get all item for client's roster #and convert them to expected format @@ -285,6 +285,12 @@ ret.append([item.jid.userhost(), attr, item.groups]) return ret + def getContactsFromGroup(self, group, profile_key): + client = self.getClient(profile_key) + if not client: + raise ProfileUnknownError(_("Asking group's contacts for a non-existant profile")) + return client.roster.getJidsFromGroup(group) + def purgeClient(self, profile): """Remove reference to a profile client and purge cache the garbage collector can then free the memory""" diff -r 00d3679976ab -r e9634d2e7b38 src/core/xmpp.py --- a/src/core/xmpp.py Wed Sep 05 00:19:32 2012 +0200 +++ b/src/core/xmpp.py Tue Sep 25 00:58:34 2012 +0200 @@ -23,6 +23,7 @@ from twisted.words.protocols.jabber import jid, xmlstream from wokkel import client, disco, xmppim, generic, compat from logging import debug, info, error +from sat.core import exceptions class SatXMPPClient(client.XMPPClient): @@ -164,9 +165,9 @@ """Return dictionary of attributes as used in bridge from a RosterItem @param item: RosterItem @return: dictionary of attributes""" - item_attr = {'to': str(item.subscriptionTo), - 'from': str(item.subscriptionFrom), - 'ask': str(item.ask) + item_attr = {'to': unicode(item.subscriptionTo), + 'from': unicode(item.subscriptionFrom), + 'ask': unicode(item.ask) } if item.name: item_attr['name'] = item.name @@ -235,6 +236,12 @@ def getItems(self): """Return all items of the roster""" return self._jids.values() + + def getJidsFromGroup(self, group): + try: + return self._groups[group] + except KeyError: + return exceptions.UnknownGroupError class SatPresenceProtocol(xmppim.PresenceClientProtocol): diff -r 00d3679976ab -r e9634d2e7b38 src/tools/misc.py --- a/src/tools/misc.py Wed Sep 05 00:19:32 2012 +0200 +++ b/src/tools/misc.py Tue Sep 25 00:58:34 2012 +0200 @@ -55,7 +55,7 @@ """This put a trigger point All the trigger for that point will be run @param point_name: name of the trigger point - @return: True if the action must be continue, False else""" + @return: True if the action must be continued, False else""" if not self.__triggers.has_key(point_name): return True