Newer
Older
## Copyright (C) 2003-2013 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2005-2007 Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Stefan Bethge <stefan AT lanpartei.de>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## James Newton <redshodan AT gmail.com>
## Tomasz Melcer <liori AT exroot.org>
## Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Jonathan Schleifer <js-gajim AT webkeks.org>
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import common.sleepy
import history_window
import dialogs

Yann Leboulanger
committed
import gui_menu_builder
import tooltips
import plugins
import plugins.gui
from common import gajim
from common import helpers
from common.exceptions import GajimGeneralException

Yann Leboulanger
committed
from common import i18n
from common import pep
from common import location_listener
from common import ged

nicfit
committed
from message_window import MessageWindowMgr
from common import dbus_support
if dbus_support.supported:
from nbxmpp.protocol import NS_FILE, NS_ROSTERX
#(icon, name, type, jid, account, editable, second pixbuf)
C_IMG, # image to show state (online, new message etc)
C_NAME, # cellrenderer text that holds contact nickame
C_TYPE, # account, group or contact?
C_JID, # the jid of the row
C_ACCOUNT, # cellrenderer text that holds account name
C_MOOD_PIXBUF,
C_ACTIVITY_PIXBUF,
C_TUNE_PIXBUF,
C_LOCATION_PIXBUF,
C_AVATAR_PIXBUF, # avatar_pixbuf
C_PADLOCK_PIXBUF, # use for account row only

Yann Leboulanger
committed
) = range(11)
"""
Class for main window of the GTK+ interface
"""
def _get_account_iter(self, name, model=None):
"""
Return the gtk.TreeIter of the given account or None if not found
Keyword arguments:
name -- the account name
model -- the data model (default TreeFilterModel)
"""
if model is None:
model = self.modelfilter
if model is None:
return
if self.regroup:
name = 'MERGED'
it = self._iters[name]['account']
if model == self.model or it is None:
return it
try:
return self.modelfilter.convert_child_iter_to_iter(it)
except RuntimeError:
return None
def _get_group_iter(self, name, account, model=None):
"""
Return the gtk.TreeIter of the given group or None if not found
Keyword arguments:
name -- the group name
account -- the account name
model -- the data model (default TreeFilterModel)
"""
model = self.modelfilter
if model is None:
return
if self.regroup:
account = 'MERGED'
if name not in self._iters[account]['groups']:
return None
it = self._iters[account]['groups'][name]
if model == self.model or it is None:
return it
try:
return self.modelfilter.convert_child_iter_to_iter(it)
except RuntimeError:
return None
def _get_self_contact_iter(self, account, model=None):
"""
Return the gtk.TreeIter of SelfContact or None if not found
Keyword arguments:
account -- the account of SelfContact
model -- the data model (default TreeFilterModel)
"""
jid = gajim.get_jid_from_account(account)
its = self._get_contact_iter(jid, account, model=model)
if its:
return its[0]
return None
def _get_contact_iter(self, jid, account, contact=None, model=None):
"""
Return a list of gtk.TreeIter of the given contact
Keyword arguments:
jid -- the jid without resource
account -- the account
contact -- the contact (default None)
model -- the data model (default TreeFilterModel)
"""
model = self.modelfilter
# when closing Gajim model can be none (async pbs?)
if model is None:
return []
if not contact:
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
if not contact:
# We don't know this contact
return []
if account not in self._iters:
return []
if jid not in self._iters[account]['contacts']:
return []
its = self._iters[account]['contacts'][jid]
if not its:
return []
if model == self.model:
return its
its2 = []
for it in its:
try:
its2.append(self.modelfilter.convert_child_iter_to_iter(it))
except RuntimeError:
pass
return its2
def _iter_is_separator(self, model, titer):
"""
Return True if the given iter is a separator
Keyword arguments:
model -- the data model
iter -- the gtk.TreeIter to test
"""
if model[titer][0] == 'SEPARATOR':
return True
return False
#############################################################################
### Methods for adding and removing roster window items
#############################################################################
def add_account(self, account):
"""
Add account to roster and draw it. Do nothing if it is already in
"""
if self._get_account_iter(account):
# Will happen on reconnect or for merged accounts
return
if self.regroup:
# Merged accounts view
show = helpers.get_global_show()
it = self.model.append(None, [
gajim.interface.jabber_state_images['16'][show],
_('Merged accounts'), 'account', '', 'all', None, None, None,

Yann Leboulanger
committed
None, None, None] + [None] * self.nb_ext_renderers)
self._iters['MERGED']['account'] = it
else:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
our_jid = gajim.get_jid_from_account(account)
tls_pixbuf = None
if gajim.account_is_securely_connected(account):
# the only way to create a pixbuf from stock
tls_pixbuf = self.window.render_icon(
gtk.STOCK_DIALOG_AUTHENTICATION,
gtk.ICON_SIZE_MENU)
it = self.model.append(None, [
gajim.interface.jabber_state_images['16'][show],
gobject.markup_escape_text(account), 'account', our_jid,

Yann Leboulanger
committed
account, None, None, None, None, None, tls_pixbuf] +
[None] * self.nb_ext_renderers)
self._iters[account]['account'] = it
self.draw_account(account)

Yann Leboulanger
committed
def add_account_contacts(self, account, improve_speed=True,
draw_contacts=True):
Add all contacts and groups of the given account to roster, draw them
and account

Yann Leboulanger
committed
if improve_speed:
self._before_fill()
jids = gajim.contacts.get_jid_list(account)
for jid in jids:
self.add_contact(jid, account)

Yann Leboulanger
committed
if draw_contacts:
# Do not freeze the GUI when drawing the contacts
if jids:
# Overhead is big, only invoke when needed
self._idle_draw_jids_of_account(jids, account)

Yann Leboulanger
committed
# Draw all known groups
for group in gajim.groups[account]:
self.draw_group(group, account)
self.draw_account(account)

Yann Leboulanger
committed
if improve_speed:
self._after_fill()

Yann Leboulanger
committed
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def _add_group_iter(self, account, group):
"""
Add a group iter in roster and return the newly created iter
"""
if self.regroup:
account_group = 'MERGED'
else:
account_group = account
delimiter = gajim.connections[account].nested_group_delimiter
group_splited = group.split(delimiter)
parent_group = delimiter.join(group_splited[:-1])
if parent_group in self._iters[account_group]['groups']:
iter_parent = self._iters[account_group]['groups'][parent_group]
elif parent_group:
iter_parent = self._add_group_iter(account, parent_group)
if parent_group not in gajim.groups[account]:
if account + parent_group in self.collapsed_rows:
is_expanded = False
else:
is_expanded = True
gajim.groups[account][parent_group] = {'expand': is_expanded}
else:
iter_parent = self._get_account_iter(account, self.model)
iter_group = self.model.append(iter_parent,
[gajim.interface.jabber_state_images['16']['closed'],
gobject.markup_escape_text(group), 'group', group, account, None,
None, None, None, None, None] + [None] * self.nb_ext_renderers)
self.draw_group(group, account)
self._iters[account_group]['groups'][group] = iter_group
return iter_group
def _add_entity(self, contact, account, groups=None,
big_brother_contact=None, big_brother_account=None):
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
"""
Add the given contact to roster data model
Contact is added regardless if he is already in roster or not. Return
list of newly added iters.
Keyword arguments:
contact -- the contact to add
account -- the contacts account
groups -- list of groups to add the contact to.
(default groups in contact.get_shown_groups()).
Parameter ignored when big_brother_contact is specified.
big_brother_contact -- if specified contact is added as child
big_brother_contact. (default None)
"""
added_iters = []
if big_brother_contact:
# Add contact under big brother
parent_iters = self._get_contact_iter(
big_brother_contact.jid, big_brother_account,
big_brother_contact, self.model)
assert len(parent_iters) > 0, 'Big brother is not yet in roster!'
# Do not confuse get_contact_iter: Sync groups of family members
contact.groups = big_brother_contact.get_shown_groups()[:]
for child_iter in parent_iters:

Yann Leboulanger
committed
it = self.model.append(child_iter, [None,
contact.get_shown_name(), 'contact', contact.jid, account,

Yann Leboulanger
committed
None, None, None, None, None, None] + \
[None] * self.nb_ext_renderers)
added_iters.append(it)
if contact.jid in self._iters[account]['contacts']:
self._iters[account]['contacts'][contact.jid].append(it)
else:
self._iters[account]['contacts'][contact.jid] = [it]
else:
# We are a normal contact. Add us to our groups.
if not groups:
groups = contact.get_shown_groups()
for group in groups:
child_iterG = self._get_group_iter(group, account,
if not child_iterG:
# Group is not yet in roster, add it!

Yann Leboulanger
committed
child_iterG = self._add_group_iter(account, group)
if contact.is_transport():
typestr = 'agent'
elif contact.is_groupchat():
typestr = 'groupchat'
else:
typestr = 'contact'
# we add some values here. see draw_contact
# for more

Yann Leboulanger
committed
i_ = self.model.append(child_iterG, [None,
contact.get_shown_name(), typestr, contact.jid, account,
None, None, None, None, None, None] + \
[None] * self.nb_ext_renderers)
added_iters.append(i_)
if contact.jid in self._iters[account]['contacts']:
self._iters[account]['contacts'][contact.jid].append(i_)
else:
self._iters[account]['contacts'][contact.jid] = [i_]
# Restore the group expand state
if account + group in self.collapsed_rows:
is_expanded = False
else:
is_expanded = True
if group not in gajim.groups[account]:
gajim.groups[account][group] = {'expand': is_expanded}
assert len(added_iters), '%s has not been added to roster!' % \
contact.jid
return added_iters
def _remove_entity(self, contact, account, groups=None):
"""
Remove the given contact from roster data model
Empty groups after contact removal are removed too.
Return False if contact still has children and deletion was
not performed.
Return True on success.
Keyword arguments:
contact -- the contact to add
account -- the contacts account
groups -- list of groups to remove the contact from.
"""
iters = self._get_contact_iter(contact.jid, account, contact,
self.model)
assert iters, '%s shall be removed but is not in roster' % contact.jid
parent_iter = self.model.iter_parent(iters[0])
parent_type = self.model[parent_iter][C_TYPE]
if groups:
# Only remove from specified groups
all_iters = iters[:]
group_iters = [self._get_group_iter(group, account)
for group in groups]
iters = [titer for titer in all_iters
if self.model.iter_parent(titer) in group_iters]
iter_children = self.model.iter_children(iters[0])
if iter_children:
# We have children. We cannot be removed!
return False
# Remove us and empty groups from the model
for i in iters:
assert self.model[i][C_JID] == contact.jid and \
self.model[i][C_ACCOUNT] == account, \
"Invalidated iters of %s" % contact.jid
parent_i = self.model.iter_parent(i)
parent_type = self.model[parent_i][C_TYPE]
to_be_removed = i
while parent_type == 'group' and \
self.model.iter_n_children(parent_i) == 1:
if self.regroup:
account_group = 'MERGED'
else:
account_group = account
group = self.model[parent_i][C_JID].decode('utf-8')
if group in gajim.groups[account]:
del gajim.groups[account][group]
to_be_removed = parent_i
del self._iters[account_group]['groups'][group]
parent_i = self.model.iter_parent(parent_i)

Yann Leboulanger
committed
parent_type = self.model[parent_i][C_TYPE]
del self._iters[account]['contacts'][contact.jid]
return True
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
def _add_metacontact_family(self, family, account):
"""
Add the give Metacontact family to roster data model
Add Big Brother to his groups and all others under him.
Return list of all added (contact, account) tuples with
Big Brother as first element.
Keyword arguments:
family -- the family, see Contacts.get_metacontacts_family()
"""
nearby_family, big_brother_jid, big_brother_account = \
self._get_nearby_family_and_big_brother(family, account)
big_brother_contact = gajim.contacts.get_first_contact_from_jid(
big_brother_account, big_brother_jid)
assert len(self._get_contact_iter(big_brother_jid,
big_brother_account, big_brother_contact, self.model)) == 0, \
'Big brother %s already in roster\n Family: %s' \
% (big_brother_jid, family)
self._add_entity(big_brother_contact, big_brother_account)
brothers = []
# Filter family members
for data in nearby_family:
_account = data['account']
_jid = data['jid']
_contact = gajim.contacts.get_first_contact_from_jid(
_account, _jid)
if not _contact or _contact == big_brother_contact:
# Corresponding account is not connected
# or brother already added
continue
assert len(self._get_contact_iter(_jid, _account,
_contact, self.model)) == 0, \
"%s already in roster.\n Family: %s" % (_jid, nearby_family)
self._add_entity(_contact, _account,
big_brother_contact = big_brother_contact,
big_brother_account = big_brother_account)
brothers.append((_contact, _account))
brothers.insert(0, (big_brother_contact, big_brother_account))
return brothers
def _remove_metacontact_family(self, family, account):
"""
Remove the given Metacontact family from roster data model
See Contacts.get_metacontacts_family() and
RosterWindow._remove_entity()
"""
nearby_family = self._get_nearby_family_and_big_brother(
family, account)[0]
# Family might has changed (actual big brother not on top).
# Remove childs first then big brother
family_in_roster = False
for data in nearby_family:
_account = data['account']
_jid = data['jid']
_contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
iters = self._get_contact_iter(_jid, _account, _contact, self.model)
if not iters or not _contact:
# Family might not be up to date.
# Only try to remove what is actually in the roster
continue
assert iters, '%s shall be removed but is not in roster \
\n Family: %s' % (_jid, family)
family_in_roster = True
parent_iter = self.model.iter_parent(iters[0])
parent_type = self.model[parent_iter][C_TYPE]
if parent_type != 'contact':
# The contact on top
old_big_account = _account
old_big_contact = _contact
old_big_jid = _jid
continue
ok = self._remove_entity(_contact, _account)
assert ok, '%s was not removed' % _jid
assert len(self._get_contact_iter(_jid, _account, _contact,
self.model)) == 0, '%s is removed but still in roster' % _jid
if not family_in_roster:
return False
assert old_big_jid, 'No Big Brother in nearby family % (Family: %)' % \
(nearby_family, family)
iters = self._get_contact_iter(old_big_jid, old_big_account,
old_big_contact, self.model)
assert len(iters) > 0, 'Old Big Brother %s is not in roster anymore' % \
old_big_jid
assert not self.model.iter_children(iters[0]), \
'Old Big Brother %s still has children' % old_big_jid
ok = self._remove_entity(old_big_contact, old_big_account)
assert ok, "Old Big Brother %s not removed" % old_big_jid
assert len(self._get_contact_iter(old_big_jid, old_big_account,
old_big_contact, self.model)) == 0, \
'Old Big Brother %s is removed but still in roster' % old_big_jid
return True
def _recalibrate_metacontact_family(self, family, account):
"""
Regroup metacontact family if necessary
"""
brothers = []
nearby_family, big_brother_jid, big_brother_account = \
self._get_nearby_family_and_big_brother(family, account)
big_brother_contact = gajim.contacts.get_contact(big_brother_account,
big_brother_jid)
child_iters = self._get_contact_iter(big_brother_jid,
big_brother_account, model=self.model)
if child_iters:
parent_iter = self.model.iter_parent(child_iters[0])
parent_type = self.model[parent_iter][C_TYPE]
# Check if the current BigBrother has even been before.
if parent_type == 'contact':
for data in nearby_family:
# recalibrate after remove to keep highlight
if data['jid'] in gajim.to_be_removed[data['account']]:
return
self._remove_metacontact_family(family, account)
brothers = self._add_metacontact_family(family, account)
for c, acc in brothers:
self.draw_completely(c.jid, acc)
# Check is small brothers are under the big brother
for child in nearby_family:
_jid = child['jid']
_account = child['account']
if _account == big_brother_account and _jid == big_brother_jid:
continue
child_iters = self._get_contact_iter(_jid, _account,
model=self.model)
if not child_iters:
continue
parent_iter = self.model.iter_parent(child_iters[0])
parent_type = self.model[parent_iter][C_TYPE]
if parent_type != 'contact':
_contact = gajim.contacts.get_contact(_account, _jid)
self._remove_entity(_contact, _account)
self._add_entity(_contact, _account, groups=None,
big_brother_contact=big_brother_contact,
big_brother_account=big_brother_account)
def _get_nearby_family_and_big_brother(self, family, account):
return gajim.contacts.get_nearby_family_and_big_brother(family, account)
def _add_self_contact(self, account):
"""
Add account's SelfContact to roster and draw it and the account
Return the SelfContact contact instance
"""
jid = gajim.get_jid_from_account(account)
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
assert len(self._get_contact_iter(jid, account, contact,
self.model)) == 0, 'Self contact %s already in roster' % jid
child_iterA = self._get_account_iter(account, self.model)
self._iters[account]['contacts'][jid] = [self.model.append(child_iterA,

Yann Leboulanger
committed
[None, gajim.nicks[account], 'self_contact', jid, account, None,
None, None, None, None, None] + [None] * self.nb_ext_renderers)]
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
self.draw_completely(jid, account)
self.draw_account(account)
return contact
def redraw_metacontacts(self, account):
for family in gajim.contacts.iter_metacontacts_families(account):
self._recalibrate_metacontact_family(family, account)
def add_contact(self, jid, account):
"""
Add contact to roster and draw him
Add contact to all its group and redraw the groups, the contact and the
account. If it's a Metacontact, add and draw the whole family.
Do nothing if the contact is already in roster.
Return the added contact instance. If it is a Metacontact return
Big Brother.
Keyword arguments:
jid -- the contact's jid or SelfJid to add SelfContact
account -- the corresponding account.
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if len(self._get_contact_iter(jid, account, contact, self.model)):
# If contact already in roster, do nothing
return
if jid == gajim.get_jid_from_account(account):
show_self_contact = gajim.config.get('show_self_contact')
if show_self_contact == 'never':
return
if (contact.resource != gajim.connections[account].server_resource \
and show_self_contact == 'when_other_resource') or \
show_self_contact == 'always':
return self._add_self_contact(account)
return
is_observer = contact.is_observer()
if is_observer:
# if he has a tag, remove it
gajim.contacts.remove_metacontact(account, jid)
# Add contact to roster
family = gajim.contacts.get_metacontacts_family(account, jid)
contacts = []
if family:
# We have a family. So we are a metacontact.
# Add all family members that we shall be grouped with
if self.regroup:
# remove existing family members to regroup them
self._remove_metacontact_family(family, account)
contacts = self._add_metacontact_family(family, account)
else:
# We are a normal contact
contacts = [(contact, account), ]
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
self._add_entity(contact, account)
# Draw the contact and its groups contact
if not self.starting:
for c, acc in contacts:
self.draw_completely(c.jid, acc)
for group in contact.get_shown_groups():
self.draw_group(group, account)
self._adjust_group_expand_collapse_state(group, account)
self.draw_account(account)
return contacts[0][0] # it's contact/big brother with highest priority
def remove_contact(self, jid, account, force=False, backend=False):
"""
Remove contact from roster
Remove contact from all its group. Remove empty groups or redraw
otherwise.
Draw the account.
If it's a Metacontact, remove the whole family.
Do nothing if the contact is not in roster.
Keyword arguments:
jid -- the contact's jid or SelfJid to remove SelfContact
account -- the corresponding account.
force -- remove contact even it has pending evens (Default False)
backend -- also remove contact instance (Default False)
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if not contact:
return

Yann Leboulanger
committed
if not force and self.contact_has_pending_roster_events(contact,
account):
return False
iters = self._get_contact_iter(jid, account, contact, self.model)
if iters:
# no more pending events
# Remove contact from roster directly
family = gajim.contacts.get_metacontacts_family(account, jid)
if family:
# We have a family. So we are a metacontact.
self._remove_metacontact_family(family, account)
else:
self._remove_entity(contact, account)

Yann Leboulanger
committed
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
old_grps = []
if backend:
if not gajim.interface.msg_win_mgr.get_control(jid, account) or \
force:
# If a window is still opened: don't remove contact instance
# Remove contact before redrawing, otherwise the old
# numbers will still be show
gajim.contacts.remove_jid(account, jid, remove_meta=True)
if iters:
rest_of_family = [data for data in family
if account != data['account'] or jid != data['jid']]
if rest_of_family:
# reshow the rest of the family
brothers = self._add_metacontact_family(rest_of_family,
account)
for c, acc in brothers:
self.draw_completely(c.jid, acc)
else:
for c in gajim.contacts.get_contacts(account, jid):
c.sub = 'none'
c.show = 'not in roster'
c.status = ''
old_grps = c.get_shown_groups()
c.groups = [_('Not in Roster')]
self._add_entity(c, account)
self.draw_contact(jid, account)
if iters:
# Draw all groups of the contact

Yann Leboulanger
committed
for group in contact.get_shown_groups() + old_grps:
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
self.draw_group(group, account)
self.draw_account(account)
return True
def rename_self_contact(self, old_jid, new_jid, account):
"""
Rename the self_contact jid
Keyword arguments:
old_jid -- our old jid
new_jid -- our new jid
account -- the corresponding account.
"""
gajim.contacts.change_contact_jid(old_jid, new_jid, account)
self_iter = self._get_self_contact_iter(account, model=self.model)
if not self_iter:
return
self.model[self_iter][C_JID] = new_jid
self.draw_contact(new_jid, account)
def add_groupchat(self, jid, account, status=''):
"""
Add groupchat to roster and draw it. Return the added contact instance
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
# Do not show gc if we are disconnected and minimize it
if gajim.account_is_connected(account):
show = 'online'
else:
show = 'offline'
status = ''
if contact is None:
gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid,
account)
if gc_control:
# there is a window that we can minimize
gajim.interface.minimized_controls[account][jid] = gc_control
name = gc_control.name
elif jid in gajim.interface.minimized_controls[account]:
name = gajim.interface.minimized_controls[account][jid].name
else:
name = jid.split('@')[0]
# New groupchat
contact = gajim.contacts.create_contact(jid=jid, account=account,
name=name, groups=[_('Groupchats')], show=show, status=status,
sub='none')
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
else:
if jid not in gajim.interface.minimized_controls[account]:
# there is a window that we can minimize
gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid,
account)
gajim.interface.minimized_controls[account][jid] = gc_control
contact.show = show
contact.status = status
self.adjust_and_draw_contact_context(jid, account)
return contact
def remove_groupchat(self, jid, account):
"""
Remove groupchat from roster and redraw account and group
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if contact.is_groupchat():
if jid in gajim.interface.minimized_controls[account]:
del gajim.interface.minimized_controls[account][jid]
self.remove_contact(jid, account, force=True, backend=True)
return True
else:
return False
# FIXME: This function is yet unused! Port to new API
def add_transport(self, jid, account):
"""
Add transport to roster and draw it. Return the added contact instance
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if contact is None:
contact = gajim.contacts.create_contact(jid=jid, account=account,
name=jid, groups=[_('Transports')], show='offline',
status='offline', sub='from')
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
return contact
def remove_transport(self, jid, account):
"""
Remove transport from roster and redraw account and group
"""
self.remove_contact(jid, account, force=True, backend=True)
return True
def rename_group(self, old_name, new_name, account):
"""
Rename a roster group
"""
if old_name == new_name:
return
# Groups may not change name from or to a special groups
for g in helpers.special_groups:
if g in (new_name, old_name):
return
# update all contacts in the given group
if self.regroup:
accounts = gajim.connections.keys()
else:
accounts = [account, ]
for acc in accounts:
changed_contacts = []
for jid in gajim.contacts.get_jid_list(acc):
contact = gajim.contacts.get_first_contact_from_jid(acc, jid)
if old_name not in contact.groups:
continue
self.remove_contact(jid, acc, force=True)
contact.groups.remove(old_name)
if new_name not in contact.groups:
contact.groups.append(new_name)
changed_contacts.append({'jid': jid, 'name': contact.name,
'groups':contact.groups})
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
gajim.connections[acc].update_contacts(changed_contacts)
for c in changed_contacts:
self.add_contact(c['jid'], acc)
self._adjust_group_expand_collapse_state(new_name, acc)
self.draw_group(old_name, acc)
self.draw_group(new_name, acc)
def add_contact_to_groups(self, jid, account, groups, update=True):
"""
Add contact to given groups and redraw them
Contact on server is updated too. When the contact has a family,
the action will be performed for all members.
Keyword Arguments:
jid -- the jid
account -- the corresponding account
groups -- list of Groups to add the contact to.
update -- update contact on the server
"""
self.remove_contact(jid, account, force=True)
for contact in gajim.contacts.get_contacts(account, jid):
for group in groups:
if group not in contact.groups:
# we might be dropped from meta to group
contact.groups.append(group)
if update:
gajim.connections[account].update_contact(jid, contact.name,
contact.groups)
self.add_contact(jid, account)
for group in groups:
self._adjust_group_expand_collapse_state(group, account)
def remove_contact_from_groups(self, jid, account, groups, update=True):
"""
Remove contact from given groups and redraw them
Contact on server is updated too. When the contact has a family,
the action will be performed for all members.
Keyword Arguments:
jid -- the jid
account -- the corresponding account
groups -- list of Groups to remove the contact from
update -- update contact on the server
"""
self.remove_contact(jid, account, force=True)
for contact in gajim.contacts.get_contacts(account, jid):
for group in groups:
if group in contact.groups:
# Needed when we remove from "General" or "Observers"
contact.groups.remove(group)
if update:
gajim.connections[account].update_contact(jid, contact.name,
contact.groups)
self.add_contact(jid, account)
# Also redraw old groups
for group in groups:
self.draw_group(group, account)
# FIXME: maybe move to gajim.py
def remove_newly_added(self, jid, account):
if jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(jid)
self.draw_contact(jid, account)
# FIXME: maybe move to gajim.py
def remove_to_be_removed(self, jid, account):
if account not in gajim.interface.instances:
# Account has been deleted during the timeout that called us
return
if jid in gajim.newly_added[account]:
return
if jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(jid)
family = gajim.contacts.get_metacontacts_family(account, jid)
if family:
# Peform delayed recalibration
self._recalibrate_metacontact_family(family, account)
self.draw_contact(jid, account)
# FIXME: integrate into add_contact()
def add_to_not_in_the_roster(self, account, jid, nick='', resource=''):
keyID = ''