New metacontact way. JEP is not published yet, but I talked with the author. Old metacontact info are removed automaticaly

This commit is contained in:
Yann Leboulanger 2006-03-24 12:55:56 +00:00
parent 21a27b6072
commit f8db75e255
6 changed files with 302 additions and 287 deletions

View File

@ -302,7 +302,6 @@ class ChatControlBase(MessageControl):
# NOTE: handles mykeypress which is custom signal connected to this # NOTE: handles mykeypress which is custom signal connected to this
# CB in new_tab(). for this singal see message_textview.py # CB in new_tab(). for this singal see message_textview.py
jid = self.contact.jid
message_textview = widget message_textview = widget
message_buffer = message_textview.get_buffer() message_buffer = message_textview.get_buffer()
start_iter, end_iter = message_buffer.get_bounds() start_iter, end_iter = message_buffer.get_bounds()
@ -1450,12 +1449,7 @@ class ChatControl(ChatControlBase):
gajim.interface.roster.draw_contact(jid, self.account) gajim.interface.roster.draw_contact(jid, self.account)
# Redraw parent too # Redraw parent too
contact = gajim.contacts.get_contact_with_highest_priority(self.account, gajim.interface.roster.draw_parent_contact(jid, self.account)
jid)
if gajim.contacts.is_subcontact(self.account, contact):
parent_contact = gajim.contacts.get_parent_contact(
self.account, contact)
gajim.interface.roster.draw_contact(parent_contact.jid, self.account)
if gajim.interface.systray_enabled: if gajim.interface.systray_enabled:
gajim.interface.systray.remove_jid(jid, self.account, typ) gajim.interface.systray.remove_jid(jid, self.account, typ)
if (self.contact.show == 'offline' or self.contact.show == 'error'): if (self.contact.show == 'offline' or self.contact.show == 'error'):

View File

@ -342,8 +342,8 @@ class Connection(ConnectionHandlers):
con.RegisterDisconnectHandler(self._disconnectedReconnCB) con.RegisterDisconnectHandler(self._disconnectedReconnCB)
gajim.log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'], gajim.log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'],
self._current_host['port'], con_type)) self._current_host['port'], con_type))
# Ask meta_contacts before roster # Ask metacontacts before roster
self.get_meta_contacts() self.get_metacontacts()
self._register_handlers(con, con_type) self._register_handlers(con, con_type)
return True return True
@ -494,8 +494,8 @@ class Connection(ConnectionHandlers):
if self.connection: if self.connection:
con.set_send_timeout(self.keepalives, self.send_keepalive) con.set_send_timeout(self.keepalives, self.send_keepalive)
self.connection.onreceive(None) self.connection.onreceive(None)
# Ask meta_contacts before roster # Ask metacontacts before roster
self.get_meta_contacts() self.get_metacontacts()
def change_status(self, show, msg, sync = False, auto = False): def change_status(self, show, msg, sync = False, auto = False):
if not show in STATUS_LIST: if not show in STATUS_LIST:
@ -807,26 +807,35 @@ class Connection(ConnectionHandlers):
iq5 = iq4.setTagData('password', bm['password']) iq5 = iq4.setTagData('password', bm['password'])
self.connection.send(iq) self.connection.send(iq)
def get_meta_contacts(self): def get_metacontacts(self):
'''Get meta_contacts list from storage as described in JEP 0049''' '''Get metacontacts list from storage as described in JEP 0049'''
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='get') iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private') iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='storage', namespace='storage:metacontacts')
self.connection.send(iq)
#FIXME: remove the old infos, remove that before 0.10
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq2.addChild(name='gajim', namespace='gajim:metacontacts') iq2.addChild(name='gajim', namespace='gajim:metacontacts')
self.connection.send(iq) self.connection.send(iq)
def store_meta_contacts(self, children_list): def store_metacontacts(self, tags_list):
''' Send meta contacts to the storage namespace ''' ''' Send meta contacts to the storage namespace '''
if not self.connection: if not self.connection:
return return
iq = common.xmpp.Iq(typ='set') iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace='jabber:iq:private') iq2 = iq.addChild(name='query', namespace='jabber:iq:private')
iq3 = iq2.addChild(name='gajim', namespace='gajim:metacontacts') iq3 = iq2.addChild(name='storage', namespace='storage:metacontacts')
for parent_jid in children_list: for tag in tags_list:
parent_tag = iq3.addChild(name='parent', attrs = {'name': parent_jid}) for data in tags_list[tag]:
for child_jid in children_list[parent_jid]: jid = data['jid']
parent_tag.addChild(name='child', attrs = {'name': child_jid}) dict_ = {'jid': jid, 'tag': tag}
if data.has_key('priority'):
dict_['priority'] = data['priority']
iq3.addChild(name = 'meta', attrs = dict_)
self.connection.send(iq) self.connection.send(iq)
def send_agent_status(self, agent, ptype): def send_agent_status(self, agent, ptype):

View File

@ -1061,20 +1061,23 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
self.bookmarks.append(bm) self.bookmarks.append(bm)
self.dispatch('BOOKMARKS', self.bookmarks) self.dispatch('BOOKMARKS', self.bookmarks)
gajim_tag = query.getTag('gajim') elif ns == 'storage:metacontacts':
if gajim_tag: # Metacontact tags
ns = gajim_tag.getNamespace() # http://www.jabber.org/jeps/jep-XXXX.html
if ns == 'gajim:metacontacts': meta_list = {}
# Meta contacts list metas = storage.getTags('meta')
children_list = {} for meta in metas:
for child in gajim_tag.getChildren(): jid = meta.getAttr('jid')
parent_jid = child.getAttr('name') tag = meta.getAttr('tag')
if not parent_jid: data = {'jid': jid}
continue prio = meta.getAttr('priority')
children_list[parent_jid] = [] if prio != None:
for cchild in child.getChildren(): data['priority'] = prio
children_list[parent_jid].append(cchild.getAttr('name')) if meta_list.has_key(tag):
self.dispatch('META_CONTACTS', children_list) meta_list[tag].append(data)
else:
meta_list[tag] = [data]
self.dispatch('METACONTACTS', meta_list)
# We can now continue connection by requesting the roster # We can now continue connection by requesting the roster
self.connection.initRoster() self.connection.initRoster()
@ -1087,14 +1090,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco)
def _PrivateErrorCB(self, con, iq_obj): def _PrivateErrorCB(self, con, iq_obj):
gajim.log.debug('PrivateErrorCB') gajim.log.debug('PrivateErrorCB')
query = iq_obj.getTag('query') query = iq_obj.getTag('query')
gajim_tag = query.getTag('gajim') storage_tag = query.getTag('storage')
if gajim_tag: if storage_tag:
ns = gajim_tag.getNamespace() ns = gajim_tag.getNamespace()
if ns == 'gajim:metacontacts': if ns == 'storage:metacontacts':
# Private XML Storage (JEP49) is not supported by server # Private XML Storage (JEP49) is not supported by server
# Continue connecting # Continue connecting
self.connection.initRoster() self.connection.initRoster()
def _rosterSetCB(self, con, iq_obj): def _rosterSetCB(self, con, iq_obj):
gajim.log.debug('rosterSetCB') gajim.log.debug('rosterSetCB')
for item in iq_obj.getTag('query').getChildren(): for item in iq_obj.getTag('query').getChildren():

View File

@ -27,7 +27,7 @@ import common.gajim
class Contact: class Contact:
'''Information concerning each contact''' '''Information concerning each contact'''
def __init__(self, jid='', name='', groups=[], show='', status='', sub='', def __init__(self, jid='', name='', groups=[], show='', status='', sub='',
ask='', resource='', priority=5, keyID='', our_chatstate=None, ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, msg_id = None, composing_jep = None): chatstate=None, last_status_time=None, msg_id = None, composing_jep = None):
self.jid = jid self.jid = jid
self.name = name self.name = name
@ -93,25 +93,21 @@ class Contacts:
self._gc_contacts = {} # list of contacts that are in gc {acct: {room_jid: {nick: C}}} self._gc_contacts = {} # list of contacts that are in gc {acct: {room_jid: {nick: C}}}
# For meta contacts: # For meta contacts:
self._children_meta_contacts = {} self._metacontacts_tags = {}
self._parent_meta_contacts = {}
def change_account_name(self, old_name, new_name): def change_account_name(self, old_name, new_name):
self._contacts[new_name] = self._contacts[old_name] self._contacts[new_name] = self._contacts[old_name]
self._gc_contacts[new_name] = self._gc_contacts[old_name] self._gc_contacts[new_name] = self._gc_contacts[old_name]
self._children_meta_contacts[new_name] = self._children_meta_contacts[old_name] self._metacontacts_tags[new_name] = self._metacontacts_tags[old_name]
self._parent_meta_contacts[new_name] = self._parent_meta_contacts[old_name]
del self._contacts[old_name] del self._contacts[old_name]
del self._gc_contacts[old_name] del self._gc_contacts[old_name]
del self._children_meta_contacts[old_name] del self._metacontacts_tags[old_name]
del self._parent_meta_contacts[old_name]
def add_account(self, account): def add_account(self, account):
self._contacts[account] = {} self._contacts[account] = {}
self._gc_contacts[account] = {} self._gc_contacts[account] = {}
if not self._children_meta_contacts.has_key(account): if not self._metacontacts_tags.has_key(account):
self._children_meta_contacts[account] = {} self._metacontacts_tags[account] = {}
self._parent_meta_contacts[account] = {}
def get_accounts(self): def get_accounts(self):
return self._contacts.keys() return self._contacts.keys()
@ -119,11 +115,10 @@ class Contacts:
def remove_account(self, account): def remove_account(self, account):
del self._contacts[account] del self._contacts[account]
del self._gc_contacts[account] del self._gc_contacts[account]
del self._children_meta_contacts[account] del self._metacontacts_tags[account]
del self._parent_meta_contacts[account]
def create_contact(self, jid='', name='', groups=[], show='', status='', def create_contact(self, jid='', name='', groups=[], show='', status='',
sub='', ask='', resource='', priority=5, keyID='', our_chatstate=None, sub='', ask='', resource='', priority=0, keyID='', our_chatstate=None,
chatstate=None, last_status_time=None, composing_jep=None): chatstate=None, last_status_time=None, composing_jep=None):
return Contact(jid, name, groups, show, status, sub, ask, resource, return Contact(jid, name, groups, show, status, sub, ask, resource,
priority, keyID, our_chatstate, chatstate, last_status_time, priority, keyID, our_chatstate, chatstate, last_status_time,
@ -172,11 +167,8 @@ class Contacts:
if not self._contacts[account].has_key(jid): if not self._contacts[account].has_key(jid):
return return
del self._contacts[account][jid] del self._contacts[account][jid]
if self._parent_meta_contacts[account].has_key(jid): # remove metacontacts info
self.remove_subcontact(account, jid) self.remove_metacontact(account, jid)
if self._children_meta_contacts[account].has_key(jid):
for cjid in self._children_meta_contacts[account][jid]:
self.remove_subcontact(account, cjid)
def get_contact(self, account, jid, resource = None): def get_contact(self, account, jid, resource = None):
'''Returns the list of contact instances for this jid (one per resource) '''Returns the list of contact instances for this jid (one per resource)
@ -222,77 +214,120 @@ class Contacts:
return self._contacts[account][jid][0] return self._contacts[account][jid][0]
return None return None
def define_meta_contacts(self, account, children_list): def define_metacontacts(self, account, tags_list):
self._parent_meta_contacts[account] = {} self._metacontacts_tags[account] = tags_list
for parent_jid in children_list:
list = self._children_meta_contacts[account][parent_jid] = [] def get_new_metacontacts_tag(self, jid):
for children_jid in children_list[parent_jid]: if not jid in self._metacontacts_tags.keys():
if not children_jid in list: return jid
list.append(children_jid) #FIXME: can this append ?
self._parent_meta_contacts[account][children_jid] = parent_jid assert False
def add_subcontact(self, account, parent_jid, child_jid): def get_metacontacts_tag(self, account, jid):
self._parent_meta_contacts[account][child_jid] = parent_jid '''Returns the tag of a jid'''
if self._children_meta_contacts[account].has_key(parent_jid): if not self._metacontacts_tags.has_key(account):
list = self._children_meta_contacts[account][parent_jid] return None
if not child_jid in list: for tag in self._metacontacts_tags[account]:
list.append(child_jid) for data in self._metacontacts_tags[account][tag]:
if data['jid'] == jid:
return tag
return None
def add_metacontact(self, brother_account, brother_jid, account, jid):
tag = self.get_metacontacts_tag(brother_account, brother_jid)
if not tag:
tag = self.get_new_metacontacts_tag(brother_jid)
self._metacontacts_tags[brother_account][tag] = [{'jid': brother_jid,
'tag': tag}]
common.gajim.connections[brother_account].store_metacontacts(
self._metacontacts_tags[brother_account])
# be sure jid has no other tag
old_tag = self.get_metacontacts_tag(account, jid)
while old_tag:
self.remove_metacontact(account, jid)
old_tag = self.get_metacontacts_tag(account, jid)
if not self._metacontacts_tags[account].has_key(tag):
self._metacontacts_tags[account][tag] = [{'jid': jid, 'tag': tag}]
else: else:
self._children_meta_contacts[account][parent_jid] = [child_jid] self._metacontacts_tags[account][tag].append({'jid': jid,
common.gajim.connections[account].store_meta_contacts( 'tag': tag})
self._children_meta_contacts[account]) common.gajim.connections[account].store_metacontacts(
self._metacontacts_tags[account])
def remove_subcontact(self, account, child_jid): def remove_metacontact(self, account, jid):
parent_jid = self._parent_meta_contacts[account][child_jid] found = None
self._children_meta_contacts[account][parent_jid].remove(child_jid) for tag in self._metacontacts_tags[account]:
if len(self._children_meta_contacts[account][parent_jid]) == 0: for data in self._metacontacts_tags[account][tag]:
del self._children_meta_contacts[account][parent_jid] if data['jid'] == jid:
del self._parent_meta_contacts[account][child_jid] found = data
common.gajim.connections[account].store_meta_contacts( break
self._children_meta_contacts[account]) if found:
self._metacontacts_tags[account][tag].remove(data)
break
common.gajim.connections[account].store_metacontacts(
self._metacontacts_tags[account])
def is_subcontact(self, account, contact): def has_brother(self, account, jid):
jid = contact.jid for account in self._metacontacts_tags:
if jid in self._parent_meta_contacts[account] and \ tag = self.get_metacontacts_tag(account, jid)
self._contacts[account].has_key( if tag and len(self._metacontacts_tags[account][tag]) > 1:
self._parent_meta_contacts[account][jid]): return True
return True return False
def has_children(self, account, contact): def get_metacontacts_jids(self, tag):
jid = contact.jid '''Returns all jid for the given tag in the form {acct: [jid1, jid2],.}'''
if jid in self._children_meta_contacts[account]: answers = {}
for c in self._children_meta_contacts[account][jid]: for account in self._metacontacts_tags:
if self._contacts[account].has_key(c): if self._metacontacts_tags[account].has_key(tag):
return True answers[account] = []
for data in self._metacontacts_tags[account][tag]:
answers[account].append(data['jid'])
return answers
def get_children_contacts(self, account, contact): def get_metacontacts_family(self, account, jid):
'''Returns the children contacts of contact if it's a parent-contact, '''return the family of the given jid, including jid in the form:
else []''' [{'account': acct, 'jid': jid, 'priority': prio}, ]
jid = contact.jid 'priority' is optional'''
if jid not in self._children_meta_contacts[account]: tag = self.get_metacontacts_tag(account, jid)
if not tag:
return [] return []
contacts = [] answers = []
for j in self._children_meta_contacts[account][jid]: for account in self._metacontacts_tags:
c = self.get_contact_with_highest_priority(account, j) if self._metacontacts_tags[account].has_key(tag):
if c: for data in self._metacontacts_tags[account][tag]:
contacts.append(c) data['account'] = account
return contacts answers.append(data)
return answers
def get_parent_contact(self, account, contact): def _get_data_score(self, data):
'''Returns the parent contact of contact if it's a sub-contact, '''compute thescore of a gived data
else contact''' data is {'jid': jid, 'account': account, 'priority': priority}
if self.is_subcontact(account, contact): priority is optional
parent_jid = self._parent_meta_contacts[account][contact.jid] score = meta_priority*10000 + is_jabber*priority*10 + status'''
return self.get_contact_with_highest_priority(account, parent_jid) jid = data['jid']
return contact account = data['account']
priority = 0
if data.has_key('priority'):
priority = data['priority']
contact = self.get_contact_with_highest_priority(account, jid)
score = priority*10000
if not common.gajim.jid_is_transport(jid):
score += contact.priority*10
score += ['offline', 'invisible', 'dnd', 'xa', 'away', 'chat',
'online'].index(contact.show)
return score
def get_master_contact(self, account, contact): def get_metacontacts_big_brother(self, family):
'''Returns the master contact of contact (parent of parent...) if it's a '''which of the family will be the big brother under wich all
sub-contact, else contact''' others will be ?'''
while self.is_subcontact(account, contact): max_score = 0
parent_jid = self._parent_meta_contacts[account][contact.jid] max_data = family[0]
contact = self.get_contact_with_highest_priority(account, parent_jid) for data in family:
return contact score = self._get_data_score(data)
if score > max_score:
max_score = score
max_data = data
return max_data
def is_pm_from_jid(self, account, jid): def is_pm_from_jid(self, account, jid):
'''Returns True if the given jid is a private message jid''' '''Returns True if the given jid is a private message jid'''

View File

@ -1244,8 +1244,8 @@ class Interface:
password = gajim.gc_passwords[room_jid] password = gajim.gc_passwords[room_jid]
gajim.connections[account].join_gc(nick, room, server, password) gajim.connections[account].join_gc(nick, room, server, password)
def handle_event_meta_contacts(self, account, children_list): def handle_event_metacontacts(self, account, tags_list):
gajim.contacts.define_meta_contacts(account, children_list) gajim.contacts.define_metacontacts(account, tags_list)
def read_sleepy(self): def read_sleepy(self):
'''Check idle status and change that status if needed''' '''Check idle status and change that status if needed'''
@ -1505,7 +1505,7 @@ class Interface:
'VCARD_NOT_PUBLISHED': self.handle_event_vcard_not_published, 'VCARD_NOT_PUBLISHED': self.handle_event_vcard_not_published,
'ASK_NEW_NICK': self.handle_event_ask_new_nick, 'ASK_NEW_NICK': self.handle_event_ask_new_nick,
'SIGNED_IN': self.handle_event_signed_in, 'SIGNED_IN': self.handle_event_signed_in,
'META_CONTACTS': self.handle_event_meta_contacts, 'METACONTACTS': self.handle_event_metacontacts,
} }
gajim.handlers = self.handlers gajim.handlers = self.handlers

View File

@ -169,49 +169,24 @@ class RosterWindow:
gajim.newly_added[account].remove(jid) gajim.newly_added[account].remove(jid)
self.draw_contact(jid, account) self.draw_contact(jid, account)
def add_contact_to_roster(self, jid, account, force = False, def add_contact_to_roster(self, jid, account):
add_children = False):
'''Add a contact to the roster and add groups if they aren't in roster '''Add a contact to the roster and add groups if they aren't in roster
force is about force to add it, even if it is offline and show offline force is about force to add it, even if it is offline and show offline
is False, because it has online children, so we need to show it. is False, because it has online children, so we need to show it.
If add_children is True, we also add all children, even if they were not If add_children is True, we also add all children, even if they were not
already drawn''' already drawn'''
showOffline = gajim.config.get('showoffline') showOffline = gajim.config.get('showoffline')
model = self.tree.get_model()
contact = gajim.contacts.get_first_contact_from_jid(account, jid) contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if not contact: if not contact:
return return
# If contact already in roster, do not add it # If contact already in roster, do not add it
if len(self.get_contact_iter(contact.jid, account)): if len(self.get_contact_iter(jid, account)):
return return
if contact.jid.find('@') <= 0: if contact.jid.find('@') <= 0:
# if not '@' or '@' starts the jid ==> agent # if not '@' or '@' starts the jid ==> agent
contact.groups = [_('Transports')] contact.groups = [_('Transports')]
model = self.tree.get_model()
if gajim.contacts.is_subcontact(account, contact):
if contact.show in ('offline', 'error') and \
not showOffline and not gajim.awaiting_events[account].has_key(jid):
return
parent_contact = gajim.contacts.get_parent_contact(account, contact)
# is parent shown ?
parent_iters = self.get_contact_iter(parent_contact.jid, account)
if not len(parent_iters):
# Add parent and children
self.add_contact_to_roster(parent_contact.jid, account, True)
parent_iters = self.get_contact_iter(parent_contact.jid, account)
name = contact.get_shown_name()
for i in parent_iters:
# we add some values here. see draw_contact for more
model.append(i, (None, name, 'contact', contact.jid, account,
False, None))
if add_children:
for cc in gajim.contacts.get_children_contacts(account, contact):
self.add_contact_to_roster(cc.jid, account)
self.draw_contact(contact.jid, account)
self.draw_avatar(contact.jid, account)
# Redraw parent to change icon
self.draw_contact(parent_contact.jid, account)
return
# JEP-0162 # JEP-0162
hide = True hide = True
if contact.sub in ('both', 'to'): if contact.sub in ('both', 'to'):
@ -228,21 +203,61 @@ class RosterWindow:
else: else:
return return
if observer:
# if he has a tag, remove it
tag = gajim.contacts.get_metacontacts_tag(account, jid)
if tag:
gajim.contacts.remove_metacontact(account, jid)
# family is [{'account': acct, 'jid': jid, 'priority': prio}, ]
# 'priority' is optional
family = gajim.contacts.get_metacontacts_family(account, jid)
shown_family = [] # family members that are in roster.
if family:
for data in family:
_jid = data['jid']
_account = data['account']
if self.get_contact_iter(_jid, _account):
shown_family.append(data)
if _jid == jid:
our_data = data
shown_family.append(our_data)
big_brother_data = gajim.contacts.get_metacontacts_big_brother(
shown_family)
big_brother_jid = big_brother_data['jid']
big_brother_account = big_brother_data['account']
if big_brother_jid != jid:
# We are adding a child contact
if contact.show in ('offline', 'error') and \
not showOffline and not gajim.awaiting_events[account].has_key(jid):
return
parent_iters = self.get_contact_iter(big_brother_jid,
big_brother_account)
name = contact.get_shown_name()
for i in parent_iters:
# we add some values here. see draw_contact for more
model.append(i, (None, name, 'contact', jid, account,
False, None))
self.draw_contact(jid, account)
self.draw_avatar(jid, account)
# Redraw parent to change icon
self.draw_contact(big_brother_jid, big_brother_account)
return
if (contact.show in ('offline', 'error') or hide) and \ if (contact.show in ('offline', 'error') or hide) and \
not showOffline and (not _('Transports') in contact.groups or \ not showOffline and (not _('Transports') in contact.groups or \
gajim.connections[account].connected < 2) and \ gajim.connections[account].connected < 2) and \
not gajim.awaiting_events[account].has_key(jid) and not force: not gajim.awaiting_events[account].has_key(jid):
return return
# Remove child contacts that are already in roster to add them # Remove brother contacts that are already in roster to add them
# under this iter # under this iter
children_contacts = gajim.contacts.get_children_contacts(account, for data in shown_family:
contact) contacts = gajim.contacts.get_contact(data['account'],
ccs = [] # children contacts that were relly in roster data['jid'])
for cc in children_contacts: for c in contacts:
if self.get_contact_iter(cc.jid, account) or add_children: self.remove_contact(c, data['account'])
self.remove_contact(cc, account)
ccs.append(cc)
groups = contact.groups groups = contact.groups
if observer: if observer:
groups = [_('Observers')] groups = [_('Observers')]
@ -256,7 +271,7 @@ class RosterWindow:
self.jabber_state_images['16']['closed'], self.jabber_state_images['16']['closed'],
gtkgui_helpers.escape_for_pango_markup(g), 'group', g, account, gtkgui_helpers.escape_for_pango_markup(g), 'group', g, account,
False, None]) False, None])
if not gajim.groups[account].has_key(g): #It can probably never append if not gajim.groups[account].has_key(g): # It can probably never append
if account + g in self.collapsed_rows: if account + g in self.collapsed_rows:
ishidden = False ishidden = False
else: else:
@ -279,8 +294,10 @@ class RosterWindow:
self.draw_contact(jid, account) self.draw_contact(jid, account)
self.draw_avatar(jid, account) self.draw_avatar(jid, account)
# put the children under this iter # put the children under this iter
for cc in ccs: for data in shown_family:
self.add_contact_to_roster(cc.jid, account) contacts = gajim.contacts.get_contact(data['account'],
data['jid'])
self.add_contact_to_roster(data['jid'], data['account'])
def add_transport_to_roster(self, account, transport): def add_transport_to_roster(self, account, transport):
c = gajim.contacts.create_contact(jid = transport, name = transport, c = gajim.contacts.create_contact(jid = transport, name = transport,
@ -292,20 +309,11 @@ class RosterWindow:
def really_remove_contact(self, contact, account): def really_remove_contact(self, contact, account):
if contact.jid in gajim.newly_added[account]: if contact.jid in gajim.newly_added[account]:
return return
if contact.jid.find('@') < 1 and gajim.connections[account].connected > 1: # It's an agent if contact.jid.find('@') < 1 and gajim.connections[account].connected > 1:
# It's an agent
return return
if contact.jid in gajim.to_be_removed[account]: if contact.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(contact.jid) gajim.to_be_removed[account].remove(contact.jid)
has_connected_children = False
children_contacts = gajim.contacts.get_children_contacts(account,
contact)
for cc in children_contacts:
if cc.show not in ('offline', 'error'):
has_connected_children = True
break
if gajim.config.get('showoffline') or has_connected_children:
self.draw_contact(contact.jid, account)
return
self.remove_contact(contact, account) self.remove_contact(contact, account)
def remove_contact(self, contact, account): def remove_contact(self, contact, account):
@ -313,36 +321,45 @@ class RosterWindow:
if contact.jid in gajim.to_be_removed[account]: if contact.jid in gajim.to_be_removed[account]:
return return
model = self.tree.get_model() model = self.tree.get_model()
for i in self.get_contact_iter(contact.jid, account): iters = self.get_contact_iter(contact.jid, account)
if not iters:
return
parent_iter = model.iter_parent(iters[0])
parent_type = model[parent_iter][C_TYPE]
# remember children to re-add them
children = []
child_iter = model.iter_children(iters[0])
while child_iter:
c_jid = model[child_iter][C_JID].decode('utf-8')
c_account = model[child_iter][C_ACCOUNT].decode('utf-8')
children.append((c_jid, c_account))
child_iter = model.iter_next(child_iter)
# Remove iters and group iter if they are empty
for i in iters:
parent_i = model.iter_parent(i) parent_i = model.iter_parent(i)
model.remove(i) model.remove(i)
if gajim.contacts.is_subcontact(account, contact): if parent_type == 'group':
# Is it the last subcontact with offline parent? group = model[parent_i][C_JID].decode('utf-8')
parent_contact = gajim.contacts.get_parent_contact(account, contact) if model.iter_n_children(parent_i) == 0:
if parent_contact.show in ('offline', 'error'): model.remove(parent_i)
has_another_child = False # We need to check all contacts, even offline contacts
children_contacts = gajim.contacts.get_children_contacts(account, for jid in gajim.contacts.get_jid_list(account):
contact) if group in gajim.contacts.get_contact_with_highest_priority(
for cc in children_contacts: account, jid).groups:
if len(self.get_contact_iter(cc.jid, account)):
has_another_child = True
break break
if not has_another_child: else:
# Remove parent contact if gajim.groups[account].has_key(group):
self.remove_contact(parent_contact, account) del gajim.groups[account][group]
self.draw_contact(parent_contact.jid, account)
return # re-add children
group = model[parent_i][C_JID].decode('utf-8') for child in children:
if model.iter_n_children(parent_i) == 0: self.add_contact_to_roster(child[0], child[1])
model.remove(parent_i) # redraw parent
# We need to check all contacts, even offline contacts if parent_type == 'contact':
for jid in gajim.contacts.get_jid_list(account): parent_jid = model[parent_iter][C_JID].decode('utf-8')
if group in gajim.contacts.get_contact_with_highest_priority( parent_account = model[parent_iter][C_ACCOUNT].decode('utf-8')
account, jid).groups: self.draw_contact(parent_jid, parent_account)
break
else:
if gajim.groups[account].has_key(group):
del gajim.groups[account][group]
def get_appropriate_state_images(self, jid, size = '16', def get_appropriate_state_images(self, jid, size = '16',
icon_name = 'online'): icon_name = 'online'):
@ -431,9 +448,7 @@ class RosterWindow:
size = 'closed', icon_name = icon_name) size = 'closed', icon_name = icon_name)
else: else:
# redraw parent # redraw parent
if gajim.contacts.is_subcontact(account, contact): self.draw_parent_contact(jid, account)
parent_jid = gajim.contacts.get_parent_contact(account, contact).jid
self.draw_contact(parent_jid, account)
state_images = self.get_appropriate_state_images(jid, state_images = self.get_appropriate_state_images(jid,
icon_name = icon_name) icon_name = icon_name)
@ -443,6 +458,18 @@ class RosterWindow:
model[iter][C_IMG] = img model[iter][C_IMG] = img
model[iter][C_NAME] = name model[iter][C_NAME] = name
def draw_parent_contact(self, jid, account):
model = self.tree.get_model()
iters = self.get_contact_iter(jid, account)
if not len(iters):
return
parent_iter = model.iter_parent(iters[0])
if model[parent_iter][C_TYPE] != 'contact':
# parent is not a contact
return
parent_jid = model[parent_iter][C_JID].decode('utf-8')
self.draw_contact(parent_jid, account)
def draw_avatar(self, jid, account): def draw_avatar(self, jid, account):
'''draw the avatar''' '''draw the avatar'''
model = self.tree.get_model() model = self.tree.get_model()
@ -884,16 +911,8 @@ class RosterWindow:
if len(contact_instances) > 1: if len(contact_instances) > 1:
# if multiple resources # if multiple resources
gajim.contacts.remove_contact(account, contact) gajim.contacts.remove_contact(account, contact)
self.draw_contact(contact.jid, account) self.remove_contact(contact, account)
elif not showOffline: self.add_contact_to_roster(contact.jid, account)
# we don't show offline contacts
self.remove_contact(contact, account)
else:
self.draw_contact(contact.jid, account)
else:
if not self.get_contact_iter(contact.jid, account):
self.add_contact_to_roster(contact.jid, account)
self.draw_contact(contact.jid, account)
# print status in chat window and update status/GPG image # print status in chat window and update status/GPG image
for j in (contact.jid, contact.get_full_jid()): for j in (contact.jid, contact.get_full_jid()):
if gajim.interface.msg_win_mgr.has_window(j, account): if gajim.interface.msg_win_mgr.has_window(j, account):
@ -1599,7 +1618,6 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
iter = model.get_iter(path) iter = model.get_iter(path)
type = model[iter][C_TYPE] type = model[iter][C_TYPE]
if type in ('agent', 'contact'): if type in ('agent', 'contact'):
#TODO
account = model[iter][C_ACCOUNT].decode('utf-8') account = model[iter][C_ACCOUNT].decode('utf-8')
jid = model[iter][C_JID].decode('utf-8') jid = model[iter][C_JID].decode('utf-8')
win = None win = None
@ -1665,11 +1683,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
gajim.connections[account].unsubscribe(contact.jid, remove_auth) gajim.connections[account].unsubscribe(contact.jid, remove_auth)
for u in gajim.contacts.get_contact(account, contact.jid): for u in gajim.contacts.get_contact(account, contact.jid):
self.remove_contact(u, account) self.remove_contact(u, account)
ccs = gajim.contacts.get_children_contacts(account, contact)
gajim.contacts.remove_jid(account, u.jid) gajim.contacts.remove_jid(account, u.jid)
for cc in ccs:
cc.groups = contact.groups
self.add_contact_to_roster(cc.jid, account)
if not remove_auth: if not remove_auth:
contact.name = '' contact.name = ''
contact.groups = [] contact.groups = []
@ -2065,10 +2079,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
if no_queue: # We didn't have a queue: we change icons if no_queue: # We didn't have a queue: we change icons
self.draw_contact(jid, account) self.draw_contact(jid, account)
# Redraw parent too # Redraw parent too
if gajim.contacts.is_subcontact(account, contact): self.draw_parent_contact(jid, account)
parent_contact = gajim.contacts.get_parent_contact(account,
contact)
self.draw_contact(parent_contact.jid, account)
if gajim.interface.systray_enabled: if gajim.interface.systray_enabled:
gajim.interface.systray.add_jid(jid, account, kind) gajim.interface.systray.add_jid(jid, account, kind)
self.show_title() # we show the * or [n] self.show_title() # we show the * or [n]
@ -2774,8 +2785,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
# We first compare by show if sort_by_show is True or if it's a child # We first compare by show if sort_by_show is True or if it's a child
# contact # contact
if type1 == 'contact' and type2 == 'contact' and \ if type1 == 'contact' and type2 == 'contact' and \
(gajim.config.get('sort_by_show') or gajim.contacts.is_subcontact( gajim.config.get('sort_by_show'):
account1, contact1)):
cshow = {'online':0, 'chat': 1, 'away': 2, 'xa': 3, 'dnd': 4, cshow = {'online':0, 'chat': 1, 'away': 2, 'xa': 3, 'dnd': 4,
'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8} 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
s = self.get_show(lcontact1) s = self.get_show(lcontact1)
@ -2823,25 +2833,12 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
etime): etime):
# remove the source row # remove the source row
model = self.tree.get_model() model = self.tree.get_model()
for iter in self.get_contact_iter(c_source.jid, account): self.remove_contact(c_source, account)
# get group iter # brother inherite big brother groups
iter_group = iter c_source.groups = []
while model[iter_group][C_TYPE] == 'contact': for g in c_dest.groups:
iter_group = model.iter_parent(iter_group) c_source.groups.append(g)
group = model[iter_group][C_JID].decode('utf-8') gajim.contacts.add_metacontact(account, c_dest.jid, account, c_source.jid)
model.remove(iter)
if model.iter_n_children(iter_group) == 0:
# this was the only child
model.remove(iter_group)
# delete the group if it is empty (need to look for offline users
# too)
for jid in gajim.contacts.get_jid_list(account):
if group in gajim.contacts.get_contact_with_highest_priority(
account, jid).groups:
break
else:
del gajim.groups[account][group]
gajim.contacts.add_subcontact(account, c_dest.jid, c_source.jid)
# Add it under parent contact # Add it under parent contact
self.add_contact_to_roster(c_source.jid, account) self.add_contact_to_roster(c_source.jid, account)
self.draw_contact(c_dest.jid, account) self.draw_contact(c_dest.jid, account)
@ -2851,6 +2848,8 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
etime, grp_source = None): etime, grp_source = None):
if grp_source: if grp_source:
self.remove_contact_from_group(account, c_source, grp_source) self.remove_contact_from_group(account, c_source, grp_source)
# remove tag
gajim.contacts.remove_metacontact(account, c_source.jid)
self.add_contact_to_group(account, c_source, grp_dest) self.add_contact_to_group(account, c_source, grp_dest)
if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY): if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
context.finish(True, True, etime) context.finish(True, True, etime)
@ -2873,17 +2872,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
model = self.tree.get_model() model = self.tree.get_model()
# Make sure contact was in the group # Make sure contact was in the group
contact.groups.remove(group) contact.groups.remove(group)
group_iter = self.get_group_iter(group, account) self.remove_contact(contact, account)
if model.iter_n_children(group_iter) == 1:
# this was the only child
model.remove(group_iter)
# delete the group if it is empty (need to look for offline users too)
for jid in gajim.contacts.get_jid_list(account):
if group in gajim.contacts.get_contact_with_highest_priority(
account, jid).groups:
break
else:
del gajim.groups[account][group]
def drag_data_received_data(self, treeview, context, x, y, selection, info, def drag_data_received_data(self, treeview, context, x, y, selection, info,
etime): etime):
@ -2919,6 +2908,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
gajim.interface.instances['file_transfers'].send_file(account, gajim.interface.instances['file_transfers'].send_file(account,
c_dest, path) c_dest, path)
return return
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2: if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
# dropped before a group : we drop it in the previous group # dropped before a group : we drop it in the previous group
path_dest = (path_dest[0], path_dest[1]-1) path_dest = (path_dest[0], path_dest[1]-1)
@ -2949,14 +2939,13 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
grp_dest = model[iter_dest][C_JID].decode('utf-8') grp_dest = model[iter_dest][C_JID].decode('utf-8')
if grp_dest == _('Transports') or grp_dest == _('Not in Roster'): if grp_dest == _('Transports') or grp_dest == _('Not in Roster'):
return return
if not gajim.contacts.is_subcontact(account, c_source): if context.action == gtk.gdk.ACTION_COPY:
if context.action == gtk.gdk.ACTION_COPY:
self.on_drop_in_group(None, account, c_source, grp_dest, context,
etime)
return
self.on_drop_in_group(None, account, c_source, grp_dest, context, self.on_drop_in_group(None, account, c_source, grp_dest, context,
etime, grp_source) etime)
return return
self.on_drop_in_group(None, account, c_source, grp_dest, context,
etime, grp_source)
return
else: else:
it = iter_dest it = iter_dest
while model[it][C_TYPE] != 'group': while model[it][C_TYPE] != 'group':
@ -2975,20 +2964,6 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
self.on_drop_in_group(None, account, c_source, grp_dest, context, self.on_drop_in_group(None, account, c_source, grp_dest, context,
etime, grp_source) etime, grp_source)
return return
if gajim.contacts.is_subcontact(account, c_source):
# Remove meta contact
#FIXME: doesn't work under windows: http://bugzilla.gnome.org/show_bug.cgi?id=329797
# if context.action == gtk.gdk.ACTION_COPY:
# return
c_source.groups = [grp_dest]
gajim.connections[account].update_contact(jid_source, c_source.name,
c_source.groups)
parent_jid = gajim.contacts.get_parent_contact(account, c_source).jid
gajim.contacts.remove_subcontact(account, jid_source)
context.finish(True, True, etime)
self.add_contact_to_roster(jid_source, account, add_children = True)
self.draw_contact(parent_jid, account)
return
if grp_source == grp_dest: if grp_source == grp_dest:
# Add meta contact # Add meta contact
#FIXME: doesn't work under windows: http://bugzilla.gnome.org/show_bug.cgi?id=329797 #FIXME: doesn't work under windows: http://bugzilla.gnome.org/show_bug.cgi?id=329797
@ -2997,15 +2972,15 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
# return # return
c_dest = gajim.contacts.get_contact_with_highest_priority(account, c_dest = gajim.contacts.get_contact_with_highest_priority(account,
jid_dest) jid_dest)
# are we creating a loop (child is parent and parent is child)? # brother inherite big brother groups
if gajim.contacts.is_subcontact(account, c_dest) and \ c_source.groups = []
gajim.contacts.get_parent_contact(account, c_dest).jid == jid_source: for g in c_dest.groups:
return c_source.groups.append(g)
gajim.contacts.add_subcontact(account, jid_dest, jid_source) gajim.contacts.add_metacontact(account, jid_dest, account, jid_source)
# remove the source row # remove the source row
context.finish(True, True, etime) context.finish(True, True, etime)
# Add it under parent contact # Add it under parent contact
self.add_contact_to_roster(jid_source, account, add_children = True) self.add_contact_to_roster(jid_source, account)
self.draw_contact(jid_dest, account) self.draw_contact(jid_dest, account)
return return
# We upgrade only the first user because user2.groups is a pointer to # We upgrade only the first user because user2.groups is a pointer to
@ -3022,7 +2997,7 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
menu.append(item) menu.append(item)
c_dest = gajim.contacts.get_contact_with_highest_priority(account, c_dest = gajim.contacts.get_contact_with_highest_priority(account,
jid_dest) jid_dest)
item = gtk.MenuItem(_('Make %s as subcontact of %s') % (c_source.name, item = gtk.MenuItem(_('Make %s and %s metacontacts') % (c_source.name,
c_dest.name)) c_dest.name))
item.connect('activate', self.on_drop_in_contact, account, c_source, item.connect('activate', self.on_drop_in_contact, account, c_source,
c_dest, context, etime) c_dest, context, etime)
@ -3077,14 +3052,13 @@ _('If "%s" accepts this request you will know his or her status.') % jid)
if selected_iter is None: if selected_iter is None:
self._last_selected_contact = None self._last_selected_contact = None
return return
contact = model[selected_iter] contact_row = model[selected_iter]
self._last_selected_contact = (contact[C_JID].decode('utf-8'), if contact_row[C_TYPE] != 'contact':
contact[C_ACCOUNT].decode('utf-8'))
# FIXME: we first set last selected contact and then test if contact??
if contact[C_TYPE] != 'contact':
return return
self.draw_contact(contact[C_JID].decode('utf-8'), jid = contact_row[C_JID].decode('utf-8')
contact[C_ACCOUNT].decode('utf-8'), selected = True) account = contact_row[C_ACCOUNT].decode('utf-8')
self._last_selected_contact = (jid, account)
self.draw_contact(jid, account, selected = True)
def __init__(self): def __init__(self):
self.xml = gtk.glade.XML(GTKGUI_GLADE, 'roster_window', APP) self.xml = gtk.glade.XML(GTKGUI_GLADE, 'roster_window', APP)