1 # -*- coding: utf-8 -*-
2 ## src/roster_window.py
4 ## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
5 ## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
6 ## Stéphan Kochen <stephan AT kochen.nl>
7 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
8 ## Copyright (C) 2005-2007 Travis Shirk <travis AT pobox.com>
9 ## Nikos Kouremenos <kourem AT gmail.com>
10 ## Copyright (C) 2006 Stefan Bethge <stefan AT lanpartei.de>
11 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
12 ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
13 ## James Newton <redshodan AT gmail.com>
14 ## Tomasz Melcer <liori AT exroot.org>
15 ## Julien Pivotto <roidelapluie AT gmail.com>
16 ## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
17 ## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
18 ## Jonathan Schleifer <js-gajim AT webkeks.org>
20 ## This file is part of Gajim.
22 ## Gajim is free software; you can redistribute it and/or modify
23 ## it under the terms of the GNU General Public License as published
24 ## by the Free Software Foundation; version 3 only.
26 ## Gajim is distributed in the hope that it will be useful,
27 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
28 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 ## GNU General Public License for more details.
31 ## You should have received a copy of the GNU General Public License
32 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
50 import gui_menu_builder
51 import cell_renderer_image
53 import message_control
55 import features_window
57 from common
import gajim
58 from common
import helpers
59 from common
.exceptions
import GajimGeneralException
60 from common
import i18n
61 from common
import pep
63 from message_window
import MessageWindowMgr
65 from common
import dbus_support
66 if dbus_support
.supported
:
69 from common
.xmpp
.protocol
import NS_FILE
70 from common
.pep
import MOODS
, ACTIVITIES
72 #(icon, name, type, jid, account, editable, second pixbuf)
74 C_IMG
, # image to show state (online, new message etc)
75 C_NAME
, # cellrenderer text that holds contact nickame
76 C_TYPE
, # account, group or contact?
77 C_JID
, # the jid of the row
78 C_ACCOUNT
, # cellrenderer text that holds account name
82 C_AVATAR_PIXBUF
, # avatar_pixbuf
83 C_PADLOCK_PIXBUF
, # use for account row only
87 '''Class for main window of the GTK+ interface'''
89 def _get_account_iter(self
, name
, model
=None):
91 Return the gtk.TreeIter of the given account or None
95 name -- the account name
96 model -- the data model (default TreeFilterModel)
99 model
= self
.modelfilter
102 account_iter
= model
.get_iter_root()
106 account_name
= model
[account_iter
][C_ACCOUNT
]
107 if account_name
and name
== account_name
.decode('utf-8'):
109 account_iter
= model
.iter_next(account_iter
)
113 def _get_group_iter(self
, name
, account
, account_iter
=None, model
=None):
115 Return the gtk.TreeIter of the given group or None if not found.
118 name -- the group name
119 account -- the account name
120 account_iter -- the iter of the account the model (default None)
121 model -- the data model (default TreeFilterModel)
125 model
= self
.modelfilter
127 account_iter
= self
._get
_account
_iter
(account
, model
)
128 group_iter
= model
.iter_children(account_iter
)
129 # C_NAME column contacts the pango escaped group name
131 group_name
= model
[group_iter
][C_JID
].decode('utf-8')
132 if name
== group_name
:
134 group_iter
= model
.iter_next(group_iter
)
138 def _get_self_contact_iter(self
, account
, model
=None):
139 ''' Return the gtk.TreeIter of SelfContact or None if not found.
142 account -- the account of SelfContact
143 model -- the data model (default TreeFilterModel)
148 model
= self
.modelfilter
149 iterAcct
= self
._get
_account
_iter
(account
, model
)
150 iterC
= model
.iter_children(iterAcct
)
152 # There might be several SelfContacts in merged account view
154 if model
[iterC
][C_TYPE
] != 'self_contact':
156 iter_account
= model
[iterC
][C_ACCOUNT
]
157 if account
== iter_account
.decode('utf-8'):
159 iterC
= model
.iter_next(iterC
)
163 def _get_contact_iter(self
, jid
, account
, contact
=None, model
=None):
164 ''' Return a list of gtk.TreeIter of the given contact.
167 jid -- the jid without resource
168 account -- the account
169 contact -- the contact (default None)
170 model -- the data model (default TreeFilterModel)
174 model
= self
.modelfilter
175 # when closing Gajim model can be none (async pbs?)
179 if jid
== gajim
.get_jid_from_account(account
):
180 contact_iter
= self
._get
_self
_contact
_iter
(account
, model
)
182 return [contact_iter
]
187 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
189 # We don't know this contact
192 acct
= self
._get
_account
_iter
(account
, model
)
193 found
= [] # the contact iters. One per group
194 for group
in contact
.get_shown_groups():
195 group_iter
= self
._get
_group
_iter
(group
, account
, acct
, model
)
196 contact_iter
= model
.iter_children(group_iter
)
199 # Loop over all contacts in this group
200 iter_jid
= model
[contact_iter
][C_JID
]
201 if iter_jid
and jid
== iter_jid
.decode('utf-8') and \
202 account
== model
[contact_iter
][C_ACCOUNT
].decode('utf-8'):
203 # only one iter per group
204 found
.append(contact_iter
)
206 elif model
.iter_has_child(contact_iter
):
207 # it's a big brother and has children
208 contact_iter
= model
.iter_children(contact_iter
)
210 # try to find next contact:
211 # other contact in this group or
213 next_contact_iter
= model
.iter_next(contact_iter
)
214 if next_contact_iter
:
215 contact_iter
= next_contact_iter
218 # Go up if we are big brother
219 parent_iter
= model
.iter_parent(contact_iter
)
220 if parent_iter
and model
[parent_iter
][C_TYPE
] == 'contact':
221 contact_iter
= model
.iter_next(parent_iter
)
224 # contacts in this group
229 def _iter_is_separator(self
, model
, titer
):
230 ''' Return True if the given iter is a separator.
233 model -- the data model
234 iter -- the gtk.TreeIter to test
236 if model
[titer
][0] == 'SEPARATOR':
241 def _iter_contact_rows(self
, model
=None):
242 '''Iterate over all contact rows in given model.
245 model -- the data model (default TreeFilterModel)
248 model
= self
.modelfilter
249 account_iter
= model
.get_iter_root()
251 group_iter
= model
.iter_children(account_iter
)
253 contact_iter
= model
.iter_children(group_iter
)
255 yield model
[contact_iter
]
256 contact_iter
= model
.iter_next(
258 group_iter
= model
.iter_next(group_iter
)
259 account_iter
= model
.iter_next(account_iter
)
262 #############################################################################
263 ### Methods for adding and removing roster window items
264 #############################################################################
266 def add_account(self
, account
):
268 Add account to roster and draw it. Do nothing if it is
271 if self
._get
_account
_iter
(account
):
272 # Will happen on reconnect or for merged accounts
276 # Merged accounts view
277 show
= helpers
.get_global_show()
278 self
.model
.append(None, [
279 gajim
.interface
.jabber_state_images
['16'][show
],
280 _('Merged accounts'), 'account', '', 'all',
281 None, None, None, None, None])
283 show
= gajim
.SHOW_LIST
[gajim
.connections
[account
].connected
]
284 our_jid
= gajim
.get_jid_from_account(account
)
287 if gajim
.account_is_securely_connected(account
):
288 # the only way to create a pixbuf from stock
289 tls_pixbuf
= self
.window
.render_icon(
290 gtk
.STOCK_DIALOG_AUTHENTICATION
,
293 self
.model
.append(None, [
294 gajim
.interface
.jabber_state_images
['16'][show
],
295 gobject
.markup_escape_text(account
), 'account',
296 our_jid
, account
, None, None, None, None,
299 self
.draw_account(account
)
302 def add_account_contacts(self
, account
):
303 '''Add all contacts and groups of the given account to roster,
304 draw them and account.
307 jids
= gajim
.contacts
.get_jid_list(account
)
309 self
.tree
.freeze_child_notify()
311 self
.add_contact(jid
, account
)
312 self
.tree
.thaw_child_notify()
314 # Do not freeze the GUI when drawing the contacts
316 # Overhead is big, only invoke when needed
317 self
._idle
_draw
_jids
_of
_account
(jids
, account
)
319 # Draw all known groups
320 for group
in gajim
.groups
[account
]:
321 self
.draw_group(group
, account
)
322 self
.draw_account(account
)
323 self
.starting
= False
326 def _add_entity(self
, contact
, account
, groups
=None,
327 big_brother_contact
=None, big_brother_account
=None):
328 '''Add the given contact to roster data model.
330 Contact is added regardless if he is already in roster or not.
331 Return list of newly added iters.
334 contact -- the contact to add
335 account -- the contacts account
336 groups -- list of groups to add the contact to.
337 (default groups in contact.get_shown_groups()).
338 Parameter ignored when big_brother_contact is specified.
339 big_brother_contact -- if specified contact is added as child
340 big_brother_contact. (default None)
343 if big_brother_contact
:
344 # Add contact under big brother
346 parent_iters
= self
._get
_contact
_iter
(
347 big_brother_contact
.jid
, big_brother_account
,
348 big_brother_contact
, self
.model
)
349 assert len(parent_iters
) > 0, 'Big brother is not yet in roster!'
351 # Do not confuse get_contact_iter: Sync groups of family members
352 contact
.groups
= big_brother_contact
.get_shown_groups()[:]
354 for child_iter
in parent_iters
:
355 it
= self
.model
.append(child_iter
, (None, contact
.get_shown_name(),
356 'contact', contact
.jid
, account
, None, None, None, None, None))
357 added_iters
.append(it
)
359 # We are a normal contact. Add us to our groups.
361 groups
= contact
.get_shown_groups()
363 child_iterG
= self
._get
_group
_iter
(group
, account
,
366 # Group is not yet in roster, add it!
367 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
368 child_iterG
= self
.model
.append(child_iterA
,
369 [gajim
.interface
.jabber_state_images
['16']['closed'],
370 gobject
.markup_escape_text(group
),
371 'group', group
, account
, None, None, None, None, None])
372 self
.draw_group(group
, account
)
374 if contact
.is_transport():
376 elif contact
.is_groupchat():
377 typestr
= 'groupchat'
381 # we add some values here. see draw_contact
383 i_
= self
.model
.append(child_iterG
, (None,
384 contact
.get_shown_name(), typestr
,
385 contact
.jid
, account
, None, None, None,
387 added_iters
.append(i_
)
389 # Restore the group expand state
390 if account
+ group
in self
.collapsed_rows
:
394 if group
not in gajim
.groups
[account
]:
395 gajim
.groups
[account
][group
] = {'expand': is_expanded
}
397 assert len(added_iters
), '%s has not been added to roster!' % contact
.jid
400 def _remove_entity(self
, contact
, account
, groups
=None):
401 '''Remove the given contact from roster data model.
403 Empty groups after contact removal are removed too.
404 Return False if contact still has children and deletion was
406 Return True on success.
409 contact -- the contact to add
410 account -- the contacts account
411 groups -- list of groups to remove the contact from.
413 iters
= self
._get
_contact
_iter
(contact
.jid
, account
, contact
, self
.model
)
414 assert iters
, '%s shall be removed but is not in roster' % contact
.jid
416 parent_iter
= self
.model
.iter_parent(iters
[0])
417 parent_type
= self
.model
[parent_iter
][C_TYPE
]
420 # Only remove from specified groups
422 group_iters
= [self
._get
_group
_iter
(group
, account
)
424 iters
= [titer
for titer
in all_iters
425 if self
.model
.iter_parent(titer
) in group_iters
]
427 iter_children
= self
.model
.iter_children(iters
[0])
430 # We have children. We cannot be removed!
433 # Remove us and empty groups from the model
435 assert self
.model
[i
][C_JID
] == contact
.jid
and \
436 self
.model
[i
][C_ACCOUNT
] == account
, \
437 "Invalidated iters of %s" % contact
.jid
439 parent_i
= self
.model
.iter_parent(i
)
441 if parent_type
== 'group' and \
442 self
.model
.iter_n_children(parent_i
) == 1:
443 group
= self
.model
[parent_i
][C_JID
].decode('utf-8')
444 if group
in gajim
.groups
[account
]:
445 del gajim
.groups
[account
][group
]
446 self
.model
.remove(parent_i
)
451 def _add_metacontact_family(self
, family
, account
):
453 Add the give Metacontact family to roster data model.
455 Add Big Brother to his groups and all others under him.
456 Return list of all added (contact, account) tuples with
457 Big Brother as first element.
460 family -- the family, see Contacts.get_metacontacts_family()
463 nearby_family
, big_brother_jid
, big_brother_account
= \
464 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)
465 big_brother_contact
= gajim
.contacts
.get_first_contact_from_jid(
466 big_brother_account
, big_brother_jid
)
468 assert len(self
._get
_contact
_iter
(big_brother_jid
,
469 big_brother_account
, big_brother_contact
, self
.model
)) == 0, \
470 'Big brother %s already in roster\n Family: %s' \
471 % (big_brother_jid
, family
)
472 self
._add
_entity
(big_brother_contact
, big_brother_account
)
475 # Filter family members
476 for data
in nearby_family
:
477 _account
= data
['account']
479 _contact
= gajim
.contacts
.get_first_contact_from_jid(
482 if not _contact
or _contact
== big_brother_contact
:
483 # Corresponding account is not connected
484 # or brother already added
487 assert len(self
._get
_contact
_iter
(_jid
, _account
,
488 _contact
, self
.model
)) == 0, \
489 "%s already in roster.\n Family: %s" % (_jid
, nearby_family
)
490 self
._add
_entity
(_contact
, _account
,
491 big_brother_contact
= big_brother_contact
,
492 big_brother_account
= big_brother_account
)
493 brothers
.append((_contact
, _account
))
495 brothers
.insert(0, (big_brother_contact
, big_brother_account
))
498 def _remove_metacontact_family(self
, family
, account
):
500 Remove the given Metacontact family from roster data model.
502 See Contacts.get_metacontacts_family() and
503 RosterWindow._remove_entity()
505 nearby_family
= self
._get
_nearby
_family
_and
_big
_brother
(
508 # Family might has changed (actual big brother not on top).
509 # Remove childs first then big brother
510 family_in_roster
= False
511 for data
in nearby_family
:
512 _account
= data
['account']
514 _contact
= gajim
.contacts
.get_first_contact_from_jid(_account
, _jid
)
516 iters
= self
._get
_contact
_iter
(_jid
, _account
, _contact
, self
.model
)
517 if not iters
or not _contact
:
518 # Family might not be up to date.
519 # Only try to remove what is actually in the roster
521 assert iters
, '%s shall be removed but is not in roster \
522 \n Family: %s' % (_jid
, family
)
524 family_in_roster
= True
526 parent_iter
= self
.model
.iter_parent(iters
[0])
527 parent_type
= self
.model
[parent_iter
][C_TYPE
]
529 if parent_type
!= 'contact':
531 old_big_account
= _account
532 old_big_contact
= _contact
536 ok
= self
._remove
_entity
(_contact
, _account
)
537 assert ok
, '%s was not removed' % _jid
538 assert len(self
._get
_contact
_iter
(_jid
, _account
, _contact
,
539 self
.model
)) == 0, '%s is removed but still in roster' % _jid
541 if not family_in_roster
:
544 assert old_big_jid
, 'No Big Brother in nearby family % (Family: %)' % \
545 (nearby_family
, family
)
546 iters
= self
._get
_contact
_iter
(old_big_jid
, old_big_account
,
547 old_big_contact
, self
.model
)
548 assert len(iters
) > 0, 'Old Big Brother %s is not in roster anymore' % \
550 assert not self
.model
.iter_children(iters
[0]),\
551 'Old Big Brother %s still has children' % old_big_jid
553 ok
= self
._remove
_entity
(old_big_contact
, old_big_account
)
554 assert ok
, "Old Big Brother %s not removed" % old_big_jid
555 assert len(self
._get
_contact
_iter
(old_big_jid
, old_big_account
,
556 old_big_contact
, self
.model
)) == 0,\
557 'Old Big Brother %s is removed but still in roster' % old_big_jid
562 def _recalibrate_metacontact_family(self
, family
, account
):
563 '''Regroup metacontact family if necessary.'''
566 nearby_family
, big_brother_jid
, big_brother_account
= \
567 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)
568 big_brother_contact
= gajim
.contacts
.get_contact(big_brother_account
,
570 child_iters
= self
._get
_contact
_iter
(big_brother_jid
, big_brother_account
,
573 parent_iter
= self
.model
.iter_parent(child_iters
[0])
574 parent_type
= self
.model
[parent_iter
][C_TYPE
]
576 # Check if the current BigBrother has even been before.
577 if parent_type
== 'contact':
578 for data
in nearby_family
:
579 # recalibrate after remove to keep highlight
580 if data
['jid'] in gajim
.to_be_removed
[data
['account']]:
583 self
._remove
_metacontact
_family
(family
, account
)
584 brothers
= self
._add
_metacontact
_family
(family
, account
)
586 for c
, acc
in brothers
:
587 self
.draw_completely(c
.jid
, acc
)
589 # Check is small brothers are under the big brother
590 for child
in nearby_family
:
592 _account
= child
['account']
593 if _account
== big_brother_account
and _jid
== big_brother_jid
:
595 child_iters
= self
._get
_contact
_iter
(_jid
, _account
, model
=self
.model
)
598 parent_iter
= self
.model
.iter_parent(child_iters
[0])
599 parent_type
= self
.model
[parent_iter
][C_TYPE
]
600 if parent_type
!= 'contact':
601 _contact
= gajim
.contacts
.get_contact(_account
, _jid
)
602 self
._remove
_entity
(_contact
, _account
)
603 self
._add
_entity
(_contact
, _account
, groups
=None,
604 big_brother_contact
=big_brother_contact
,
605 big_brother_account
=big_brother_account
)
607 def _get_nearby_family_and_big_brother(self
, family
, account
):
608 '''Return the nearby family and its Big Brother
610 Nearby family is the part of the family that is grouped with the metacontact.
611 A metacontact may be over different accounts. If regroup is s False the
612 given family is split account wise.
614 (nearby_family, big_brother_jid, big_brother_account)
618 nearby_family
= family
620 # we want one nearby_family per account
621 nearby_family
= [data
for data
in family
622 if account
== data
['account']]
624 big_brother_data
= gajim
.contacts
.get_metacontacts_big_brother(
626 big_brother_jid
= big_brother_data
['jid']
627 big_brother_account
= big_brother_data
['account']
629 return (nearby_family
, big_brother_jid
, big_brother_account
)
632 def _add_self_contact(self
, account
):
633 '''Add account's SelfContact to roster and draw it and the account.
635 Return the SelfContact contact instance
637 jid
= gajim
.get_jid_from_account(account
)
638 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
640 assert len(self
._get
_contact
_iter
(jid
, account
, contact
, self
.model
)) == \
641 0, 'Self contact %s already in roster' % jid
643 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
644 self
.model
.append(child_iterA
, (None, gajim
.nicks
[account
],
645 'self_contact', jid
, account
, None, None, None, None,
648 self
.draw_completely(jid
, account
)
649 self
.draw_account(account
)
654 def redraw_metacontacts(self
, account
):
655 for tag
in gajim
.contacts
.get_metacontacts_tags(account
):
656 family
= gajim
.contacts
.get_metacontacts_family_from_tag(account
, tag
)
657 self
._recalibrate
_metacontact
_family
(family
, account
)
659 def add_contact(self
, jid
, account
):
660 '''Add contact to roster and draw him.
662 Add contact to all its group and redraw the groups, the contact and the
663 account. If it's a Metacontact, add and draw the whole family.
664 Do nothing if the contact is already in roster.
666 Return the added contact instance. If it is a Metacontact return
670 jid -- the contact's jid or SelfJid to add SelfContact
671 account -- the corresponding account.
674 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
675 if len(self
._get
_contact
_iter
(jid
, account
, contact
, self
.model
)):
676 # If contact already in roster, do nothing
679 if jid
== gajim
.get_jid_from_account(account
):
680 show_self_contact
= gajim
.config
.get('show_self_contact')
681 if show_self_contact
== 'never':
683 if (contact
.resource
!= gajim
.connections
[account
].server_resource
and\
684 show_self_contact
== 'when_other_resource') or show_self_contact
== \
686 return self
._add
_self
_contact
(account
)
689 is_observer
= contact
.is_observer()
691 # if he has a tag, remove it
692 tag
= gajim
.contacts
.get_metacontacts_tag(account
, jid
)
694 gajim
.contacts
.remove_metacontact(account
, jid
)
696 # Add contact to roster
697 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
700 # We have a family. So we are a metacontact.
701 # Add all family members that we shall be grouped with
703 # remove existing family members to regroup them
704 self
._remove
_metacontact
_family
(family
, account
)
705 contacts
= self
._add
_metacontact
_family
(family
, account
)
707 # We are a normal contact
708 contacts
= [(contact
, account
),]
709 self
._add
_entity
(contact
, account
)
711 # Draw the contact and its groups contact
712 if not self
.starting
:
713 for c
, acc
in contacts
:
714 self
.draw_completely(c
.jid
, acc
)
715 for group
in contact
.get_shown_groups():
716 self
.draw_group(group
, account
)
717 self
._adjust
_group
_expand
_collapse
_state
(group
, account
)
718 self
.draw_account(account
)
720 return contacts
[0][0] # it's contact/big brother with highest priority
722 def remove_contact(self
, jid
, account
, force
=False, backend
=False):
723 '''Remove contact from roster.
725 Remove contact from all its group. Remove empty groups or redraw
728 If it's a Metacontact, remove the whole family.
729 Do nothing if the contact is not in roster.
732 jid -- the contact's jid or SelfJid to remove SelfContact
733 account -- the corresponding account.
734 force -- remove contact even it has pending evens (Default False)
735 backend -- also remove contact instance (Default False)
738 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
742 if not force
and (self
.contact_has_pending_roster_events(contact
,
743 account
) or gajim
.interface
.msg_win_mgr
.get_control(jid
, account
)):
744 # Contact has pending events or window
745 #TODO: or single message windows? Bur they are not listed for the
748 if not key
in self
.contacts_to_be_removed
:
749 self
.contacts_to_be_removed
[key
] = {'backend': backend
}
750 # if more pending event, don't remove from roster
751 if self
.contact_has_pending_roster_events(contact
, account
):
754 iters
= self
._get
_contact
_iter
(jid
, account
, contact
, self
.model
)
756 # no more pending events
757 # Remove contact from roster directly
758 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
760 # We have a family. So we are a metacontact.
761 self
._remove
_metacontact
_family
(family
, account
)
763 self
._remove
_entity
(contact
, account
)
765 if backend
and (not gajim
.interface
.msg_win_mgr
.get_control(jid
, account
)\
767 # If a window is still opened: don't remove contact instance
768 # Remove contact before redrawing, otherwise the old
769 # numbers will still be show
770 gajim
.contacts
.remove_jid(account
, jid
, remove_meta
=True)
772 rest_of_family
= [data
for data
in family
773 if account
!= data
['account'] or jid
!= data
['jid']]
775 # reshow the rest of the family
776 brothers
= self
._add
_metacontact
_family
(rest_of_family
, account
)
777 for c
, acc
in brothers
:
778 self
.draw_completely(c
.jid
, acc
)
781 # Draw all groups of the contact
782 for group
in contact
.get_shown_groups():
783 self
.draw_group(group
, account
)
784 self
.draw_account(account
)
788 def rename_self_contact(self
, old_jid
, new_jid
, account
):
789 '''Rename the self_contact jid
792 old_jid -- our old jid
793 new_jid -- our new jid
794 account -- the corresponding account.
796 gajim
.contacts
.change_contact_jid(old_jid
, new_jid
, account
)
797 self_iter
= self
._get
_self
_contact
_iter
(account
, model
=self
.model
)
800 self
.model
[self_iter
][C_JID
] = new_jid
801 self
.draw_contact(new_jid
, account
)
803 def add_groupchat(self
, jid
, account
, status
=''):
804 '''Add groupchat to roster and draw it.
805 Return the added contact instance.
807 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
808 # Do not show gc if we are disconnected and minimize it
809 if gajim
.account_is_connected(account
):
816 gc_control
= gajim
.interface
.msg_win_mgr
.get_gc_control(jid
, account
)
818 # there is a window that we can minimize
819 gajim
.interface
.minimized_controls
[account
][jid
] = gc_control
820 name
= gc_control
.name
821 elif jid
in gajim
.interface
.minimized_controls
[account
]:
822 name
= gajim
.interface
.minimized_controls
[account
][jid
].name
824 name
= jid
.split('@')[0]
826 contact
= gajim
.contacts
.create_contact(jid
=jid
, name
=name
,
827 groups
=[_('Groupchats')], show
=show
, status
=status
, sub
='none')
828 gajim
.contacts
.add_contact(account
, contact
)
829 self
.add_contact(jid
, account
)
831 if jid
not in gajim
.interface
.minimized_controls
[account
]:
832 # there is a window that we can minimize
833 gc_control
= gajim
.interface
.msg_win_mgr
.get_gc_control(jid
,
835 gajim
.interface
.minimized_controls
[account
][jid
] = gc_control
837 contact
.status
= status
838 self
.adjust_and_draw_contact_context(jid
, account
)
843 def remove_groupchat(self
, jid
, account
):
844 '''Remove groupchat from roster and redraw account and group.'''
845 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
846 if contact
.is_groupchat():
847 if jid
in gajim
.interface
.minimized_controls
[account
]:
848 del gajim
.interface
.minimized_controls
[account
][jid
]
849 self
.remove_contact(jid
, account
, force
=True, backend
=True)
855 # FIXME: This function is yet unused! Port to new API
856 def add_transport(self
, jid
, account
):
857 '''Add transport to roster and draw it.
858 Return the added contact instance.'''
859 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
861 contact
= gajim
.contacts
.create_contact(jid
=jid
, name
=jid
,
862 groups
=[_('Transports')], show
='offline', status
='offline',
864 gajim
.contacts
.add_contact(account
, contact
)
865 self
.add_contact(jid
, account
)
868 def remove_transport(self
, jid
, account
):
869 '''Remove transport from roster and redraw account and group.'''
870 self
.remove_contact(jid
, account
, force
=True, backend
=True)
873 def rename_group(self
, old_name
, new_name
, account
):
875 rename a roster group
877 if old_name
== new_name
:
880 # Groups may not change name from or to a special groups
881 for g
in helpers
.special_groups
:
882 if g
in (new_name
, old_name
):
885 # update all contacts in the given group
887 accounts
= gajim
.connections
.keys()
889 accounts
= [account
,]
892 changed_contacts
= []
893 for jid
in gajim
.contacts
.get_jid_list(acc
):
894 contact
= gajim
.contacts
.get_first_contact_from_jid(acc
, jid
)
895 if old_name
not in contact
.groups
:
898 self
.remove_contact(jid
, acc
, force
=True)
900 contact
.groups
.remove(old_name
)
901 if new_name
not in contact
.groups
:
902 contact
.groups
.append(new_name
)
904 changed_contacts
.append({'jid':jid
, 'name':contact
.name
,
905 'groups':contact
.groups
})
907 gajim
.connections
[acc
].update_contacts(changed_contacts
)
909 for c
in changed_contacts
:
910 self
.add_contact(c
['jid'], acc
)
912 self
._adjust
_group
_expand
_collapse
_state
(new_name
, acc
)
914 self
.draw_group(old_name
, acc
)
915 self
.draw_group(new_name
, acc
)
918 def add_contact_to_groups(self
, jid
, account
, groups
, update
=True):
919 '''Add contact to given groups and redraw them.
921 Contact on server is updated too. When the contact has a family,
922 the action will be performed for all members.
926 account -- the corresponding account
927 groups -- list of Groups to add the contact to.
928 update -- update contact on the server
931 self
.remove_contact(jid
, account
, force
=True)
932 for contact
in gajim
.contacts
.get_contacts(account
, jid
):
934 if group
not in contact
.groups
:
935 # we might be dropped from meta to group
936 contact
.groups
.append(group
)
938 gajim
.connections
[account
].update_contact(jid
, contact
.name
,
941 self
.add_contact(jid
, account
)
944 self
._adjust
_group
_expand
_collapse
_state
(group
, account
)
946 def remove_contact_from_groups(self
, jid
, account
, groups
, update
=True):
947 '''Remove contact from given groups and redraw them.
949 Contact on server is updated too. When the contact has a family,
950 the action will be performed for all members.
954 account -- the corresponding account
955 groups -- list of Groups to remove the contact from
956 update -- update contact on the server
959 self
.remove_contact(jid
, account
, force
=True)
960 for contact
in gajim
.contacts
.get_contacts(account
, jid
):
962 if group
in contact
.groups
:
963 # Needed when we remove from "General" or "Observers"
964 contact
.groups
.remove(group
)
966 gajim
.connections
[account
].update_contact(jid
, contact
.name
,
968 self
.add_contact(jid
, account
)
970 # Also redraw old groups
972 self
.draw_group(group
, account
)
974 # FIXME: maybe move to gajim.py
975 def remove_newly_added(self
, jid
, account
):
976 if jid
in gajim
.newly_added
[account
]:
977 gajim
.newly_added
[account
].remove(jid
)
978 self
.draw_contact(jid
, account
)
980 # FIXME: maybe move to gajim.py
981 def remove_to_be_removed(self
, jid
, account
):
982 if account
not in gajim
.interface
.instances
:
983 # Account has been deleted during the timeout that called us
985 if jid
in gajim
.newly_added
[account
]:
987 if jid
in gajim
.to_be_removed
[account
]:
988 gajim
.to_be_removed
[account
].remove(jid
)
989 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
991 # Peform delayed recalibration
992 self
._recalibrate
_metacontact
_family
(family
, account
)
993 self
.draw_contact(jid
, account
)
995 #FIXME: integrate into add_contact()
996 def add_to_not_in_the_roster(self
, account
, jid
, nick
='', resource
=''):
998 attached_keys
= gajim
.config
.get_per('accounts', account
,
999 'attached_gpg_keys').split()
1000 if jid
in attached_keys
:
1001 keyID
= attached_keys
[attached_keys
.index(jid
) + 1]
1002 contact
= gajim
.contacts
.create_contact(jid
=jid
, name
=nick
,
1003 groups
=[_('Not in Roster')], show
='not in roster', status
='',
1004 sub
='none', resource
=resource
, keyID
=keyID
)
1005 gajim
.contacts
.add_contact(account
, contact
)
1006 self
.add_contact(contact
.jid
, account
)
1010 ################################################################################
1011 ### Methods for adding and removing roster window items
1012 ################################################################################
1014 def draw_account(self
, account
):
1015 child_iter
= self
._get
_account
_iter
(account
, self
.model
)
1017 assert False, 'Account iter of %s could not be found.' % account
1020 num_of_accounts
= gajim
.get_number_of_connected_accounts()
1021 num_of_secured
= gajim
.get_number_of_securely_connected_accounts()
1023 if gajim
.account_is_securely_connected(account
) and not self
.regroup
or \
1024 self
.regroup
and num_of_secured
and num_of_secured
== num_of_accounts
:
1025 tls_pixbuf
= self
.window
.render_icon(gtk
.STOCK_DIALOG_AUTHENTICATION
,
1026 gtk
.ICON_SIZE_MENU
) # the only way to create a pixbuf from stock
1027 self
.model
[child_iter
][C_PADLOCK_PIXBUF
] = tls_pixbuf
1029 self
.model
[child_iter
][C_PADLOCK_PIXBUF
] = None
1032 account_name
= _('Merged accounts')
1035 account_name
= account
1036 accounts
= [account
]
1038 if account
in self
.collapsed_rows
and \
1039 self
.model
.iter_has_child(child_iter
):
1040 account_name
= '[%s]' % account_name
1042 if (gajim
.account_is_connected(account
) or (self
.regroup
and \
1043 gajim
.get_number_of_connected_accounts())) and gajim
.config
.get(
1044 'show_contacts_number'):
1045 nbr_on
, nbr_total
= gajim
.contacts
.get_nb_online_total_contacts(
1046 accounts
= accounts
)
1047 account_name
+= ' (%s/%s)' % (repr(nbr_on
), repr(nbr_total
))
1049 self
.model
[child_iter
][C_NAME
] = account_name
1051 if gajim
.config
.get('show_mood_in_roster') \
1052 and 'mood' in gajim
.connections
[account
].mood \
1053 and gajim
.connections
[account
].mood
['mood'].strip() in MOODS
:
1055 self
.model
[child_iter
][C_MOOD_PIXBUF
] = gtkgui_helpers
.load_mood_icon(
1056 gajim
.connections
[account
].mood
['mood'].strip()).get_pixbuf()
1058 elif gajim
.config
.get('show_mood_in_roster') \
1059 and 'mood' in gajim
.connections
[account
].mood
:
1060 self
.model
[child_iter
][C_MOOD_PIXBUF
] = \
1061 gtkgui_helpers
.load_mood_icon('unknown'). \
1064 self
.model
[child_iter
][C_MOOD_PIXBUF
] = None
1066 if gajim
.config
.get('show_activity_in_roster') \
1067 and 'activity' in gajim
.connections
[account
].activity \
1068 and gajim
.connections
[account
].activity
['activity'].strip() \
1070 if 'subactivity' in gajim
.connections
[account
].activity \
1071 and gajim
.connections
[account
].activity
['subactivity'].strip() \
1072 in ACTIVITIES
[gajim
.connections
[account
].activity
['activity'].strip()]:
1073 self
.model
[child_iter
][C_ACTIVITY_PIXBUF
] = \
1074 gtkgui_helpers
.load_activity_icon(
1075 gajim
.connections
[account
].activity
['activity'].strip(),
1076 gajim
.connections
[account
].activity
['subactivity'].strip()). \
1079 self
.model
[child_iter
][C_ACTIVITY_PIXBUF
] = \
1080 gtkgui_helpers
.load_activity_icon(
1081 gajim
.connections
[account
].activity
['activity'].strip()). \
1083 elif gajim
.config
.get('show_activity_in_roster') \
1084 and 'activity' in gajim
.connections
[account
].activity
:
1085 self
.model
[child_iter
][C_ACTIVITY_PIXBUF
] = \
1086 gtkgui_helpers
.load_activity_icon('unknown'). \
1089 self
.model
[child_iter
][C_ACTIVITY_PIXBUF
] = None
1091 if gajim
.config
.get('show_tunes_in_roster') \
1092 and ('artist' in gajim
.connections
[account
].tune \
1093 or 'title' in gajim
.connections
[account
].tune
):
1094 path
= os
.path
.join(gajim
.DATA_DIR
, 'emoticons', 'static', 'music.png')
1095 self
.model
[child_iter
][C_TUNE_PIXBUF
] = \
1096 gtk
.gdk
.pixbuf_new_from_file(path
)
1098 self
.model
[child_iter
][C_TUNE_PIXBUF
] = None
1102 def draw_group(self
, group
, account
):
1103 child_iter
= self
._get
_group
_iter
(group
, account
, model
=self
.model
)
1105 # Eg. We redraw groups after we removed a entitiy
1106 # and its empty groups
1111 accounts
= [account
]
1112 text
= gobject
.markup_escape_text(group
)
1113 if helpers
.group_is_blocked(account
, group
):
1114 text
= '<span strikethrough="true">%s</span>' % text
1115 if gajim
.config
.get('show_contacts_number'):
1116 nbr_on
, nbr_total
= gajim
.contacts
.get_nb_online_total_contacts(
1117 accounts
= accounts
, groups
= [group
])
1118 text
+= ' (%s/%s)' % (repr(nbr_on
), repr(nbr_total
))
1120 self
.model
[child_iter
][C_NAME
] = text
1123 def draw_parent_contact(self
, jid
, account
):
1124 child_iters
= self
._get
_contact
_iter
(jid
, account
, model
=self
.model
)
1127 parent_iter
= self
.model
.iter_parent(child_iters
[0])
1128 if self
.model
[parent_iter
][C_TYPE
] != 'contact':
1129 # parent is not a contact
1131 parent_jid
= self
.model
[parent_iter
][C_JID
].decode('utf-8')
1132 parent_account
= self
.model
[parent_iter
][C_ACCOUNT
].decode('utf-8')
1133 self
.draw_contact(parent_jid
, parent_account
)
1136 def draw_contact(self
, jid
, account
, selected
=False, focus
=False):
1137 '''draw the correct state image, name BUT not avatar'''
1138 # focus is about if the roster window has toplevel-focus or not
1139 # FIXME: We really need a custom cell_renderer
1141 contact_instances
= gajim
.contacts
.get_contacts(account
, jid
)
1142 contact
= gajim
.contacts
.get_highest_prio_contact_from_contacts(
1145 child_iters
= self
._get
_contact
_iter
(jid
, account
, contact
, self
.model
)
1149 name
= gobject
.markup_escape_text(contact
.get_shown_name())
1151 # gets number of unread gc marked messages
1152 if jid
in gajim
.interface
.minimized_controls
[account
] and \
1153 gajim
.interface
.minimized_controls
[account
][jid
]:
1154 nb_unread
= len(gajim
.events
.get_events(account
, jid
,
1155 ['printed_marked_gc_msg']))
1156 nb_unread
+= gajim
.interface
.minimized_controls \
1157 [account
][jid
].get_nb_unread_pm()
1160 name
= '%s *' % name
1162 name
= '%s [%s]' % (name
, str(nb_unread
))
1164 # Strike name if blocked
1166 if helpers
.jid_is_blocked(account
, jid
):
1169 for group
in contact
.get_shown_groups():
1170 if helpers
.group_is_blocked(account
, group
):
1174 name
= '<span strikethrough="true">%s</span>' % name
1176 # Show resource counter
1177 nb_connected_contact
= 0
1178 for c
in contact_instances
:
1179 if c
.show
not in ('error', 'offline'):
1180 nb_connected_contact
+= 1
1181 if nb_connected_contact
> 1:
1182 # switch back to default writing direction
1183 name
+= i18n
.paragraph_direction_mark(unicode(name
))
1184 name
+= u
' (%d)' % nb_connected_contact
1186 # show (account_name) if there are 2 contact with same jid
1190 # look through all contacts of all accounts
1191 for account_
in gajim
.connections
:
1192 # useless to add account name
1193 if account_
== account
:
1195 for jid_
in gajim
.contacts
.get_jid_list(account_
):
1196 contact_
= gajim
.contacts
.get_first_contact_from_jid(
1198 if contact_
.get_shown_name() == contact
.get_shown_name() and \
1199 (jid_
, account_
) != (jid
, account
):
1203 # No need to continue in other account
1204 # if we already found one
1207 name
+= ' (' + account
+ ')'
1209 # add status msg, if not empty, under contact name in
1211 if contact
.status
and gajim
.config
.get('show_status_msgs_in_roster'):
1212 status
= contact
.status
.strip()
1214 status
= helpers
.reduce_chars_newlines(status
,
1216 # escape markup entities and make them small
1217 # italic and fg color color is calcuted to be
1219 color
= gtkgui_helpers
._get
_fade
_color
(self
.tree
, selected
, focus
)
1220 colorstring
= '#%04x%04x%04x' % (color
.red
, color
.green
, color
.blue
)
1221 name
+= '\n<span size="small" style="italic" ' \
1222 'foreground="%s">%s</span>' % (
1224 gobject
.markup_escape_text(status
))
1226 icon_name
= helpers
.get_icon_name_to_show(contact
, account
)
1227 # look if another resource has awaiting events
1228 for c
in contact_instances
:
1229 c_icon_name
= helpers
.get_icon_name_to_show(c
, account
)
1230 if c_icon_name
in ('event', 'muc_active', 'muc_inactive'):
1231 icon_name
= c_icon_name
1234 # Check for events of collapsed (hidden) brothers
1235 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
1236 is_big_brother
= False
1237 have_visible_children
= False
1239 bb_jid
, bb_account
= \
1240 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)[1:]
1241 is_big_brother
= (jid
, account
) == (bb_jid
, bb_account
)
1242 iters
= self
._get
_contact
_iter
(jid
, account
)
1243 have_visible_children
= iters \
1244 and self
.modelfilter
.iter_has_child(iters
[0])
1246 if have_visible_children
:
1247 # We are the big brother and have a visible family
1248 for child_iter
in child_iters
:
1249 child_path
= self
.model
.get_path(child_iter
)
1250 path
= self
.modelfilter
.convert_child_path_to_path(child_path
)
1255 if not self
.tree
.row_expanded(path
) and icon_name
!= 'event':
1256 iterC
= self
.model
.iter_children(child_iter
)
1258 # a child has awaiting messages?
1259 jidC
= self
.model
[iterC
][C_JID
].decode('utf-8')
1260 accountC
= self
.model
[iterC
][C_ACCOUNT
].decode('utf-8')
1261 if len(gajim
.events
.get_events(accountC
, jidC
)):
1264 iterC
= self
.model
.iter_next(iterC
)
1266 if self
.tree
.row_expanded(path
):
1267 state_images
= self
.get_appropriate_state_images(
1268 jid
, size
= 'opened',
1269 icon_name
= icon_name
)
1271 state_images
= self
.get_appropriate_state_images(
1272 jid
, size
= 'closed',
1273 icon_name
= icon_name
)
1275 # Expand/collapse icon might differ per iter
1277 img
= state_images
[icon_name
]
1278 self
.model
[child_iter
][C_IMG
] = img
1279 self
.model
[child_iter
][C_NAME
] = name
1281 # A normal contact or little brother
1282 state_images
= self
.get_appropriate_state_images(jid
,
1283 icon_name
= icon_name
)
1285 # All iters have the same icon (no expand/collapse)
1286 img
= state_images
[icon_name
]
1287 for child_iter
in child_iters
:
1288 self
.model
[child_iter
][C_IMG
] = img
1289 self
.model
[child_iter
][C_NAME
] = name
1291 # We are a little brother
1292 if family
and not is_big_brother
and not self
.starting
:
1293 self
.draw_parent_contact(jid
, account
)
1295 for group
in contact
.get_shown_groups():
1296 # We need to make sure that _visible_func is called for
1297 # our groups otherwise we might not be shown
1298 iterG
= self
._get
_group
_iter
(group
, account
, model
=self
.model
)
1300 # it's not self contact
1301 self
.model
[iterG
][C_JID
] = self
.model
[iterG
][C_JID
]
1306 def draw_mood(self
, jid
, account
):
1307 iters
= self
._get
_contact
_iter
(jid
, account
, model
=self
.model
)
1308 if not iters
or not gajim
.config
.get('show_mood_in_roster'):
1310 jid
= self
.model
[iters
[0]][C_JID
]
1311 jid
= jid
.decode('utf-8')
1312 contact
= gajim
.contacts
.get_contact(account
, jid
)
1313 if 'mood' in contact
.mood
and contact
.mood
['mood'].strip() in MOODS
:
1314 pixbuf
= gtkgui_helpers
.load_mood_icon(
1315 contact
.mood
['mood'].strip()).get_pixbuf()
1316 elif 'mood' in contact
.mood
:
1317 pixbuf
= gtkgui_helpers
.load_mood_icon(
1318 'unknown').get_pixbuf()
1321 for child_iter
in iters
:
1322 self
.model
[child_iter
][C_MOOD_PIXBUF
] = pixbuf
1326 def draw_activity(self
, jid
, account
):
1327 iters
= self
._get
_contact
_iter
(jid
, account
, model
=self
.model
)
1328 if not iters
or not gajim
.config
.get('show_activity_in_roster'):
1330 jid
= self
.model
[iters
[0]][C_JID
]
1331 jid
= jid
.decode('utf-8')
1332 contact
= gajim
.contacts
.get_contact(account
, jid
)
1333 if 'activity' in contact
.activity \
1334 and contact
.activity
['activity'].strip() in ACTIVITIES
:
1335 if 'subactivity' in contact
.activity \
1336 and contact
.activity
['subactivity'].strip() in \
1337 ACTIVITIES
[contact
.activity
['activity'].strip()]:
1338 pixbuf
= gtkgui_helpers
.load_activity_icon(
1339 contact
.activity
['activity'].strip(),
1340 contact
.activity
['subactivity'].strip()).get_pixbuf()
1342 pixbuf
= gtkgui_helpers
.load_activity_icon(
1343 contact
.activity
['activity'].strip()).get_pixbuf()
1344 elif 'activity' in contact
.activity
:
1345 pixbuf
= gtkgui_helpers
.load_activity_icon(
1346 'unknown').get_pixbuf()
1349 for child_iter
in iters
:
1350 self
.model
[child_iter
][C_ACTIVITY_PIXBUF
] = pixbuf
1354 def draw_tune(self
, jid
, account
):
1355 iters
= self
._get
_contact
_iter
(jid
, account
, model
=self
.model
)
1356 if not iters
or not gajim
.config
.get('show_tunes_in_roster'):
1358 jid
= self
.model
[iters
[0]][C_JID
]
1359 jid
= jid
.decode('utf-8')
1360 contact
= gajim
.contacts
.get_contact(account
, jid
)
1361 if 'artist' in contact
.tune
or 'title' in contact
.tune
:
1362 path
= os
.path
.join(gajim
.DATA_DIR
, 'emoticons', 'static', 'music.png')
1363 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(path
)
1366 for child_iter
in iters
:
1367 self
.model
[child_iter
][C_TUNE_PIXBUF
] = pixbuf
1371 def draw_avatar(self
, jid
, account
):
1372 iters
= self
._get
_contact
_iter
(jid
, account
, model
=self
.model
)
1373 if not iters
or not gajim
.config
.get('show_avatars_in_roster'):
1375 jid
= self
.model
[iters
[0]][C_JID
]
1376 jid
= jid
.decode('utf-8')
1377 pixbuf
= gtkgui_helpers
.get_avatar_pixbuf_from_cache(jid
)
1378 if pixbuf
is None or pixbuf
== 'ask':
1379 scaled_pixbuf
= None
1381 scaled_pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'roster')
1382 for child_iter
in iters
:
1383 self
.model
[child_iter
][C_AVATAR_PIXBUF
] = scaled_pixbuf
1386 def draw_completely(self
, jid
, account
):
1387 self
.draw_contact(jid
, account
)
1388 self
.draw_mood(jid
, account
)
1389 self
.draw_activity(jid
, account
)
1390 self
.draw_tune(jid
, account
)
1391 self
.draw_avatar(jid
, account
)
1393 def adjust_and_draw_contact_context(self
, jid
, account
):
1394 '''Draw contact, account and groups of given jid
1395 Show contact if it has pending events
1397 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
1399 # idle draw or just removed SelfContact
1402 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
1404 # There might be a new big brother
1405 self
._recalibrate
_metacontact
_family
(family
, account
)
1406 self
.draw_contact(jid
, account
)
1407 self
.draw_account(account
)
1409 for group
in contact
.get_shown_groups():
1410 self
.draw_group(group
, account
)
1411 self
._adjust
_group
_expand
_collapse
_state
(group
, account
)
1413 def _idle_draw_jids_of_account(self
, jids
, account
):
1414 '''Draw given contacts and their avatars in a lazy fashion.
1417 jids -- a list of jids to draw
1418 account -- the corresponding account
1420 def _draw_all_contacts(jids
, account
):
1422 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
1424 # For metacontacts over several accounts:
1425 # When we connect a new account existing brothers
1426 # must be redrawn (got removed and readded)
1428 self
.draw_completely(data
['jid'], data
['account'])
1430 self
.draw_completely(jid
, account
)
1434 task
= _draw_all_contacts(jids
, account
)
1435 gobject
.idle_add(task
.next
)
1437 def setup_and_draw_roster(self
):
1438 '''create new empty model and draw roster'''
1439 self
.modelfilter
= None
1440 # (icon, name, type, jid, account, editable, mood_pixbuf,
1441 # activity_pixbuf, tune_pixbuf avatar_pixbuf, padlock_pixbuf)
1442 self
.model
= gtk
.TreeStore(gtk
.Image
, str, str, str, str,
1443 gtk
.gdk
.Pixbuf
, gtk
.gdk
.Pixbuf
, gtk
.gdk
.Pixbuf
,
1444 gtk
.gdk
.Pixbuf
, gtk
.gdk
.Pixbuf
)
1446 self
.model
.set_sort_func(1, self
._compareIters
)
1447 self
.model
.set_sort_column_id(1, gtk
.SORT_ASCENDING
)
1448 self
.modelfilter
= self
.model
.filter_new()
1449 self
.modelfilter
.set_visible_func(self
._visible
_func
)
1450 self
.modelfilter
.connect('row-has-child-toggled',
1451 self
.on_modelfilter_row_has_child_toggled
)
1452 self
.tree
.set_model(self
.modelfilter
)
1454 for acct
in gajim
.connections
:
1455 self
.add_account(acct
)
1456 self
.add_account_contacts(acct
)
1457 # Recalculate column width for ellipsizing
1458 self
.tree
.columns_autosize()
1461 def select_contact(self
, jid
, account
):
1462 '''Select contact in roster. If contact is hidden but has events,
1464 # Refiltering SHOULD NOT be needed:
1465 # When a contact gets a new event he will be redrawn and his
1466 # icon changes, so _visible_func WILL be called on him anyway
1467 iters
= self
._get
_contact
_iter
(jid
, account
)
1469 # Not visible in roster
1471 path
= self
.modelfilter
.get_path(iters
[0])
1472 if self
.dragging
or not gajim
.config
.get('scroll_roster_to_last_message'):
1473 # do not change selection while DND'ing
1475 # Expand his parent, so this path is visible, don't expand it.
1476 self
.tree
.expand_to_path(path
[:-1])
1477 self
.tree
.scroll_to_cell(path
)
1478 self
.tree
.set_cursor(path
)
1481 def _adjust_account_expand_collapse_state(self
, account
):
1482 '''Expand/collapse account row based on self.collapsed_rows'''
1483 iterA
= self
._get
_account
_iter
(account
)
1485 # thank you modelfilter
1487 path
= self
.modelfilter
.get_path(iterA
)
1488 if account
in self
.collapsed_rows
:
1489 self
.tree
.collapse_row(path
)
1491 self
.tree
.expand_row(path
, False)
1495 def _adjust_group_expand_collapse_state(self
, group
, account
):
1496 '''Expand/collapse group row based on self.collapsed_rows'''
1497 iterG
= self
._get
_group
_iter
(group
, account
)
1501 path
= self
.modelfilter
.get_path(iterG
)
1502 if account
+ group
in self
.collapsed_rows
:
1503 self
.tree
.collapse_row(path
)
1505 self
.tree
.expand_row(path
, False)
1508 ##############################################################################
1509 ### Roster and Modelfilter handling
1510 ##############################################################################
1512 def _search_roster_func(self
, model
, column
, key
, titer
):
1513 key
= key
.decode('utf-8').lower()
1514 name
= model
[titer
][C_NAME
].decode('utf-8').lower()
1515 return not (key
in name
)
1517 def refilter_shown_roster_items(self
):
1518 self
.filtering
= True
1519 self
.modelfilter
.refilter()
1520 self
.filtering
= False
1522 def contact_has_pending_roster_events(self
, contact
, account
):
1523 '''Return True if the contact or one if it resources has pending events'''
1524 # jid has pending events
1525 if gajim
.events
.get_nb_roster_events(account
, contact
.jid
) > 0:
1527 # check events of all resources
1528 for contact_
in gajim
.contacts
.get_contacts(account
, contact
.jid
):
1529 if contact_
.resource
and gajim
.events
.get_nb_roster_events(account
,
1530 contact_
.get_full_jid()) > 0:
1534 def contact_is_visible(self
, contact
, account
):
1535 if self
.contact_has_pending_roster_events(contact
, account
):
1538 if contact
.show
in ('offline', 'error'):
1539 if contact
.jid
in gajim
.to_be_removed
[account
]:
1542 if gajim
.config
.get('show_only_chat_and_online') and contact
.show
in (
1543 'away', 'xa', 'busy'):
1547 def _visible_func(self
, model
, titer
):
1548 '''Determine whether iter should be visible in the treeview'''
1549 type_
= model
[titer
][C_TYPE
]
1552 if type_
== 'account':
1553 # Always show account
1556 account
= model
[titer
][C_ACCOUNT
]
1560 account
= account
.decode('utf-8')
1561 jid
= model
[titer
][C_JID
]
1564 jid
= jid
.decode('utf-8')
1565 if type_
== 'group':
1567 if group
== _('Transports'):
1569 accounts
= gajim
.contacts
.get_accounts()
1571 accounts
= [account
]
1572 for _acc
in accounts
:
1573 for contact
in gajim
.contacts
.iter_contacts(_acc
):
1574 if group
in contact
.get_shown_groups() and \
1575 self
.contact_has_pending_roster_events(contact
, _acc
):
1577 return gajim
.config
.get('show_transports_group') and \
1578 (gajim
.account_is_connected(account
) or \
1579 gajim
.config
.get('showoffline'))
1580 if gajim
.config
.get('showoffline'):
1585 # C_ACCOUNT for groups depends on the order
1586 # accounts were connected
1587 # Check all accounts for online group contacts
1588 accounts
= gajim
.contacts
.get_accounts()
1590 accounts
= [account
]
1591 for _acc
in accounts
:
1592 for contact
in gajim
.contacts
.iter_contacts(_acc
):
1593 # Is this contact in this group ? (last part of if check if it's
1595 if group
in contact
.get_shown_groups():
1596 if self
.contact_is_visible(contact
, _acc
):
1599 if type_
== 'contact':
1600 if gajim
.config
.get('showoffline'):
1604 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
1606 nearby_family
, bb_jid
, bb_account
= \
1607 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)
1608 if (bb_jid
, bb_account
) == (jid
, account
):
1609 # Show the big brother if a child has pending events
1610 for data
in nearby_family
:
1612 account
= data
['account']
1613 contact
= gajim
.contacts
.get_contact_with_highest_priority(
1615 if contact
and self
.contact_is_visible(contact
, account
):
1619 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
1621 return self
.contact_is_visible(contact
, account
)
1622 if type_
== 'agent':
1623 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
1625 return self
.contact_has_pending_roster_events(contact
, account
) or \
1626 (gajim
.config
.get('show_transports_group') and \
1627 (gajim
.account_is_connected(account
) or \
1628 gajim
.config
.get('showoffline')))
1631 def _compareIters(self
, model
, iter1
, iter2
, data
=None):
1632 '''Compare two iters to sort them'''
1633 name1
= model
[iter1
][C_NAME
]
1634 name2
= model
[iter2
][C_NAME
]
1635 if not name1
or not name2
:
1637 name1
= name1
.decode('utf-8')
1638 name2
= name2
.decode('utf-8')
1639 type1
= model
[iter1
][C_TYPE
]
1640 type2
= model
[iter2
][C_TYPE
]
1641 if type1
== 'self_contact':
1643 if type2
== 'self_contact':
1645 if type1
== 'group':
1646 name1
= model
[iter1
][C_JID
]
1647 name2
= model
[iter2
][C_JID
]
1648 if name1
== _('Transports'):
1650 if name2
== _('Transports'):
1652 if name1
== _('Not in Roster'):
1654 if name2
== _('Not in Roster'):
1656 if name1
== _('Groupchats'):
1658 if name2
== _('Groupchats'):
1660 account1
= model
[iter1
][C_ACCOUNT
]
1661 account2
= model
[iter2
][C_ACCOUNT
]
1662 if not account1
or not account2
:
1664 account1
= account1
.decode('utf-8')
1665 account2
= account2
.decode('utf-8')
1666 if type1
== 'account':
1667 return locale
.strcoll(account1
, account2
)
1668 jid1
= model
[iter1
][C_JID
].decode('utf-8')
1669 jid2
= model
[iter2
][C_JID
].decode('utf-8')
1670 if type1
== 'contact':
1671 lcontact1
= gajim
.contacts
.get_contacts(account1
, jid1
)
1672 contact1
= gajim
.contacts
.get_first_contact_from_jid(account1
, jid1
)
1675 name1
= contact1
.get_shown_name()
1676 if type2
== 'contact':
1677 lcontact2
= gajim
.contacts
.get_contacts(account2
, jid2
)
1678 contact2
= gajim
.contacts
.get_first_contact_from_jid(account2
, jid2
)
1681 name2
= contact2
.get_shown_name()
1682 # We first compare by show if sort_by_show_in_roster is True or if it's a
1684 if type1
== 'contact' and type2
== 'contact' and \
1685 gajim
.config
.get('sort_by_show_in_roster'):
1686 cshow
= {'chat':0, 'online': 1, 'away': 2, 'xa': 3, 'dnd': 4,
1687 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
1688 s
= self
.get_show(lcontact1
)
1689 show1
= cshow
.get(s
, 9)
1690 s
= self
.get_show(lcontact2
)
1691 show2
= cshow
.get(s
, 9)
1694 if show1
== 6 and jid1
in gajim
.to_be_removed
[account1
]:
1696 if show2
== 6 and jid2
in gajim
.to_be_removed
[account2
]:
1698 if removing1
and not removing2
:
1700 if removing2
and not removing1
:
1704 # none and from goes after
1705 if sub1
not in ['none', 'from'] and sub2
in ['none', 'from']:
1707 if sub1
in ['none', 'from'] and sub2
not in ['none', 'from']:
1714 cmp_result
= locale
.strcoll(name1
.lower(), name2
.lower())
1719 if type1
== 'contact' and type2
== 'contact':
1720 # We compare account names
1721 cmp_result
= locale
.strcoll(account1
.lower(), account2
.lower())
1727 cmp_result
= locale
.strcoll(jid1
.lower(), jid2
.lower())
1734 ################################################################################
1735 ### FIXME: Methods that don't belong to roster window...
1736 ### ... atleast not in there current form
1737 ################################################################################
1739 def fire_up_unread_messages_events(self
, account
):
1740 '''reads from db the unread messages, and fire them up, and
1741 if we find very old unread messages, delete them from unread table'''
1742 results
= gajim
.logger
.get_unread_msgs()
1743 for result
in results
:
1746 if gajim
.contacts
.get_first_contact_from_jid(account
, jid
) and not \
1748 # We have this jid in our contacts list
1749 # XXX unread messages should probably have their session saved with
1751 session
= gajim
.connections
[account
].make_new_session(jid
)
1753 tim
= time
.localtime(float(result
[2]))
1754 session
.roster_message(jid
, result
[1], tim
, msg_type
='chat',
1756 gajim
.logger
.set_shown_unread_msgs(result
[0])
1758 elif (time
.time() - result
[2]) > 2592000:
1759 # ok, here we see that we have a message in unread messages table
1760 # that is older than a month. It is probably from someone not in our
1761 # roster for accounts we usually launch, so we will delete this id
1762 # from unread message tables.
1763 gajim
.logger
.set_read_messages([result
[0]])
1765 def fill_contacts_and_groups_dicts(self
, array
, account
):
1766 '''fill gajim.contacts and gajim.groups'''
1767 # FIXME: This function needs to be splitted
1768 # Most of the logic SHOULD NOT be done at GUI level
1769 if account
not in gajim
.contacts
.get_accounts():
1770 gajim
.contacts
.add_account(account
)
1771 if account
not in gajim
.groups
:
1772 gajim
.groups
[account
] = {}
1773 if gajim
.config
.get('show_self_contact') == 'always':
1774 self_jid
= gajim
.get_jid_from_account(account
)
1775 if gajim
.connections
[account
].server_resource
:
1776 self_jid
+= '/' + gajim
.connections
[account
].server_resource
1777 array
[self_jid
] = {'name': gajim
.nicks
[account
],
1778 'groups': ['self_contact'], 'subscription': 'both', 'ask': 'none'}
1780 for jid
in array
.keys():
1781 # Remove the contact in roster. It might has changed
1782 self
.remove_contact(jid
, account
, force
=True)
1783 # Remove old Contact instances
1784 gajim
.contacts
.remove_jid(account
, jid
, remove_meta
=False)
1785 jids
= jid
.split('/')
1791 resource
= '/'.join(jids
[1:])
1793 name
= array
[jid
]['name'] or ''
1794 show
= 'offline' # show is offline by default
1795 status
= '' # no status message by default
1798 attached_keys
= gajim
.config
.get_per('accounts', account
,
1799 'attached_gpg_keys').split()
1800 if jid
in attached_keys
:
1801 keyID
= attached_keys
[attached_keys
.index(jid
) + 1]
1803 if gajim
.jid_is_transport(jid
):
1804 array
[jid
]['groups'] = [_('Transports')]
1805 contact1
= gajim
.contacts
.create_contact(jid
=ji
, name
=name
,
1806 groups
=array
[jid
]['groups'], show
=show
, status
=status
,
1807 sub
=array
[jid
]['subscription'], ask
=array
[jid
]['ask'],
1808 resource
=resource
, keyID
=keyID
)
1809 gajim
.contacts
.add_contact(account
, contact1
)
1811 if gajim
.config
.get('ask_avatars_on_startup'):
1812 pixbuf
= gtkgui_helpers
.get_avatar_pixbuf_from_cache(ji
)
1814 transport
= gajim
.get_transport_name_from_jid(contact1
.jid
)
1815 if not transport
or gajim
.jid_is_transport(contact1
.jid
):
1816 jid_with_resource
= contact1
.jid
1817 if contact1
.resource
:
1818 jid_with_resource
+= '/' + contact1
.resource
1819 gajim
.connections
[account
].request_vcard(jid_with_resource
)
1821 host
= gajim
.get_server_from_jid(contact1
.jid
)
1822 if host
not in gajim
.transport_avatar
[account
]:
1823 gajim
.transport_avatar
[account
][host
] = [contact1
.jid
]
1825 gajim
.transport_avatar
[account
][host
].append(contact1
.jid
)
1827 # If we already have chat windows opened, update them with new contact
1829 chat_control
= gajim
.interface
.msg_win_mgr
.get_control(ji
, account
)
1831 chat_control
.contact
= contact1
1833 def connected_rooms(self
, account
):
1834 if account
in gajim
.gc_connected
[account
].values():
1838 def on_event_removed(self
, event_list
):
1839 '''Remove contacts on last events removed.
1841 Only performed if removal was requested before but the contact
1842 still had pending events
1844 contact_list
= ((event
.jid
.split('/')[0], event
.account
) for event
in \
1847 for jid
, account
in contact_list
:
1848 self
.draw_contact(jid
, account
)
1849 # Remove contacts in roster if removal was requested
1850 key
= (jid
, account
)
1851 if key
in self
.contacts_to_be_removed
.keys():
1852 backend
= self
.contacts_to_be_removed
[key
]['backend']
1853 del self
.contacts_to_be_removed
[key
]
1854 # Remove contact will delay removal if there are more events pending
1855 self
.remove_contact(jid
, account
, backend
=backend
)
1858 def open_event(self
, account
, jid
, event
):
1859 '''If an event was handled, return True, else return False'''
1860 data
= event
.parameters
1861 ft
= gajim
.interface
.instances
['file_transfers']
1862 event
= gajim
.events
.get_first_event(account
, jid
, event
.type_
)
1863 if event
.type_
== 'normal':
1864 dialogs
.SingleMessageWindow(account
, jid
,
1865 action
='receive', from_whom
=jid
, subject
=data
[1], message
=data
[0],
1866 resource
=data
[5], session
=data
[8], form_node
=data
[9])
1867 gajim
.events
.remove_events(account
, jid
, event
)
1869 elif event
.type_
== 'file-request':
1870 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
1872 ft
.show_file_request(account
, contact
, data
)
1873 gajim
.events
.remove_events(account
, jid
, event
)
1875 elif event
.type_
in ('file-request-error', 'file-send-error'):
1876 ft
.show_send_error(data
)
1877 gajim
.events
.remove_events(account
, jid
, event
)
1879 elif event
.type_
in ('file-error', 'file-stopped'):
1881 if data
['error'] == -1:
1882 msg_err
= _('Remote contact stopped transfer')
1883 elif data
['error'] == -6:
1884 msg_err
= _('Error opening file')
1885 ft
.show_stopped(jid
, data
, error_msg
=msg_err
)
1886 gajim
.events
.remove_events(account
, jid
, event
)
1888 elif event
.type_
== 'file-completed':
1889 ft
.show_completed(jid
, data
)
1890 gajim
.events
.remove_events(account
, jid
, event
)
1892 elif event
.type_
== 'gc-invitation':
1893 dialogs
.InvitationReceivedDialog(account
, data
[0], jid
, data
[2],
1895 gajim
.events
.remove_events(account
, jid
, event
)
1897 elif event
.type_
== 'subscription_request':
1898 dialogs
.SubscriptionRequestWindow(jid
, data
[0], account
, data
[1])
1899 gajim
.events
.remove_events(account
, jid
, event
)
1901 elif event
.type_
== 'unsubscribed':
1902 gajim
.interface
.show_unsubscribed_dialog(account
, data
)
1903 gajim
.events
.remove_events(account
, jid
, event
)
1907 ################################################################################
1908 ### This and that... random.
1909 ################################################################################
1911 def show_roster_vbox(self
, active
):
1913 self
.xml
.get_widget('roster_vbox2').show()
1915 self
.xml
.get_widget('roster_vbox2').hide()
1918 def show_tooltip(self
, contact
):
1919 pointer
= self
.tree
.get_pointer()
1920 props
= self
.tree
.get_path_at_pos(pointer
[0], pointer
[1])
1921 # check if the current pointer is at the same path
1922 # as it was before setting the timeout
1923 if props
and self
.tooltip
.id == props
[0]:
1924 # bounding rectangle of coordinates for the cell within the treeview
1925 rect
= self
.tree
.get_cell_area(props
[0], props
[1])
1927 # position of the treeview on the screen
1928 position
= self
.tree
.window
.get_origin()
1929 self
.tooltip
.show_tooltip(contact
, rect
.height
, position
[1] + rect
.y
)
1931 self
.tooltip
.hide_tooltip()
1934 def authorize(self
, widget
, jid
, account
):
1935 '''Authorize a contact (by re-sending auth menuitem)'''
1936 gajim
.connections
[account
].send_authorization(jid
)
1937 dialogs
.InformationDialog(_('Authorization has been sent'),
1938 _('Now "%s" will know your status.') %jid
)
1940 def req_sub(self
, widget
, jid
, txt
, account
, groups
=[], nickname
=None,
1942 '''Request subscription to a contact'''
1943 gajim
.connections
[account
].request_subscription(jid
, txt
, nickname
,
1944 groups
, auto_auth
, gajim
.nicks
[account
])
1945 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
1948 attached_keys
= gajim
.config
.get_per('accounts', account
,
1949 'attached_gpg_keys').split()
1950 if jid
in attached_keys
:
1951 keyID
= attached_keys
[attached_keys
.index(jid
) + 1]
1952 contact
= gajim
.contacts
.create_contact(jid
=jid
, name
=nickname
,
1953 groups
=groups
, show
='requested', status
='', ask
='none',
1954 sub
='subscribe', keyID
=keyID
)
1955 gajim
.contacts
.add_contact(account
, contact
)
1957 if not _('Not in Roster') in contact
.get_shown_groups():
1958 dialogs
.InformationDialog(_('Subscription request has been sent'),
1959 _('If "%s" accepts this request you will know his or her status.'
1962 self
.remove_contact(contact
.jid
, account
, force
=True)
1963 contact
.groups
= groups
1965 contact
.name
= nickname
1966 self
.add_contact(jid
, account
)
1968 def revoke_auth(self
, widget
, jid
, account
):
1969 '''Revoke a contact's authorization'''
1970 gajim
.connections
[account
].refuse_authorization(jid
)
1971 dialogs
.InformationDialog(_('Authorization has been removed'),
1972 _('Now "%s" will always see you as offline.') %jid
)
1974 def set_state(self
, account
, state
):
1975 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
1977 self
.model
[child_iterA
][0] = \
1978 gajim
.interface
.jabber_state_images
['16'][state
]
1979 if gajim
.interface
.systray_enabled
:
1980 gajim
.interface
.systray
.change_status(state
)
1982 def set_connecting_state(self
, account
):
1983 self
.set_state(account
, 'connecting')
1985 def send_status(self
, account
, status
, txt
, auto
=False, to
=None):
1986 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
1987 if status
!= 'offline':
1989 if status
== gajim
.connections
[account
].get_status() and \
1990 txt
== gajim
.connections
[account
].status
:
1992 gajim
.config
.set_per('accounts', account
, 'last_status', status
)
1993 gajim
.config
.set_per('accounts', account
, 'last_status_msg',
1994 helpers
.to_one_line(txt
))
1995 if gajim
.connections
[account
].connected
< 2:
1996 self
.set_connecting_state(account
)
1998 keyid
= gajim
.config
.get_per('accounts', account
, 'keyid')
1999 if keyid
and not gajim
.connections
[account
].gpg
:
2000 dialogs
.WarningDialog(_('GPG is not usable'),
2001 _('You will be connected to %s without OpenPGP.') % account
)
2003 self
.send_status_continue(account
, status
, txt
, auto
, to
)
2005 def send_pep(self
, account
, pep_dict
=None):
2006 '''Sends pep information (activity, mood)'''
2010 if 'activity' in pep_dict
and pep_dict
['activity'] in pep
.ACTIVITIES
:
2011 activity
= pep_dict
['activity']
2012 if 'subactivity' in pep_dict
and \
2013 pep_dict
['subactivity'] in pep
.ACTIVITIES
[activity
]:
2014 subactivity
= pep_dict
['subactivity']
2016 subactivity
= 'other'
2017 if 'activity_text' in pep_dict
:
2018 activity_text
= pep_dict
['activity_text']
2021 pep
.user_send_activity(account
, activity
, subactivity
, activity_text
)
2023 pep
.user_send_activity(account
, '')
2026 if 'mood' in pep_dict
and pep_dict
['mood'] in pep
.MOODS
:
2027 mood
= pep_dict
['mood']
2028 if 'mood_text' in pep_dict
:
2029 mood_text
= pep_dict
['mood_text']
2032 pep
.user_send_mood(account
, mood
, mood_text
)
2034 pep
.user_send_mood(account
, '')
2036 def send_status_continue(self
, account
, status
, txt
, auto
, to
):
2037 if gajim
.account_is_connected(account
) and not to
:
2038 if status
== 'online' and gajim
.interface
.sleeper
.getState() != \
2039 common
.sleepy
.STATE_UNKNOWN
:
2040 gajim
.sleeper_state
[account
] = 'online'
2041 elif gajim
.sleeper_state
[account
] not in ('autoaway', 'autoxa') or \
2042 status
== 'offline':
2043 gajim
.sleeper_state
[account
] = 'off'
2046 gajim
.connections
[account
].send_custom_status(status
, txt
, to
)
2048 if status
in ('invisible', 'offline'):
2049 pep
.delete_pep(gajim
.get_jid_from_account(account
), \
2051 was_invisible
= gajim
.connections
[account
].connected
== \
2052 gajim
.SHOW_LIST
.index('invisible')
2053 gajim
.connections
[account
].change_status(status
, txt
, auto
)
2055 if account
in gajim
.interface
.status_sent_to_users
:
2056 gajim
.interface
.status_sent_to_users
[account
] = {}
2057 if account
in gajim
.interface
.status_sent_to_groups
:
2058 gajim
.interface
.status_sent_to_groups
[account
] = {}
2059 for gc_control
in gajim
.interface
.msg_win_mgr
.get_controls(
2060 message_control
.TYPE_GC
) + \
2061 gajim
.interface
.minimized_controls
[account
].values():
2062 if gc_control
.account
== account
:
2063 if gajim
.gc_connected
[account
][gc_control
.room_jid
]:
2064 gajim
.connections
[account
].send_gc_status(gc_control
.nick
,
2065 gc_control
.room_jid
, status
, txt
)
2067 # for some reason, we are not connected to the room even if
2068 # tab is opened, send initial join_gc()
2069 gajim
.connections
[account
].join_gc(gc_control
.nick
,
2070 gc_control
.room_jid
, None)
2071 if was_invisible
and status
!= 'offline':
2072 # We come back from invisible, join bookmarks
2073 gajim
.interface
.auto_join_bookmarks(account
)
2076 def chg_contact_status(self
, contact
, show
, status
, account
):
2077 '''When a contact changes his or her status'''
2078 contact_instances
= gajim
.contacts
.get_contacts(account
, contact
.jid
)
2080 contact
.status
= status
2081 # name is to show in conversation window
2082 name
= contact
.get_shown_name()
2083 fjid
= contact
.get_full_jid()
2085 # The contact has several resources
2086 if len(contact_instances
) > 1:
2087 if contact
.resource
!= '':
2088 name
+= '/' + contact
.resource
2090 # Remove resource when going offline
2091 if show
in ('offline', 'error') and \
2092 not self
.contact_has_pending_roster_events(contact
, account
):
2093 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(fjid
, account
)
2096 ctrl
.parent_win
.redraw_tab(ctrl
)
2097 # keep the contact around, since it's
2098 # already attached to the control
2100 gajim
.contacts
.remove_contact(account
, contact
)
2102 elif contact
.jid
== gajim
.get_jid_from_account(account
) and \
2103 show
in ('offline', 'error'):
2104 if gajim
.config
.get('show_self_contact') != 'never':
2105 # SelfContact went offline. Remove him when last pending
2107 self
.remove_contact(contact
.jid
, account
, backend
=True)
2109 uf_show
= helpers
.get_uf_show(show
)
2111 # print status in chat window and update status/GPG image
2112 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(contact
.jid
, account
)
2113 if ctrl
and ctrl
.type_id
!= message_control
.TYPE_GC
:
2114 ctrl
.contact
= gajim
.contacts
.get_contact_with_highest_priority(
2115 account
, contact
.jid
)
2116 ctrl
.update_status_display(name
, uf_show
, status
)
2118 if contact
.resource
:
2119 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(fjid
, account
)
2121 ctrl
.update_status_display(name
, uf_show
, status
)
2123 # Delete pep if needed
2124 keep_pep
= any(c
.show
not in ('error', 'offline') for c
in
2126 if not keep_pep
and contact
.jid
!= gajim
.get_jid_from_account(account
) \
2127 and not contact
.is_groupchat():
2128 pep
.delete_pep(contact
.jid
, account
)
2130 # Redraw everything and select the sender
2131 self
.adjust_and_draw_contact_context(contact
.jid
, account
)
2134 def on_status_changed(self
, account
, show
):
2135 '''the core tells us that our status has changed'''
2136 if account
not in gajim
.contacts
.get_accounts():
2138 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
2139 if gajim
.config
.get('show_self_contact') == 'always':
2140 self_resource
= gajim
.connections
[account
].server_resource
2141 self_contact
= gajim
.contacts
.get_contact(account
,
2142 gajim
.get_jid_from_account(account
), resource
=self_resource
)
2144 status
= gajim
.connections
[account
].status
2145 self
.chg_contact_status(self_contact
, show
, status
, account
)
2146 self
.set_account_status_icon(account
)
2147 if show
== 'offline':
2148 if self
.quit_on_next_offline
> -1:
2149 # we want to quit, we are waiting for all accounts to be offline
2150 self
.quit_on_next_offline
-= 1
2151 if self
.quit_on_next_offline
< 1:
2152 # all accounts offline, quit
2153 self
.quit_gtkgui_interface()
2155 # No need to redraw contacts if we're quitting
2157 self
.model
[child_iterA
][C_AVATAR_PIXBUF
] = None
2158 if account
in gajim
.con_types
:
2159 gajim
.con_types
[account
] = None
2160 for jid
in gajim
.contacts
.get_jid_list(account
):
2161 lcontact
= gajim
.contacts
.get_contacts(account
, jid
)
2162 ctrl
= gajim
.interface
.msg_win_mgr
.get_gc_control(jid
, account
)
2163 for contact
in [c
for c
in lcontact
if ((c
.show
!= 'offline' or \
2164 c
.is_transport()) and not ctrl
)]:
2165 self
.chg_contact_status(contact
, 'offline', '', account
)
2166 self
.actions_menu_needs_rebuild
= True
2167 self
.update_status_combobox()
2169 def get_status_message(self
, show
, on_response
, show_pep
=True,
2171 ''' get the status message by:
2172 1/ looking in default status message
2173 2/ asking to user if needed depending on ask_on(ff)line_status and
2175 show_pep can be False to hide pep things from status message or True
2177 empty_pep
= {'activity': '', 'subactivity': '', 'activity_text': '',
2178 'mood': '', 'mood_text': ''}
2179 if show
in gajim
.config
.get_per('defaultstatusmsg'):
2180 if gajim
.config
.get_per('defaultstatusmsg', show
, 'enabled'):
2181 msg
= gajim
.config
.get_per('defaultstatusmsg', show
, 'message')
2182 msg
= helpers
.from_one_line(msg
)
2183 on_response(msg
, empty_pep
)
2185 if not always_ask
and ((show
== 'online' and not gajim
.config
.get(
2186 'ask_online_status')) or (show
in ('offline', 'invisible') and not \
2187 gajim
.config
.get('ask_offline_status'))):
2188 on_response('', empty_pep
)
2191 dlg
= dialogs
.ChangeStatusMessageDialog(on_response
, show
, show_pep
)
2192 dlg
.dialog
.present() # show it on current workspace
2194 def change_status(self
, widget
, account
, status
):
2195 def change(account
, status
):
2196 def on_response(message
, pep_dict
):
2198 # user pressed Cancel to change status message dialog
2200 self
.send_status(account
, status
, message
)
2201 self
.send_pep(account
, pep_dict
)
2202 self
.get_status_message(status
, on_response
)
2204 if status
== 'invisible' and self
.connected_rooms(account
):
2205 dialogs
.ConfirmationDialog(
2206 _('You are participating in one or more group chats'),
2207 _('Changing your status to invisible will result in disconnection '
2208 'from those group chats. Are you sure you want to go invisible?'),
2209 on_response_ok
= (change
, account
, status
))
2211 change(account
, status
)
2213 def update_status_combobox(self
):
2214 # table to change index in connection.connected to index in combobox
2215 table
= {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
2216 'xa':3, 'dnd':4, 'invisible':5}
2218 # we check if there are more options in the combobox that it should
2219 # if yes, we remove the first ones
2220 while len(self
.status_combobox
.get_model()) > len(table
)+2:
2221 self
.status_combobox
.remove_text(0)
2223 show
= helpers
.get_global_show()
2224 # temporarily block signal in order not to send status that we show
2226 self
.combobox_callback_active
= False
2227 if helpers
.statuses_unified():
2228 self
.status_combobox
.set_active(table
[show
])
2230 uf_show
= helpers
.get_uf_show(show
)
2231 liststore
= self
.status_combobox
.get_model()
2232 liststore
.prepend(['SEPARATOR', None, '', True])
2233 status_combobox_text
= uf_show
+ ' (' + _("desync'ed") +')'
2234 liststore
.prepend([status_combobox_text
,
2235 gajim
.interface
.jabber_state_images
['16'][show
], show
, False])
2236 self
.status_combobox
.set_active(0)
2237 gajim
.interface
._change
_awn
_icon
_status
(show
)
2238 self
.combobox_callback_active
= True
2239 if gajim
.interface
.systray_enabled
:
2240 gajim
.interface
.systray
.change_status(show
)
2242 def get_show(self
, lcontact
):
2243 prio
= lcontact
[0].priority
2244 show
= lcontact
[0].show
2246 if u
.priority
> prio
:
2251 def on_message_window_delete(self
, win_mgr
, msg_win
):
2252 if gajim
.config
.get('one_message_window') == 'always_with_roster':
2253 self
.show_roster_vbox(True)
2254 gtkgui_helpers
.resize_window(self
.window
,
2255 gajim
.config
.get('roster_width'),
2256 gajim
.config
.get('roster_height'))
2258 def close_all_from_dict(self
, dic
):
2259 '''close all the windows in the given dictionary'''
2260 for w
in dic
.values():
2261 if isinstance(w
, dict):
2262 self
.close_all_from_dict(w
)
2266 def close_all(self
, account
, force
=False):
2267 '''close all the windows from an account
2268 if force is True, do not ask confirmation before closing chat/gc windows
2270 if account
in gajim
.interface
.instances
:
2271 self
.close_all_from_dict(gajim
.interface
.instances
[account
])
2272 for ctrl
in gajim
.interface
.msg_win_mgr
.get_controls(acct
=account
):
2273 ctrl
.parent_win
.remove_tab(ctrl
, ctrl
.parent_win
.CLOSE_CLOSE_BUTTON
,
2276 def on_roster_window_delete_event(self
, widget
, event
):
2277 '''Main window X button was clicked'''
2278 if gajim
.interface
.systray_enabled
and not gajim
.config
.get(
2279 'quit_on_roster_x_button') and gajim
.config
.get('trayicon') != 'on_event':
2280 self
.tooltip
.hide_tooltip()
2282 elif gajim
.config
.get('quit_on_roster_x_button'):
2283 self
.on_quit_request()
2287 gajim
.config
.set('quit_on_roster_x_button', True)
2288 self
.on_quit_request()
2289 dialogs
.ConfirmationDialogCheck(_('Really quit Gajim?'),
2290 _('Are you sure you want to quit Gajim?'),
2291 _('Always close Gajim'), on_response_ok
=on_ok
)
2292 return True # do NOT destroy the window
2294 def prepare_quit(self
):
2295 msgwin_width_adjust
= 0
2297 # in case show_roster_on_start is False and roster is never shown
2298 # window.window is None
2299 if self
.window
.window
is not None:
2300 x
, y
= self
.window
.window
.get_root_origin()
2301 gajim
.config
.set('roster_x-position', x
)
2302 gajim
.config
.set('roster_y-position', y
)
2303 width
, height
= self
.window
.get_size()
2304 # For the width use the size of the vbox containing the tree and
2305 # status combo, this will cancel out any hpaned width
2306 width
= self
.xml
.get_widget('roster_vbox2').allocation
.width
2307 gajim
.config
.set('roster_width', width
)
2308 gajim
.config
.set('roster_height', height
)
2309 if not self
.xml
.get_widget('roster_vbox2').get_property('visible'):
2310 # The roster vbox is hidden, so the message window is larger
2311 # then we want to save (i.e. the window will grow every startup)
2313 msgwin_width_adjust
= -1 * width
2314 gajim
.config
.set('show_roster_on_startup',
2315 self
.window
.get_property('visible'))
2316 gajim
.interface
.msg_win_mgr
.shutdown(msgwin_width_adjust
)
2318 gajim
.config
.set('collapsed_rows', '\t'.join(self
.collapsed_rows
))
2319 gajim
.interface
.save_config()
2320 for account
in gajim
.connections
:
2321 gajim
.connections
[account
].quit(True)
2322 self
.close_all(account
)
2323 if gajim
.interface
.systray_enabled
:
2324 gajim
.interface
.hide_systray()
2326 def quit_gtkgui_interface(self
):
2327 '''When we quit the gtk interface : exit gtk'''
2331 def on_quit_request(self
, widget
=None):
2332 ''' user want to quit. Check if he should be warned about messages
2333 pending. Terminate all sessions and send offline to all connected
2334 account. We do NOT really quit gajim here '''
2335 accounts
= gajim
.connections
.keys()
2337 for acct
in accounts
:
2338 if gajim
.connections
[acct
].connected
:
2342 def on_continue2(message
, pep_dict
):
2343 self
.quit_on_next_offline
= 0
2344 accounts_to_disconnect
= []
2345 for acct
in accounts
:
2346 if gajim
.connections
[acct
].connected
:
2347 self
.quit_on_next_offline
+= 1
2348 accounts_to_disconnect
.append(acct
)
2350 for acct
in accounts_to_disconnect
:
2351 self
.send_status(acct
, 'offline', message
)
2352 self
.send_pep(acct
, pep_dict
)
2354 if not self
.quit_on_next_offline
:
2355 self
.quit_gtkgui_interface()
2357 def on_continue(message
, pep_dict
):
2359 # user pressed Cancel to change status message dialog
2361 # check if we have unread messages
2362 unread
= gajim
.events
.get_nb_events()
2363 if not gajim
.config
.get('notify_on_all_muc_messages'):
2364 unread_not_to_notify
= gajim
.events
.get_nb_events(
2366 unread
-= unread_not_to_notify
2368 # check if we have recent messages
2370 for win
in gajim
.interface
.msg_win_mgr
.windows():
2371 for ctrl
in win
.controls():
2372 fjid
= ctrl
.get_full_jid()
2373 if fjid
in gajim
.last_message_time
[ctrl
.account
]:
2374 if time
.time() - gajim
.last_message_time
[ctrl
.account
][fjid
] \
2381 if unread
or recent
:
2382 dialogs
.ConfirmationDialog(_('You have unread messages'),
2383 _('Messages will only be available for reading them later if you'
2384 ' have history enabled and contact is in your roster.'),
2385 on_response_ok
=(on_continue2
, message
, pep_dict
))
2387 on_continue2(message
, pep_dict
)
2390 self
.get_status_message('offline', on_continue
, show_pep
=False)
2392 on_continue('', None)
2394 ################################################################################
2395 ### Menu and GUI callbacks
2396 ### FIXME: order callbacks in itself...
2397 ################################################################################
2399 def on_actions_menuitem_activate(self
, widget
):
2402 def on_edit_menuitem_activate(self
, widget
):
2403 '''need to call make_menu to build profile, avatar item'''
2406 def on_bookmark_menuitem_activate(self
, widget
, account
, bookmark
):
2407 gajim
.interface
.join_gc_room(account
, bookmark
['jid'], bookmark
['nick'],
2408 bookmark
['password'])
2410 def on_send_server_message_menuitem_activate(self
, widget
, account
):
2411 server
= gajim
.config
.get_per('accounts', account
, 'hostname')
2412 server
+= '/announce/online'
2413 dialogs
.SingleMessageWindow(account
, server
, 'send')
2415 def on_xml_console_menuitem_activate(self
, widget
, account
):
2416 if 'xml_console' in gajim
.interface
.instances
[account
]:
2417 gajim
.interface
.instances
[account
]['xml_console'].window
.present()
2419 gajim
.interface
.instances
[account
]['xml_console'] = \
2420 dialogs
.XMLConsoleWindow(account
)
2422 def on_privacy_lists_menuitem_activate(self
, widget
, account
):
2423 if 'privacy_lists' in gajim
.interface
.instances
[account
]:
2424 gajim
.interface
.instances
[account
]['privacy_lists'].window
.present()
2426 gajim
.interface
.instances
[account
]['privacy_lists'] = \
2427 dialogs
.PrivacyListsWindow(account
)
2429 def on_set_motd_menuitem_activate(self
, widget
, account
):
2430 server
= gajim
.config
.get_per('accounts', account
, 'hostname')
2431 server
+= '/announce/motd'
2432 dialogs
.SingleMessageWindow(account
, server
, 'send')
2434 def on_update_motd_menuitem_activate(self
, widget
, account
):
2435 server
= gajim
.config
.get_per('accounts', account
, 'hostname')
2436 server
+= '/announce/motd/update'
2437 dialogs
.SingleMessageWindow(account
, server
, 'send')
2439 def on_delete_motd_menuitem_activate(self
, widget
, account
):
2440 server
= gajim
.config
.get_per('accounts', account
, 'hostname')
2441 server
+= '/announce/motd/delete'
2442 gajim
.connections
[account
].send_motd(server
)
2444 def on_history_manager_menuitem_activate(self
, widget
):
2446 if os
.path
.exists('history_manager.exe'): # user is running stable
2447 helpers
.exec_command('history_manager.exe')
2448 else: # user is running svn
2449 helpers
.exec_command('%s history_manager.py' % sys
.executable
)
2451 helpers
.exec_command('%s history_manager.py' % sys
.executable
)
2453 def on_info(self
, widget
, contact
, account
):
2454 '''Call vcard_information_window class to display contact's information'''
2455 if gajim
.connections
[account
].is_zeroconf
:
2456 self
.on_info_zeroconf(widget
, contact
, account
)
2459 info
= gajim
.interface
.instances
[account
]['infos']
2460 if contact
.jid
in info
:
2461 info
[contact
.jid
].window
.present()
2463 info
[contact
.jid
] = vcard
.VcardWindow(contact
, account
)
2465 def on_info_zeroconf(self
, widget
, contact
, account
):
2466 info
= gajim
.interface
.instances
[account
]['infos']
2467 if contact
.jid
in info
:
2468 info
[contact
.jid
].window
.present()
2470 contact
= gajim
.contacts
.get_first_contact_from_jid(account
,
2472 if contact
.show
in ('offline', 'error'):
2473 # don't show info on offline contacts
2475 info
[contact
.jid
] = vcard
.ZeroconfVcardWindow(contact
, account
)
2477 def on_roster_treeview_leave_notify_event(self
, widget
, event
):
2478 props
= widget
.get_path_at_pos(int(event
.x
), int(event
.y
))
2479 if self
.tooltip
.timeout
> 0:
2480 if not props
or self
.tooltip
.id == props
[0]:
2481 self
.tooltip
.hide_tooltip()
2483 def on_roster_treeview_motion_notify_event(self
, widget
, event
):
2484 model
= widget
.get_model()
2485 props
= widget
.get_path_at_pos(int(event
.x
), int(event
.y
))
2486 if self
.tooltip
.timeout
> 0:
2487 if not props
or self
.tooltip
.id != props
[0]:
2488 self
.tooltip
.hide_tooltip()
2493 titer
= model
.get_iter(row
)
2495 self
.tooltip
.hide_tooltip()
2497 if model
[titer
][C_TYPE
] in ('contact', 'self_contact'):
2498 # we're on a contact entry in the roster
2499 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
2500 jid
= model
[titer
][C_JID
].decode('utf-8')
2501 if self
.tooltip
.timeout
== 0 or self
.tooltip
.id != props
[0]:
2502 self
.tooltip
.id = row
2503 contacts
= gajim
.contacts
.get_contacts(account
, jid
)
2504 connected_contacts
= []
2506 if c
.show
not in ('offline', 'error'):
2507 connected_contacts
.append(c
)
2508 if not connected_contacts
:
2509 # no connected contacts, show the ofline one
2510 connected_contacts
= contacts
2511 self
.tooltip
.account
= account
2512 self
.tooltip
.timeout
= gobject
.timeout_add(500,
2513 self
.show_tooltip
, connected_contacts
)
2514 elif model
[titer
][C_TYPE
] == 'groupchat':
2515 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
2516 jid
= model
[titer
][C_JID
].decode('utf-8')
2517 if self
.tooltip
.timeout
== 0 or self
.tooltip
.id != props
[0]:
2518 self
.tooltip
.id = row
2519 contact
= gajim
.contacts
.get_contacts(account
, jid
)
2520 self
.tooltip
.account
= account
2521 self
.tooltip
.timeout
= gobject
.timeout_add(500,
2522 self
.show_tooltip
, contact
)
2523 elif model
[titer
][C_TYPE
] == 'account':
2524 # we're on an account entry in the roster
2525 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
2526 if account
== 'all':
2527 if self
.tooltip
.timeout
== 0 or self
.tooltip
.id != props
[0]:
2528 self
.tooltip
.id = row
2529 self
.tooltip
.account
= None
2530 self
.tooltip
.timeout
= gobject
.timeout_add(500,
2531 self
.show_tooltip
, [])
2533 jid
= gajim
.get_jid_from_account(account
)
2535 connection
= gajim
.connections
[account
]
2536 # get our current contact info
2538 nbr_on
, nbr_total
= gajim
.contacts
.get_nb_online_total_contacts(
2539 accounts
= [account
])
2540 account_name
= account
2541 if gajim
.account_is_connected(account
):
2542 account_name
+= ' (%s/%s)' % (repr(nbr_on
), repr(nbr_total
))
2543 contact
= gajim
.contacts
.create_contact(jid
=jid
, name
=account_name
,
2544 show
=connection
.get_status(), sub
='', status
=connection
.status
,
2545 resource
=connection
.server_resource
,
2546 priority
=connection
.priority
, mood
=connection
.mood
,
2547 tune
=connection
.tune
, activity
=connection
.activity
)
2548 if gajim
.connections
[account
].gpg
:
2549 contact
.keyID
= gajim
.config
.get_per('accounts', connection
.name
,
2551 contacts
.append(contact
)
2552 # if we're online ...
2553 if connection
.connection
:
2554 roster
= connection
.connection
.getRoster()
2555 # in threadless connection when no roster stanza is sent,
2557 if roster
and roster
.getItem(jid
):
2558 resources
= roster
.getResources(jid
)
2559 # ...get the contact info for our other online resources
2560 for resource
in resources
:
2561 # Check if we already have this resource
2563 for contact_
in contacts
:
2564 if contact_
.resource
== resource
:
2569 show
= roster
.getShow(jid
+'/'+resource
)
2572 contact
= gajim
.contacts
.create_contact(jid
=jid
,
2573 name
=account
, groups
=['self_contact'], show
=show
,
2574 status
=roster
.getStatus(jid
+ '/' + resource
),
2576 priority
=roster
.getPriority(jid
+ '/' + resource
))
2577 contacts
.append(contact
)
2578 if self
.tooltip
.timeout
== 0 or self
.tooltip
.id != props
[0]:
2579 self
.tooltip
.id = row
2580 self
.tooltip
.account
= None
2581 self
.tooltip
.timeout
= gobject
.timeout_add(500,
2582 self
.show_tooltip
, contacts
)
2584 def on_agent_logging(self
, widget
, jid
, state
, account
):
2585 '''When an agent is requested to log in or off'''
2586 gajim
.connections
[account
].send_agent_status(jid
, state
)
2588 def on_edit_agent(self
, widget
, contact
, account
):
2589 '''When we want to modify the agent registration'''
2590 gajim
.connections
[account
].request_register_agent_info(contact
.jid
)
2592 def on_remove_agent(self
, widget
, list_
):
2593 '''When an agent is requested to be removed. list_ is a list of
2594 (contact, account) tuple'''
2595 for (contact
, account
) in list_
:
2596 if gajim
.config
.get_per('accounts', account
, 'hostname') == \
2598 # We remove the server contact
2599 # remove it from treeview
2600 gajim
.connections
[account
].unsubscribe(contact
.jid
)
2601 self
.remove_contact(contact
.jid
, account
, backend
=True)
2605 for (contact
, account
) in list_
:
2606 full_jid
= contact
.get_full_jid()
2607 gajim
.connections
[account
].unsubscribe_agent(full_jid
)
2608 # remove transport from treeview
2609 self
.remove_contact(contact
.jid
, account
, backend
=True)
2611 # Check if there are unread events from some contacts
2612 has_unread_events
= False
2613 for (contact
, account
) in list_
:
2614 for jid
in gajim
.events
.get_events(account
):
2615 if jid
.endswith(contact
.jid
):
2616 has_unread_events
= True
2618 if has_unread_events
:
2619 dialogs
.ErrorDialog(_('You have unread messages'),
2620 _('You must read them before removing this transport.'))
2623 pritext
= _('Transport "%s" will be removed') % list_
[0][0].jid
2624 sectext
= _('You will no longer be able to send and receive messages '
2625 'from contacts using this transport.')
2627 pritext
= _('Transports will be removed')
2629 for (contact
, account
) in list_
:
2630 jids
+= '\n ' + contact
.get_shown_name() + ','
2631 jids
= jids
[:-1] + '.'
2632 sectext
= _('You will no longer be able to send and receive messages '
2633 'to contacts from these transports: %s') % jids
2634 dialogs
.ConfirmationDialog(pritext
, sectext
,
2635 on_response_ok
= (remove
, list_
))
2637 def on_block(self
, widget
, list_
, group
=None):
2638 ''' When clicked on the 'block' button in context menu.
2639 list_ is a list of (contact, account)'''
2640 def on_continue(msg
, pep_dict
):
2642 # user pressed Cancel to change status message dialog
2646 for (contact
, account
) in list_
:
2647 if account
not in accounts
:
2648 if not gajim
.connections
[account
].privacy_rules_supported
:
2650 accounts
.append(account
)
2651 self
.send_status(account
, 'offline', msg
, to
=contact
.jid
)
2652 new_rule
= {'order': u
'1', 'type': u
'jid', 'action': u
'deny',
2653 'value' : contact
.jid
, 'child': [u
'message', u
'iq',
2655 gajim
.connections
[account
].blocked_list
.append(new_rule
)
2656 # needed for draw_contact:
2657 gajim
.connections
[account
].blocked_contacts
.append(
2659 self
.draw_contact(contact
.jid
, account
)
2661 for (contact
, account
) in list_
:
2662 if account
not in accounts
:
2663 if not gajim
.connections
[account
].privacy_rules_supported
:
2665 accounts
.append(account
)
2666 # needed for draw_group:
2667 gajim
.connections
[account
].blocked_groups
.append(group
)
2668 self
.draw_group(group
, account
)
2669 self
.send_status(account
, 'offline', msg
, to
=contact
.jid
)
2670 self
.draw_contact(contact
.jid
, account
)
2671 new_rule
= {'order': u
'1', 'type': u
'group', 'action': u
'deny',
2672 'value' : group
, 'child': [u
'message', u
'iq', u
'presence-out']}
2673 gajim
.connections
[account
].blocked_list
.append(new_rule
)
2674 for account
in accounts
:
2675 connection
= gajim
.connections
[account
]
2676 connection
.set_privacy_list('block', connection
.blocked_list
)
2677 if len(connection
.blocked_list
) == 1:
2678 connection
.set_active_list('block')
2679 connection
.set_default_list('block')
2680 connection
.get_privacy_list('block')
2682 def _block_it(is_checked
=None):
2683 if is_checked
is not None: # dialog has been shown
2684 if is_checked
: # user does not want to be asked again
2685 gajim
.config
.set('confirm_block', 'no')
2687 gajim
.config
.set('confirm_block', 'yes')
2688 self
.get_status_message('offline', on_continue
, show_pep
=False)
2690 confirm_block
= gajim
.config
.get('confirm_block')
2691 if confirm_block
== 'no':
2694 pritext
= _('You are about to block a contact. Are you sure you want'
2696 sectext
= _('This contact will see you offline and you will not receive '
2697 'messages he will send you.')
2698 dlg
= dialogs
.ConfirmationDialogCheck(pritext
, sectext
,
2699 _('Do _not ask me again'), on_response_ok
=_block_it
)
2701 def on_unblock(self
, widget
, list_
, group
=None):
2702 ''' When clicked on the 'unblock' button in context menu. '''
2705 for (contact
, account
) in list_
:
2706 if account
not in accounts
:
2707 if gajim
.connections
[account
].privacy_rules_supported
:
2708 accounts
.append(account
)
2709 gajim
.connections
[account
].new_blocked_list
= []
2710 gajim
.connections
[account
].to_unblock
= []
2711 gajim
.connections
[account
].to_unblock
.append(contact
.jid
)
2713 gajim
.connections
[account
].to_unblock
.append(contact
.jid
)
2714 # needed for draw_contact:
2715 if contact
.jid
in gajim
.connections
[account
].blocked_contacts
:
2716 gajim
.connections
[account
].blocked_contacts
.remove(contact
.jid
)
2717 self
.draw_contact(contact
.jid
, account
)
2718 for account
in accounts
:
2719 for rule
in gajim
.connections
[account
].blocked_list
:
2720 if rule
['action'] != 'deny' or rule
['type'] != 'jid' \
2721 or rule
['value'] not in gajim
.connections
[account
].to_unblock
:
2722 gajim
.connections
[account
].new_blocked_list
.append(rule
)
2724 for (contact
, account
) in list_
:
2725 if account
not in accounts
:
2726 if gajim
.connections
[account
].privacy_rules_supported
:
2727 accounts
.append(account
)
2728 # needed for draw_group:
2729 if group
in gajim
.connections
[account
].blocked_groups
:
2730 gajim
.connections
[account
].blocked_groups
.remove(group
)
2731 self
.draw_group(group
, account
)
2732 gajim
.connections
[account
].new_blocked_list
= []
2733 for rule
in gajim
.connections
[account
].blocked_list
:
2734 if rule
['action'] != 'deny' or rule
['type'] != 'group' \
2735 or rule
['value'] != group
:
2736 gajim
.connections
[account
].new_blocked_list
.append(rule
)
2737 self
.draw_contact(contact
.jid
, account
)
2738 for account
in accounts
:
2739 gajim
.connections
[account
].set_privacy_list('block',
2740 gajim
.connections
[account
].new_blocked_list
)
2741 gajim
.connections
[account
].get_privacy_list('block')
2742 if len(gajim
.connections
[account
].new_blocked_list
) == 0:
2743 gajim
.connections
[account
].blocked_list
= []
2744 gajim
.connections
[account
].blocked_contacts
= []
2745 gajim
.connections
[account
].blocked_groups
= []
2746 gajim
.connections
[account
].set_default_list('')
2747 gajim
.connections
[account
].set_active_list('')
2748 gajim
.connections
[account
].del_privacy_list('block')
2749 if 'blocked_contacts' in gajim
.interface
.instances
[account
]:
2750 gajim
.interface
.instances
[account
]['blocked_contacts'].\
2751 privacy_list_received([])
2752 for (contact
, account
) in list_
:
2753 if not self
.regroup
:
2754 show
= gajim
.SHOW_LIST
[gajim
.connections
[account
].connected
]
2755 else: # accounts merged
2756 show
= helpers
.get_global_show()
2757 if show
== 'invisible':
2758 # Don't send our presence if we're invisible
2760 if account
not in accounts
:
2761 accounts
.append(account
)
2762 if gajim
.connections
[account
].privacy_rules_supported
:
2763 self
.send_status(account
, show
,
2764 gajim
.connections
[account
].status
, to
=contact
.jid
)
2766 self
.send_status(account
, show
,
2767 gajim
.connections
[account
].status
, to
=contact
.jid
)
2769 def on_rename(self
, widget
, row_type
, jid
, account
):
2770 # this function is called either by F2 or by Rename menuitem
2771 if 'rename' in gajim
.interface
.instances
:
2772 gajim
.interface
.instances
['rename'].dialog
.present()
2775 # account is offline, don't allow to rename
2776 if gajim
.connections
[account
].connected
< 2:
2778 if row_type
in ('contact', 'agent'):
2780 title
= _('Rename Contact')
2781 message
= _('Enter a new nickname for contact %s') % jid
2782 old_text
= gajim
.contacts
.get_contact_with_highest_priority(account
,
2784 elif row_type
== 'group':
2785 if jid
in helpers
.special_groups
+ (_('General'),):
2788 title
= _('Rename Group')
2789 message
= _('Enter a new name for group %s') % \
2790 gobject
.markup_escape_text(jid
)
2792 def on_renamed(new_text
, account
, row_type
, jid
, old_text
):
2793 if 'rename' in gajim
.interface
.instances
:
2794 del gajim
.interface
.instances
['rename']
2795 if row_type
in ('contact', 'agent'):
2796 if old_text
== new_text
:
2798 for contact
in gajim
.contacts
.get_contacts(account
, jid
):
2799 contact
.name
= new_text
2800 gajim
.connections
[account
].update_contact(jid
, new_text
, \
2802 self
.draw_contact(jid
, account
)
2803 # Update opened chats
2804 for ctrl
in gajim
.interface
.msg_win_mgr
.get_controls(jid
, account
):
2806 win
= gajim
.interface
.msg_win_mgr
.get_window(jid
, account
)
2807 win
.redraw_tab(ctrl
)
2809 elif row_type
== 'group':
2810 # in C_JID column, we hold the group name (which is not escaped)
2811 self
.rename_group(old_text
, new_text
, account
)
2814 if 'rename' in gajim
.interface
.instances
:
2815 del gajim
.interface
.instances
['rename']
2817 gajim
.interface
.instances
['rename'] = dialogs
.InputDialog(title
, message
,
2818 old_text
, False, (on_renamed
, account
, row_type
, jid
, old_text
),
2821 def on_remove_group_item_activated(self
, widget
, group
, account
):
2823 for contact
in gajim
.contacts
.get_contacts_from_group(account
, group
):
2825 self
.remove_contact_from_groups(contact
.jid
,account
, [group
])
2827 gajim
.connections
[account
].unsubscribe(contact
.jid
)
2828 self
.remove_contact(contact
.jid
, account
, backend
=True)
2830 dialogs
.ConfirmationDialogCheck(_('Remove Group'),
2831 _('Do you want to remove group %s from the roster?') % group
,
2832 _('Also remove all contacts in this group from your roster'),
2833 on_response_ok
=on_ok
)
2835 def on_assign_pgp_key(self
, widget
, contact
, account
):
2836 attached_keys
= gajim
.config
.get_per('accounts', account
,
2837 'attached_gpg_keys').split()
2840 for i
in xrange(len(attached_keys
)/2):
2841 keys
[attached_keys
[2*i
]] = attached_keys
[2*i
+1]
2842 if attached_keys
[2*i
] == contact
.jid
:
2843 keyID
= attached_keys
[2*i
+1]
2844 public_keys
= gajim
.connections
[account
].ask_gpg_keys()
2845 public_keys
[_('None')] = _('None')
2847 def on_key_selected(keyID
):
2850 if keyID
[0] == _('None'):
2851 if contact
.jid
in keys
:
2852 del keys
[contact
.jid
]
2856 keys
[contact
.jid
] = keyID
2858 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(contact
.jid
, account
)
2864 keys_str
+= jid
+ ' ' + keys
[jid
] + ' '
2865 gajim
.config
.set_per('accounts', account
, 'attached_gpg_keys',
2867 for u
in gajim
.contacts
.get_contacts(account
, contact
.jid
):
2868 u
.keyID
= helpers
.prepare_and_validate_gpg_keyID(account
,
2871 dialogs
.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
2872 _('Select a key to apply to the contact'), public_keys
,
2873 on_key_selected
, selected
=keyID
)
2875 def on_set_custom_avatar_activate(self
, widget
, contact
, account
):
2876 def on_ok(widget
, path_to_file
):
2877 filesize
= os
.path
.getsize(path_to_file
) # in bytes
2878 invalid_file
= False
2880 if os
.path
.isfile(path_to_file
):
2881 stat
= os
.stat(path_to_file
)
2884 msg
= _('File is empty')
2887 msg
= _('File does not exist')
2889 dialogs
.ErrorDialog(_('Could not load image'), msg
)
2892 pixbuf
= gtk
.gdk
.pixbuf_new_from_file(path_to_file
)
2893 if filesize
> 16384: # 16 kb
2894 # get the image at 'tooltip size'
2895 # and hope that user did not specify in ACE crazy size
2896 pixbuf
= gtkgui_helpers
.get_scaled_pixbuf(pixbuf
, 'tooltip')
2897 except gobject
.GError
, msg
: # unknown format
2898 # msg should be string, not object instance
2900 dialogs
.ErrorDialog(_('Could not load image'), msg
)
2902 gajim
.interface
.save_avatar_files(contact
.jid
, pixbuf
, local
=True)
2904 self
.update_avatar_in_gui(contact
.jid
, account
)
2906 def on_clear(widget
):
2909 gajim
.interface
.remove_avatar_files(contact
.jid
, local
=True)
2910 self
.update_avatar_in_gui(contact
.jid
, account
)
2912 dlg
= dialogs
.AvatarChooserDialog(on_response_ok
=on_ok
,
2913 on_response_clear
=on_clear
)
2915 def on_edit_groups(self
, widget
, list_
):
2916 dialogs
.EditGroupsDialog(list_
)
2918 def on_history(self
, widget
, contact
, account
):
2919 '''When history menuitem is activated: call log window'''
2920 if 'logs' in gajim
.interface
.instances
:
2921 gajim
.interface
.instances
['logs'].window
.present()
2922 gajim
.interface
.instances
['logs'].open_history(contact
.jid
, account
)
2924 gajim
.interface
.instances
['logs'] = history_window
.\
2925 HistoryWindow(contact
.jid
, account
)
2927 def on_disconnect(self
, widget
, jid
, account
):
2928 '''When disconnect menuitem is activated: disconect from room'''
2929 if jid
in gajim
.interface
.minimized_controls
[account
]:
2930 ctrl
= gajim
.interface
.minimized_controls
[account
][jid
]
2932 ctrl
.got_disconnected()
2933 self
.remove_groupchat(jid
, account
)
2935 def on_reconnect(self
, widget
, jid
, account
):
2936 '''When disconnect menuitem is activated: disconect from room'''
2937 if jid
in gajim
.interface
.minimized_controls
[account
]:
2938 ctrl
= gajim
.interface
.minimized_controls
[account
][jid
]
2939 gajim
.interface
.join_gc_room(account
, jid
, ctrl
.nick
,
2940 gajim
.gc_passwords
.get(jid
, ''))
2942 def on_send_single_message_menuitem_activate(self
, widget
, account
,
2945 dialogs
.SingleMessageWindow(account
, action
='send')
2946 elif isinstance(contact
, list):
2947 dialogs
.SingleMessageWindow(account
, contact
, 'send')
2950 if contact
.jid
== gajim
.get_jid_from_account(account
):
2951 jid
+= '/' + contact
.resource
2952 dialogs
.SingleMessageWindow(account
, jid
, 'send')
2954 def on_send_file_menuitem_activate(self
, widget
, contact
, account
,
2956 gajim
.interface
.instances
['file_transfers'].show_file_send_request(
2959 def on_add_special_notification_menuitem_activate(self
, widget
, jid
):
2960 dialogs
.AddSpecialNotificationDialog(jid
)
2962 def on_invite_to_new_room(self
, widget
, list_
, resource
=None):
2963 ''' resource parameter MUST NOT be used if more than one contact in
2967 for (contact
, account
) in list_
:
2968 if contact
.jid
not in jid_list
:
2969 if resource
: # we MUST have one contact only in list_
2970 fjid
= contact
.jid
+ '/' + resource
2971 jid_list
.append(fjid
)
2973 jid_list
.append(contact
.jid
)
2974 if account
not in account_list
:
2975 account_list
.append(account
)
2976 # transform None in 'jabber'
2977 type_
= gajim
.get_transport_name_from_jid(jid_list
[0]) or 'jabber'
2978 for account
in account_list
:
2979 if gajim
.connections
[account
].muc_jid
[type_
]:
2980 # create the room on this muc server
2981 if 'join_gc' in gajim
.interface
.instances
[account
]:
2982 gajim
.interface
.instances
[account
]['join_gc'].window
.destroy()
2984 gajim
.interface
.instances
[account
]['join_gc'] = \
2985 dialogs
.JoinGroupchatWindow(account
,
2986 gajim
.connections
[account
].muc_jid
[type_
],
2987 automatic
= {'invities': jid_list
})
2988 except GajimGeneralException
:
2992 def on_invite_to_room(self
, widget
, list_
, room_jid
, room_account
,
2994 ''' resource parameter MUST NOT be used if more than one contact in
2998 contact_jid
= contact
.jid
2999 if resource
: # we MUST have one contact only in list_
3000 contact_jid
+= '/' + resource
3001 gajim
.connections
[room_account
].send_invite(room_jid
, contact_jid
)
3003 def on_all_groupchat_maximized(self
, widget
, group_list
):
3004 for (contact
, account
) in group_list
:
3005 self
.on_groupchat_maximized(widget
, contact
.jid
, account
)
3007 def on_groupchat_maximized(self
, widget
, jid
, account
):
3008 '''When a groupchat is maximised'''
3009 if not jid
in gajim
.interface
.minimized_controls
[account
]:
3011 gc_control
= gajim
.interface
.msg_win_mgr
.get_gc_control(jid
, account
)
3013 mw
= gajim
.interface
.msg_win_mgr
.get_window(jid
, account
)
3014 mw
.set_active_tab(gc_control
)
3015 mw
.window
.window
.focus(gtk
.get_current_event_time())
3017 ctrl
= gajim
.interface
.minimized_controls
[account
][jid
]
3018 mw
= gajim
.interface
.msg_win_mgr
.get_window(jid
, account
)
3020 mw
= gajim
.interface
.msg_win_mgr
.create_window(ctrl
.contact
,
3021 ctrl
.account
, ctrl
.type_id
)
3022 ctrl
.parent_win
= mw
3024 mw
.set_active_tab(ctrl
)
3025 mw
.window
.window
.focus(gtk
.get_current_event_time())
3026 self
.remove_groupchat(jid
, account
)
3028 def on_edit_account(self
, widget
, account
):
3029 if 'accounts' in gajim
.interface
.instances
:
3030 gajim
.interface
.instances
['accounts'].window
.present()
3032 gajim
.interface
.instances
['accounts'] = config
.AccountsWindow()
3033 gajim
.interface
.instances
['accounts'].select_account(account
)
3035 def on_zeroconf_properties(self
, widget
, account
):
3036 if 'accounts' in gajim
.interface
.instances
:
3037 gajim
.interface
.instances
['accounts'].window
.present()
3039 gajim
.interface
.instances
['accounts'] = config
.AccountsWindow()
3040 gajim
.interface
.instances
['accounts'].select_account(account
)
3042 def on_open_gmail_inbox(self
, widget
, account
):
3043 url
= gajim
.connections
[account
].gmail_url
3045 helpers
.launch_browser_mailer('url', url
)
3047 def on_change_status_message_activate(self
, widget
, account
):
3048 show
= gajim
.SHOW_LIST
[gajim
.connections
[account
].connected
]
3049 def on_response(message
, pep_dict
):
3050 if message
is None: # None is if user pressed Cancel
3052 self
.send_status(account
, show
, message
)
3053 self
.send_pep(account
, pep_dict
)
3054 dialogs
.ChangeStatusMessageDialog(on_response
, show
)
3056 def on_add_to_roster(self
, widget
, contact
, account
):
3057 dialogs
.AddNewContactWindow(account
, contact
.jid
, contact
.name
)
3060 def on_roster_treeview_scroll_event(self
, widget
, event
):
3061 self
.tooltip
.hide_tooltip()
3063 def on_roster_treeview_key_press_event(self
, widget
, event
):
3064 '''when a key is pressed in the treeviews'''
3065 self
.tooltip
.hide_tooltip()
3066 if event
.keyval
== gtk
.keysyms
.Escape
:
3067 self
.tree
.get_selection().unselect_all()
3068 elif event
.keyval
== gtk
.keysyms
.F2
:
3069 treeselection
= self
.tree
.get_selection()
3070 model
, list_of_paths
= treeselection
.get_selected_rows()
3071 if len(list_of_paths
) != 1:
3073 path
= list_of_paths
[0]
3074 type_
= model
[path
][C_TYPE
]
3075 if type_
in ('contact', 'group', 'agent'):
3076 jid
= model
[path
][C_JID
].decode('utf-8')
3077 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3078 self
.on_rename(widget
, type_
, jid
, account
)
3080 elif event
.keyval
== gtk
.keysyms
.Delete
:
3081 treeselection
= self
.tree
.get_selection()
3082 model
, list_of_paths
= treeselection
.get_selected_rows()
3083 if not len(list_of_paths
):
3085 type_
= model
[list_of_paths
[0]][C_TYPE
]
3086 account
= model
[list_of_paths
[0]][C_ACCOUNT
].decode('utf-8')
3087 if type_
in ('account', 'group', 'self_contact') or \
3088 account
== gajim
.ZEROCONF_ACC_NAME
:
3091 for path
in list_of_paths
:
3092 if model
[path
][C_TYPE
] != type_
:
3094 jid
= model
[path
][C_JID
].decode('utf-8')
3095 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3096 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
3098 list_
.append((contact
, account
))
3099 if type_
== 'contact':
3100 self
.on_req_usub(widget
, list_
)
3101 elif type_
== 'agent':
3102 self
.on_remove_agent(widget
, list_
)
3104 def on_roster_treeview_button_release_event(self
, widget
, event
):
3106 path
= self
.tree
.get_path_at_pos(int(event
.x
), int(event
.y
))[0]
3110 if event
.button
== 1: # Left click
3111 if gajim
.single_click
and not event
.state
& gtk
.gdk
.SHIFT_MASK
and \
3112 not event
.state
& gtk
.gdk
.CONTROL_MASK
:
3113 # Check if button has been pressed on the same row
3114 if self
.clicked_path
== path
:
3115 self
.on_row_activated(widget
, path
)
3116 self
.clicked_path
= None
3118 def on_roster_treeview_button_press_event(self
, widget
, event
):
3119 # hide tooltip, no matter the button is pressed
3120 self
.tooltip
.hide_tooltip()
3122 pos
= self
.tree
.get_path_at_pos(int(event
.x
), int(event
.y
))
3123 path
, x
= pos
[0], pos
[2]
3125 self
.tree
.get_selection().unselect_all()
3128 if event
.button
== 3: # Right click
3130 model
, list_of_paths
= self
.tree
.get_selection().get_selected_rows()
3133 if path
not in list_of_paths
:
3134 self
.tree
.get_selection().unselect_all()
3135 self
.tree
.get_selection().select_path(path
)
3136 return self
.show_treeview_menu(event
)
3138 elif event
.button
== 2: # Middle click
3140 model
, list_of_paths
= self
.tree
.get_selection().get_selected_rows()
3143 if list_of_paths
!= [path
]:
3144 self
.tree
.get_selection().unselect_all()
3145 self
.tree
.get_selection().select_path(path
)
3146 type_
= model
[path
][C_TYPE
]
3147 if type_
in ('agent', 'contact', 'self_contact', 'groupchat'):
3148 self
.on_row_activated(widget
, path
)
3149 elif type_
== 'account':
3150 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3151 if account
!= 'all':
3152 show
= gajim
.connections
[account
].connected
3153 if show
> 1: # We are connected
3154 self
.on_change_status_message_activate(widget
, account
)
3156 show
= helpers
.get_global_show()
3157 if show
== 'offline':
3159 def on_response(message
, pep_dict
):
3162 for acct
in gajim
.connections
:
3163 if not gajim
.config
.get_per('accounts', acct
,
3164 'sync_with_global_status'):
3166 current_show
= gajim
.SHOW_LIST
[gajim
.connections
[acct
].\
3168 self
.send_status(acct
, current_show
, message
)
3169 self
.send_pep(acct
, pep_dict
)
3170 dialogs
.ChangeStatusMessageDialog(on_response
, show
)
3173 elif event
.button
== 1: # Left click
3174 model
= self
.modelfilter
3175 type_
= model
[path
][C_TYPE
]
3176 # x_min is the x start position of status icon column
3177 if gajim
.config
.get('avatar_position_in_roster') == 'left':
3178 x_min
= gajim
.config
.get('roster_avatar_width')
3181 if gajim
.single_click
and not event
.state
& gtk
.gdk
.SHIFT_MASK
and \
3182 not event
.state
& gtk
.gdk
.CONTROL_MASK
:
3183 # Don't handle double click if we press icon of a metacontact
3184 titer
= model
.get_iter(path
)
3185 if x
> x_min
and x
< x_min
+ 27 and type_
== 'contact' and \
3186 model
.iter_has_child(titer
):
3187 if (self
.tree
.row_expanded(path
)):
3188 self
.tree
.collapse_row(path
)
3190 self
.tree
.expand_row(path
, False)
3192 # We just save on which row we press button, and open chat window on
3193 # button release to be able to do DND without opening chat window
3194 self
.clicked_path
= path
3197 if type_
== 'group' and x
< 27:
3198 # first cell in 1st column (the arrow SINGLE clicked)
3199 if (self
.tree
.row_expanded(path
)):
3200 self
.tree
.collapse_row(path
)
3202 self
.tree
.expand_row(path
, False)
3204 elif type_
== 'contact' and x
> x_min
and x
< x_min
+ 27:
3205 if (self
.tree
.row_expanded(path
)):
3206 self
.tree
.collapse_row(path
)
3208 self
.tree
.expand_row(path
, False)
3210 def on_req_usub(self
, widget
, list_
):
3211 '''Remove a contact. list_ is a list of (contact, account) tuples'''
3212 def on_ok(is_checked
, list_
):
3215 contact
= list_
[0][0]
3216 if contact
.sub
!= 'to' and is_checked
:
3218 for (contact
, account
) in list_
:
3219 if _('Not in Roster') not in contact
.get_shown_groups():
3220 gajim
.connections
[account
].unsubscribe(contact
.jid
, remove_auth
)
3221 self
.remove_contact(contact
.jid
, account
, backend
=True)
3222 if not remove_auth
and contact
.sub
== 'both':
3225 contact
.sub
= 'from'
3226 # we can't see him, but have to set it manually in contact
3227 contact
.show
= 'offline'
3228 gajim
.contacts
.add_contact(account
, contact
)
3229 self
.add_contact(contact
.jid
, account
)
3234 contact
= list_
[0][0]
3235 pritext
= _('Contact "%s" will be removed from your roster') % \
3236 contact
.get_shown_name()
3237 sectext
= _('You are about to remove "%(name)s" (%(jid)s) from your '
3238 'roster.\n') % {'name': contact
.get_shown_name(),
3240 if contact
.sub
== 'to':
3241 dialogs
.ConfirmationDialog(pritext
, sectext
+ \
3242 _('By removing this contact you also remove authorization '
3243 'resulting in him or her always seeing you as offline.'),
3244 on_response_ok
= (on_ok2
, list_
))
3245 elif _('Not in Roster') in contact
.get_shown_groups():
3246 # Contact is not in roster
3247 dialogs
.ConfirmationDialog(pritext
, sectext
+ \
3248 _('Do you want to continue?'), on_response_ok
= (on_ok2
, list_
))
3250 dialogs
.ConfirmationDialogCheck(pritext
, sectext
+ \
3251 _('By removing this contact you also by default remove '
3252 'authorization resulting in him or her always seeing you as '
3254 _('I want this contact to know my status after removal'),
3255 on_response_ok
= (on_ok
, list_
))
3257 # several contact to remove at the same time
3258 pritext
= _('Contacts will be removed from your roster')
3260 for (contact
, account
) in list_
:
3261 jids
+= '\n ' + contact
.get_shown_name() + ' (%s)' % contact
.jid
+\
3263 sectext
= _('By removing these contacts:%s\nyou also remove '
3264 'authorization resulting in them always seeing you as offline.') % \
3266 dialogs
.ConfirmationDialog(pritext
, sectext
,
3267 on_response_ok
= (on_ok2
, list_
))
3269 def on_send_custom_status(self
, widget
, contact_list
, show
, group
=None):
3270 '''send custom status'''
3271 # contact_list has only one element except if group != None
3272 def on_response(message
, pep_dict
):
3273 if message
is None: # None if user pressed Cancel
3276 for (contact
, account
) in contact_list
:
3277 if account
not in account_list
:
3278 account_list
.append(account
)
3279 # 1. update status_sent_to_[groups|users] list
3281 for account
in account_list
:
3282 if account
not in gajim
.interface
.status_sent_to_groups
:
3283 gajim
.interface
.status_sent_to_groups
[account
] = {}
3284 gajim
.interface
.status_sent_to_groups
[account
][group
] = show
3286 for (contact
, account
) in contact_list
:
3287 if account
not in gajim
.interface
.status_sent_to_users
:
3288 gajim
.interface
.status_sent_to_users
[account
] = {}
3289 gajim
.interface
.status_sent_to_users
[account
][contact
.jid
] = show
3291 # 2. update privacy lists if main status is invisible
3292 for account
in account_list
:
3293 if gajim
.SHOW_LIST
[gajim
.connections
[account
].connected
] == \
3295 gajim
.connections
[account
].set_invisible_rule()
3297 # 3. send directed presence
3298 for (contact
, account
) in contact_list
:
3299 our_jid
= gajim
.get_jid_from_account(account
)
3302 jid
+= '/' + contact
.resource
3303 self
.send_status(account
, show
, message
, to
=jid
)
3305 def send_it(is_checked
=None):
3306 if is_checked
is not None: # dialog has been shown
3307 if is_checked
: # user does not want to be asked again
3308 gajim
.config
.set('confirm_custom_status', 'no')
3310 gajim
.config
.set('confirm_custom_status', 'yes')
3311 self
.get_status_message(show
, on_response
, show_pep
=False,
3314 confirm_custom_status
= gajim
.config
.get('confirm_custom_status')
3315 if confirm_custom_status
== 'no':
3318 pritext
= _('You are about to send a custom status. Are you sure you want'
3320 sectext
= _('This contact will temporarily see you as %(status)s, '
3321 'but only until you change your status. Then he will see your global '
3322 'status.') % {'status': show
}
3323 dlg
= dialogs
.ConfirmationDialogCheck(pritext
, sectext
,
3324 _('Do _not ask me again'), on_response_ok
=send_it
)
3326 def on_status_combobox_changed(self
, widget
):
3327 '''When we change our status via the combobox'''
3328 model
= self
.status_combobox
.get_model()
3329 active
= self
.status_combobox
.get_active()
3330 if active
== -1: # no active item
3332 if not self
.combobox_callback_active
:
3333 self
.previous_status_combobox_active
= active
3335 accounts
= gajim
.connections
.keys()
3336 if len(accounts
) == 0:
3337 dialogs
.ErrorDialog(_('No account available'),
3338 _('You must create an account before you can chat with other contacts.'))
3339 self
.update_status_combobox()
3341 status
= model
[active
][2].decode('utf-8')
3342 statuses_unified
= helpers
.statuses_unified() # status "desync'ed" or not
3343 if (active
== 7 and statuses_unified
) or (active
== 9 and \
3344 not statuses_unified
):
3345 # 'Change status message' selected:
3346 # do not change show, just show change status dialog
3347 status
= model
[self
.previous_status_combobox_active
][2].decode('utf-8')
3348 def on_response(message
, pep_dict
):
3349 if message
is not None: # None if user pressed Cancel
3350 for account
in accounts
:
3351 if not gajim
.config
.get_per('accounts', account
,
3352 'sync_with_global_status'):
3354 current_show
= gajim
.SHOW_LIST
[
3355 gajim
.connections
[account
].connected
]
3356 self
.send_status(account
, current_show
, message
)
3357 self
.send_pep(account
, pep_dict
)
3358 self
.combobox_callback_active
= False
3359 self
.status_combobox
.set_active(
3360 self
.previous_status_combobox_active
)
3361 self
.combobox_callback_active
= True
3362 dialogs
.ChangeStatusMessageDialog(on_response
, status
)
3364 # we are about to change show, so save this new show so in case
3365 # after user chooses "Change status message" menuitem
3366 # we can return to this show
3367 self
.previous_status_combobox_active
= active
3368 connected_accounts
= gajim
.get_number_of_connected_accounts()
3370 def on_continue(message
, pep_dict
):
3372 # user pressed Cancel to change status message dialog
3373 self
.update_status_combobox()
3375 global_sync_accounts
= []
3376 for acct
in accounts
:
3377 if gajim
.config
.get_per('accounts', acct
,
3378 'sync_with_global_status'):
3379 global_sync_accounts
.append(acct
)
3380 global_sync_connected_accounts
= \
3381 gajim
.get_number_of_connected_accounts(global_sync_accounts
)
3382 for account
in accounts
:
3383 if not gajim
.config
.get_per('accounts', account
,
3384 'sync_with_global_status'):
3386 # we are connected (so we wanna change show and status)
3387 # or no account is connected and we want to connect with new show
3390 if not global_sync_connected_accounts
> 0 or \
3391 gajim
.connections
[account
].connected
> 0:
3392 self
.send_status(account
, status
, message
)
3393 self
.send_pep(account
, pep_dict
)
3394 self
.update_status_combobox()
3396 if status
== 'invisible':
3398 for account
in accounts
:
3399 if connected_accounts
< 1 or gajim
.account_is_connected(account
):
3400 if not gajim
.config
.get_per('accounts', account
,
3401 'sync_with_global_status'):
3403 # We're going to change our status to invisible
3404 if self
.connected_rooms(account
):
3409 self
.get_status_message(status
, on_continue
, show_pep
=False)
3412 self
.update_status_combobox()
3414 dialogs
.ConfirmationDialog(
3415 _('You are participating in one or more group chats'),
3416 _('Changing your status to invisible will result in '
3417 'disconnection from those group chats. Are you sure you want to '
3418 'go invisible?'), on_reponse_ok
=on_ok
,
3419 on_response_cancel
=on_cancel
)
3422 self
.get_status_message(status
, on_continue
)
3424 def on_preferences_menuitem_activate(self
, widget
):
3425 if 'preferences' in gajim
.interface
.instances
:
3426 gajim
.interface
.instances
['preferences'].window
.present()
3428 gajim
.interface
.instances
['preferences'] = config
.PreferencesWindow()
3430 def on_publish_tune_toggled(self
, widget
, account
):
3431 act
= widget
.get_active()
3432 gajim
.config
.set_per('accounts', account
, 'publish_tune', act
)
3434 gajim
.interface
.enable_music_listener()
3436 # disable it only if no other account use it
3437 for acct
in gajim
.connections
:
3438 if gajim
.config
.get_per('accounts', acct
, 'publish_tune'):
3441 gajim
.interface
.disable_music_listener()
3443 if gajim
.connections
[account
].pep_supported
:
3444 # As many implementations don't support retracting items, we send a
3445 # "Stopped" event first
3446 pep
.user_send_tune(account
, '')
3447 pep
.user_retract_tune(account
)
3448 helpers
.update_optional_features(account
)
3450 def on_pep_services_menuitem_activate(self
, widget
, account
):
3451 if 'pep_services' in gajim
.interface
.instances
[account
]:
3452 gajim
.interface
.instances
[account
]['pep_services'].window
.present()
3454 gajim
.interface
.instances
[account
]['pep_services'] = \
3455 config
.ManagePEPServicesWindow(account
)
3457 def on_add_new_contact(self
, widget
, account
):
3458 dialogs
.AddNewContactWindow(account
)
3460 def on_join_gc_activate(self
, widget
, account
):
3461 '''when the join gc menuitem is clicked, show the join gc window'''
3462 invisible_show
= gajim
.SHOW_LIST
.index('invisible')
3463 if gajim
.connections
[account
].connected
== invisible_show
:
3464 dialogs
.ErrorDialog(_('You cannot join a group chat while you are '
3467 if 'join_gc' in gajim
.interface
.instances
[account
]:
3468 gajim
.interface
.instances
[account
]['join_gc'].window
.present()
3470 # c http://nkour.blogspot.com/2005/05/pythons-init-return-none-doesnt-return.html
3472 gajim
.interface
.instances
[account
]['join_gc'] = \
3473 dialogs
.JoinGroupchatWindow(account
)
3474 except GajimGeneralException
:
3477 def on_new_chat_menuitem_activate(self
, widget
, account
):
3478 dialogs
.NewChatDialog(account
)
3480 def on_contents_menuitem_activate(self
, widget
):
3481 helpers
.launch_browser_mailer('url', 'http://trac.gajim.org/wiki')
3483 def on_faq_menuitem_activate(self
, widget
):
3484 helpers
.launch_browser_mailer('url',
3485 'http://trac.gajim.org/wiki/GajimFaq')
3487 def on_features_menuitem_activate(self
, widget
):
3488 features_window
.FeaturesWindow()
3490 def on_about_menuitem_activate(self
, widget
):
3491 dialogs
.AboutDialog()
3493 def on_accounts_menuitem_activate(self
, widget
):
3494 if 'accounts' in gajim
.interface
.instances
:
3495 gajim
.interface
.instances
['accounts'].window
.present()
3497 gajim
.interface
.instances
['accounts'] = config
.AccountsWindow()
3499 def on_file_transfers_menuitem_activate(self
, widget
):
3500 if gajim
.interface
.instances
['file_transfers'].window
.get_property(
3502 gajim
.interface
.instances
['file_transfers'].window
.present()
3504 gajim
.interface
.instances
['file_transfers'].window
.show_all()
3506 def on_history_menuitem_activate(self
, widget
):
3507 if 'logs' in gajim
.interface
.instances
:
3508 gajim
.interface
.instances
['logs'].window
.present()
3510 gajim
.interface
.instances
['logs'] = history_window
.\
3513 def on_show_transports_menuitem_activate(self
, widget
):
3514 gajim
.config
.set('show_transports_group', widget
.get_active())
3515 self
.refilter_shown_roster_items()
3517 def on_manage_bookmarks_menuitem_activate(self
, widget
):
3518 config
.ManageBookmarksWindow()
3520 def on_profile_avatar_menuitem_activate(self
, widget
, account
):
3521 gajim
.interface
.edit_own_details(account
)
3523 def on_execute_command(self
, widget
, contact
, account
, resource
=None):
3524 '''Execute command. Full JID needed; if it is other contact,
3525 resource is necessary. Widget is unnecessary, only to be
3526 able to make this a callback.'''
3528 if resource
is not None:
3529 jid
= jid
+ u
'/' + resource
3530 adhoc_commands
.CommandWindow(account
, jid
)
3532 def on_roster_window_focus_in_event(self
, widget
, event
):
3533 # roster received focus, so if we had urgency REMOVE IT
3534 # NOTE: we do not have to read the message to remove urgency
3535 # so this functions does that
3536 gtkgui_helpers
.set_unset_urgency_hint(widget
, False)
3538 # if a contact row is selected, update colors (eg. for status msg)
3539 # because gtk engines may differ in bg when window is selected
3541 if len(self
._last
_selected
_contact
):
3542 for (jid
, account
) in self
._last
_selected
_contact
:
3543 self
.draw_contact(jid
, account
, selected
=True, focus
=True)
3545 def on_roster_window_focus_out_event(self
, widget
, event
):
3546 # if a contact row is selected, update colors (eg. for status msg)
3547 # because gtk engines may differ in bg when window is selected
3549 if len(self
._last
_selected
_contact
):
3550 for (jid
, account
) in self
._last
_selected
_contact
:
3551 self
.draw_contact(jid
, account
, selected
=True, focus
=False)
3553 def on_roster_window_key_press_event(self
, widget
, event
):
3554 if event
.keyval
== gtk
.keysyms
.Escape
:
3555 if gajim
.interface
.msg_win_mgr
.mode
== \
3556 MessageWindowMgr
.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER
and \
3557 gajim
.interface
.msg_win_mgr
.one_window_opened():
3558 # let message window close the tab
3560 list_of_paths
= self
.tree
.get_selection().get_selected_rows()[1]
3561 if not len(list_of_paths
) and gajim
.interface
.systray_enabled
and \
3562 not gajim
.config
.get('quit_on_roster_x_button'):
3563 self
.tooltip
.hide_tooltip()
3565 elif event
.state
& gtk
.gdk
.CONTROL_MASK
and event
.keyval
== gtk
.keysyms
.i
:
3566 treeselection
= self
.tree
.get_selection()
3567 model
, list_of_paths
= treeselection
.get_selected_rows()
3568 for path
in list_of_paths
:
3569 type_
= model
[path
][C_TYPE
]
3570 if type_
in ('contact', 'agent'):
3571 jid
= model
[path
][C_JID
].decode('utf-8')
3572 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3573 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
3574 self
.on_info(widget
, contact
, account
)
3575 elif event
.state
& gtk
.gdk
.CONTROL_MASK
and event
.keyval
== gtk
.keysyms
.h
:
3576 treeselection
= self
.tree
.get_selection()
3577 model
, list_of_paths
= treeselection
.get_selected_rows()
3578 if len(list_of_paths
) != 1:
3580 path
= list_of_paths
[0]
3581 type_
= model
[path
][C_TYPE
]
3582 if type_
in ('contact', 'agent'):
3583 jid
= model
[path
][C_JID
].decode('utf-8')
3584 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3585 contact
= gajim
.contacts
.get_first_contact_from_jid(account
, jid
)
3586 self
.on_history(widget
, contact
, account
)
3588 def on_roster_window_popup_menu(self
, widget
):
3589 event
= gtk
.gdk
.Event(gtk
.gdk
.KEY_PRESS
)
3590 self
.show_treeview_menu(event
)
3592 def on_row_activated(self
, widget
, path
):
3593 '''When an iter is activated (double-click or single click if gnome is
3595 model
= self
.modelfilter
3596 account
= model
[path
][C_ACCOUNT
].decode('utf-8')
3597 type_
= model
[path
][C_TYPE
]
3598 if type_
in ('group', 'account'):
3599 if self
.tree
.row_expanded(path
):
3600 self
.tree
.collapse_row(path
)
3602 self
.tree
.expand_row(path
, False)
3604 jid
= model
[path
][C_JID
].decode('utf-8')
3606 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
3607 titer
= model
.get_iter(path
)
3608 if contact
.is_groupchat():
3609 first_ev
= gajim
.events
.get_first_event(account
, jid
)
3610 if first_ev
and self
.open_event(account
, jid
, first_ev
):
3611 # We are invited to a GC
3612 # open event cares about connecting to it
3613 self
.remove_groupchat(jid
, account
)
3615 self
.on_groupchat_maximized(None, jid
, account
)
3619 first_ev
= gajim
.events
.get_first_event(account
, jid
)
3621 # look in other resources
3622 for c
in gajim
.contacts
.get_contacts(account
, jid
):
3623 fjid
= c
.get_full_jid()
3624 first_ev
= gajim
.events
.get_first_event(account
, fjid
)
3626 resource
= c
.resource
3628 if not first_ev
and model
.iter_has_child(titer
):
3629 child_iter
= model
.iter_children(titer
)
3630 while not first_ev
and child_iter
:
3631 child_jid
= model
[child_iter
][C_JID
].decode('utf-8')
3632 first_ev
= gajim
.events
.get_first_event(account
, child_jid
)
3636 child_iter
= model
.iter_next(child_iter
)
3639 if first_ev
.type_
in ('chat', 'normal'):
3640 session
= first_ev
.parameters
[8]
3643 fjid
+= '/' + resource
3644 if self
.open_event(account
, fjid
, first_ev
):
3647 contact
= gajim
.contacts
.get_contact(account
, jid
, resource
)
3648 if not contact
or isinstance(contact
, list):
3649 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
3651 if jid
== gajim
.get_jid_from_account(account
):
3652 resource
= contact
.resource
3654 gajim
.interface
.on_open_chat_window(None, contact
, account
, \
3655 resource
=resource
, session
=session
)
3657 def on_roster_treeview_row_activated(self
, widget
, path
, col
=0):
3658 '''When an iter is double clicked: open the first event window'''
3659 if not gajim
.single_click
:
3660 self
.on_row_activated(widget
, path
)
3662 def on_roster_treeview_row_expanded(self
, widget
, titer
, path
):
3663 '''When a row is expanded change the icon of the arrow'''
3664 self
._toggeling
_row
= True
3665 model
= widget
.get_model()
3666 child_model
= model
.get_model()
3667 child_iter
= model
.convert_iter_to_child_iter(titer
)
3669 if self
.regroup
: # merged accounts
3670 accounts
= gajim
.connections
.keys()
3672 accounts
= [model
[titer
][C_ACCOUNT
].decode('utf-8')]
3674 type_
= model
[titer
][C_TYPE
]
3675 if type_
== 'group':
3676 group
= model
[titer
][C_JID
].decode('utf-8')
3677 child_model
[child_iter
][C_IMG
] = gajim
.interface
.jabber_state_images
[
3679 for account
in accounts
:
3680 if group
in gajim
.groups
[account
]: # This account has this group
3681 gajim
.groups
[account
][group
]['expand'] = True
3682 if account
+ group
in self
.collapsed_rows
:
3683 self
.collapsed_rows
.remove(account
+ group
)
3684 for contact
in gajim
.contacts
.iter_contacts(account
):
3686 if group
in contact
.groups
and gajim
.contacts
.is_big_brother(
3687 account
, jid
, accounts
) and account
+ group
+ jid \
3688 not in self
.collapsed_rows
:
3689 titers
= self
._get
_contact
_iter
(jid
, account
)
3690 for titer
in titers
:
3691 path
= model
.get_path(titer
)
3692 self
.tree
.expand_row(path
, False)
3693 elif type_
== 'account':
3694 account
= accounts
[0] # There is only one cause we don't use merge
3695 if account
in self
.collapsed_rows
:
3696 self
.collapsed_rows
.remove(account
)
3697 self
.draw_account(account
)
3698 # When we expand, groups are collapsed. Restore expand state
3699 for group
in gajim
.groups
[account
]:
3700 if gajim
.groups
[account
][group
]['expand']:
3701 titer
= self
._get
_group
_iter
(group
, account
)
3703 path
= model
.get_path(titer
)
3704 self
.tree
.expand_row(path
, False)
3705 elif type_
== 'contact':
3706 # Metacontact got toggled, update icon
3707 jid
= model
[titer
][C_JID
].decode('utf-8')
3708 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
3709 contact
= gajim
.contacts
.get_contact(account
, jid
)
3710 for group
in contact
.groups
:
3711 if account
+ group
+ jid
in self
.collapsed_rows
:
3712 self
.collapsed_rows
.remove(account
+ group
+ jid
)
3713 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
3715 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)[0]
3716 # Redraw all brothers to show pending events
3717 for data
in nearby_family
:
3718 self
.draw_contact(data
['jid'], data
['account'])
3720 self
._toggeling
_row
= False
3722 def on_roster_treeview_row_collapsed(self
, widget
, titer
, path
):
3723 '''When a row is collapsed change the icon of the arrow'''
3724 self
._toggeling
_row
= True
3725 model
= widget
.get_model()
3726 child_model
= model
.get_model()
3727 child_iter
= model
.convert_iter_to_child_iter(titer
)
3729 if self
.regroup
: # merged accounts
3730 accounts
= gajim
.connections
.keys()
3732 accounts
= [model
[titer
][C_ACCOUNT
].decode('utf-8')]
3734 type_
= model
[titer
][C_TYPE
]
3735 if type_
== 'group':
3736 child_model
[child_iter
][C_IMG
] = gajim
.interface
.jabber_state_images
[
3738 group
= model
[titer
][C_JID
].decode('utf-8')
3739 for account
in accounts
:
3740 if group
in gajim
.groups
[account
]: # This account has this group
3741 gajim
.groups
[account
][group
]['expand'] = False
3742 if account
+ group
not in self
.collapsed_rows
:
3743 self
.collapsed_rows
.append(account
+ group
)
3744 elif type_
== 'account':
3745 account
= accounts
[0] # There is only one cause we don't use merge
3746 if account
not in self
.collapsed_rows
:
3747 self
.collapsed_rows
.append(account
)
3748 self
.draw_account(account
)
3749 elif type_
== 'contact':
3750 # Metacontact got toggled, update icon
3751 jid
= model
[titer
][C_JID
].decode('utf-8')
3752 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
3753 contact
= gajim
.contacts
.get_contact(account
, jid
)
3754 for group
in contact
.groups
:
3755 if account
+ group
+ jid
not in self
.collapsed_rows
:
3756 self
.collapsed_rows
.append(account
+ group
+ jid
)
3757 family
= gajim
.contacts
.get_metacontacts_family(account
, jid
)
3759 self
._get
_nearby
_family
_and
_big
_brother
(family
, account
)[0]
3760 # Redraw all brothers to show pending events
3761 for data
in nearby_family
:
3762 self
.draw_contact(data
['jid'], data
['account'])
3764 self
._toggeling
_row
= False
3766 def on_modelfilter_row_has_child_toggled(self
, model
, path
, titer
):
3767 '''Called when a row has gotten the first or lost its last child row.
3769 Expand Parent if necessary.
3771 if self
._toggeling
_row
:
3772 # Signal is emitted when we write to our model
3775 type_
= model
[titer
][C_TYPE
]
3776 account
= model
[titer
][C_ACCOUNT
]
3780 account
= account
.decode('utf-8')
3782 if type_
== 'contact':
3783 child_iter
= model
.convert_iter_to_child_iter(titer
)
3784 if self
.model
.iter_has_child(child_iter
):
3785 # we are a bigbrother metacontact
3786 # redraw us to show/hide expand icon
3788 # Prevent endless loops
3789 jid
= model
[titer
][C_JID
].decode('utf-8')
3790 gobject
.idle_add(self
.draw_contact
, jid
, account
)
3791 elif type_
== 'group':
3792 group
= model
[titer
][C_JID
].decode('utf-8')
3793 self
._adjust
_group
_expand
_collapse
_state
(group
, account
)
3794 elif type_
== 'account':
3795 self
._adjust
_account
_expand
_collapse
_state
(account
)
3797 # Selection can change when the model is filtered
3798 # Only write to the model when filtering is finished!
3800 # FIXME: When we are filtering our custom colors are somehow lost
3802 # def on_treeview_selection_changed(self, selection):
3803 # '''Called when selection in TreeView has changed.
3805 # Redraw unselected rows to make status message readable
3806 # on all possible backgrounds.
3808 # model, list_of_paths = selection.get_selected_rows()
3809 # if len(self._last_selected_contact):
3810 # # update unselected rows
3811 # for (jid, account) in self._last_selected_contact:
3812 # gobject.idle_add(self.draw_contact, jid, account)
3813 # self._last_selected_contact = []
3814 # if len(list_of_paths) == 0:
3816 # for path in list_of_paths:
3818 # if row[C_TYPE] != 'contact':
3819 # self._last_selected_contact = []
3821 # jid = row[C_JID].decode('utf-8')
3822 # account = row[C_ACCOUNT].decode('utf-8')
3823 # self._last_selected_contact.append((jid, account))
3824 # gobject.idle_add(self.draw_contact, jid, account, True)
3826 def on_service_disco_menuitem_activate(self
, widget
, account
):
3827 server_jid
= gajim
.config
.get_per('accounts', account
, 'hostname')
3828 if server_jid
in gajim
.interface
.instances
[account
]['disco']:
3829 gajim
.interface
.instances
[account
]['disco'][server_jid
].\
3833 # Object will add itself to the window dict
3834 disco
.ServiceDiscoveryWindow(account
, address_entry
=True)
3835 except GajimGeneralException
:
3838 def on_show_offline_contacts_menuitem_activate(self
, widget
):
3839 '''when show offline option is changed:
3840 redraw the treeview'''
3841 gajim
.config
.set('showoffline', not gajim
.config
.get('showoffline'))
3842 self
.refilter_shown_roster_items()
3843 w
= self
.xml
.get_widget('show_only_active_contacts_menuitem')
3844 if gajim
.config
.get('showoffline'):
3845 # We need to filter twice to show groups with no contacts inside
3846 # in the correct expand state
3847 self
.refilter_shown_roster_items()
3848 w
.set_sensitive(False)
3850 w
.set_sensitive(True)
3852 def on_show_only_active_contacts_menuitem_activate(self
, widget
):
3853 '''when show only active contact option is changed:
3854 redraw the treeview'''
3855 gajim
.config
.set('show_only_chat_and_online', not gajim
.config
.get(
3856 'show_only_chat_and_online'))
3857 self
.refilter_shown_roster_items()
3858 w
= self
.xml
.get_widget('show_offline_contacts_menuitem')
3859 if gajim
.config
.get('show_only_chat_and_online'):
3860 # We need to filter twice to show groups with no contacts inside
3861 # in the correct expand state
3862 self
.refilter_shown_roster_items()
3863 w
.set_sensitive(False)
3865 w
.set_sensitive(True)
3867 def on_view_menu_activate(self
, widget
):
3868 # Hide the show roster menu if we are not in the right windowing mode.
3869 if self
.hpaned
.get_child2() is not None:
3870 self
.xml
.get_widget('show_roster_menuitem').show()
3872 self
.xml
.get_widget('show_roster_menuitem').hide()
3874 def on_show_roster_menuitem_toggled(self
, widget
):
3875 # when num controls is 0 this menuitem is hidden, but still need to
3876 # disable keybinding
3877 if self
.hpaned
.get_child2() is not None:
3878 self
.show_roster_vbox(widget
.get_active())
3880 ################################################################################
3881 ### Drag and Drop handling
3882 ################################################################################
3884 def drag_data_get_data(self
, treeview
, context
, selection
, target_id
, etime
):
3885 model
, list_of_paths
= self
.tree
.get_selection().get_selected_rows()
3886 if len(list_of_paths
) != 1:
3888 path
= list_of_paths
[0]
3891 data
= model
[path
][C_JID
]
3892 selection
.set(selection
.target
, 8, data
)
3894 def drag_begin(self
, treeview
, context
):
3895 self
.dragging
= True
3897 def drag_end(self
, treeview
, context
):
3898 self
.dragging
= False
3900 def on_drop_rosterx(self
, widget
, account_source
, c_source
, account_dest
,
3901 c_dest
, was_big_brother
, context
, etime
):
3902 gajim
.connections
[account_dest
].send_contacts([c_source
], c_dest
.jid
)
3904 def on_drop_in_contact(self
, widget
, account_source
, c_source
, account_dest
,
3905 c_dest
, was_big_brother
, context
, etime
):
3907 if not gajim
.connections
[account_source
].private_storage_supported
or not\
3908 gajim
.connections
[account_dest
].private_storage_supported
:
3909 dialogs
.WarningDialog(_('Metacontacts storage not supported by your '
3911 _('Your server does not support storing metacontacts information. '
3912 'So those information will not be saved on next reconnection.'))
3914 def merge_contacts(is_checked
=None):
3916 if is_checked
is not None: # dialog has been shown
3917 if is_checked
: # user does not want to be asked again
3918 gajim
.config
.set('confirm_metacontacts', 'no')
3920 gajim
.config
.set('confirm_metacontacts', 'yes')
3922 # We might have dropped on a metacontact.
3923 # Remove it and readd later with updated family info
3924 dest_family
= gajim
.contacts
.get_metacontacts_family(account_dest
,
3927 self
._remove
_metacontact
_family
(dest_family
, account_dest
)
3928 source_family
= gajim
.contacts
.get_metacontacts_family(account_source
, c_source
.jid
)
3929 if dest_family
== source_family
:
3930 n
= contacts
= len(dest_family
)
3931 for tag
in source_family
:
3932 if tag
['jid'] == c_source
.jid
:
3933 tag
['order'] = contacts
3939 self
._remove
_entity
(c_dest
, account_dest
)
3941 old_family
= gajim
.contacts
.get_metacontacts_family(account_source
,
3943 old_groups
= c_source
.groups
3945 # Remove old source contact(s)
3947 # We have got little brothers. Readd them all
3948 self
._remove
_metacontact
_family
(old_family
, account_source
)
3950 # We are only a litle brother. Simply remove us from our big brother
3951 if self
._get
_contact
_iter
(c_source
.jid
, account_source
):
3952 # When we have been in the group before.
3953 # Do not try to remove us again
3954 self
._remove
_entity
(c_source
, account_source
)
3957 own_data
['jid'] = c_source
.jid
3958 own_data
['account'] = account_source
3959 # Don't touch the rest of the family
3960 old_family
= [own_data
]
3962 # Apply new tag and update contact
3963 for data
in old_family
:
3964 if account_source
!= data
['account'] and not self
.regroup
:
3967 _account
= data
['account']
3969 _contact
= gajim
.contacts
.get_first_contact_from_jid(_account
, _jid
)
3971 # One of the metacontacts may be not connected.
3974 _contact
.groups
= c_dest
.groups
[:]
3975 gajim
.contacts
.add_metacontact(account_dest
, c_dest
.jid
,
3976 _account
, _contact
.jid
, contacts
)
3977 gajim
.connections
[account_source
].update_contact(_contact
.jid
,
3978 _contact
.name
, _contact
.groups
)
3980 # Re-add all and update GUI
3981 new_family
= gajim
.contacts
.get_metacontacts_family(account_source
,
3983 brothers
= self
._add
_metacontact
_family
(new_family
, account_source
)
3985 for c
, acc
in brothers
:
3986 self
.draw_completely(c
.jid
, acc
)
3988 old_groups
.extend(c_dest
.groups
)
3989 for g
in old_groups
:
3990 self
.draw_group(g
, account_source
)
3992 self
.draw_account(account_source
)
3993 context
.finish(True, True, etime
)
3995 confirm_metacontacts
= gajim
.config
.get('confirm_metacontacts')
3996 if confirm_metacontacts
== 'no':
3999 pritext
= _('You are about to create a metacontact. Are you sure you want'
4001 sectext
= _('Metacontacts are a way to regroup several contacts in one '
4002 'line. Generally it is used when the same person has several Jabber '
4003 'accounts or transport accounts.')
4004 dlg
= dialogs
.ConfirmationDialogCheck(pritext
, sectext
,
4005 _('Do _not ask me again'), on_response_ok
=merge_contacts
)
4006 if not confirm_metacontacts
: # First time we see this window
4007 dlg
.checkbutton
.set_active(True)
4010 def on_drop_in_group(self
, widget
, account
, c_source
, grp_dest
,
4011 is_big_brother
, context
, etime
, grp_source
= None):
4013 # add whole metacontact to new group
4014 self
.add_contact_to_groups(c_source
.jid
, account
, [grp_dest
,])
4015 # remove afterwards so the contact is not moved to General in the
4017 if grp_dest
!= grp_source
:
4018 self
.remove_contact_from_groups(c_source
.jid
, account
, [grp_source
])
4020 # Normal contact or little brother
4021 family
= gajim
.contacts
.get_metacontacts_family(account
,
4025 # Remove whole family. Remove us from the family.
4026 # Then re-add other family members.
4027 self
._remove
_metacontact
_family
(family
, account
)
4028 gajim
.contacts
.remove_metacontact(account
, c_source
.jid
)
4030 if account
!= data
['account'] and not self
.regroup
:
4032 if data
['jid'] == c_source
.jid
and\
4033 data
['account'] == account
:
4035 self
.add_contact(data
['jid'], data
['account'])
4038 self
.add_contact_to_groups(c_source
.jid
, account
, [grp_dest
,])
4042 self
.add_contact_to_groups(c_source
.jid
, account
, [grp_dest
,])
4043 # remove afterwards so the contact is not moved to General in the
4045 if grp_dest
!= grp_source
:
4046 self
.remove_contact_from_groups(c_source
.jid
, account
,
4049 if context
.action
in (gtk
.gdk
.ACTION_MOVE
, gtk
.gdk
.ACTION_COPY
):
4050 context
.finish(True, True, etime
)
4053 def drag_drop(self
, treeview
, context
, x
, y
, timestamp
):
4054 target_list
= treeview
.drag_dest_get_target_list()
4055 target
= treeview
.drag_dest_find_target(context
, target_list
)
4056 treeview
.drag_get_data(context
, target
)
4057 context
.finish(False, True)
4060 def drag_data_received_data(self
, treeview
, context
, x
, y
, selection
, info
,
4062 treeview
.stop_emission('drag_data_received')
4063 drop_info
= treeview
.get_dest_row_at_pos(x
, y
)
4066 if not selection
.data
:
4067 return # prevents tb when several entrys are dragged
4068 model
= treeview
.get_model()
4069 data
= selection
.data
4070 path_dest
, position
= drop_info
4072 if position
== gtk
.TREE_VIEW_DROP_BEFORE
and len(path_dest
) == 2 \
4073 and path_dest
[1] == 0: # dropped before the first group
4075 if position
== gtk
.TREE_VIEW_DROP_BEFORE
and len(path_dest
) == 2:
4076 # dropped before a group: we drop it in the previous group every time
4077 path_dest
= (path_dest
[0], path_dest
[1]-1)
4078 # destination: the row something got dropped on
4079 iter_dest
= model
.get_iter(path_dest
)
4080 type_dest
= model
[iter_dest
][C_TYPE
].decode('utf-8')
4081 jid_dest
= model
[iter_dest
][C_JID
].decode('utf-8')
4082 account_dest
= model
[iter_dest
][C_ACCOUNT
].decode('utf-8')
4084 # drop on account row in merged mode, we cannot know the desired account
4085 if account_dest
== 'all':
4087 # nothing can be done, if destination account is offline
4088 if gajim
.connections
[account_dest
].connected
< 2:
4091 # A file got dropped on the roster
4092 if info
== self
.TARGET_TYPE_URI_LIST
:
4093 if len(path_dest
) < 3:
4095 if type_dest
!= 'contact':
4097 c_dest
= gajim
.contacts
.get_contact_with_highest_priority(account_dest
,
4099 if not gajim
.capscache
.is_supported(c_dest
, NS_FILE
):
4102 uri_splitted
= uri
.split() # we may have more than one file dropped
4104 # This is always the last element in windows
4105 uri_splitted
.remove('\0')
4108 nb_uri
= len(uri_splitted
)
4111 for a_uri
in uri_splitted
:
4112 path
= helpers
.get_file_path_from_dnd_dropped_uri(a_uri
)
4113 if not os
.path
.isfile(path
):
4114 bad_uris
.append(a_uri
)
4116 dialogs
.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris
))
4118 def _on_send_files(account
, jid
, uris
):
4119 c
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
4121 path
= helpers
.get_file_path_from_dnd_dropped_uri(uri
)
4122 if os
.path
.isfile(path
): # is it file?
4123 gajim
.interface
.instances
['file_transfers'].send_file(
4125 # Popup dialog to confirm sending
4126 prim_text
= 'Send file?'
4127 sec_text
= i18n
.ngettext('Do you want to send this file to %s:',
4128 'Do you want to send these files to %s:', nb_uri
) %\
4129 c_dest
.get_shown_name()
4130 for uri
in uri_splitted
:
4131 path
= helpers
.get_file_path_from_dnd_dropped_uri(uri
)
4132 sec_text
+= '\n' + os
.path
.basename(path
)
4133 dialog
= dialogs
.NonModalConfirmationDialog(prim_text
, sec_text
,
4134 on_response_ok
= (_on_send_files
, account_dest
, jid_dest
,
4139 # a roster entry was dragged and dropped somewhere in the roster
4141 # source: the row that was dragged
4142 path_source
= treeview
.get_selection().get_selected_rows()[1][0]
4143 iter_source
= model
.get_iter(path_source
)
4144 type_source
= model
[iter_source
][C_TYPE
]
4145 account_source
= model
[iter_source
][C_ACCOUNT
].decode('utf-8')
4147 # Only normal contacts can be dragged
4148 if type_source
!= 'contact':
4150 if gajim
.config
.get_per('accounts', account_source
, 'is_zeroconf'):
4153 # A contact was dropped
4154 if gajim
.config
.get_per('accounts', account_dest
, 'is_zeroconf'):
4155 # drop on zeroconf account, adding not possible
4157 if type_dest
== 'self_contact':
4158 # drop on self contact row
4160 if type_dest
== 'account' and account_source
== account_dest
:
4161 # drop on the account it was dragged from
4163 if type_dest
== 'groupchat':
4164 # drop on a minimized groupchat
4165 # TODO: Invite to groupchat
4168 # Get valid source group, jid and contact
4170 while model
[it
][C_TYPE
] == 'contact':
4171 it
= model
.iter_parent(it
)
4172 grp_source
= model
[it
][C_JID
].decode('utf-8')
4173 if grp_source
in helpers
.special_groups
and \
4174 grp_source
not in ('Not in Roster', 'Observers'):
4175 # a transport or a minimized groupchat was dragged
4176 # we can add it to other accounts but not move it to another group,
4179 jid_source
= data
.decode('utf-8')
4180 c_source
= gajim
.contacts
.get_contact_with_highest_priority(
4181 account_source
, jid_source
)
4183 # Get destination group
4185 if type_dest
== 'group':
4186 grp_dest
= model
[iter_dest
][C_JID
].decode('utf-8')
4187 elif type_dest
in ('contact', 'agent'):
4189 while model
[it
][C_TYPE
] != 'group':
4190 it
= model
.iter_parent(it
)
4191 grp_dest
= model
[it
][C_JID
].decode('utf-8')
4192 if grp_dest
in helpers
.special_groups
:
4195 if jid_source
== jid_dest
:
4196 if grp_source
== grp_dest
and account_source
== account_dest
:
4200 # contact drop somewhere in or on a foreign account
4201 if (type_dest
== 'account' or not self
.regroup
) and \
4202 account_source
!= account_dest
:
4203 # add to account in specified group
4204 dialogs
.AddNewContactWindow(account
=account_dest
, jid
=jid_source
,
4205 user_nick
=c_source
.name
, group
=grp_dest
)
4208 # we may not add contacts from special_groups
4209 if grp_source
in helpers
.special_groups
:
4212 # Is the contact we drag a meta contact?
4213 accounts
= (self
.regroup
and gajim
.contacts
.get_accounts()) or \
4215 is_big_brother
= gajim
.contacts
.is_big_brother(account_source
, jid_source
,
4218 drop_in_middle_of_meta
= False
4219 if type_dest
== 'contact':
4220 if position
== gtk
.TREE_VIEW_DROP_BEFORE
and len(path_dest
) == 4:
4221 drop_in_middle_of_meta
= True
4222 if position
== gtk
.TREE_VIEW_DROP_AFTER
and (len(path_dest
) == 4 or \
4223 self
.modelfilter
.iter_has_child(iter_dest
)):
4224 drop_in_middle_of_meta
= True
4225 # Contact drop on group row or between two contacts that are
4227 if (type_dest
== 'group' or position
in (gtk
.TREE_VIEW_DROP_BEFORE
,
4228 gtk
.TREE_VIEW_DROP_AFTER
)) and not drop_in_middle_of_meta
:
4229 self
.on_drop_in_group(None, account_source
, c_source
, grp_dest
,
4230 is_big_brother
, context
, etime
, grp_source
)
4233 # Contact drop on another contact, make meta contacts
4234 if position
== gtk
.TREE_VIEW_DROP_INTO_OR_AFTER
or \
4235 position
== gtk
.TREE_VIEW_DROP_INTO_OR_BEFORE
or drop_in_middle_of_meta
:
4236 c_dest
= gajim
.contacts
.get_contact_with_highest_priority(account_dest
,
4239 # c_dest is None if jid_dest doesn't belong to account
4242 item
= gtk
.MenuItem(_('Send %s to %s') % (c_source
.get_shown_name(),
4243 c_dest
.get_shown_name()))
4244 item
.connect('activate', self
.on_drop_rosterx
, account_source
,
4245 c_source
, account_dest
, c_dest
, is_big_brother
, context
, etime
)
4248 item
= gtk
.MenuItem(_('Make %s and %s metacontacts') % (
4249 c_source
.get_shown_name(), c_dest
.get_shown_name()))
4250 item
.connect('activate', self
.on_drop_in_contact
, account_source
,
4251 c_source
, account_dest
, c_dest
, is_big_brother
, context
, etime
)
4255 menu
.attach_to_widget(self
.tree
, None)
4256 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
4258 menu
.popup(None, None, None, 1, etime
)
4260 ################################################################################
4261 ### Everything about images and icons....
4262 ### Cleanup assigned to Jim++ :-)
4263 ################################################################################
4265 def get_appropriate_state_images(self
, jid
, size
='16', icon_name
='online'):
4266 '''check jid and return the appropriate state images dict for
4267 the demanded size. icon_name is taken into account when jid is from
4268 transport: transport iconset doesn't contain all icons, so we fall back
4270 transport
= gajim
.get_transport_name_from_jid(jid
)
4271 if transport
and size
in self
.transports_state_images
:
4272 if transport
not in self
.transports_state_images
[size
]:
4273 # we don't have iconset for this transport loaded yet. Let's do it
4274 self
.make_transport_state_images(transport
)
4275 if transport
in self
.transports_state_images
[size
] and \
4276 icon_name
in self
.transports_state_images
[size
][transport
]:
4277 return self
.transports_state_images
[size
][transport
]
4278 return gajim
.interface
.jabber_state_images
[size
]
4280 def make_transport_state_images(self
, transport
):
4281 '''initialise opened and closed 'transport' iconset dict'''
4282 if gajim
.config
.get('use_transports_iconsets'):
4283 folder
= os
.path
.join(helpers
.get_transport_path(transport
),
4285 pixo
, pixc
= gtkgui_helpers
.load_icons_meta()
4286 self
.transports_state_images
['opened'][transport
] = \
4287 gtkgui_helpers
.load_iconset(folder
, pixo
, transport
=True)
4288 self
.transports_state_images
['closed'][transport
] = \
4289 gtkgui_helpers
.load_iconset(folder
, pixc
, transport
=True)
4290 folder
= os
.path
.join(helpers
.get_transport_path(transport
), '32x32')
4291 self
.transports_state_images
['32'][transport
] = \
4292 gtkgui_helpers
.load_iconset(folder
, transport
=True)
4293 folder
= os
.path
.join(helpers
.get_transport_path(transport
), '16x16')
4294 self
.transports_state_images
['16'][transport
] = \
4295 gtkgui_helpers
.load_iconset(folder
, transport
=True)
4297 def update_jabber_state_images(self
):
4299 self
.setup_and_draw_roster()
4300 # Update the status combobox
4301 model
= self
.status_combobox
.get_model()
4302 titer
= model
.get_iter_root()
4304 if model
[titer
][2] != '':
4305 # If it's not change status message iter
4306 # eg. if it has show parameter not ''
4307 model
[titer
][1] = gajim
.interface
.jabber_state_images
['16'][model
[
4309 titer
= model
.iter_next(titer
)
4310 # Update the systray
4311 if gajim
.interface
.systray_enabled
:
4312 gajim
.interface
.systray
.set_img()
4314 for win
in gajim
.interface
.msg_win_mgr
.windows():
4315 for ctrl
in win
.controls():
4317 win
.redraw_tab(ctrl
)
4319 self
.update_status_combobox()
4321 def set_account_status_icon(self
, account
):
4322 status
= gajim
.connections
[account
].connected
4323 child_iterA
= self
._get
_account
_iter
(account
, self
.model
)
4326 if not self
.regroup
:
4327 show
= gajim
.SHOW_LIST
[status
]
4328 else: # accounts merged
4329 show
= helpers
.get_global_show()
4330 self
.model
[child_iterA
][C_IMG
] = gajim
.interface
.jabber_state_images
[
4333 ################################################################################
4334 ### Style and theme related methods
4335 ################################################################################
4337 def show_title(self
):
4338 change_title_allowed
= gajim
.config
.get('change_roster_title')
4339 if not change_title_allowed
:
4342 if gajim
.config
.get('one_message_window') == 'always_with_roster':
4343 # always_with_roster mode defers to the MessageWindow
4344 if not gajim
.interface
.msg_win_mgr
.one_window_opened():
4345 # No MessageWindow to defer to
4346 self
.window
.set_title('Gajim')
4351 for account
in gajim
.connections
:
4352 # Count events in roster title only if we don't auto open them
4353 if not helpers
.allow_popup_window(account
):
4354 nb_unread
+= gajim
.events
.get_nb_events(['chat', 'normal',
4355 'file-request', 'file-error', 'file-completed',
4356 'file-request-error', 'file-send-error', 'file-stopped',
4357 'printed_chat'], account
)
4359 start
= '[' + str(nb_unread
) + '] '
4360 elif nb_unread
== 1:
4363 self
.window
.set_title(start
+ 'Gajim')
4365 gtkgui_helpers
.set_unset_urgency_hint(self
.window
, nb_unread
)
4367 def _change_style(self
, model
, path
, titer
, option
):
4368 if option
is None or model
[titer
][C_TYPE
] == option
:
4369 # We changed style for this type of row
4370 model
[titer
][C_NAME
] = model
[titer
][C_NAME
]
4372 def change_roster_style(self
, option
):
4373 self
.model
.foreach(self
._change
_style
, option
)
4374 for win
in gajim
.interface
.msg_win_mgr
.windows():
4375 win
.repaint_themed_widgets()
4377 def repaint_themed_widgets(self
):
4378 '''Notify windows that contain themed widgets to repaint them'''
4379 for win
in gajim
.interface
.msg_win_mgr
.windows():
4380 win
.repaint_themed_widgets()
4381 for account
in gajim
.connections
:
4382 for addr
in gajim
.interface
.instances
[account
]['disco']:
4383 gajim
.interface
.instances
[account
]['disco'][addr
].paint_banner()
4384 for ctrl
in gajim
.interface
.minimized_controls
[account
].values():
4385 ctrl
.repaint_themed_widgets()
4387 def update_avatar_in_gui(self
, jid
, account
):
4389 self
.draw_avatar(jid
, account
)
4390 # Update chat window
4392 ctrl
= gajim
.interface
.msg_win_mgr
.get_control(jid
, account
)
4396 def on_roster_treeview_style_set(self
, treeview
, style
):
4397 '''When style (theme) changes, redraw all contacts'''
4398 for contact
in self
._iter
_contact
_rows
():
4399 self
.draw_contact(contact
[C_JID
].decode('utf-8'),
4400 contact
[C_ACCOUNT
].decode('utf-8'))
4402 def set_renderer_color(self
, renderer
, style
, set_background
=True):
4403 '''set style for treeview cell, using PRELIGHT system color'''
4405 bgcolor
= self
.tree
.style
.bg
[style
]
4406 renderer
.set_property('cell-background-gdk', bgcolor
)
4408 fgcolor
= self
.tree
.style
.fg
[style
]
4409 renderer
.set_property('foreground-gdk', fgcolor
)
4411 def _iconCellDataFunc(self
, column
, renderer
, model
, titer
, data
=None):
4412 '''When a row is added, set properties for icon renderer'''
4413 theme
= gajim
.config
.get('roster_theme')
4414 type_
= model
[titer
][C_TYPE
]
4415 if type_
== 'account':
4416 color
= gajim
.config
.get_per('themes', theme
, 'accountbgcolor')
4418 renderer
.set_property('cell-background', color
)
4420 self
.set_renderer_color(renderer
, gtk
.STATE_ACTIVE
)
4421 renderer
.set_property('xalign', 0)
4422 elif type_
== 'group':
4423 color
= gajim
.config
.get_per('themes', theme
, 'groupbgcolor')
4425 renderer
.set_property('cell-background', color
)
4427 self
.set_renderer_color(renderer
, gtk
.STATE_PRELIGHT
)
4428 renderer
.set_property('xalign', 0.2)
4429 elif type_
: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4430 if not model
[titer
][C_JID
] or not model
[titer
][C_ACCOUNT
]:
4431 # This can append when at the moment we add the row
4433 jid
= model
[titer
][C_JID
].decode('utf-8')
4434 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4435 if jid
in gajim
.newly_added
[account
]:
4436 renderer
.set_property('cell-background', gajim
.config
.get(
4437 'just_connected_bg_color'))
4438 elif jid
in gajim
.to_be_removed
[account
]:
4439 renderer
.set_property('cell-background', gajim
.config
.get(
4440 'just_disconnected_bg_color'))
4442 color
= gajim
.config
.get_per('themes', theme
, 'contactbgcolor')
4444 renderer
.set_property('cell-background', color
)
4446 renderer
.set_property('cell-background', None)
4447 parent_iter
= model
.iter_parent(titer
)
4448 if model
[parent_iter
][C_TYPE
] == 'contact':
4449 renderer
.set_property('xalign', 1)
4451 renderer
.set_property('xalign', 0.4)
4452 renderer
.set_property('width', 26)
4454 def _nameCellDataFunc(self
, column
, renderer
, model
, titer
, data
=None):
4455 '''When a row is added, set properties for name renderer'''
4456 theme
= gajim
.config
.get('roster_theme')
4457 type_
= model
[titer
][C_TYPE
]
4458 if type_
== 'account':
4459 color
= gajim
.config
.get_per('themes', theme
, 'accounttextcolor')
4461 renderer
.set_property('foreground', color
)
4463 self
.set_renderer_color(renderer
, gtk
.STATE_ACTIVE
, False)
4464 color
= gajim
.config
.get_per('themes', theme
, 'accountbgcolor')
4466 renderer
.set_property('cell-background', color
)
4468 self
.set_renderer_color(renderer
, gtk
.STATE_ACTIVE
)
4469 renderer
.set_property('font',
4470 gtkgui_helpers
.get_theme_font_for_option(theme
, 'accountfont'))
4471 renderer
.set_property('xpad', 0)
4472 renderer
.set_property('width', 3)
4473 elif type_
== 'group':
4474 color
= gajim
.config
.get_per('themes', theme
, 'grouptextcolor')
4476 renderer
.set_property('foreground', color
)
4478 self
.set_renderer_color(renderer
, gtk
.STATE_PRELIGHT
, False)
4479 color
= gajim
.config
.get_per('themes', theme
, 'groupbgcolor')
4481 renderer
.set_property('cell-background', color
)
4483 self
.set_renderer_color(renderer
, gtk
.STATE_PRELIGHT
)
4484 renderer
.set_property('font',
4485 gtkgui_helpers
.get_theme_font_for_option(theme
, 'groupfont'))
4486 renderer
.set_property('xpad', 4)
4487 elif type_
: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4488 if not model
[titer
][C_JID
] or not model
[titer
][C_ACCOUNT
]:
4489 # This can append when at the moment we add the row
4491 jid
= model
[titer
][C_JID
].decode('utf-8')
4492 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4494 if type_
== 'groupchat':
4495 ctrl
= gajim
.interface
.minimized_controls
[account
].get(jid
, None)
4496 if ctrl
and ctrl
.attention_flag
:
4497 color
= gajim
.config
.get_per('themes', theme
,
4498 'state_muc_directed_msg_color')
4499 renderer
.set_property('foreground', 'red')
4501 color
= gajim
.config
.get_per('themes', theme
, 'contacttextcolor')
4503 renderer
.set_property('foreground', color
)
4505 renderer
.set_property('foreground', None)
4506 if jid
in gajim
.newly_added
[account
]:
4507 renderer
.set_property('cell-background', gajim
.config
.get(
4508 'just_connected_bg_color'))
4509 elif jid
in gajim
.to_be_removed
[account
]:
4510 renderer
.set_property('cell-background', gajim
.config
.get(
4511 'just_disconnected_bg_color'))
4513 color
= gajim
.config
.get_per('themes', theme
, 'contactbgcolor')
4515 renderer
.set_property('cell-background', color
)
4517 renderer
.set_property('cell-background', None)
4518 renderer
.set_property('font',
4519 gtkgui_helpers
.get_theme_font_for_option(theme
, 'contactfont'))
4520 parent_iter
= model
.iter_parent(titer
)
4521 if model
[parent_iter
][C_TYPE
] == 'contact':
4522 renderer
.set_property('xpad', 16)
4524 renderer
.set_property('xpad', 8)
4527 def _fill_mood_pixbuf_renderer(self
, column
, renderer
, model
, titer
,
4529 '''When a row is added, set properties for avatar renderer'''
4530 theme
= gajim
.config
.get('roster_theme')
4531 type_
= model
[titer
][C_TYPE
]
4532 if type_
== 'group':
4533 renderer
.set_property('visible', False)
4536 # allocate space for the icon only if needed
4537 if model
[titer
][C_MOOD_PIXBUF
]:
4538 renderer
.set_property('visible', True)
4540 renderer
.set_property('visible', False)
4541 if type_
== 'account':
4542 color
= gajim
.config
.get_per('themes', theme
,
4545 renderer
.set_property('cell-background', color
)
4547 self
.set_renderer_color(renderer
,
4549 # align pixbuf to the right)
4550 renderer
.set_property('xalign', 1)
4551 # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4553 if not model
[titer
][C_JID
] \
4554 or not model
[titer
][C_ACCOUNT
]:
4555 # This can append at the moment we add the row
4557 jid
= model
[titer
][C_JID
].decode('utf-8')
4558 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4559 if jid
in gajim
.newly_added
[account
]:
4560 renderer
.set_property('cell-background',
4562 'just_connected_bg_color'))
4563 elif jid
in gajim
.to_be_removed
[account
]:
4564 renderer
.set_property('cell-background',
4566 'just_disconnected_bg_color'))
4568 color
= gajim
.config
.get_per('themes',
4569 theme
, 'contactbgcolor')
4571 renderer
.set_property(
4572 'cell-background', color
)
4574 renderer
.set_property(
4575 'cell-background', None)
4576 # align pixbuf to the right
4577 renderer
.set_property('xalign', 1)
4580 def _fill_activity_pixbuf_renderer(self
, column
, renderer
, model
, titer
,
4582 '''When a row is added, set properties for avatar renderer'''
4583 theme
= gajim
.config
.get('roster_theme')
4584 type_
= model
[titer
][C_TYPE
]
4585 if type_
== 'group':
4586 renderer
.set_property('visible', False)
4589 # allocate space for the icon only if needed
4590 if model
[titer
][C_ACTIVITY_PIXBUF
]:
4591 renderer
.set_property('visible', True)
4593 renderer
.set_property('visible', False)
4594 if type_
== 'account':
4595 color
= gajim
.config
.get_per('themes', theme
,
4598 renderer
.set_property('cell-background', color
)
4600 self
.set_renderer_color(renderer
,
4602 # align pixbuf to the right)
4603 renderer
.set_property('xalign', 1)
4604 # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4606 if not model
[titer
][C_JID
] \
4607 or not model
[titer
][C_ACCOUNT
]:
4608 # This can append at the moment we add the row
4610 jid
= model
[titer
][C_JID
].decode('utf-8')
4611 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4612 if jid
in gajim
.newly_added
[account
]:
4613 renderer
.set_property('cell-background',
4615 'just_connected_bg_color'))
4616 elif jid
in gajim
.to_be_removed
[account
]:
4617 renderer
.set_property('cell-background',
4619 'just_disconnected_bg_color'))
4621 color
= gajim
.config
.get_per('themes',
4622 theme
, 'contactbgcolor')
4624 renderer
.set_property(
4625 'cell-background', color
)
4627 renderer
.set_property(
4628 'cell-background', None)
4629 # align pixbuf to the right
4630 renderer
.set_property('xalign', 1)
4633 def _fill_tune_pixbuf_renderer(self
, column
, renderer
, model
, titer
,
4635 '''When a row is added, set properties for avatar renderer'''
4636 theme
= gajim
.config
.get('roster_theme')
4637 type_
= model
[titer
][C_TYPE
]
4638 if type_
== 'group':
4639 renderer
.set_property('visible', False)
4642 # allocate space for the icon only if needed
4643 if model
[titer
][C_TUNE_PIXBUF
]:
4644 renderer
.set_property('visible', True)
4646 renderer
.set_property('visible', False)
4647 if type_
== 'account':
4648 color
= gajim
.config
.get_per('themes', theme
,
4651 renderer
.set_property('cell-background', color
)
4653 self
.set_renderer_color(renderer
,
4655 # align pixbuf to the right)
4656 renderer
.set_property('xalign', 1)
4657 # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4659 if not model
[titer
][C_JID
] \
4660 or not model
[titer
][C_ACCOUNT
]:
4661 # This can append at the moment we add the row
4663 jid
= model
[titer
][C_JID
].decode('utf-8')
4664 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4665 if jid
in gajim
.newly_added
[account
]:
4666 renderer
.set_property('cell-background',
4668 'just_connected_bg_color'))
4669 elif jid
in gajim
.to_be_removed
[account
]:
4670 renderer
.set_property('cell-background',
4672 'just_disconnected_bg_color'))
4674 color
= gajim
.config
.get_per('themes',
4675 theme
, 'contactbgcolor')
4677 renderer
.set_property(
4678 'cell-background', color
)
4680 renderer
.set_property(
4681 'cell-background', None)
4682 # align pixbuf to the right
4683 renderer
.set_property('xalign', 1)
4686 def _fill_avatar_pixbuf_renderer(self
, column
, renderer
, model
, titer
,
4688 '''When a row is added, set properties for avatar renderer'''
4689 theme
= gajim
.config
.get('roster_theme')
4690 type_
= model
[titer
][C_TYPE
]
4691 if type_
in ('group', 'account'):
4692 renderer
.set_property('visible', False)
4695 # allocate space for the icon only if needed
4696 if model
[titer
][C_AVATAR_PIXBUF
] or \
4697 gajim
.config
.get('avatar_position_in_roster') == 'left':
4698 renderer
.set_property('visible', True)
4700 renderer
.set_property('visible', False)
4701 if type_
: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
4702 if not model
[titer
][C_JID
] or not model
[titer
][C_ACCOUNT
]:
4703 # This can append at the moment we add the row
4705 jid
= model
[titer
][C_JID
].decode('utf-8')
4706 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
4707 if jid
in gajim
.newly_added
[account
]:
4708 renderer
.set_property('cell-background', gajim
.config
.get(
4709 'just_connected_bg_color'))
4710 elif jid
in gajim
.to_be_removed
[account
]:
4711 renderer
.set_property('cell-background', gajim
.config
.get(
4712 'just_disconnected_bg_color'))
4714 color
= gajim
.config
.get_per('themes', theme
, 'contactbgcolor')
4716 renderer
.set_property('cell-background', color
)
4718 renderer
.set_property('cell-background', None)
4719 if gajim
.config
.get('avatar_position_in_roster') == 'left':
4720 renderer
.set_property('width', gajim
.config
.get('roster_avatar_width'))
4721 renderer
.set_property('xalign', 0.5)
4723 renderer
.set_property('xalign', 1) # align pixbuf to the right
4725 def _fill_padlock_pixbuf_renderer(self
, column
, renderer
, model
, titer
,
4727 '''When a row is added, set properties for padlock renderer'''
4728 theme
= gajim
.config
.get('roster_theme')
4729 type_
= model
[titer
][C_TYPE
]
4730 # allocate space for the icon only if needed
4731 if type_
== 'account' and model
[titer
][C_PADLOCK_PIXBUF
]:
4732 renderer
.set_property('visible', True)
4733 color
= gajim
.config
.get_per('themes', theme
, 'accountbgcolor')
4735 renderer
.set_property('cell-background', color
)
4737 self
.set_renderer_color(renderer
, gtk
.STATE_ACTIVE
)
4738 renderer
.set_property('xalign', 1) # align pixbuf to the right
4740 renderer
.set_property('visible', False)
4742 ################################################################################
4743 ### Everything about building menus
4744 ### FIXME: We really need to make it simpler! 1465 lines are a few to much....
4745 ################################################################################
4747 def make_menu(self
, force
=False):
4748 '''create the main window\'s menus'''
4749 if not force
and not self
.actions_menu_needs_rebuild
:
4751 new_chat_menuitem
= self
.xml
.get_widget('new_chat_menuitem')
4752 single_message_menuitem
= self
.xml
.get_widget(
4753 'send_single_message_menuitem')
4754 join_gc_menuitem
= self
.xml
.get_widget('join_gc_menuitem')
4755 muc_icon
= gtkgui_helpers
.load_icon('muc_active')
4757 join_gc_menuitem
.set_image(muc_icon
)
4758 add_new_contact_menuitem
= self
.xml
.get_widget('add_new_contact_menuitem')
4759 service_disco_menuitem
= self
.xml
.get_widget('service_disco_menuitem')
4760 advanced_menuitem
= self
.xml
.get_widget('advanced_menuitem')
4761 profile_avatar_menuitem
= self
.xml
.get_widget('profile_avatar_menuitem')
4763 # destroy old advanced menus
4764 for m
in self
.advanced_menus
:
4767 # make it sensitive. it is insensitive only if no accounts are *available*
4768 advanced_menuitem
.set_sensitive(True)
4770 if self
.add_new_contact_handler_id
:
4771 add_new_contact_menuitem
.handler_disconnect(
4772 self
.add_new_contact_handler_id
)
4773 self
.add_new_contact_handler_id
= None
4775 if self
.service_disco_handler_id
:
4776 service_disco_menuitem
.handler_disconnect(
4777 self
.service_disco_handler_id
)
4778 self
.service_disco_handler_id
= None
4780 if self
.new_chat_menuitem_handler_id
:
4781 new_chat_menuitem
.handler_disconnect(
4782 self
.new_chat_menuitem_handler_id
)
4783 self
.new_chat_menuitem_handler_id
= None
4785 if self
.single_message_menuitem_handler_id
:
4786 single_message_menuitem
.handler_disconnect(
4787 self
.single_message_menuitem_handler_id
)
4788 self
.single_message_menuitem_handler_id
= None
4790 if self
.profile_avatar_menuitem_handler_id
:
4791 profile_avatar_menuitem
.handler_disconnect(
4792 self
.profile_avatar_menuitem_handler_id
)
4793 self
.profile_avatar_menuitem_handler_id
= None
4795 # remove the existing submenus
4796 add_new_contact_menuitem
.remove_submenu()
4797 service_disco_menuitem
.remove_submenu()
4798 join_gc_menuitem
.remove_submenu()
4799 single_message_menuitem
.remove_submenu()
4800 new_chat_menuitem
.remove_submenu()
4801 advanced_menuitem
.remove_submenu()
4802 profile_avatar_menuitem
.remove_submenu()
4804 # remove the existing accelerator
4805 if self
.have_new_chat_accel
:
4806 ag
= gtk
.accel_groups_from_object(self
.window
)[0]
4807 new_chat_menuitem
.remove_accelerator(ag
, gtk
.keysyms
.n
,
4808 gtk
.gdk
.CONTROL_MASK
)
4809 self
.have_new_chat_accel
= False
4811 gc_sub_menu
= gtk
.Menu() # gc is always a submenu
4812 join_gc_menuitem
.set_submenu(gc_sub_menu
)
4814 connected_accounts
= gajim
.get_number_of_connected_accounts()
4816 connected_accounts_with_private_storage
= 0
4818 # items that get shown whether an account is zeroconf or not
4819 accounts_list
= sorted(gajim
.contacts
.get_accounts())
4820 if connected_accounts
> 1: # 2 or more accounts? make submenus
4821 new_chat_sub_menu
= gtk
.Menu()
4823 for account
in accounts_list
:
4824 if gajim
.connections
[account
].connected
<= 1:
4825 # if offline or connecting
4829 new_chat_item
= gtk
.MenuItem(_('using account %s') % account
,
4831 new_chat_sub_menu
.append(new_chat_item
)
4832 new_chat_item
.connect('activate',
4833 self
.on_new_chat_menuitem_activate
, account
)
4835 new_chat_menuitem
.set_submenu(new_chat_sub_menu
)
4836 new_chat_sub_menu
.show_all()
4838 elif connected_accounts
== 1: # user has only one account
4839 for account
in gajim
.connections
:
4840 if gajim
.account_is_connected(account
): # THE connected account
4842 if not self
.new_chat_menuitem_handler_id
:
4843 self
.new_chat_menuitem_handler_id
= new_chat_menuitem
.\
4844 connect('activate', self
.on_new_chat_menuitem_activate
,
4849 # menu items that don't apply to zeroconf connections
4850 if connected_accounts
== 1 or (connected_accounts
== 2 and \
4851 gajim
.zeroconf_is_connected()):
4852 # only one 'real' (non-zeroconf) account is connected, don't need submenus
4854 for account
in accounts_list
:
4855 if gajim
.account_is_connected(account
) and \
4856 not gajim
.config
.get_per('accounts', account
, 'is_zeroconf'):
4858 if gajim
.connections
[account
].private_storage_supported
:
4859 connected_accounts_with_private_storage
+= 1
4860 self
.add_bookmarks_list(gc_sub_menu
, account
)
4861 gc_sub_menu
.show_all()
4863 if not self
.add_new_contact_handler_id
:
4864 self
.add_new_contact_handler_id
=\
4865 add_new_contact_menuitem
.connect(
4866 'activate', self
.on_add_new_contact
, account
)
4868 if not self
.service_disco_handler_id
:
4869 self
.service_disco_handler_id
= service_disco_menuitem
.\
4871 self
.on_service_disco_menuitem_activate
, account
)
4874 if not self
.single_message_menuitem_handler_id
:
4875 self
.single_message_menuitem_handler_id
= \
4876 single_message_menuitem
.connect('activate', \
4877 self
.on_send_single_message_menuitem_activate
, account
)
4880 if not self
.have_new_chat_accel
:
4881 ag
= gtk
.accel_groups_from_object(self
.window
)[0]
4882 new_chat_menuitem
.add_accelerator('activate', ag
,
4883 gtk
.keysyms
.n
, gtk
.gdk
.CONTROL_MASK
, gtk
.ACCEL_VISIBLE
)
4884 self
.have_new_chat_accel
= True
4886 break # No other account connected
4888 # 2 or more 'real' accounts are connected, make submenus
4889 single_message_sub_menu
= gtk
.Menu()
4890 add_sub_menu
= gtk
.Menu()
4891 disco_sub_menu
= gtk
.Menu()
4893 for account
in accounts_list
:
4894 if gajim
.connections
[account
].connected
<= 1 or \
4895 gajim
.config
.get_per('accounts', account
, 'is_zeroconf'):
4896 # skip account if it's offline or connecting or is zeroconf
4900 single_message_item
= gtk
.MenuItem(_('using account %s') % account
,
4902 single_message_sub_menu
.append(single_message_item
)
4903 single_message_item
.connect('activate',
4904 self
.on_send_single_message_menuitem_activate
, account
)
4907 if gajim
.connections
[account
].private_storage_supported
:
4908 connected_accounts_with_private_storage
+= 1
4909 gc_item
= gtk
.MenuItem(_('using account %s') % account
, False)
4910 gc_sub_menu
.append(gc_item
)
4911 gc_menuitem_menu
= gtk
.Menu()
4912 self
.add_bookmarks_list(gc_menuitem_menu
, account
)
4913 gc_item
.set_submenu(gc_menuitem_menu
)
4916 add_item
= gtk
.MenuItem(_('to %s account') % account
, False)
4917 add_sub_menu
.append(add_item
)
4918 add_item
.connect('activate', self
.on_add_new_contact
, account
)
4921 disco_item
= gtk
.MenuItem(_('using %s account') % account
, False)
4922 disco_sub_menu
.append(disco_item
)
4923 disco_item
.connect('activate',
4924 self
.on_service_disco_menuitem_activate
, account
)
4926 single_message_menuitem
.set_submenu(single_message_sub_menu
)
4927 single_message_sub_menu
.show_all()
4928 gc_sub_menu
.show_all()
4929 add_new_contact_menuitem
.set_submenu(add_sub_menu
)
4930 add_sub_menu
.show_all()
4931 service_disco_menuitem
.set_submenu(disco_sub_menu
)
4932 disco_sub_menu
.show_all()
4934 if connected_accounts
== 0:
4935 # no connected accounts, make the menuitems insensitive
4936 for item
in (new_chat_menuitem
, join_gc_menuitem
,\
4937 add_new_contact_menuitem
, service_disco_menuitem
,\
4938 single_message_menuitem
):
4939 item
.set_sensitive(False)
4940 else: # we have one or more connected accounts
4941 for item
in (new_chat_menuitem
, join_gc_menuitem
,
4942 add_new_contact_menuitem
, service_disco_menuitem
,
4943 single_message_menuitem
):
4944 item
.set_sensitive(True)
4945 # disable some fields if only local account is there
4946 if connected_accounts
== 1:
4947 for account
in gajim
.connections
:
4948 if gajim
.account_is_connected(account
) and \
4949 gajim
.connections
[account
].is_zeroconf
:
4950 for item
in (join_gc_menuitem
, add_new_contact_menuitem
,
4951 service_disco_menuitem
, single_message_menuitem
):
4952 item
.set_sensitive(False)
4954 # Manage GC bookmarks
4955 newitem
= gtk
.SeparatorMenuItem() # separator
4956 gc_sub_menu
.append(newitem
)
4958 newitem
= gtk
.ImageMenuItem(_('_Manage Bookmarks...'))
4959 img
= gtk
.image_new_from_stock(gtk
.STOCK_PREFERENCES
,
4961 newitem
.set_image(img
)
4962 newitem
.connect('activate', self
.on_manage_bookmarks_menuitem_activate
)
4963 gc_sub_menu
.append(newitem
)
4964 gc_sub_menu
.show_all()
4965 if connected_accounts_with_private_storage
== 0:
4966 newitem
.set_sensitive(False)
4968 connected_accounts_with_vcard
= []
4969 for account
in gajim
.connections
:
4970 if gajim
.account_is_connected(account
) and \
4971 gajim
.connections
[account
].vcard_supported
:
4972 connected_accounts_with_vcard
.append(account
)
4973 if len(connected_accounts_with_vcard
) > 1:
4974 # 2 or more accounts? make submenus
4975 profile_avatar_sub_menu
= gtk
.Menu()
4976 for account
in connected_accounts_with_vcard
:
4978 profile_avatar_item
= gtk
.MenuItem(_('of account %s') % account
,
4980 profile_avatar_sub_menu
.append(profile_avatar_item
)
4981 profile_avatar_item
.connect('activate',
4982 self
.on_profile_avatar_menuitem_activate
, account
)
4983 profile_avatar_menuitem
.set_submenu(profile_avatar_sub_menu
)
4984 profile_avatar_sub_menu
.show_all()
4985 elif len(connected_accounts_with_vcard
) == 1: # user has only one account
4986 account
= connected_accounts_with_vcard
[0]
4988 if not self
.profile_avatar_menuitem_handler_id
:
4989 self
.profile_avatar_menuitem_handler_id
= \
4990 profile_avatar_menuitem
.connect('activate',
4991 self
.on_profile_avatar_menuitem_activate
, account
)
4993 if len(connected_accounts_with_vcard
) == 0:
4994 profile_avatar_menuitem
.set_sensitive(False)
4996 profile_avatar_menuitem
.set_sensitive(True)
4999 if len(gajim
.connections
) == 0: # user has no accounts
5000 advanced_menuitem
.set_sensitive(False)
5001 elif len(gajim
.connections
) == 1: # we have one acccount
5002 account
= gajim
.connections
.keys()[0]
5003 advanced_menuitem_menu
= self
.get_and_connect_advanced_menuitem_menu(
5005 self
.advanced_menus
.append(advanced_menuitem_menu
)
5007 self
.add_history_manager_menuitem(advanced_menuitem_menu
)
5009 advanced_menuitem
.set_submenu(advanced_menuitem_menu
)
5010 advanced_menuitem_menu
.show_all()
5011 else: # user has *more* than one account : build advanced submenus
5012 advanced_sub_menu
= gtk
.Menu()
5013 accounts
= [] # Put accounts in a list to sort them
5014 for account
in gajim
.connections
:
5015 accounts
.append(account
)
5017 for account
in accounts
:
5018 advanced_item
= gtk
.MenuItem(_('for account %s') % account
, False)
5019 advanced_sub_menu
.append(advanced_item
)
5020 advanced_menuitem_menu
= \
5021 self
.get_and_connect_advanced_menuitem_menu(account
)
5022 self
.advanced_menus
.append(advanced_menuitem_menu
)
5023 advanced_item
.set_submenu(advanced_menuitem_menu
)
5025 self
.add_history_manager_menuitem(advanced_sub_menu
)
5027 advanced_menuitem
.set_submenu(advanced_sub_menu
)
5028 advanced_sub_menu
.show_all()
5030 self
.actions_menu_needs_rebuild
= False
5032 def build_account_menu(self
, account
):
5033 # we have to create our own set of icons for the menu
5034 # using self.jabber_status_images is poopoo
5035 iconset
= gajim
.config
.get('iconset')
5036 path
= os
.path
.join(helpers
.get_iconset_path(iconset
), '16x16')
5037 state_images
= gtkgui_helpers
.load_iconset(path
)
5039 if not gajim
.config
.get_per('accounts', account
, 'is_zeroconf'):
5040 xml
= gtkgui_helpers
.get_glade('account_context_menu.glade')
5041 account_context_menu
= xml
.get_widget('account_context_menu')
5043 status_menuitem
= xml
.get_widget('status_menuitem')
5044 start_chat_menuitem
= xml
.get_widget('start_chat_menuitem')
5045 join_group_chat_menuitem
= xml
.get_widget('join_group_chat_menuitem')
5046 muc_icon
= gtkgui_helpers
.load_icon('muc_active')
5048 join_group_chat_menuitem
.set_image(muc_icon
)
5049 open_gmail_inbox_menuitem
= xml
.get_widget('open_gmail_inbox_menuitem')
5050 add_contact_menuitem
= xml
.get_widget('add_contact_menuitem')
5051 service_discovery_menuitem
= xml
.get_widget(
5052 'service_discovery_menuitem')
5053 execute_command_menuitem
= xml
.get_widget('execute_command_menuitem')
5054 edit_account_menuitem
= xml
.get_widget('edit_account_menuitem')
5055 sub_menu
= gtk
.Menu()
5056 status_menuitem
.set_submenu(sub_menu
)
5058 for show
in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
5059 uf_show
= helpers
.get_uf_show(show
, use_mnemonic
=True)
5060 item
= gtk
.ImageMenuItem(uf_show
)
5061 icon
= state_images
[show
]
5062 item
.set_image(icon
)
5063 sub_menu
.append(item
)
5064 con
= gajim
.connections
[account
]
5065 if show
== 'invisible' and con
.connected
> 1 and \
5066 not con
.privacy_rules_supported
:
5067 item
.set_sensitive(False)
5069 item
.connect('activate', self
.change_status
, account
, show
)
5071 item
= gtk
.SeparatorMenuItem()
5072 sub_menu
.append(item
)
5074 item
= gtk
.ImageMenuItem(_('_Change Status Message'))
5075 path
= os
.path
.join(gajim
.DATA_DIR
, 'pixmaps', 'kbd_input.png')
5077 img
.set_from_file(path
)
5079 sub_menu
.append(item
)
5080 item
.connect('activate', self
.on_change_status_message_activate
,
5082 if gajim
.connections
[account
].connected
< 2:
5083 item
.set_sensitive(False)
5085 item
= gtk
.SeparatorMenuItem()
5086 sub_menu
.append(item
)
5088 uf_show
= helpers
.get_uf_show('offline', use_mnemonic
=True)
5089 item
= gtk
.ImageMenuItem(uf_show
)
5090 icon
= state_images
['offline']
5091 item
.set_image(icon
)
5092 sub_menu
.append(item
)
5093 item
.connect('activate', self
.change_status
, account
, 'offline')
5095 pep_menuitem
= xml
.get_widget('pep_menuitem')
5096 if gajim
.connections
[account
].pep_supported
:
5097 have_tune
= gajim
.config
.get_per('accounts', account
,
5099 pep_submenu
= gtk
.Menu()
5100 pep_menuitem
.set_submenu(pep_submenu
)
5101 item
= gtk
.CheckMenuItem(_('Publish Tune'))
5102 pep_submenu
.append(item
)
5103 if not dbus_support
.supported
:
5104 item
.set_sensitive(False)
5106 item
.set_active(have_tune
)
5107 item
.connect('toggled', self
.on_publish_tune_toggled
, account
)
5109 pep_config
= gtk
.ImageMenuItem(_('Configure Services...'))
5110 item
= gtk
.SeparatorMenuItem()
5111 pep_submenu
.append(item
)
5112 pep_config
.set_sensitive(True)
5113 pep_submenu
.append(pep_config
)
5114 pep_config
.connect('activate',
5115 self
.on_pep_services_menuitem_activate
, account
)
5116 img
= gtk
.image_new_from_stock(gtk
.STOCK_PREFERENCES
,
5118 pep_config
.set_image(img
)
5121 pep_menuitem
.set_sensitive(False)
5123 if not gajim
.connections
[account
].gmail_url
:
5124 open_gmail_inbox_menuitem
.set_no_show_all(True)
5125 open_gmail_inbox_menuitem
.hide()
5127 open_gmail_inbox_menuitem
.connect('activate',
5128 self
.on_open_gmail_inbox
, account
)
5130 edit_account_menuitem
.connect('activate', self
.on_edit_account
,
5132 add_contact_menuitem
.connect('activate', self
.on_add_new_contact
,
5134 service_discovery_menuitem
.connect('activate',
5135 self
.on_service_disco_menuitem_activate
, account
)
5136 hostname
= gajim
.config
.get_per('accounts', account
, 'hostname')
5137 contact
= gajim
.contacts
.create_contact(jid
=hostname
) # Fake contact
5138 execute_command_menuitem
.connect('activate',
5139 self
.on_execute_command
, contact
, account
)
5141 start_chat_menuitem
.connect('activate',
5142 self
.on_new_chat_menuitem_activate
, account
)
5144 gc_sub_menu
= gtk
.Menu() # gc is always a submenu
5145 join_group_chat_menuitem
.set_submenu(gc_sub_menu
)
5146 self
.add_bookmarks_list(gc_sub_menu
, account
)
5148 # make some items insensitive if account is offline
5149 if gajim
.connections
[account
].connected
< 2:
5150 for widget
in (add_contact_menuitem
, service_discovery_menuitem
,
5151 join_group_chat_menuitem
, execute_command_menuitem
, pep_menuitem
,
5152 start_chat_menuitem
):
5153 widget
.set_sensitive(False)
5155 xml
= gtkgui_helpers
.get_glade('zeroconf_context_menu.glade')
5156 account_context_menu
= xml
.get_widget('zeroconf_context_menu')
5158 status_menuitem
= xml
.get_widget('status_menuitem')
5159 zeroconf_properties_menuitem
= xml
.get_widget(
5160 'zeroconf_properties_menuitem')
5161 sub_menu
= gtk
.Menu()
5162 status_menuitem
.set_submenu(sub_menu
)
5164 for show
in ('online', 'away', 'dnd', 'invisible'):
5165 uf_show
= helpers
.get_uf_show(show
, use_mnemonic
=True)
5166 item
= gtk
.ImageMenuItem(uf_show
)
5167 icon
= state_images
[show
]
5168 item
.set_image(icon
)
5169 sub_menu
.append(item
)
5170 item
.connect('activate', self
.change_status
, account
, show
)
5172 item
= gtk
.SeparatorMenuItem()
5173 sub_menu
.append(item
)
5175 item
= gtk
.ImageMenuItem(_('_Change Status Message'))
5176 path
= os
.path
.join(gajim
.DATA_DIR
, 'pixmaps', 'kbd_input.png')
5178 img
.set_from_file(path
)
5180 sub_menu
.append(item
)
5181 item
.connect('activate', self
.on_change_status_message_activate
,
5183 if gajim
.connections
[account
].connected
< 2:
5184 item
.set_sensitive(False)
5186 uf_show
= helpers
.get_uf_show('offline', use_mnemonic
=True)
5187 item
= gtk
.ImageMenuItem(uf_show
)
5188 icon
= state_images
['offline']
5189 item
.set_image(icon
)
5190 sub_menu
.append(item
)
5191 item
.connect('activate', self
.change_status
, account
, 'offline')
5193 zeroconf_properties_menuitem
.connect('activate',
5194 self
.on_zeroconf_properties
, account
)
5195 #gc_sub_menu = gtk.Menu() # gc is always a submenu
5196 #join_group_chat_menuitem.set_submenu(gc_sub_menu)
5197 #self.add_bookmarks_list(gc_sub_menu, account)
5198 #new_message_menuitem.connect('activate',
5199 # self.on_new_message_menuitem_activate, account)
5201 # make some items insensitive if account is offline
5202 #if gajim.connections[account].connected < 2:
5203 # for widget in [join_group_chat_menuitem, new_message_menuitem]:
5204 # widget.set_sensitive(False)
5205 # new_message_menuitem.set_sensitive(False)
5207 return account_context_menu
5209 def make_account_menu(self
, event
, titer
):
5210 '''Make account's popup menu'''
5211 model
= self
.modelfilter
5212 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5214 if account
!= 'all': # not in merged mode
5215 menu
= self
.build_account_menu(account
)
5218 iconset
= gajim
.config
.get('iconset')
5219 path
= os
.path
.join(helpers
.get_iconset_path(iconset
), '16x16')
5220 accounts
= [] # Put accounts in a list to sort them
5221 for account
in gajim
.connections
:
5222 accounts
.append(account
)
5224 for account
in accounts
:
5225 state_images
= gtkgui_helpers
.load_iconset(path
)
5226 item
= gtk
.ImageMenuItem(account
)
5227 show
= gajim
.SHOW_LIST
[gajim
.connections
[account
].connected
]
5228 icon
= state_images
[show
]
5229 item
.set_image(icon
)
5230 account_menu
= self
.build_account_menu(account
)
5231 item
.set_submenu(account_menu
)
5234 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5236 menu
.attach_to_widget(self
.tree
, None)
5237 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
5239 menu
.popup(None, None, None, event_button
, event
.time
)
5241 def make_group_menu(self
, event
, titer
):
5242 '''Make group's popup menu'''
5243 model
= self
.modelfilter
5244 path
= model
.get_path(titer
)
5245 group
= model
[titer
][C_JID
].decode('utf-8')
5246 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5248 list_
= [] # list of (jid, account) tuples
5249 list_online
= [] # list of (jid, account) tuples
5251 group
= model
[titer
][C_JID
]
5252 for jid
in gajim
.contacts
.get_jid_list(account
):
5253 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
5255 if group
in contact
.get_shown_groups():
5256 if contact
.show
not in ('offline', 'error'):
5257 list_online
.append((contact
, account
))
5258 list_
.append((contact
, account
))
5261 # Make special context menu if group is Groupchats
5262 if group
== _('Groupchats'):
5263 maximize_menuitem
= gtk
.ImageMenuItem(_('_Maximize All'))
5264 icon
= gtk
.image_new_from_stock(gtk
.STOCK_GOTO_TOP
, gtk
.ICON_SIZE_MENU
)
5265 maximize_menuitem
.set_image(icon
)
5266 maximize_menuitem
.connect('activate', self
.on_all_groupchat_maximized
,\
5268 menu
.append(maximize_menuitem
)
5270 # Send Group Message
5271 send_group_message_item
= gtk
.ImageMenuItem(_('Send Group M_essage'))
5272 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NEW
, gtk
.ICON_SIZE_MENU
)
5273 send_group_message_item
.set_image(icon
)
5275 send_group_message_submenu
= gtk
.Menu()
5276 send_group_message_item
.set_submenu(send_group_message_submenu
)
5277 menu
.append(send_group_message_item
)
5279 group_message_to_all_item
= gtk
.MenuItem(_('To all users'))
5280 send_group_message_submenu
.append(group_message_to_all_item
)
5282 group_message_to_all_online_item
= gtk
.MenuItem(
5283 _('To all online users'))
5284 send_group_message_submenu
.append(group_message_to_all_online_item
)
5286 group_message_to_all_online_item
.connect('activate',
5287 self
.on_send_single_message_menuitem_activate
, account
, list_online
)
5288 group_message_to_all_item
.connect('activate',
5289 self
.on_send_single_message_menuitem_activate
, account
, list_
)
5292 invite_menuitem
= gtk
.ImageMenuItem(_('In_vite to'))
5293 muc_icon
= gtkgui_helpers
.load_icon('muc_active')
5295 invite_menuitem
.set_image(muc_icon
)
5297 gui_menu_builder
.build_invite_submenu(invite_menuitem
, list_online
)
5298 menu
.append(invite_menuitem
)
5300 # Send Custom Status
5301 send_custom_status_menuitem
= gtk
.ImageMenuItem(
5302 _('Send Cus_tom Status'))
5303 # add a special img for this menuitem
5304 if helpers
.group_is_blocked(account
, group
):
5305 send_custom_status_menuitem
.set_image(gtkgui_helpers
.load_icon(
5307 send_custom_status_menuitem
.set_sensitive(False)
5309 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NETWORK
,
5311 send_custom_status_menuitem
.set_image(icon
)
5312 status_menuitems
= gtk
.Menu()
5313 send_custom_status_menuitem
.set_submenu(status_menuitems
)
5314 iconset
= gajim
.config
.get('iconset')
5315 path
= os
.path
.join(helpers
.get_iconset_path(iconset
), '16x16')
5316 for s
in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
5317 # icon MUST be different instance for every item
5318 state_images
= gtkgui_helpers
.load_iconset(path
)
5319 status_menuitem
= gtk
.ImageMenuItem(helpers
.get_uf_show(s
))
5320 status_menuitem
.connect('activate', self
.on_send_custom_status
,
5322 icon
= state_images
[s
]
5323 status_menuitem
.set_image(icon
)
5324 status_menuitems
.append(status_menuitem
)
5325 menu
.append(send_custom_status_menuitem
)
5327 # there is no singlemessage and custom status for zeroconf
5328 if gajim
.config
.get_per('accounts', account
, 'is_zeroconf'):
5329 send_custom_status_menuitem
.set_sensitive(False)
5330 send_group_message_item
.set_sensitive(False)
5332 if not group
in helpers
.special_groups
:
5333 item
= gtk
.SeparatorMenuItem() # separator
5337 rename_item
= gtk
.ImageMenuItem(_('Re_name'))
5338 # add a special img for rename menuitem
5339 path_to_kbd_input_img
= os
.path
.join(gajim
.DATA_DIR
, 'pixmaps',
5342 img
.set_from_file(path_to_kbd_input_img
)
5343 rename_item
.set_image(img
)
5344 menu
.append(rename_item
)
5345 rename_item
.connect('activate', self
.on_rename
, 'group', group
,
5351 for g_account
in gajim
.connections
:
5352 if helpers
.group_is_blocked(g_account
, group
):
5355 if helpers
.group_is_blocked(account
, group
):
5358 if is_blocked
and gajim
.connections
[account
].privacy_rules_supported
:
5359 unblock_menuitem
= gtk
.ImageMenuItem(_('_Unblock'))
5360 icon
= gtk
.image_new_from_stock(gtk
.STOCK_STOP
, gtk
.ICON_SIZE_MENU
)
5361 unblock_menuitem
.set_image(icon
)
5362 unblock_menuitem
.connect('activate', self
.on_unblock
, list_
, group
)
5363 menu
.append(unblock_menuitem
)
5365 block_menuitem
= gtk
.ImageMenuItem(_('_Block'))
5366 icon
= gtk
.image_new_from_stock(gtk
.STOCK_STOP
, gtk
.ICON_SIZE_MENU
)
5367 block_menuitem
.set_image(icon
)
5368 block_menuitem
.connect('activate', self
.on_block
, list_
, group
)
5369 menu
.append(block_menuitem
)
5370 if not gajim
.connections
[account
].privacy_rules_supported
:
5371 block_menuitem
.set_sensitive(False)
5374 remove_item
= gtk
.ImageMenuItem(_('_Remove'))
5375 icon
= gtk
.image_new_from_stock(gtk
.STOCK_REMOVE
, gtk
.ICON_SIZE_MENU
)
5376 remove_item
.set_image(icon
)
5377 menu
.append(remove_item
)
5378 remove_item
.connect('activate', self
.on_remove_group_item_activated
,
5381 # unsensitive if account is not connected
5382 if gajim
.connections
[account
].connected
< 2:
5383 rename_item
.set_sensitive(False)
5385 # General group cannot be changed
5386 if group
== _('General'):
5387 rename_item
.set_sensitive(False)
5388 block_menuitem
.set_sensitive(False)
5389 remove_item
.set_sensitive(False)
5391 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5393 menu
.attach_to_widget(self
.tree
, None)
5394 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
5396 menu
.popup(None, None, None, event_button
, event
.time
)
5398 def make_contact_menu(self
, event
, titer
):
5399 '''Make contact\'s popup menu'''
5400 model
= self
.modelfilter
5401 jid
= model
[titer
][C_JID
].decode('utf-8')
5402 tree_path
= model
.get_path(titer
)
5403 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5404 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
5405 menu
= gui_menu_builder
.get_contact_menu(contact
, account
)
5406 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5407 menu
.attach_to_widget(self
.tree
, None)
5408 menu
.popup(None, None, None, event_button
, event
.time
)
5410 def make_multiple_contact_menu(self
, event
, iters
):
5411 '''Make group's popup menu'''
5412 model
= self
.modelfilter
5413 list_
= [] # list of (jid, account) tuples
5414 one_account_offline
= False
5416 privacy_rules_supported
= True
5418 jid
= model
[titer
][C_JID
].decode('utf-8')
5419 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5420 if gajim
.connections
[account
].connected
< 2:
5421 one_account_offline
= True
5422 if not gajim
.connections
[account
].privacy_rules_supported
:
5423 privacy_rules_supported
= False
5424 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
,
5426 if helpers
.jid_is_blocked(account
, jid
):
5428 list_
.append((contact
, account
))
5432 for (contact
, current_account
) in list_
:
5433 # check that we use the same account for every sender
5434 if account
is not None and account
!= current_account
:
5437 account
= current_account
5438 if account
is not None:
5439 send_group_message_item
= gtk
.ImageMenuItem(_('Send Group M_essage'))
5440 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NEW
, gtk
.ICON_SIZE_MENU
)
5441 send_group_message_item
.set_image(icon
)
5442 menu
.append(send_group_message_item
)
5443 send_group_message_item
.connect('activate',
5444 self
.on_send_single_message_menuitem_activate
, account
, list_
)
5446 # Invite to Groupchat
5447 invite_item
= gtk
.ImageMenuItem(_('In_vite to'))
5448 muc_icon
= gtkgui_helpers
.load_icon('muc_active')
5450 invite_item
.set_image(muc_icon
)
5452 gui_menu_builder
.build_invite_submenu(invite_item
, list_
)
5453 menu
.append(invite_item
)
5455 item
= gtk
.SeparatorMenuItem() # separator
5458 # Manage Transport submenu
5459 item
= gtk
.ImageMenuItem(_('_Manage Contacts'))
5460 icon
= gtk
.image_new_from_stock(gtk
.STOCK_PROPERTIES
, gtk
.ICON_SIZE_MENU
)
5461 item
.set_image(icon
)
5462 manage_contacts_submenu
= gtk
.Menu()
5463 item
.set_submenu(manage_contacts_submenu
)
5467 edit_groups_item
= gtk
.ImageMenuItem(_('Edit _Groups'))
5468 icon
= gtk
.image_new_from_stock(gtk
.STOCK_EDIT
, gtk
.ICON_SIZE_MENU
)
5469 edit_groups_item
.set_image(icon
)
5470 manage_contacts_submenu
.append(edit_groups_item
)
5471 edit_groups_item
.connect('activate', self
.on_edit_groups
, list_
)
5473 item
= gtk
.SeparatorMenuItem() # separator
5474 manage_contacts_submenu
.append(item
)
5477 if is_blocked
and privacy_rules_supported
:
5478 unblock_menuitem
= gtk
.ImageMenuItem(_('_Unblock'))
5479 icon
= gtk
.image_new_from_stock(gtk
.STOCK_STOP
, gtk
.ICON_SIZE_MENU
)
5480 unblock_menuitem
.set_image(icon
)
5481 unblock_menuitem
.connect('activate', self
.on_unblock
, list_
)
5482 manage_contacts_submenu
.append(unblock_menuitem
)
5484 block_menuitem
= gtk
.ImageMenuItem(_('_Block'))
5485 icon
= gtk
.image_new_from_stock(gtk
.STOCK_STOP
, gtk
.ICON_SIZE_MENU
)
5486 block_menuitem
.set_image(icon
)
5487 block_menuitem
.connect('activate', self
.on_block
, list_
)
5488 manage_contacts_submenu
.append(block_menuitem
)
5490 if not privacy_rules_supported
:
5491 block_menuitem
.set_sensitive(False)
5494 remove_item
= gtk
.ImageMenuItem(_('_Remove'))
5495 icon
= gtk
.image_new_from_stock(gtk
.STOCK_REMOVE
, gtk
.ICON_SIZE_MENU
)
5496 remove_item
.set_image(icon
)
5497 manage_contacts_submenu
.append(remove_item
)
5498 remove_item
.connect('activate', self
.on_req_usub
, list_
)
5499 # unsensitive remove if one account is not connected
5500 if one_account_offline
:
5501 remove_item
.set_sensitive(False)
5503 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5505 menu
.attach_to_widget(self
.tree
, None)
5506 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
5508 menu
.popup(None, None, None, event_button
, event
.time
)
5510 def make_transport_menu(self
, event
, titer
):
5511 '''Make transport\'s popup menu'''
5512 model
= self
.modelfilter
5513 jid
= model
[titer
][C_JID
].decode('utf-8')
5514 path
= model
.get_path(titer
)
5515 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5516 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
5519 # Send single message
5520 item
= gtk
.ImageMenuItem(_('Send Single Message'))
5521 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NEW
, gtk
.ICON_SIZE_MENU
)
5522 item
.set_image(icon
)
5523 item
.connect('activate',
5524 self
.on_send_single_message_menuitem_activate
, account
, contact
)
5528 if helpers
.jid_is_blocked(account
, jid
):
5531 # Send Custom Status
5532 send_custom_status_menuitem
= gtk
.ImageMenuItem(_('Send Cus_tom Status'))
5533 # add a special img for this menuitem
5535 send_custom_status_menuitem
.set_image(gtkgui_helpers
.load_icon(
5537 send_custom_status_menuitem
.set_sensitive(False)
5539 if account
in gajim
.interface
.status_sent_to_users
and \
5540 jid
in gajim
.interface
.status_sent_to_users
[account
]:
5541 send_custom_status_menuitem
.set_image(gtkgui_helpers
.load_icon(
5542 gajim
.interface
.status_sent_to_users
[account
][jid
]))
5544 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NETWORK
,
5546 send_custom_status_menuitem
.set_image(icon
)
5547 status_menuitems
= gtk
.Menu()
5548 send_custom_status_menuitem
.set_submenu(status_menuitems
)
5549 iconset
= gajim
.config
.get('iconset')
5550 path
= os
.path
.join(helpers
.get_iconset_path(iconset
), '16x16')
5551 for s
in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
5552 # icon MUST be different instance for every item
5553 state_images
= gtkgui_helpers
.load_iconset(path
)
5554 status_menuitem
= gtk
.ImageMenuItem(helpers
.get_uf_show(s
))
5555 status_menuitem
.connect('activate', self
.on_send_custom_status
,
5556 [(contact
, account
)], s
)
5557 icon
= state_images
[s
]
5558 status_menuitem
.set_image(icon
)
5559 status_menuitems
.append(status_menuitem
)
5560 menu
.append(send_custom_status_menuitem
)
5562 item
= gtk
.SeparatorMenuItem() # separator
5566 item
= gtk
.ImageMenuItem(_('Execute Command...'))
5567 icon
= gtk
.image_new_from_stock(gtk
.STOCK_EXECUTE
, gtk
.ICON_SIZE_MENU
)
5568 item
.set_image(icon
)
5570 item
.connect('activate', self
.on_execute_command
, contact
, account
,
5572 if gajim
.account_is_disconnected(account
):
5573 item
.set_sensitive(False)
5575 # Manage Transport submenu
5576 item
= gtk
.ImageMenuItem(_('_Manage Transport'))
5577 icon
= gtk
.image_new_from_stock(gtk
.STOCK_PROPERTIES
, gtk
.ICON_SIZE_MENU
)
5578 item
.set_image(icon
)
5579 manage_transport_submenu
= gtk
.Menu()
5580 item
.set_submenu(manage_transport_submenu
)
5584 item
= gtk
.ImageMenuItem(_('_Modify Transport'))
5585 icon
= gtk
.image_new_from_stock(gtk
.STOCK_PREFERENCES
, gtk
.ICON_SIZE_MENU
)
5586 item
.set_image(icon
)
5587 manage_transport_submenu
.append(item
)
5588 item
.connect('activate', self
.on_edit_agent
, contact
, account
)
5589 if gajim
.account_is_disconnected(account
):
5590 item
.set_sensitive(False)
5593 item
= gtk
.ImageMenuItem(_('_Rename'))
5594 # add a special img for rename menuitem
5595 path_to_kbd_input_img
= os
.path
.join(gajim
.DATA_DIR
, 'pixmaps',
5598 img
.set_from_file(path_to_kbd_input_img
)
5600 manage_transport_submenu
.append(item
)
5601 item
.connect('activate', self
.on_rename
, 'agent', jid
, account
)
5602 if gajim
.account_is_disconnected(account
):
5603 item
.set_sensitive(False)
5605 item
= gtk
.SeparatorMenuItem() # separator
5606 manage_transport_submenu
.append(item
)
5610 item
= gtk
.ImageMenuItem(_('_Unblock'))
5611 item
.connect('activate', self
.on_unblock
, [(contact
, account
)])
5613 item
= gtk
.ImageMenuItem(_('_Block'))
5614 item
.connect('activate', self
.on_block
, [(contact
, account
)])
5616 icon
= gtk
.image_new_from_stock(gtk
.STOCK_STOP
, gtk
.ICON_SIZE_MENU
)
5617 item
.set_image(icon
)
5618 manage_transport_submenu
.append(item
)
5619 if gajim
.account_is_disconnected(account
):
5620 item
.set_sensitive(False)
5623 item
= gtk
.ImageMenuItem(_('_Remove'))
5624 icon
= gtk
.image_new_from_stock(gtk
.STOCK_REMOVE
, gtk
.ICON_SIZE_MENU
)
5625 item
.set_image(icon
)
5626 manage_transport_submenu
.append(item
)
5627 item
.connect('activate', self
.on_remove_agent
, [(contact
, account
)])
5628 if gajim
.account_is_disconnected(account
):
5629 item
.set_sensitive(False)
5631 item
= gtk
.SeparatorMenuItem() # separator
5635 information_menuitem
= gtk
.ImageMenuItem(_('_Information'))
5636 icon
= gtk
.image_new_from_stock(gtk
.STOCK_INFO
, gtk
.ICON_SIZE_MENU
)
5637 information_menuitem
.set_image(icon
)
5638 menu
.append(information_menuitem
)
5639 information_menuitem
.connect('activate', self
.on_info
, contact
, account
)
5642 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5644 menu
.attach_to_widget(self
.tree
, None)
5645 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
5647 menu
.popup(None, None, None, event_button
, event
.time
)
5649 def make_groupchat_menu(self
, event
, titer
):
5650 model
= self
.modelfilter
5652 jid
= model
[titer
][C_JID
].decode('utf-8')
5653 account
= model
[titer
][C_ACCOUNT
].decode('utf-8')
5654 contact
= gajim
.contacts
.get_contact_with_highest_priority(account
, jid
)
5657 if jid
in gajim
.interface
.minimized_controls
[account
]:
5658 maximize_menuitem
= gtk
.ImageMenuItem(_('_Maximize'))
5659 icon
= gtk
.image_new_from_stock(gtk
.STOCK_GOTO_TOP
, gtk
.ICON_SIZE_MENU
)
5660 maximize_menuitem
.set_image(icon
)
5661 maximize_menuitem
.connect('activate', self
.on_groupchat_maximized
, \
5663 menu
.append(maximize_menuitem
)
5665 if not gajim
.gc_connected
[account
].get(jid
, False):
5666 connect_menuitem
= gtk
.ImageMenuItem(_('_Reconnect'))
5667 connect_icon
= gtk
.image_new_from_stock(gtk
.STOCK_CONNECT
, \
5669 connect_menuitem
.set_image(connect_icon
)
5670 connect_menuitem
.connect('activate', self
.on_reconnect
, jid
, account
)
5671 menu
.append(connect_menuitem
)
5672 disconnect_menuitem
= gtk
.ImageMenuItem(_('_Disconnect'))
5673 disconnect_icon
= gtk
.image_new_from_stock(gtk
.STOCK_DISCONNECT
, \
5675 disconnect_menuitem
.set_image(disconnect_icon
)
5676 disconnect_menuitem
.connect('activate', self
.on_disconnect
, jid
, account
)
5677 menu
.append(disconnect_menuitem
)
5679 item
= gtk
.SeparatorMenuItem() # separator
5682 history_menuitem
= gtk
.ImageMenuItem(_('_History'))
5683 history_icon
= gtk
.image_new_from_stock(gtk
.STOCK_JUSTIFY_FILL
, \
5685 history_menuitem
.set_image(history_icon
)
5686 history_menuitem
.connect('activate', self
.on_history
, \
5688 menu
.append(history_menuitem
)
5690 event_button
= gtkgui_helpers
.get_possible_button_event(event
)
5692 menu
.attach_to_widget(self
.tree
, None)
5693 menu
.connect('selection-done', gtkgui_helpers
.destroy_widget
)
5695 menu
.popup(None, None, None, event_button
, event
.time
)
5697 def get_and_connect_advanced_menuitem_menu(self
, account
):
5698 '''adds FOR ACCOUNT options'''
5699 xml
= gtkgui_helpers
.get_glade('advanced_menuitem_menu.glade')
5700 advanced_menuitem_menu
= xml
.get_widget('advanced_menuitem_menu')
5702 xml_console_menuitem
= xml
.get_widget('xml_console_menuitem')
5703 privacy_lists_menuitem
= xml
.get_widget('privacy_lists_menuitem')
5704 administrator_menuitem
= xml
.get_widget('administrator_menuitem')
5705 send_server_message_menuitem
= xml
.get_widget(
5706 'send_server_message_menuitem')
5707 set_motd_menuitem
= xml
.get_widget('set_motd_menuitem')
5708 update_motd_menuitem
= xml
.get_widget('update_motd_menuitem')
5709 delete_motd_menuitem
= xml
.get_widget('delete_motd_menuitem')
5711 xml_console_menuitem
.connect('activate',
5712 self
.on_xml_console_menuitem_activate
, account
)
5714 if gajim
.connections
[account
] and gajim
.connections
[account
].\
5715 privacy_rules_supported
:
5716 privacy_lists_menuitem
.connect('activate',
5717 self
.on_privacy_lists_menuitem_activate
, account
)
5719 privacy_lists_menuitem
.set_sensitive(False)
5721 if gajim
.connections
[account
].is_zeroconf
:
5722 administrator_menuitem
.set_sensitive(False)
5723 send_server_message_menuitem
.set_sensitive(False)
5724 set_motd_menuitem
.set_sensitive(False)
5725 update_motd_menuitem
.set_sensitive(False)
5726 delete_motd_menuitem
.set_sensitive(False)
5728 send_server_message_menuitem
.connect('activate',
5729 self
.on_send_server_message_menuitem_activate
, account
)
5731 set_motd_menuitem
.connect('activate',
5732 self
.on_set_motd_menuitem_activate
, account
)
5734 update_motd_menuitem
.connect('activate',
5735 self
.on_update_motd_menuitem_activate
, account
)
5737 delete_motd_menuitem
.connect('activate',
5738 self
.on_delete_motd_menuitem_activate
, account
)
5740 advanced_menuitem_menu
.show_all()
5742 return advanced_menuitem_menu
5744 def add_history_manager_menuitem(self
, menu
):
5745 '''adds a seperator and History Manager menuitem BELOW for account
5747 item
= gtk
.SeparatorMenuItem() # separator
5751 item
= gtk
.ImageMenuItem(_('History Manager'))
5752 icon
= gtk
.image_new_from_stock(gtk
.STOCK_JUSTIFY_FILL
,
5754 item
.set_image(icon
)
5756 item
.connect('activate', self
.on_history_manager_menuitem_activate
)
5758 def add_bookmarks_list(self
, gc_sub_menu
, account
):
5759 '''Show join new group chat item and bookmarks list for an account'''
5760 item
= gtk
.ImageMenuItem(_('_Join New Group Chat'))
5761 icon
= gtk
.image_new_from_stock(gtk
.STOCK_NEW
, gtk
.ICON_SIZE_MENU
)
5762 item
.set_image(icon
)
5763 item
.connect('activate', self
.on_join_gc_activate
, account
)
5764 gc_sub_menu
.append(item
)
5766 # user has at least one bookmark
5767 if len(gajim
.connections
[account
].bookmarks
) > 0:
5768 item
= gtk
.SeparatorMenuItem() # separator
5769 gc_sub_menu
.append(item
)
5771 for bookmark
in gajim
.connections
[account
].bookmarks
:
5772 item
= gtk
.MenuItem(bookmark
['name'], False) # Do not use underline
5773 item
.connect('activate', self
.on_bookmark_menuitem_activate
,
5775 gc_sub_menu
.append(item
)
5777 def set_actions_menu_needs_rebuild(self
):
5778 self
.actions_menu_needs_rebuild
= True
5780 def show_appropriate_context_menu(self
, event
, iters
):
5781 # iters must be all of the same type
5782 model
= self
.modelfilter
5783 type_
= model
[iters
[0]][C_TYPE
]
5784 for titer
in iters
[1:]:
5785 if model
[titer
][C_TYPE
] != type_
:
5787 if type_
== 'group' and len(iters
) == 1:
5788 self
.make_group_menu(event
, iters
[0])
5789 if type_
== 'groupchat' and len(iters
) == 1:
5790 self
.make_groupchat_menu(event
, iters
[0])
5791 elif type_
== 'agent' and len(iters
) == 1:
5792 self
.make_transport_menu(event
, iters
[0])
5793 elif type_
in ('contact', 'self_contact') and len(iters
) == 1:
5794 self
.make_contact_menu(event
, iters
[0])
5795 elif type_
== 'contact':
5796 self
.make_multiple_contact_menu(event
, iters
)
5797 elif type_
== 'account' and len(iters
) == 1:
5798 self
.make_account_menu(event
, iters
[0])
5800 def show_treeview_menu(self
, event
):
5802 model
, list_of_paths
= self
.tree
.get_selection().get_selected_rows()
5804 self
.tree
.get_selection().unselect_all()
5806 if not len(list_of_paths
):
5807 # no row is selected
5809 if len(list_of_paths
) > 1:
5811 for path
in list_of_paths
:
5812 iters
.append(model
.get_iter(path
))
5814 path
= list_of_paths
[0]
5815 iters
= [model
.get_iter(path
)]
5816 self
.show_appropriate_context_menu(event
, iters
)
5820 ################################################################################
5822 ################################################################################
5825 self
.filtering
= False
5826 self
.xml
= gtkgui_helpers
.get_glade('roster_window.glade')
5827 self
.window
= self
.xml
.get_widget('roster_window')
5828 self
.hpaned
= self
.xml
.get_widget('roster_hpaned')
5829 gajim
.interface
.msg_win_mgr
= MessageWindowMgr(self
.window
, self
.hpaned
)
5830 gajim
.interface
.msg_win_mgr
.connect('window-delete',
5831 self
.on_message_window_delete
)
5832 self
.advanced_menus
= [] # We keep them to destroy them
5833 if gajim
.config
.get('roster_window_skip_taskbar'):
5834 self
.window
.set_property('skip-taskbar-hint', True)
5835 self
.tree
= self
.xml
.get_widget('roster_treeview')
5836 sel
= self
.tree
.get_selection()
5837 sel
.set_mode(gtk
.SELECTION_MULTIPLE
)
5838 #sel.connect('changed',
5839 # self.on_treeview_selection_changed)
5841 self
._last
_selected
_contact
= [] # holds a list of (jid, account) tupples
5842 self
.transports_state_images
= {'16': {}, '32': {}, 'opened': {},
5845 self
.last_save_dir
= None
5846 self
.editing_path
= None # path of row with cell in edit mode
5847 self
.add_new_contact_handler_id
= False
5848 self
.service_disco_handler_id
= False
5849 self
.new_chat_menuitem_handler_id
= False
5850 self
.single_message_menuitem_handler_id
= False
5851 self
.profile_avatar_menuitem_handler_id
= False
5852 self
.actions_menu_needs_rebuild
= True
5853 self
.regroup
= gajim
.config
.get('mergeaccounts')
5854 self
.clicked_path
= None # Used remember on wich row we clicked
5855 if len(gajim
.connections
) < 2: # Do not merge accounts if only one exists
5856 self
.regroup
= False
5857 #FIXME: When list_accel_closures will be wrapped in pygtk
5858 # no need of this variable
5859 self
.have_new_chat_accel
= False # Is the "Ctrl+N" shown ?
5860 gtkgui_helpers
.resize_window(self
.window
,
5861 gajim
.config
.get('roster_width'),
5862 gajim
.config
.get('roster_height'))
5863 gtkgui_helpers
.move_window(self
.window
,
5864 gajim
.config
.get('roster_x-position'),
5865 gajim
.config
.get('roster_y-position'))
5867 self
.popups_notification_height
= 0
5868 self
.popup_notification_windows
= []
5870 # Remove contact from roster when last event opened
5871 # { (contact, account): { backend: boolean }
5872 self
.contacts_to_be_removed
= {}
5873 gajim
.events
.event_removed_subscribe(self
.on_event_removed
)
5875 # when this value become 0 we quit main application. If it's more than 0
5876 # it means we are waiting for this number of accounts to disconnect before
5878 self
.quit_on_next_offline
= -1
5880 # uf_show, img, show, sensitive
5881 liststore
= gtk
.ListStore(str, gtk
.Image
, str, bool)
5882 self
.status_combobox
= self
.xml
.get_widget('status_combobox')
5884 cell
= cell_renderer_image
.CellRendererImage(0, 1)
5885 self
.status_combobox
.pack_start(cell
, False)
5887 # img to show is in in 2nd column of liststore
5888 self
.status_combobox
.add_attribute(cell
, 'image', 1)
5889 # if it will be sensitive or not it is in the fourth column
5890 # all items in the 'row' must have sensitive to False
5891 # if we want False (so we add it for img_cell too)
5892 self
.status_combobox
.add_attribute(cell
, 'sensitive', 3)
5894 cell
= gtk
.CellRendererText()
5895 cell
.set_property('xpad', 5) # padding for status text
5896 self
.status_combobox
.pack_start(cell
, True)
5897 # text to show is in in first column of liststore
5898 self
.status_combobox
.add_attribute(cell
, 'text', 0)
5899 # if it will be sensitive or not it is in the fourth column
5900 self
.status_combobox
.add_attribute(cell
, 'sensitive', 3)
5902 self
.status_combobox
.set_row_separator_func(self
._iter
_is
_separator
)
5904 for show
in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
5905 uf_show
= helpers
.get_uf_show(show
)
5906 liststore
.append([uf_show
, gajim
.interface
.jabber_state_images
['16'][
5908 # Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
5909 liststore
.append(['SEPARATOR', None, '', True])
5911 path
= os
.path
.join(gajim
.DATA_DIR
, 'pixmaps', 'kbd_input.png')
5913 img
.set_from_file(path
)
5914 # sensitivity to False because by default we're offline
5915 self
.status_message_menuitem_iter
= liststore
.append(
5916 [_('Change Status Message...'), img
, '', False])
5917 # Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
5918 liststore
.append(['SEPARATOR', None, '', True])
5920 uf_show
= helpers
.get_uf_show('offline')
5921 liststore
.append([uf_show
, gajim
.interface
.jabber_state_images
['16'][
5922 'offline'], 'offline', True])
5924 status_combobox_items
= ['online', 'chat', 'away', 'xa', 'dnd',
5925 'invisible', 'separator1', 'change_status_msg', 'separator2',
5927 self
.status_combobox
.set_model(liststore
)
5929 # default to offline
5930 number_of_menuitem
= status_combobox_items
.index('offline')
5931 self
.status_combobox
.set_active(number_of_menuitem
)
5933 # holds index to previously selected item so if "change status message..."
5934 # is selected we can fallback to previously selected item and not stay
5935 # with that item selected
5936 self
.previous_status_combobox_active
= number_of_menuitem
5938 showOffline
= gajim
.config
.get('showoffline')
5939 showOnlyChatAndOnline
= gajim
.config
.get('show_only_chat_and_online')
5941 w
= self
.xml
.get_widget('show_offline_contacts_menuitem')
5942 w
.set_active(showOffline
)
5943 if showOnlyChatAndOnline
:
5944 w
.set_sensitive(False)
5946 w
= self
.xml
.get_widget('show_only_active_contacts_menuitem')
5947 w
.set_active(showOnlyChatAndOnline
)
5949 w
.set_sensitive(False)
5951 show_transports_group
= gajim
.config
.get('show_transports_group')
5952 self
.xml
.get_widget('show_transports_menuitem').set_active(
5953 show_transports_group
)
5955 self
.xml
.get_widget('show_roster_menuitem').set_active(True)
5959 # this col has 3 cells:
5960 # first one img, second one text, third is sec pixbuf
5961 col
= gtk
.TreeViewColumn()
5963 def add_avatar_renderer():
5964 render_pixbuf
= gtk
.CellRendererPixbuf() # avatar img
5965 col
.pack_start(render_pixbuf
, expand
=False)
5966 col
.add_attribute(render_pixbuf
, 'pixbuf',
5968 col
.set_cell_data_func(render_pixbuf
,
5969 self
._fill
_avatar
_pixbuf
_renderer
, None)
5971 if gajim
.config
.get('avatar_position_in_roster') == 'left':
5972 add_avatar_renderer()
5974 render_image
= cell_renderer_image
.CellRendererImage(0, 0)
5976 col
.pack_start(render_image
, expand
=False)
5977 col
.add_attribute(render_image
, 'image', C_IMG
)
5978 col
.set_cell_data_func(render_image
, self
._iconCellDataFunc
, None)
5980 render_text
= gtk
.CellRendererText() # contact or group or account name
5981 render_text
.set_property('ellipsize', pango
.ELLIPSIZE_END
)
5982 col
.pack_start(render_text
, expand
=True)
5983 col
.add_attribute(render_text
, 'markup', C_NAME
) # where we hold the name
5984 col
.set_cell_data_func(render_text
, self
._nameCellDataFunc
, None)
5986 render_pixbuf
= gtk
.CellRendererPixbuf()
5987 col
.pack_start(render_pixbuf
, expand
=False)
5988 col
.add_attribute(render_pixbuf
, 'pixbuf', C_MOOD_PIXBUF
)
5989 col
.set_cell_data_func(render_pixbuf
,
5990 self
._fill
_mood
_pixbuf
_renderer
, None)
5992 render_pixbuf
= gtk
.CellRendererPixbuf()
5993 col
.pack_start(render_pixbuf
, expand
=False)
5994 col
.add_attribute(render_pixbuf
, 'pixbuf', C_ACTIVITY_PIXBUF
)
5995 col
.set_cell_data_func(render_pixbuf
,
5996 self
._fill
_activity
_pixbuf
_renderer
, None)
5998 render_pixbuf
= gtk
.CellRendererPixbuf()
5999 col
.pack_start(render_pixbuf
, expand
=False)
6000 col
.add_attribute(render_pixbuf
, 'pixbuf', C_TUNE_PIXBUF
)
6001 col
.set_cell_data_func(render_pixbuf
,
6002 self
._fill
_tune
_pixbuf
_renderer
, None)
6004 if gajim
.config
.get('avatar_position_in_roster') == 'right':
6005 add_avatar_renderer()
6007 render_pixbuf
= gtk
.CellRendererPixbuf() # tls/ssl img
6008 col
.pack_start(render_pixbuf
, expand
=False)
6009 col
.add_attribute(render_pixbuf
, 'pixbuf', C_PADLOCK_PIXBUF
)
6010 col
.set_cell_data_func(render_pixbuf
,
6011 self
._fill
_padlock
_pixbuf
_renderer
, None)
6012 self
.tree
.append_column(col
)
6014 # do not show gtk arrows workaround
6015 col
= gtk
.TreeViewColumn()
6016 render_pixbuf
= gtk
.CellRendererPixbuf()
6017 col
.pack_start(render_pixbuf
, expand
=False)
6018 self
.tree
.append_column(col
)
6019 col
.set_visible(False)
6020 self
.tree
.set_expander_column(col
)
6022 # set search function
6023 self
.tree
.set_search_equal_func(self
._search
_roster
_func
)
6026 self
.TARGET_TYPE_URI_LIST
= 80
6027 TARGETS
= [('MY_TREE_MODEL_ROW',
6028 gtk
.TARGET_SAME_APP | gtk
.TARGET_SAME_WIDGET
, 0)]
6029 TARGETS2
= [('MY_TREE_MODEL_ROW', gtk
.TARGET_SAME_WIDGET
, 0),
6030 ('text/uri-list', 0, self
.TARGET_TYPE_URI_LIST
)]
6031 self
.tree
.enable_model_drag_source(gtk
.gdk
.BUTTON1_MASK
, TARGETS
,
6032 gtk
.gdk
.ACTION_DEFAULT | gtk
.gdk
.ACTION_MOVE | gtk
.gdk
.ACTION_COPY
)
6033 self
.tree
.enable_model_drag_dest(TARGETS2
, gtk
.gdk
.ACTION_DEFAULT
)
6034 self
.tree
.connect('drag_begin', self
.drag_begin
)
6035 self
.tree
.connect('drag_end', self
.drag_end
)
6036 self
.tree
.connect('drag_drop', self
.drag_drop
)
6037 self
.tree
.connect('drag_data_get', self
.drag_data_get_data
)
6038 self
.tree
.connect('drag_data_received', self
.drag_data_received_data
)
6039 self
.dragging
= False
6040 self
.xml
.signal_autoconnect(self
)
6041 self
.combobox_callback_active
= True
6043 self
.collapsed_rows
= gajim
.config
.get('collapsed_rows').split('\t')
6044 self
.tooltip
= tooltips
.RosterTooltip()
6045 # Workaroung: For strange reasons signal is behaving like row-changed
6046 self
._toggeling
_row
= False
6047 self
.setup_and_draw_roster()
6049 if gajim
.config
.get('show_roster_on_startup'):
6050 self
.window
.show_all()
6052 if not gajim
.config
.get('trayicon') or not \
6053 gajim
.interface
.systray_capabilities
:
6054 # cannot happen via GUI, but I put this incase user touches
6055 # config. without trayicon, he or she should see the roster!
6056 self
.window
.show_all()
6057 gajim
.config
.set('show_roster_on_startup', True)
6059 if len(gajim
.connections
) == 0: # if we have no account
6061 gajim
.interface
.instances
['account_creation_wizard'] = \
6062 config
.AccountCreationWizardWindow()
6063 # Open wizard only after roster is created, so we can make it transient
6064 # for the roster window
6065 gobject
.idle_add(_open_wizard
)
6066 if not gajim
.ZEROCONF_ACC_NAME
in gajim
.config
.get_per('accounts'):
6067 # Create zeroconf in config file
6068 from common
.zeroconf
import connection_zeroconf
6069 connection_zeroconf
.ConnectionZeroconf(gajim
.ZEROCONF_ACC_NAME
)