Quick fix: What does not exist cannot support anything. Fixes #4031. Fix variable referencing. Fixes #4076.
		
			
				
	
	
		
			5946 lines
		
	
	
	
		
			214 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			5946 lines
		
	
	
	
		
			214 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# -*- coding: utf-8 -*-
 | 
						||
##	roster_window.py
 | 
						||
##
 | 
						||
## Copyright (C) 2003-2007 Yann Leboulanger <asterix@lagaule.org>
 | 
						||
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem@gmail.com>
 | 
						||
##                         Travis Shirk <travis@pobox.com>
 | 
						||
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com>
 | 
						||
## Copyright (C) 2007 Lukas Petrovicky <lukas@petrovicky.net>
 | 
						||
## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com>
 | 
						||
## Copyright (C) 2007-2008 Stephan Erb <steve-e@h3c.de>
 | 
						||
##
 | 
						||
## This file is part of Gajim.
 | 
						||
##
 | 
						||
## Gajim is free software; you can redistribute it and/or modify
 | 
						||
## it under the terms of the GNU General Public License as published
 | 
						||
## by the Free Software Foundation; version 3 only.
 | 
						||
##
 | 
						||
## Gajim is distributed in the hope that it will be useful,
 | 
						||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						||
## GNU General Public License for more details.
 | 
						||
##
 | 
						||
## You should have received a copy of the GNU General Public License
 | 
						||
## along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
 | 
						||
##
 | 
						||
 | 
						||
import gtk
 | 
						||
import pango
 | 
						||
import gobject
 | 
						||
import os
 | 
						||
import sys
 | 
						||
import time
 | 
						||
 | 
						||
import common.sleepy
 | 
						||
import history_window
 | 
						||
import dialogs
 | 
						||
import vcard
 | 
						||
import config
 | 
						||
import disco
 | 
						||
import gtkgui_helpers
 | 
						||
import cell_renderer_image
 | 
						||
import tooltips
 | 
						||
import message_control
 | 
						||
import adhoc_commands
 | 
						||
import notify
 | 
						||
import features_window
 | 
						||
 | 
						||
from common import gajim
 | 
						||
from common import helpers
 | 
						||
from common import passwords
 | 
						||
from common.exceptions import GajimGeneralException
 | 
						||
from common import i18n
 | 
						||
 | 
						||
from message_window import MessageWindowMgr
 | 
						||
 | 
						||
from session import ChatControlSession
 | 
						||
 | 
						||
from common import dbus_support
 | 
						||
if dbus_support.supported:
 | 
						||
	from music_track_listener import MusicTrackListener
 | 
						||
	import dbus
 | 
						||
from lastfm_track_listener import LastFMTrackListener
 | 
						||
 | 
						||
from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
 | 
						||
 | 
						||
try:
 | 
						||
	from osx import syncmenu
 | 
						||
except ImportError:
 | 
						||
	pass
 | 
						||
 | 
						||
#(icon, name, type, jid, account, editable, second pixbuf)
 | 
						||
(
 | 
						||
C_IMG, # image to show state (online, new message etc)
 | 
						||
C_NAME, # cellrenderer text that holds contact nickame
 | 
						||
C_TYPE, # account, group or contact?
 | 
						||
C_JID, # the jid of the row
 | 
						||
C_ACCOUNT, # cellrenderer text that holds account name
 | 
						||
C_AVATAR_PIXBUF, # avatar_pixbuf
 | 
						||
C_PADLOCK_PIXBUF, # use for account row only
 | 
						||
) = range(7)
 | 
						||
 | 
						||
class RosterWindow:
 | 
						||
	'''Class for main window of the GTK+ interface'''
 | 
						||
 | 
						||
	def _get_account_iter(self, name, model = None):
 | 
						||
		''' Return the gtk.TreeIter of the given account or None if not found.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		name -- the account name
 | 
						||
		model -- the data model (default TreeFilterModel)
 | 
						||
 | 
						||
		'''
 | 
						||
		if not model:
 | 
						||
			model = self.modelfilter
 | 
						||
		if model is None:
 | 
						||
			return
 | 
						||
		account_iter = model.get_iter_root()
 | 
						||
		if self.regroup:
 | 
						||
			return account_iter
 | 
						||
		while account_iter:
 | 
						||
			account_name = model[account_iter][C_ACCOUNT]
 | 
						||
			if account_name and name == account_name.decode('utf-8'):
 | 
						||
				break
 | 
						||
			account_iter = model.iter_next(account_iter)
 | 
						||
		return account_iter
 | 
						||
 | 
						||
 | 
						||
	def _get_group_iter(self, name, account, account_iter = None, model = None):
 | 
						||
		''' Return the gtk.TreeIter of the given group or None if not found.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		name -- the group name
 | 
						||
		account -- the account name
 | 
						||
		account_iter -- the iter of the account the model (default None)
 | 
						||
		model -- the data model (default TreeFilterModel)
 | 
						||
 | 
						||
		'''
 | 
						||
		if not model:
 | 
						||
			model = self.modelfilter
 | 
						||
		if not account_iter:
 | 
						||
			account_iter = self._get_account_iter(account, model)
 | 
						||
		group_iter = model.iter_children(account_iter)
 | 
						||
		# C_NAME column contacts the pango escaped group name
 | 
						||
		while group_iter:
 | 
						||
			group_name = model[group_iter][C_JID].decode('utf-8')
 | 
						||
			if name == group_name:
 | 
						||
				break
 | 
						||
			group_iter = model.iter_next(group_iter)
 | 
						||
		return group_iter
 | 
						||
 | 
						||
 | 
						||
	def _get_self_contact_iter(self, jid, account, model = None):
 | 
						||
		''' Return the gtk.TreeIter of SelfContact or None if not found.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		jid -- the jid of SelfContact
 | 
						||
		account -- the account of SelfContact
 | 
						||
		model -- the data model (default TreeFilterModel)
 | 
						||
 | 
						||
		'''
 | 
						||
 | 
						||
		if not model:
 | 
						||
			model = self.modelfilter
 | 
						||
		iterAcct = self._get_account_iter(account, model)
 | 
						||
		iterC = model.iter_children(iterAcct)
 | 
						||
 | 
						||
		# There might be several SelfContacts in merged account view
 | 
						||
		while iterC:
 | 
						||
			if model[iterC][C_TYPE] != 'self_contact':
 | 
						||
				break
 | 
						||
			iter_jid = model[iterC][C_JID]
 | 
						||
			if iter_jid and jid == iter_jid.decode('utf-8'):
 | 
						||
				return iterC
 | 
						||
			iterC = model.iter_next(iterC)
 | 
						||
		return None
 | 
						||
 | 
						||
 | 
						||
	def _get_contact_iter(self, jid, account, contact = None, model = None):
 | 
						||
		''' Return a list of gtk.TreeIter of the given contact.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		jid -- the jid without resource
 | 
						||
		account -- the account
 | 
						||
		contact -- the contact (default None)
 | 
						||
		model -- the data model (default TreeFilterModel)
 | 
						||
 | 
						||
		'''
 | 
						||
		if not model:
 | 
						||
			model = self.modelfilter
 | 
						||
			if model is None: # when closing Gajim model can be none (async pbs?)
 | 
						||
				return []
 | 
						||
 | 
						||
		if jid == gajim.get_jid_from_account(account):
 | 
						||
			contact_iter = self._get_self_contact_iter(jid, account, model)
 | 
						||
			if contact_iter:
 | 
						||
				return [contact_iter]
 | 
						||
			else:
 | 
						||
				return []
 | 
						||
 | 
						||
		if not contact:
 | 
						||
			contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
						||
			if not contact:
 | 
						||
				# We don't know this contact
 | 
						||
				return
 | 
						||
 | 
						||
		acct = self._get_account_iter(account, model)
 | 
						||
		found = [] # the contact iters. One per group
 | 
						||
		for group in contact.get_shown_groups():
 | 
						||
			group_iter = self._get_group_iter(group, account, acct,  model)
 | 
						||
			contact_iter = model.iter_children(group_iter)
 | 
						||
 | 
						||
			while contact_iter:
 | 
						||
				# Loop over all contacts in this group
 | 
						||
				iter_jid = model[contact_iter][C_JID]
 | 
						||
				if iter_jid and jid == iter_jid.decode('utf-8') and \
 | 
						||
				account == model[contact_iter][C_ACCOUNT].decode('utf-8'):
 | 
						||
					# only one iter per group
 | 
						||
					found.append(contact_iter)
 | 
						||
					contact_iter = None
 | 
						||
				elif model.iter_has_child(contact_iter):
 | 
						||
					# it's a big brother and has children
 | 
						||
					contact_iter = model.iter_children(contact_iter)
 | 
						||
				else:
 | 
						||
					# try to find next contact:
 | 
						||
					# other contact in this group or brother contact
 | 
						||
					next_contact_iter = model.iter_next(contact_iter)
 | 
						||
					if next_contact_iter:
 | 
						||
						contact_iter = next_contact_iter
 | 
						||
					else:
 | 
						||
						# It's the last one. Go up if we are big brother
 | 
						||
						parent_iter = model.iter_parent(contact_iter)
 | 
						||
						if parent_iter and model[parent_iter][C_TYPE] == 'contact':
 | 
						||
							contact_iter = model.iter_next(parent_iter)
 | 
						||
						else:
 | 
						||
							# we tested all contacts in this group
 | 
						||
							contact_iter = None
 | 
						||
		return found
 | 
						||
 | 
						||
 | 
						||
	def _iter_is_separator(self, model, titer):
 | 
						||
		''' Return True if the given iter is a separator.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		model -- the data model
 | 
						||
		iter -- the gtk.TreeIter to test
 | 
						||
		'''
 | 
						||
		if model[titer][0] == 'SEPARATOR':
 | 
						||
			return True
 | 
						||
		return False
 | 
						||
 | 
						||
 | 
						||
	def _iter_contact_rows(self, model = None):
 | 
						||
		'''Iterate over all contact rows in given model.
 | 
						||
 | 
						||
		Keyword argument
 | 
						||
		model -- the data model (default TreeFilterModel)
 | 
						||
		'''
 | 
						||
		if not model:
 | 
						||
			model = self.modelfilter
 | 
						||
		account_iter = model.get_iter_root()
 | 
						||
		while account_iter:
 | 
						||
			group_iter = model.iter_children(account_iter)
 | 
						||
			while group_iter:
 | 
						||
				contact_iter = model.iter_children(group_iter)
 | 
						||
				while contact_iter:
 | 
						||
					yield model[contact_iter]
 | 
						||
					contact_iter = model.iter_next(contact_iter)
 | 
						||
				group_iter = model.iter_next(group_iter)
 | 
						||
			account_iter = model.iter_next(account_iter)
 | 
						||
 | 
						||
 | 
						||
#############################################################################
 | 
						||
### Methods for adding and removing roster window items
 | 
						||
#############################################################################
 | 
						||
 | 
						||
	def add_account(self, account):
 | 
						||
		'''Add account to roster and draw it. Do nothing if it is already in.'''
 | 
						||
		if self._get_account_iter(account):
 | 
						||
			# Will happen on reconnect or for merged accounts
 | 
						||
			return
 | 
						||
 | 
						||
		if self.regroup:
 | 
						||
			# Merged accounts view
 | 
						||
			show = helpers.get_global_show()
 | 
						||
			self.model.append(None, [gajim.interface.jabber_state_images['16'][
 | 
						||
				show], _('Merged accounts'), 'account', '', 'all', None, None])
 | 
						||
		else:
 | 
						||
			show = gajim.SHOW_LIST[gajim.connections[account].connected]
 | 
						||
			our_jid = gajim.get_jid_from_account(account)
 | 
						||
 | 
						||
			tls_pixbuf = None
 | 
						||
			if gajim.account_is_securely_connected(account):
 | 
						||
				# the only way to create a pixbuf from stock
 | 
						||
				tls_pixbuf = self.window.render_icon(
 | 
						||
					gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
 | 
						||
 | 
						||
			self.model.append(None, [gajim.interface.jabber_state_images['16'][
 | 
						||
				show], gobject.markup_escape_text(account), 'account', our_jid,
 | 
						||
				account, None, tls_pixbuf])
 | 
						||
 | 
						||
		self.draw_account(account)
 | 
						||
 | 
						||
 | 
						||
	def add_account_contacts(self, account):
 | 
						||
		'''Add all contacts and groups of the given account to roster,
 | 
						||
		draw them and account.
 | 
						||
		'''
 | 
						||
		self.starting = True
 | 
						||
		jids = gajim.contacts.get_jid_list(account)
 | 
						||
 | 
						||
		self.tree.freeze_child_notify()
 | 
						||
		for jid in jids:
 | 
						||
			self.add_contact(jid, account)
 | 
						||
		self.tree.thaw_child_notify()
 | 
						||
 | 
						||
		# Do not freeze the GUI when drawing the contacts
 | 
						||
		if jids:
 | 
						||
			# Overhead is big, only invoke when needed
 | 
						||
			self._idle_draw_jids_of_account(jids, account)
 | 
						||
 | 
						||
		# Draw all known groups
 | 
						||
		for group in gajim.groups[account].keys():
 | 
						||
			self.draw_group(group, account)
 | 
						||
		self.draw_account(account)
 | 
						||
		self.starting = False
 | 
						||
 | 
						||
 | 
						||
	def _add_entity(self, contact, account, groups = None,
 | 
						||
	big_brother_contact = None, big_brother_account = None):
 | 
						||
		'''Add the given contact to roster data model.
 | 
						||
 | 
						||
		Contact is added regardless if he is already in roster or not.
 | 
						||
		Return list of newly added iters.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		contact -- the contact to add
 | 
						||
		account -- the contacts account
 | 
						||
		groups -- list of groups to add the contact to. (default groups in 
 | 
						||
						contact.get_shown_groups()).
 | 
						||
			Parameter ignored when big_brother_contact is specified.
 | 
						||
		big_brother_contact -- if specified contact is added as child 
 | 
						||
										big_brother_contact. (default None)
 | 
						||
		'''
 | 
						||
		added_iters = []
 | 
						||
		if big_brother_contact:
 | 
						||
			# Add contact under big brother
 | 
						||
 | 
						||
			parent_iters = self._get_contact_iter(big_brother_contact.jid,
 | 
						||
				big_brother_account, big_brother_contact, self.model)
 | 
						||
			assert len(parent_iters) > 0,\
 | 
						||
				'Big brother is not yet in roster!'
 | 
						||
 | 
						||
			# Do not confuse get_contact_iter
 | 
						||
			# Sync groups of family members
 | 
						||
			contact.groups = big_brother_contact.get_shown_groups()[:]
 | 
						||
 | 
						||
			for child_iter in parent_iters:
 | 
						||
				it = self.model.append(child_iter, (None, contact.get_shown_name(),
 | 
						||
					'contact', contact.jid,	account, None, None))
 | 
						||
				added_iters.append(it)
 | 
						||
		else:
 | 
						||
			# We are a normal contact. Add us to our groups.
 | 
						||
			if not groups:
 | 
						||
				groups = contact.get_shown_groups()
 | 
						||
			for group in groups:
 | 
						||
				child_iterG = self._get_group_iter(group, account, model=self.model)
 | 
						||
				if not child_iterG:
 | 
						||
					# Group is not yet in roster, add it!
 | 
						||
					child_iterA = self._get_account_iter(account, self.model)
 | 
						||
					child_iterG = self.model.append(child_iterA, [
 | 
						||
						gajim.interface.jabber_state_images['16']['closed'],
 | 
						||
						gobject.markup_escape_text(group), 'group',
 | 
						||
						group, account, None, None])
 | 
						||
					self.draw_group(group, account)
 | 
						||
 | 
						||
				if contact.is_transport():
 | 
						||
					typestr = 'agent'
 | 
						||
				elif contact.is_groupchat():
 | 
						||
					typestr = 'groupchat'
 | 
						||
				else:
 | 
						||
					typestr = 'contact'
 | 
						||
 | 
						||
				# we add some values here. see draw_contact for more
 | 
						||
				i_ = self.model.append(child_iterG, (None, contact.get_shown_name(),
 | 
						||
					typestr, contact.jid, account, None, None))
 | 
						||
				added_iters.append(i_)
 | 
						||
 | 
						||
				# Restore the group expand state
 | 
						||
				if account + group in self.collapsed_rows:
 | 
						||
					is_expanded = False
 | 
						||
				else:
 | 
						||
					is_expanded = True
 | 
						||
				if group not in gajim.groups[account]:
 | 
						||
					gajim.groups[account][group] = {'expand': is_expanded}
 | 
						||
 | 
						||
		assert len(added_iters), '%s has not been added to roster!' % contact.jid
 | 
						||
		for titer in added_iters:
 | 
						||
			assert self.model[titer][C_JID] == contact.jid and \
 | 
						||
				self.model[titer][C_ACCOUNT] == account, \
 | 
						||
				"Iters invalidated for %s" % contact.jid
 | 
						||
		return added_iters
 | 
						||
 | 
						||
	def _remove_entity(self, contact, account, groups = None):
 | 
						||
		'''Remove the given contact from roster data model.
 | 
						||
 | 
						||
		Empty groups after contact removal are removed too.
 | 
						||
		Return False if contact still has children and deletion was not performed.
 | 
						||
		Return True on success.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		contact -- the contact to add
 | 
						||
		account -- the contacts account
 | 
						||
		groups -- list of groups to remove the contact from.
 | 
						||
		'''
 | 
						||
		iters = self._get_contact_iter(contact.jid, account, contact, self.model)
 | 
						||
		assert iters, '%s shall be removed but is not in roster' % contact.jid
 | 
						||
 | 
						||
		parent_iter = self.model.iter_parent(iters[0])
 | 
						||
		parent_type = self.model[parent_iter][C_TYPE]
 | 
						||
 | 
						||
		if groups:
 | 
						||
			# Only remove from specified groups
 | 
						||
			all_iters = iters[:]
 | 
						||
			group_iters = [self._get_group_iter(group, account) for group in \
 | 
						||
				groups]
 | 
						||
			iters = [titer for titer in all_iters
 | 
						||
				if self.model.iter_parent(titer) in group_iters]
 | 
						||
 | 
						||
		iter_children = self.model.iter_children(iters[0])
 | 
						||
 | 
						||
		if iter_children:
 | 
						||
			# We have children. We cannot be removed!
 | 
						||
			return False
 | 
						||
		else:
 | 
						||
			# Remove us and empty groups from the model
 | 
						||
			for i in iters:
 | 
						||
				assert self.model[i][C_JID] == contact.jid and \
 | 
						||
					self.model[i][C_ACCOUNT] == account, \
 | 
						||
					"Invalidated iters of %s" % contact.jid
 | 
						||
				parent_i = self.model.iter_parent(i)
 | 
						||
				if parent_type == 'group' and \
 | 
						||
				self.model.iter_n_children(parent_i) == 1:
 | 
						||
					group = self.model[parent_i][C_JID].decode('utf-8')
 | 
						||
					if gajim.groups[account].has_key(group):
 | 
						||
						del gajim.groups[account][group]
 | 
						||
					self.model.remove(parent_i)
 | 
						||
				else:
 | 
						||
					self.model.remove(i)
 | 
						||
			return True
 | 
						||
 | 
						||
	def _add_metacontact_family(self, family, account):
 | 
						||
		'''Add the give Metacontact family to roster data model.
 | 
						||
 | 
						||
		Add Big Brother to his groups and all others under him.
 | 
						||
		Return list of all added (contact, account) tuples with Big Brother
 | 
						||
		as first element.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		family -- the family, see Contacts.get_metacontacts_family()
 | 
						||
		'''
 | 
						||
 | 
						||
		nearby_family, big_brother_jid, big_brother_account = \
 | 
						||
			self._get_nearby_family_and_big_brother(family, account)
 | 
						||
		big_brother_contact = gajim.contacts.get_first_contact_from_jid(
 | 
						||
			big_brother_account, big_brother_jid)
 | 
						||
 | 
						||
		assert len(self._get_contact_iter(big_brother_jid, big_brother_account,
 | 
						||
			big_brother_contact, self.model)) == 0,\
 | 
						||
			'Big brother %s already in roster  \n Family: %s' % (big_brother_jid,
 | 
						||
			family)
 | 
						||
		self._add_entity(big_brother_contact, big_brother_account)
 | 
						||
 | 
						||
		brothers = []
 | 
						||
		# Filter family members
 | 
						||
		for data in nearby_family:
 | 
						||
			_account = data['account']
 | 
						||
			_jid = data['jid']
 | 
						||
			_contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
 | 
						||
 | 
						||
			if not _contact or _contact == big_brother_contact:
 | 
						||
				# Corresponding account is not connected
 | 
						||
				# or brother already added
 | 
						||
				continue
 | 
						||
 | 
						||
			assert len(self._get_contact_iter(_jid, _account, _contact, self.model)
 | 
						||
				) == 0, "%s already in roster. \n Family: %s" % (_jid, nearby_family)
 | 
						||
			self._add_entity(_contact, _account, big_brother_contact = \
 | 
						||
				big_brother_contact, big_brother_account=big_brother_account)
 | 
						||
			brothers.append((_contact, _account))
 | 
						||
 | 
						||
		brothers.insert(0, (big_brother_contact, big_brother_account))
 | 
						||
		return brothers
 | 
						||
 | 
						||
	def _remove_metacontact_family(self, family, account):
 | 
						||
		'''Remove the given Metacontact family from roster data model.
 | 
						||
 | 
						||
		See Contacts.get_metacontacts_family() and RosterWindow._remove_entity()
 | 
						||
		'''
 | 
						||
		nearby_family = self._get_nearby_family_and_big_brother(
 | 
						||
			family, account)[0]
 | 
						||
 | 
						||
		# Family might has changed (actual big brother not on top).
 | 
						||
		# Remove childs first then big brother
 | 
						||
		family_in_roster = False
 | 
						||
		big_brother_jid = None
 | 
						||
		for data in nearby_family:
 | 
						||
			_account = data['account']
 | 
						||
			_jid = data['jid']
 | 
						||
			_contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
 | 
						||
 | 
						||
			iters = self._get_contact_iter(_jid, _account, _contact, self.model)
 | 
						||
			if not iters or not _contact:
 | 
						||
				# Family might not be up to date.
 | 
						||
				# Only try to remove what is actually in the roster
 | 
						||
				continue
 | 
						||
			assert iters, '%s shall be removed but is not in roster \
 | 
						||
				\n Family: %s' % (_jid, family)
 | 
						||
 | 
						||
			family_in_roster = True
 | 
						||
 | 
						||
			parent_iter = self.model.iter_parent(iters[0])
 | 
						||
			parent_type = self.model[parent_iter][C_TYPE]
 | 
						||
 | 
						||
			if parent_type != 'contact':
 | 
						||
				# The contact on top
 | 
						||
				old_big_account = _account
 | 
						||
				old_big_contact = _contact
 | 
						||
				old_big_jid = _jid
 | 
						||
				continue
 | 
						||
 | 
						||
			ok = self._remove_entity(_contact, _account)
 | 
						||
			assert ok, '%s was not removed' % _jid
 | 
						||
			assert len(self._get_contact_iter(_jid, _account, _contact,
 | 
						||
				self.model)) == 0, '%s is removed but still in roster' % _jid
 | 
						||
 | 
						||
		if not family_in_roster:
 | 
						||
			return False
 | 
						||
	
 | 
						||
		assert old_big_jid, 'No Big Brother in nearby family % (Family: %)' % \
 | 
						||
			(nearby_family, family)
 | 
						||
		iters = self._get_contact_iter(old_big_jid, old_big_account,
 | 
						||
			old_big_contact, self.model)
 | 
						||
		assert len(iters) > 0, 'Old Big Brother %s is not in roster anymore' % \
 | 
						||
			old_big_jid
 | 
						||
		assert not self.model.iter_children(iters[0]),\
 | 
						||
			'Old Big Brother %s still has children' % old_big_jid
 | 
						||
 | 
						||
		ok = self._remove_entity(old_big_contact, old_big_account)
 | 
						||
		assert ok, "Old Big Brother %s not removed" % old_big_jid
 | 
						||
		assert len(self._get_contact_iter(old_big_jid, old_big_account,
 | 
						||
			old_big_contact, self.model)) == 0,\
 | 
						||
			'Old Big Brother %s is removed but still in roster' % old_big_jid
 | 
						||
 | 
						||
		return True
 | 
						||
 | 
						||
 | 
						||
	def _recalibrate_metacontact_family(self, family, account):
 | 
						||
		'''Regroup metacontact family if necessary.'''
 | 
						||
 | 
						||
		brothers = []
 | 
						||
		nearby_family, big_brother_jid, big_brother_account = \
 | 
						||
			self._get_nearby_family_and_big_brother(family, account)
 | 
						||
		child_iters = self._get_contact_iter(big_brother_jid, big_brother_account,
 | 
						||
			model = self.model)
 | 
						||
		parent_iter = self.model.iter_parent(child_iters[0])
 | 
						||
		parent_type = self.model[parent_iter][C_TYPE]
 | 
						||
 | 
						||
		# Check if the current BigBrother has even been before.
 | 
						||
		if parent_type == 'contact':
 | 
						||
			for data in nearby_family:
 | 
						||
				# recalibrate after remove to keep highlight
 | 
						||
				if data['jid'] in gajim.to_be_removed[data['account']]:
 | 
						||
					return
 | 
						||
 | 
						||
			self._remove_metacontact_family(family, account)
 | 
						||
			brothers = self._add_metacontact_family(family, account)
 | 
						||
 | 
						||
			for c, acc in brothers:
 | 
						||
				self.draw_contact(c.jid, acc)
 | 
						||
				self.draw_avatar(c.jid, acc)
 | 
						||
 | 
						||
 | 
						||
	def _get_nearby_family_and_big_brother(self, family, account):
 | 
						||
		'''Return the nearby family and its Big Brother
 | 
						||
 | 
						||
		Nearby family is the part of the family that is grouped with the metacontact.
 | 
						||
		A metacontact may be over different accounts. If regroup is s False the 
 | 
						||
		given family is split account wise.
 | 
						||
 | 
						||
		(nearby_family, big_brother_jid, big_brother_account)
 | 
						||
		'''
 | 
						||
		if self.regroup:
 | 
						||
			# group all together
 | 
						||
			nearby_family = family
 | 
						||
		else:
 | 
						||
			# we want one nearby_family per account
 | 
						||
			nearby_family = [data for data in family
 | 
						||
				if account == data['account']]
 | 
						||
 | 
						||
		big_brother_data = gajim.contacts.get_metacontacts_big_brother(
 | 
						||
			nearby_family)
 | 
						||
		big_brother_jid = big_brother_data['jid']
 | 
						||
		big_brother_account = big_brother_data['account']
 | 
						||
 | 
						||
		return (nearby_family, big_brother_jid, big_brother_account)
 | 
						||
 | 
						||
 | 
						||
	def _add_self_contact(self, account):
 | 
						||
		'''Add account's SelfContact to roster and draw it and the account.
 | 
						||
 | 
						||
		Return the SelfContact contact instance
 | 
						||
		'''
 | 
						||
		jid = gajim.get_jid_from_account(account)
 | 
						||
		contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
						||
 | 
						||
		assert len(self._get_contact_iter(jid, account, contact, self.model)) == \
 | 
						||
			0, 'Self contact %s already in roster' % jid
 | 
						||
 | 
						||
		child_iterA = self._get_account_iter(account, self.model)
 | 
						||
		self.model.append(child_iterA, (None, gajim.nicks[account],
 | 
						||
			'self_contact', jid, account, None, None))
 | 
						||
 | 
						||
		self.draw_contact(jid, account)
 | 
						||
		self.draw_avatar(jid, account)
 | 
						||
		self.draw_account(account)
 | 
						||
 | 
						||
		return contact
 | 
						||
 | 
						||
 | 
						||
	def add_contact(self, jid, account):
 | 
						||
		'''Add contact to roster and draw him.
 | 
						||
 | 
						||
		Add contact to all its group and redraw the groups, the contact and the
 | 
						||
		account. If it's a Metacontact, add and draw the whole family.
 | 
						||
		Do nothing if the contact is already in roster.
 | 
						||
 | 
						||
		Return the added contact instance. If it is a Metacontact return
 | 
						||
		Big Brother.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		jid -- the contact's jid or SelfJid to add SelfContact
 | 
						||
		account -- the corresponding account.
 | 
						||
 | 
						||
		'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if len(self._get_contact_iter(jid, account, contact, self.model)):
 | 
						||
			# If contact already in roster, do nothing
 | 
						||
			return
 | 
						||
 | 
						||
		if jid == gajim.get_jid_from_account(account):
 | 
						||
			if contact.resource != gajim.connections[account].server_resource:
 | 
						||
				return self._add_self_contact(account)
 | 
						||
			return
 | 
						||
 | 
						||
		is_observer = contact.is_observer()
 | 
						||
		if is_observer:
 | 
						||
			# if he has a tag, remove it
 | 
						||
			tag = gajim.contacts.get_metacontacts_tag(account, jid)
 | 
						||
			if tag:
 | 
						||
				gajim.contacts.remove_metacontact(account, jid)
 | 
						||
 | 
						||
		# Add contact to roster
 | 
						||
		family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
		contacts = []
 | 
						||
		if family:
 | 
						||
			# We have a family. So we are a metacontact.
 | 
						||
			# Add all family members that we shall be grouped with
 | 
						||
			if self.regroup:
 | 
						||
				# remove existing family members to regroup them
 | 
						||
				self._remove_metacontact_family(family, account)
 | 
						||
			contacts = self._add_metacontact_family(family, account)
 | 
						||
		else:
 | 
						||
			# We are a normal contact
 | 
						||
			contacts = [(contact, account),]
 | 
						||
			self._add_entity(contact, account)
 | 
						||
 | 
						||
		# Draw the contact and its groups contact
 | 
						||
		if not self.starting:
 | 
						||
			for c, acc in contacts:
 | 
						||
				self.draw_contact(c.jid, acc)
 | 
						||
				self.draw_avatar(c.jid, acc)
 | 
						||
			for group in contact.get_shown_groups():
 | 
						||
				self.draw_group(group, account)
 | 
						||
				self._adjust_group_expand_collapse_state(group, account)
 | 
						||
			self.draw_account(account)
 | 
						||
 | 
						||
		return contacts[0][0] # it's contact/big brother with highest priority
 | 
						||
 | 
						||
 | 
						||
	def remove_contact(self, jid, account, force=False, backend=False):
 | 
						||
		'''Remove contact from roster.
 | 
						||
 | 
						||
		Remove contact from all its group. Remove empty groups or redraw
 | 
						||
		otherwise.
 | 
						||
		Draw the account.
 | 
						||
		If it's a Metacontact, remove the whole family.
 | 
						||
		Do nothing if the contact is not in roster.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		jid -- the contact's jid or SelfJid to remove SelfContact
 | 
						||
		account -- the corresponding account.
 | 
						||
		force -- remove contact even it has pending evens (Default False)
 | 
						||
		backend -- also remove contact instance (Default False)
 | 
						||
 | 
						||
		'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
 | 
						||
		iters = self._get_contact_iter(jid, account, contact, self.model)
 | 
						||
		if not iters:
 | 
						||
			return
 | 
						||
 | 
						||
		if not force and self.contact_has_pending_roster_events(contact, account):
 | 
						||
			# Contact has pending events
 | 
						||
			key = (jid, account)
 | 
						||
			if not key in self.contacts_to_be_removed:
 | 
						||
				self.contacts_to_be_removed[key] = {'backend': backend}
 | 
						||
			return False
 | 
						||
		else:
 | 
						||
			# no more pending events
 | 
						||
			# Remove contact from roster directly
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
			if family:
 | 
						||
				# We have a family. So we are a metacontact.
 | 
						||
				self._remove_metacontact_family(family, account)
 | 
						||
			else:
 | 
						||
				self._remove_entity(contact, account)
 | 
						||
 | 
						||
 | 
						||
			if backend:
 | 
						||
				# Remove contact before redrawing, otherwise the old
 | 
						||
				# numbers will still be show
 | 
						||
				gajim.contacts.remove_jid(account, jid, remove_meta=True)
 | 
						||
				if family:
 | 
						||
					# reshow the rest of the family
 | 
						||
					brothers = self._add_metacontact_family(family, account)
 | 
						||
					for c, acc in brothers:
 | 
						||
						self.draw_contact(c.jid, acc)
 | 
						||
						self.draw_avatar(c.jid, acc)
 | 
						||
 | 
						||
			# Draw all groups of the contact
 | 
						||
			for group in contact.get_shown_groups():
 | 
						||
				self.draw_group(group, account)
 | 
						||
			self.draw_account(account)
 | 
						||
 | 
						||
			return True
 | 
						||
 | 
						||
 | 
						||
	def add_groupchat(self, jid, account, status = ''):
 | 
						||
		'''Add groupchat to roster and draw it.
 | 
						||
		Return the added contact instance.
 | 
						||
		'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if contact is None:
 | 
						||
			# Do not show gc if we are disconnected and minimize it
 | 
						||
			if gajim.account_is_connected(account):
 | 
						||
				show = 'online'
 | 
						||
			else: 
 | 
						||
				show = 'offline'
 | 
						||
				status = ''
 | 
						||
			contact = gajim.contacts.create_contact(jid=jid, name=jid,
 | 
						||
				groups=[_('Groupchats')], show=show, status=status, sub='none')
 | 
						||
			gajim.contacts.add_contact(account, contact)
 | 
						||
			self.add_contact(jid, account)
 | 
						||
		else:
 | 
						||
			contact.show = 'online'
 | 
						||
			self.draw_completely_and_show_if_needed(jid, account)
 | 
						||
		return contact
 | 
						||
 | 
						||
 | 
						||
	def remove_groupchat(self, jid, account):
 | 
						||
		'''Remove groupchat from roster and redraw account and group.'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if contact.is_groupchat():
 | 
						||
			self.remove_contact(jid, account, force=True, backend=True)
 | 
						||
			return True
 | 
						||
		else:
 | 
						||
			return False
 | 
						||
 | 
						||
 | 
						||
	# TODO: This function is yet unused! Port to new API
 | 
						||
	def add_transport(self, jid, account):
 | 
						||
		'''Add transport to roster and draw it.
 | 
						||
		Return the added contact instance.'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if contact is None:
 | 
						||
			contact = gajim.contacts.create_contact(jid=jid, name=jid,
 | 
						||
				groups=[_('Transports')], show='offline', status='offline',
 | 
						||
				sub='from')
 | 
						||
			gajim.contacts.add_contact(account, contact)
 | 
						||
		self.add_contact(jid, account)
 | 
						||
		return contact
 | 
						||
 | 
						||
	def remove_transport(self, jid, account):
 | 
						||
		'''Remove transport from roster and redraw account and group.'''
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		self.remove_contact(jid, account, force=True, backend=True)
 | 
						||
		return True
 | 
						||
 | 
						||
	def add_contact_to_groups(self, jid, account, groups, update = True):
 | 
						||
		'''Add contact to given groups and redraw them.
 | 
						||
 | 
						||
		Contact on server is updated too. When the contact has a family,
 | 
						||
		the action will be performed for all members.
 | 
						||
 | 
						||
		Keyword Arguments:
 | 
						||
		jid -- the jid
 | 
						||
		account -- the corresponding account
 | 
						||
		groups -- list of Groups to add the contact to.
 | 
						||
		update -- update contact on the server
 | 
						||
 | 
						||
		'''
 | 
						||
		self.remove_contact(jid, account, force = True)
 | 
						||
		for contact in gajim.contacts.get_contacts(account, jid):
 | 
						||
			for group in groups:
 | 
						||
				if group not in contact.groups:
 | 
						||
					# we might be dropped from meta to group
 | 
						||
					contact.groups.append(group)
 | 
						||
			if update:
 | 
						||
				gajim.connections[account].update_contact(jid, contact.name,
 | 
						||
					contact.groups)
 | 
						||
 | 
						||
		self.add_contact(jid, account)
 | 
						||
 | 
						||
		for group in groups:
 | 
						||
			self._adjust_group_expand_collapse_state(group, account)
 | 
						||
 | 
						||
	def remove_contact_from_groups(self, jid, account, groups, update = True):
 | 
						||
		'''Remove contact from given groups and redraw them.
 | 
						||
 | 
						||
		Contact on server is updated too. When the contact has a family,
 | 
						||
		the action will be performed for all members.
 | 
						||
 | 
						||
		Keyword Arguments:
 | 
						||
		jid -- the jid
 | 
						||
		account -- the corresponding account
 | 
						||
		groups -- list of Groups to remove the contact from
 | 
						||
		update -- update contact on the server
 | 
						||
 | 
						||
		'''
 | 
						||
		self.remove_contact(jid, account, force = True)
 | 
						||
		for contact in gajim.contacts.get_contacts(account, jid):
 | 
						||
			for group in groups:
 | 
						||
				if group in contact.groups:
 | 
						||
					# Needed when we remove from "General"
 | 
						||
					contact.groups.remove(group)
 | 
						||
			if update:
 | 
						||
				gajim.connections[account].update_contact(jid, contact.name,
 | 
						||
					contact.groups)
 | 
						||
 | 
						||
		self.add_contact(jid, account)
 | 
						||
		
 | 
						||
		# Also redraw old groups
 | 
						||
		for group in groups:
 | 
						||
			self.draw_group(group, account)
 | 
						||
 | 
						||
	# FIXME: maybe move to gajim.py
 | 
						||
	def remove_newly_added(self, jid, account):
 | 
						||
		if jid in gajim.newly_added[account]:
 | 
						||
			gajim.newly_added[account].remove(jid)
 | 
						||
			self.draw_contact(jid, account)
 | 
						||
 | 
						||
	# FIXME: maybe move to gajim.py
 | 
						||
	def remove_to_be_removed(self, jid, account):
 | 
						||
		if not gajim.interface.instances.has_key(account):
 | 
						||
			# Account has been deleted during the timeout that called us
 | 
						||
			return
 | 
						||
		if jid in gajim.newly_added[account]:
 | 
						||
			return
 | 
						||
		if jid in gajim.to_be_removed[account]:
 | 
						||
			gajim.to_be_removed[account].remove(jid)
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
			if family:
 | 
						||
				# Peform delayed recalibration
 | 
						||
				self._recalibrate_metacontact_family(family, account)
 | 
						||
			self.draw_contact(jid, account)
 | 
						||
 | 
						||
	#FIXME: integrate into add_contact()
 | 
						||
	def add_to_not_in_the_roster(self, account, jid, nick = '', resource = ''):
 | 
						||
		keyID = ''
 | 
						||
		attached_keys = gajim.config.get_per('accounts', account,
 | 
						||
			'attached_gpg_keys').split()
 | 
						||
		if jid in attached_keys:
 | 
						||
			keyID = attached_keys[attached_keys.index(jid) + 1]
 | 
						||
		contact = gajim.contacts.create_contact(jid=jid, name=nick,
 | 
						||
			groups=[_('Not in Roster')], show='not in roster', status='',
 | 
						||
			sub='none', resource=resource, keyID=keyID)
 | 
						||
		gajim.contacts.add_contact(account, contact)
 | 
						||
		self.add_contact(contact.jid, account)
 | 
						||
		return contact
 | 
						||
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Methods for adding and removing roster window items
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def draw_account(self, account):
 | 
						||
		child_iter = self._get_account_iter(account, self.model)
 | 
						||
		if not child_iter:
 | 
						||
			assert False, 'Account iter of %s could not be found.' % account
 | 
						||
			return
 | 
						||
 | 
						||
		num_of_accounts = gajim.get_number_of_connected_accounts()
 | 
						||
		num_of_secured = gajim.get_number_of_securely_connected_accounts()
 | 
						||
 | 
						||
		if gajim.account_is_securely_connected(account) and not self.regroup or \
 | 
						||
		self.regroup and num_of_secured and num_of_secured == num_of_accounts:
 | 
						||
			tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
 | 
						||
				gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
 | 
						||
			self.model[child_iter][C_PADLOCK_PIXBUF] = tls_pixbuf
 | 
						||
		else:
 | 
						||
			self.model[child_iter][C_PADLOCK_PIXBUF] = None
 | 
						||
 | 
						||
		if self.regroup:
 | 
						||
			account_name = _('Merged accounts')
 | 
						||
			accounts = []
 | 
						||
		else:
 | 
						||
			account_name = account
 | 
						||
			accounts = [account]
 | 
						||
 | 
						||
		if account in self.collapsed_rows and \
 | 
						||
		self.model.iter_has_child(child_iter):
 | 
						||
			account_name = '[%s]' % account_name
 | 
						||
 | 
						||
		if (gajim.account_is_connected(account) or (self.regroup and \
 | 
						||
		gajim.get_number_of_connected_accounts())) and gajim.config.get(
 | 
						||
		'show_contacts_number'):
 | 
						||
			nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
 | 
						||
				accounts = accounts)
 | 
						||
			account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
 | 
						||
 | 
						||
		self.model[child_iter][C_NAME] = account_name
 | 
						||
		return False
 | 
						||
 | 
						||
	def draw_group(self, group, account):
 | 
						||
		child_iter = self._get_group_iter(group, account, model = self.model)
 | 
						||
		if not child_iter:
 | 
						||
			# Eg. We redraw groups after we removed a entitiy
 | 
						||
			# and its empty groups
 | 
						||
			return
 | 
						||
		if self.regroup:
 | 
						||
			accounts = []
 | 
						||
		else:
 | 
						||
			accounts = [account]
 | 
						||
		text = gobject.markup_escape_text(group)
 | 
						||
		if group in gajim.connections[account].blocked_groups:
 | 
						||
			text = '<span strikethrough="true">%s</span>' % text
 | 
						||
		if gajim.config.get('show_contacts_number'):
 | 
						||
			nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
 | 
						||
				accounts = accounts, groups = [group])
 | 
						||
			text += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
 | 
						||
 | 
						||
		self.model[child_iter][C_NAME] = text
 | 
						||
		return False
 | 
						||
 | 
						||
	def draw_parent_contact(self, jid, account):
 | 
						||
		child_iters = self._get_contact_iter(jid, account, model = self.model)
 | 
						||
		if not child_iters:
 | 
						||
			return False
 | 
						||
		parent_iter = self.model.iter_parent(child_iters[0])
 | 
						||
		if self.model[parent_iter][C_TYPE] != 'contact':
 | 
						||
			# parent is not a contact
 | 
						||
			return
 | 
						||
		parent_jid = self.model[parent_iter][C_JID].decode('utf-8')
 | 
						||
		parent_account = self.model[parent_iter][C_ACCOUNT].decode('utf-8')
 | 
						||
		self.draw_contact(parent_jid, parent_account)
 | 
						||
		return False
 | 
						||
 | 
						||
	def draw_contact(self, jid, account, selected = False, focus = False):
 | 
						||
		'''draw the correct state image, name BUT not avatar'''
 | 
						||
		# focus is about if the roster window has toplevel-focus or not
 | 
						||
		# FIXME: We really need a custom cell_renderer
 | 
						||
 | 
						||
		contact_instances = gajim.contacts.get_contacts(account, jid)
 | 
						||
		contact = gajim.contacts.get_highest_prio_contact_from_contacts(
 | 
						||
			contact_instances)
 | 
						||
 | 
						||
		child_iters = self._get_contact_iter(jid, account, contact, self.model)
 | 
						||
		if not child_iters:
 | 
						||
			return False
 | 
						||
 | 
						||
		name = gobject.markup_escape_text(contact.get_shown_name())
 | 
						||
 | 
						||
		# gets number of unread gc marked messages
 | 
						||
		if jid in gajim.interface.minimized_controls[account]:
 | 
						||
			nb_unread = len(gajim.events.get_events(account, jid,
 | 
						||
				['printed_marked_gc_msg']))
 | 
						||
			nb_unread += \
 | 
						||
				gajim.interface.minimized_controls[account][jid].get_nb_unread_pm()
 | 
						||
 | 
						||
			if nb_unread == 1:
 | 
						||
				name = '%s *' % name
 | 
						||
			elif nb_unread > 1:
 | 
						||
				name = '%s [%s]' % (name, str(nb_unread))
 | 
						||
 | 
						||
		# Strike name if blocked
 | 
						||
		strike = False
 | 
						||
		if jid in gajim.connections[account].blocked_contacts:
 | 
						||
			strike = True
 | 
						||
		else:
 | 
						||
			for group in contact.get_shown_groups():
 | 
						||
				if group in gajim.connections[account].blocked_groups:
 | 
						||
					strike = True
 | 
						||
					break
 | 
						||
		if strike:
 | 
						||
			name = '<span strikethrough="true">%s</span>' % name
 | 
						||
 | 
						||
		# Show resource counter
 | 
						||
		nb_connected_contact = 0
 | 
						||
		for c in contact_instances:
 | 
						||
			if c.show not in ('error', 'offline'):
 | 
						||
				nb_connected_contact += 1
 | 
						||
		if nb_connected_contact > 1:
 | 
						||
			name += ' (' + unicode(nb_connected_contact) + ')'
 | 
						||
 | 
						||
		# show (account_name) if there are 2 contact with same jid in merged mode
 | 
						||
		if self.regroup:
 | 
						||
			add_acct = False
 | 
						||
			# look through all contacts of all accounts
 | 
						||
			for account_ in gajim.connections:
 | 
						||
				if account_ == account: # useless to add accout name
 | 
						||
					continue
 | 
						||
				for jid_ in gajim.contacts.get_jid_list(account_):
 | 
						||
					contact_ = gajim.contacts.get_first_contact_from_jid(account_,
 | 
						||
						jid_)
 | 
						||
					if contact_.get_shown_name() == contact.get_shown_name() and \
 | 
						||
					(jid_, account_) != (jid, account):
 | 
						||
						add_acct = True
 | 
						||
						break
 | 
						||
				if add_acct:
 | 
						||
					# No need to continue in other account if we already found one
 | 
						||
					break
 | 
						||
			if add_acct:
 | 
						||
				name += ' (' + account + ')'
 | 
						||
 | 
						||
		# add status msg, if not empty, under contact name in the treeview
 | 
						||
		if contact.status and gajim.config.get('show_status_msgs_in_roster'):
 | 
						||
			status = contact.status.strip()
 | 
						||
			if status != '':
 | 
						||
				status = helpers.reduce_chars_newlines(status, max_lines = 1)
 | 
						||
				# escape markup entities and make them small italic and fg color
 | 
						||
				# color is calcuted to be always readable
 | 
						||
				color = gtkgui_helpers._get_fade_color(self.tree, selected, focus)
 | 
						||
				colorstring = '#%04x%04x%04x' % (color.red, color.green, color.blue)
 | 
						||
				name += \
 | 
						||
					'\n<span size="small" style="italic" foreground="%s">%s</span>' \
 | 
						||
					% (colorstring, gobject.markup_escape_text(status))
 | 
						||
 | 
						||
		icon_name = helpers.get_icon_name_to_show(contact, account)
 | 
						||
		# look if another resource has awaiting events
 | 
						||
		for c in contact_instances:
 | 
						||
			c_icon_name = helpers.get_icon_name_to_show(c, account)
 | 
						||
			if c_icon_name in ('event', 'muc_active', 'muc_inactive'):
 | 
						||
				icon_name = c_icon_name
 | 
						||
				break
 | 
						||
 | 
						||
		# Check for events of collapsed (hidden) brothers
 | 
						||
		family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
		is_big_brother = False
 | 
						||
		have_visible_children = False
 | 
						||
		if family:
 | 
						||
			nearby_family, bb_jid, bb_account = \
 | 
						||
				self._get_nearby_family_and_big_brother(family, account)
 | 
						||
			is_big_brother = (jid, account) == (bb_jid, bb_account)
 | 
						||
			iters = self._get_contact_iter(jid, account)	
 | 
						||
			have_visible_children = iters and self.modelfilter.iter_has_child(iters[0])
 | 
						||
 | 
						||
		if have_visible_children:
 | 
						||
			# We are the big brother and have a visible family
 | 
						||
			for child_iter in child_iters:
 | 
						||
				child_path = self.model.get_path(child_iter)
 | 
						||
				path = self.modelfilter.convert_child_path_to_path(child_path)
 | 
						||
 | 
						||
				if not self.tree.row_expanded(path) and icon_name != 'event':
 | 
						||
					iterC = self.model.iter_children(child_iter)
 | 
						||
					while iterC:
 | 
						||
						# a child has awaiting messages?
 | 
						||
						jidC = self.model[iterC][C_JID].decode('utf-8')
 | 
						||
						accountC = self.model[iterC][C_ACCOUNT].decode('utf-8')
 | 
						||
						if len(gajim.events.get_events(accountC, jidC)):
 | 
						||
							icon_name = 'event'
 | 
						||
							break
 | 
						||
						iterC = self.model.iter_next(iterC)
 | 
						||
 | 
						||
				if self.tree.row_expanded(path):
 | 
						||
					state_images = self.get_appropriate_state_images(jid,
 | 
						||
						size = 'opened', icon_name = icon_name)
 | 
						||
				else:
 | 
						||
					state_images = self.get_appropriate_state_images(jid,
 | 
						||
						size = 'closed', icon_name = icon_name)
 | 
						||
				
 | 
						||
				# Expand/collapse icon might differ per iter (group)
 | 
						||
				img = state_images[icon_name]
 | 
						||
				self.model[child_iter][C_IMG] = img
 | 
						||
				self.model[child_iter][C_NAME] = name
 | 
						||
		else:
 | 
						||
			# A normal contact or little brother
 | 
						||
			state_images = self.get_appropriate_state_images(jid,
 | 
						||
				icon_name = icon_name)
 | 
						||
 | 
						||
			# All iters have the same icon (no expand/collapse)
 | 
						||
			img = state_images[icon_name]
 | 
						||
			for child_iter in child_iters:
 | 
						||
				self.model[child_iter][C_IMG] = img
 | 
						||
				self.model[child_iter][C_NAME] = name
 | 
						||
 | 
						||
			# We are a little brother
 | 
						||
			if family and not is_big_brother and not self.starting:
 | 
						||
				self.draw_parent_contact(jid, account)
 | 
						||
 | 
						||
		for group in contact.get_shown_groups():
 | 
						||
			# We need to make sure that _visible_func is called for
 | 
						||
			# our groups otherwise we might not be shown
 | 
						||
			iterG = self._get_group_iter(group, account, model = self.model)
 | 
						||
			if iterG:
 | 
						||
				# it's not self contact
 | 
						||
				self.model[iterG][C_JID] = self.model[iterG][C_JID]
 | 
						||
 | 
						||
		return False
 | 
						||
 | 
						||
 | 
						||
	def draw_avatar(self, jid, account):
 | 
						||
		iters = self._get_contact_iter(jid, account, model = self.model)
 | 
						||
		if not iters or not gajim.config.get('show_avatars_in_roster'):
 | 
						||
			return
 | 
						||
		jid = self.model[iters[0]][C_JID]
 | 
						||
		jid = jid.decode('utf-8')
 | 
						||
		pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(jid)
 | 
						||
		if pixbuf is None or pixbuf == 'ask':
 | 
						||
			scaled_pixbuf = None
 | 
						||
		else:
 | 
						||
			scaled_pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'roster')
 | 
						||
		for child_iter in iters:
 | 
						||
			self.model[child_iter][C_AVATAR_PIXBUF] = scaled_pixbuf
 | 
						||
		return False
 | 
						||
 | 
						||
 | 
						||
	def draw_completely_and_show_if_needed(self, jid, account):
 | 
						||
		'''Draw contact, account and groups of given jid
 | 
						||
		Show contact if it has pending events
 | 
						||
		'''
 | 
						||
		contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
						||
		if not contact:
 | 
						||
			# idle draw or just removed SelfContact
 | 
						||
			return
 | 
						||
 | 
						||
		family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
		if family:
 | 
						||
			# There might be a new big brother
 | 
						||
			self._recalibrate_metacontact_family(family, account)
 | 
						||
		self.draw_contact(jid, account)
 | 
						||
		self.draw_account(account)
 | 
						||
 | 
						||
		for group in contact.get_shown_groups():
 | 
						||
			self.draw_group(group, account)
 | 
						||
			self._adjust_group_expand_collapse_state(group, account)
 | 
						||
 | 
						||
	def _idle_draw_jids_of_account(self, jids, account):
 | 
						||
		'''Draw given contacts and their avatars in a lazy fashion.
 | 
						||
 | 
						||
		Keyword arguments:
 | 
						||
		jids -- a list of jids to draw
 | 
						||
		account -- the corresponding account
 | 
						||
		'''
 | 
						||
		def _draw_all_contacts(jids, account):
 | 
						||
			for jid in jids:
 | 
						||
				self.draw_contact(jid, account)
 | 
						||
				self.draw_avatar(jid, account)
 | 
						||
				yield True
 | 
						||
			yield False
 | 
						||
 | 
						||
		task = _draw_all_contacts(jids, account)
 | 
						||
		gobject.idle_add(task.next)
 | 
						||
 | 
						||
	def setup_and_draw_roster(self):
 | 
						||
		'''create new empty model and draw roster'''
 | 
						||
		self.modelfilter = None
 | 
						||
		#(icon, name, type, jid, account, editable, avatar_pixbuf, padlock_pixbuf)
 | 
						||
		self.model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf,
 | 
						||
			gtk.gdk.Pixbuf)
 | 
						||
 | 
						||
		self.model.set_sort_func(1, self._compareIters)
 | 
						||
		self.model.set_sort_column_id(1, gtk.SORT_ASCENDING)
 | 
						||
		self.modelfilter = self.model.filter_new()
 | 
						||
		self.modelfilter.set_visible_func(self._visible_func)
 | 
						||
		self.modelfilter.connect('row-has-child-toggled',
 | 
						||
			self.on_modelfilter_row_has_child_toggled)
 | 
						||
		self.tree.set_model(self.modelfilter)
 | 
						||
 | 
						||
		for acct in gajim.connections:
 | 
						||
			self.add_account(acct)
 | 
						||
			self.add_account_contacts(acct)
 | 
						||
		# Recalculate column width for ellipsizing
 | 
						||
		self.tree.columns_autosize()
 | 
						||
 | 
						||
 | 
						||
	def select_contact(self, jid, account):
 | 
						||
		'''Select contact in roster. If contact is hidden but has events,
 | 
						||
		show him.'''
 | 
						||
		# Refiltering SHOULD NOT be needed:
 | 
						||
		# When a contact gets a new event he will be redrawn and his
 | 
						||
		# icon changes, so _visible_func WILL be called on him anyway
 | 
						||
		iters = self._get_contact_iter(jid, account)
 | 
						||
		if not iters:
 | 
						||
			# Not visible in roster
 | 
						||
			return
 | 
						||
		path = self.modelfilter.get_path(iters[0])
 | 
						||
		if self.dragging or not gajim.config.get('scroll_roster_to_last_message'):
 | 
						||
			# do not change selection while DND'ing
 | 
						||
			return
 | 
						||
		self.tree.expand_to_path(path)
 | 
						||
		self.tree.scroll_to_cell(path)
 | 
						||
		self.tree.set_cursor(path)
 | 
						||
 | 
						||
 | 
						||
	def _adjust_account_expand_collapse_state(self, account):
 | 
						||
		'''Expand/collapse account row based on self.collapsed_rows'''
 | 
						||
		iterA = self._get_account_iter(account)
 | 
						||
		if not iterA:
 | 
						||
			# thank you modelfilter
 | 
						||
			return
 | 
						||
		path = self.modelfilter.get_path(iterA)
 | 
						||
		if account in self.collapsed_rows:
 | 
						||
			self.tree.collapse_row(path)
 | 
						||
		else:
 | 
						||
			self.tree.expand_row(path, False)
 | 
						||
		return False
 | 
						||
 | 
						||
 | 
						||
	def _adjust_group_expand_collapse_state(self, group, account):
 | 
						||
		'''Expand/collapse group row based on self.collapsed_rows'''
 | 
						||
		iterG = self._get_group_iter(group, account)
 | 
						||
		if not iterG:
 | 
						||
			# Group not visible
 | 
						||
			return
 | 
						||
		path = self.modelfilter.get_path(iterG)
 | 
						||
		if account + group in self.collapsed_rows:
 | 
						||
			self.tree.collapse_row(path)
 | 
						||
		else:
 | 
						||
			self.tree.expand_row(path, False)
 | 
						||
		return False
 | 
						||
 | 
						||
##############################################################################
 | 
						||
### Roster and Modelfilter handling
 | 
						||
##############################################################################
 | 
						||
 | 
						||
	def _search_roster_func(self, model, column, key, titer):
 | 
						||
		if model[titer][C_NAME].decode('utf-8').lower().startswith(
 | 
						||
		gobject.markup_escape_text(key.lower())):
 | 
						||
			return False
 | 
						||
		return True
 | 
						||
 | 
						||
	def refilter_shown_roster_items(self):
 | 
						||
		self.filtering = True
 | 
						||
		self.modelfilter.refilter()
 | 
						||
		self.filtering = False
 | 
						||
 | 
						||
	def contact_has_pending_roster_events(self, contact, account):
 | 
						||
		'''Return True if the contact or one if it resources has pending events'''
 | 
						||
		# jid has pending events
 | 
						||
		if gajim.events.get_nb_roster_events(account, contact.jid) > 0:
 | 
						||
			return True
 | 
						||
		# check events of all resources
 | 
						||
		for contact_ in gajim.contacts.get_contacts(account, contact.jid):
 | 
						||
			if contact_.resource and gajim.events.get_nb_roster_events(account,
 | 
						||
			contact_.get_full_jid()) > 0:
 | 
						||
				return True
 | 
						||
		return False
 | 
						||
 | 
						||
	def contact_is_visible(self, contact, account):
 | 
						||
		if self.contact_has_pending_roster_events(contact, account):
 | 
						||
			return True
 | 
						||
 | 
						||
		if contact.ask:
 | 
						||
			return True
 | 
						||
 | 
						||
		if contact.show in ('offline', 'error'):
 | 
						||
			if contact.jid in gajim.to_be_removed[account]:
 | 
						||
				return True
 | 
						||
			return False
 | 
						||
		return True
 | 
						||
 | 
						||
	def _visible_func(self, model, titer):
 | 
						||
		'''Determine whether iter should be visible in the treeview'''
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if not type_:
 | 
						||
			return False
 | 
						||
		if type_ == 'account':
 | 
						||
			# Always show account
 | 
						||
			return True
 | 
						||
 | 
						||
		account = model[titer][C_ACCOUNT]
 | 
						||
		if not account:
 | 
						||
			return False
 | 
						||
 | 
						||
		account = account.decode('utf-8')
 | 
						||
		jid = model[titer][C_JID]
 | 
						||
		if not jid:
 | 
						||
			return False
 | 
						||
		jid = jid.decode('utf-8')
 | 
						||
		if type_ == 'group':
 | 
						||
			group = jid
 | 
						||
			if group == _('Transports'):
 | 
						||
				return gajim.config.get('show_transports_group')
 | 
						||
			if gajim.config.get('showoffline'):
 | 
						||
				return True
 | 
						||
 | 
						||
			if self.regroup:
 | 
						||
				# C_ACCOUNT for groups depends on the order
 | 
						||
				# accounts were connected
 | 
						||
				# Check all accounts for online group contacts
 | 
						||
				accounts = gajim.contacts.get_accounts()
 | 
						||
			else:
 | 
						||
				accounts = [account]
 | 
						||
			for _acc in accounts:
 | 
						||
				for contact in gajim.contacts.iter_contacts(_acc):
 | 
						||
					# Is this contact in this group ? (last part of if check if it's 
 | 
						||
					# self contact) 
 | 
						||
					if group in contact.get_shown_groups():
 | 
						||
						if self.contact_is_visible(contact, _acc):
 | 
						||
							return True
 | 
						||
			return False
 | 
						||
		if type_ == 'contact':
 | 
						||
			if gajim.config.get('showoffline'):
 | 
						||
				return True
 | 
						||
			bb_jid = None
 | 
						||
			bb_account = None
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
			if family:
 | 
						||
				nearby_family, bb_jid, bb_account = \
 | 
						||
					self._get_nearby_family_and_big_brother(family, account)
 | 
						||
			if (bb_jid, bb_account) == (jid, account):
 | 
						||
				# Show the big brother if a child has pending events
 | 
						||
				for data in nearby_family:
 | 
						||
					jid = data['jid']
 | 
						||
					account = data['account']
 | 
						||
					contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
						||
					if contact and self.contact_is_visible(contact, account):
 | 
						||
						return True
 | 
						||
				return False 
 | 
						||
			else:
 | 
						||
				contact = gajim.contacts.get_first_contact_from_jid(account, jid)
 | 
						||
				return self.contact_is_visible(contact, account)
 | 
						||
		if type_ == 'agent':
 | 
						||
			return gajim.config.get('show_transports_group')
 | 
						||
		return True
 | 
						||
 | 
						||
	def _compareIters(self, model, iter1, iter2, data = None):
 | 
						||
		'''Compare two iters to sort them'''
 | 
						||
		name1 = model[iter1][C_NAME]
 | 
						||
		name2 = model[iter2][C_NAME]
 | 
						||
		if not name1 or not name2:
 | 
						||
			return 0
 | 
						||
		name1 = name1.decode('utf-8')
 | 
						||
		name2 = name2.decode('utf-8')
 | 
						||
		type1 = model[iter1][C_TYPE]
 | 
						||
		type2 = model[iter2][C_TYPE]
 | 
						||
		if type1 == 'self_contact':
 | 
						||
			return -1
 | 
						||
		if type2 == 'self_contact':
 | 
						||
			return 1
 | 
						||
		if type1 == 'group':
 | 
						||
			name1 = model[iter1][C_JID]
 | 
						||
			name2 = model[iter2][C_JID]
 | 
						||
			if name1 == _('Transports'):
 | 
						||
				return 1
 | 
						||
			if name2 == _('Transports'):
 | 
						||
				return -1
 | 
						||
			if name1 == _('Not in Roster'):
 | 
						||
				return 1
 | 
						||
			if name2 == _('Not in Roster'):
 | 
						||
				return -1
 | 
						||
			if name1 == _('Groupchats'):
 | 
						||
				return 1
 | 
						||
			if name2 == _('Groupchats'):
 | 
						||
				return -1
 | 
						||
		account1 = model[iter1][C_ACCOUNT]
 | 
						||
		account2 = model[iter2][C_ACCOUNT]
 | 
						||
		if not account1 or not account2:
 | 
						||
			return 0
 | 
						||
		account1 = account1.decode('utf-8')
 | 
						||
		account2 = account2.decode('utf-8')
 | 
						||
		if type1 == 'account':
 | 
						||
			if account1 < account2:
 | 
						||
				return -1
 | 
						||
			return 1
 | 
						||
		jid1 = model[iter1][C_JID].decode('utf-8')
 | 
						||
		jid2 = model[iter2][C_JID].decode('utf-8')
 | 
						||
		if type1 == 'contact':
 | 
						||
			lcontact1 = gajim.contacts.get_contacts(account1, jid1)
 | 
						||
			contact1 = gajim.contacts.get_first_contact_from_jid(account1, jid1)
 | 
						||
			if not contact1:
 | 
						||
				return 0
 | 
						||
			name1 = contact1.get_shown_name()
 | 
						||
		if type2 == 'contact':
 | 
						||
			lcontact2 = gajim.contacts.get_contacts(account2, jid2)
 | 
						||
			contact2 = gajim.contacts.get_first_contact_from_jid(account2, jid2)
 | 
						||
			if not contact2:
 | 
						||
				return 0
 | 
						||
			name2 = contact2.get_shown_name()
 | 
						||
		# We first compare by show if sort_by_show is True or if it's a child
 | 
						||
		# contact
 | 
						||
		if type1 == 'contact' and type2 == 'contact' and \
 | 
						||
		gajim.config.get('sort_by_show'):
 | 
						||
			cshow = {'online':0, 'chat': 1, 'away': 2, 'xa': 3, 'dnd': 4,
 | 
						||
				'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
 | 
						||
			s = self.get_show(lcontact1)
 | 
						||
			if s in cshow:
 | 
						||
				show1 = cshow[s]
 | 
						||
			else:
 | 
						||
				show1 = 9
 | 
						||
			s = self.get_show(lcontact2)
 | 
						||
			if s in cshow:
 | 
						||
				show2 = cshow[s]
 | 
						||
			else:
 | 
						||
				show2 = 9
 | 
						||
			removing1 = False
 | 
						||
			removing2 = False
 | 
						||
			if show1 == 6 and jid1 in gajim.to_be_removed[account1]:
 | 
						||
				removing1 = True
 | 
						||
			if show2 == 6 and jid2 in gajim.to_be_removed[account2]:
 | 
						||
				removing2 = True
 | 
						||
			if removing1 and not removing2:
 | 
						||
				return 1
 | 
						||
			if removing2 and not removing1:
 | 
						||
				return -1
 | 
						||
			if show1 < show2:
 | 
						||
				return -1
 | 
						||
			elif show1 > show2:
 | 
						||
				return 1
 | 
						||
		# We compare names
 | 
						||
		if name1.lower() < name2.lower():
 | 
						||
			return -1
 | 
						||
		if name2.lower() < name1.lower():
 | 
						||
			return 1
 | 
						||
		if type1 == 'contact' and type2 == 'contact':
 | 
						||
			# We compare account names
 | 
						||
			if account1.lower() < account2.lower():
 | 
						||
				return -1
 | 
						||
			if account2.lower() < account1.lower():
 | 
						||
				return 1
 | 
						||
			# We compare jids
 | 
						||
			if jid1.lower() < jid2.lower():
 | 
						||
				return -1
 | 
						||
			if jid2.lower() < jid1.lower():
 | 
						||
				return 1
 | 
						||
		return 0
 | 
						||
 | 
						||
################################################################################
 | 
						||
### FIXME: Methods that don't belong to roster window...
 | 
						||
###		... atleast not in there current form
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def fire_up_unread_messages_events(self, account):
 | 
						||
		'''reads from db the unread messages, and fire them up, and
 | 
						||
		if we find very old unread messages, delete them from unread table'''
 | 
						||
		results = gajim.logger.get_unread_msgs()
 | 
						||
		for result in results:
 | 
						||
			jid = result[4]
 | 
						||
			if gajim.contacts.get_first_contact_from_jid(account, jid):
 | 
						||
				# We have this jid in our contacts list
 | 
						||
				# XXX unread messages should probably have their session saved with
 | 
						||
				# them
 | 
						||
				session = gajim.connections[account].make_new_session(jid)
 | 
						||
 | 
						||
				tim = time.localtime(float(result[2]))
 | 
						||
				session.roster_message(jid, result[1], tim, msg_type='chat',
 | 
						||
					msg_id=result[0])
 | 
						||
 | 
						||
			elif (time.time() - result[2]) > 2592000:
 | 
						||
				# ok, here we see that we have a message in unread messages table
 | 
						||
				# that is older than a month. It is probably from someone not in our
 | 
						||
				# roster for accounts we usually launch, so we will delete this id
 | 
						||
				# from unread message tables.
 | 
						||
				gajim.logger.set_read_messages([result[0]])
 | 
						||
 | 
						||
	def fill_contacts_and_groups_dicts(self, array, account):
 | 
						||
		'''fill gajim.contacts and gajim.groups'''
 | 
						||
		# FIXME: This function needs to be splitted
 | 
						||
		# Most of the logic SHOULD NOT be done at GUI level
 | 
						||
		if account not in gajim.contacts.get_accounts():
 | 
						||
			gajim.contacts.add_account(account)
 | 
						||
		if not gajim.groups.has_key(account):
 | 
						||
			gajim.groups[account] = {}
 | 
						||
		for jid in array.keys():
 | 
						||
			# Remove the contact in roster. It might has changed
 | 
						||
			self.remove_contact(jid, account, force = True)
 | 
						||
			# Remove old Contact instances
 | 
						||
			gajim.contacts.remove_jid(account, jid, remove_meta=False)
 | 
						||
			jids = jid.split('/')
 | 
						||
			# get jid
 | 
						||
			ji = jids[0]
 | 
						||
			# get resource
 | 
						||
			resource = ''
 | 
						||
			if len(jids) > 1:
 | 
						||
				resource = '/'.join(jids[1:])
 | 
						||
			# get name
 | 
						||
			name = array[jid]['name'] or ''
 | 
						||
			show = 'offline' # show is offline by default
 | 
						||
			status = '' # no status message by default
 | 
						||
 | 
						||
			keyID = ''
 | 
						||
			attached_keys = gajim.config.get_per('accounts', account,
 | 
						||
				'attached_gpg_keys').split()
 | 
						||
			if jid in attached_keys:
 | 
						||
				keyID = attached_keys[attached_keys.index(jid) + 1]
 | 
						||
 | 
						||
			if gajim.jid_is_transport(jid):
 | 
						||
				array[jid]['groups'] = [_('Transports')]
 | 
						||
			contact1 = gajim.contacts.create_contact(jid=ji, name=name,
 | 
						||
				groups=array[jid]['groups'], show=show, status=status,
 | 
						||
				sub=array[jid]['subscription'], ask=array[jid]['ask'],
 | 
						||
				resource=resource, keyID=keyID)
 | 
						||
			gajim.contacts.add_contact(account, contact1)
 | 
						||
 | 
						||
			if gajim.config.get('ask_avatars_on_startup'):
 | 
						||
				pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(ji)
 | 
						||
				if pixbuf == 'ask':
 | 
						||
					transport = gajim.get_transport_name_from_jid(contact1.jid)
 | 
						||
					if not transport or gajim.jid_is_transport(contact1.jid):
 | 
						||
						jid_with_resource = contact1.jid
 | 
						||
						if contact1.resource:
 | 
						||
							jid_with_resource += '/' + contact1.resource
 | 
						||
						gajim.connections[account].request_vcard(jid_with_resource)
 | 
						||
					else:
 | 
						||
						host = gajim.get_server_from_jid(contact1.jid)
 | 
						||
						if not gajim.transport_avatar[account].has_key(host):
 | 
						||
							gajim.transport_avatar[account][host] = [contact1.jid]
 | 
						||
						else:
 | 
						||
							gajim.transport_avatar[account][host].append(contact1.jid)
 | 
						||
 | 
						||
			# If we already have chat windows opened, update them with new contact
 | 
						||
			# instance
 | 
						||
			chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
 | 
						||
			if chat_control:
 | 
						||
				chat_control.contact = contact1
 | 
						||
 | 
						||
	def _change_awn_icon_status(self, status):
 | 
						||
		if not dbus_support.supported:
 | 
						||
			# do nothing if user doesn't have D-Bus bindings
 | 
						||
			return
 | 
						||
		try:
 | 
						||
			bus = dbus.SessionBus()
 | 
						||
			if not 'com.google.code.Awn' in bus.list_names():
 | 
						||
				# Awn is not installed
 | 
						||
				return
 | 
						||
		except:
 | 
						||
			return
 | 
						||
		iconset = gajim.config.get('iconset')
 | 
						||
		prefix = os.path.join(helpers.get_iconset_path(iconset), '32x32')
 | 
						||
		if status in ('chat', 'away', 'xa', 'dnd', 'invisible', 'offline'):
 | 
						||
			status = status + '.png'
 | 
						||
		elif status == 'online':
 | 
						||
			prefix = os.path.join(gajim.DATA_DIR, 'pixmaps')
 | 
						||
			status = 'gajim.png'
 | 
						||
		path = os.path.join(prefix, status)
 | 
						||
		try:
 | 
						||
			obj = bus.get_object('com.google.code.Awn', '/com/google/code/Awn')
 | 
						||
			awn = dbus.Interface(obj, 'com.google.code.Awn')
 | 
						||
			awn.SetTaskIconByName('Gajim', os.path.abspath(path))
 | 
						||
		except Exception, e:
 | 
						||
			pass
 | 
						||
 | 
						||
	def music_track_changed(self, unused_listener, music_track_info,
 | 
						||
	account=''):
 | 
						||
		from common import pep
 | 
						||
		if account == '':
 | 
						||
			accounts = gajim.connections.keys()
 | 
						||
		if music_track_info is None:
 | 
						||
			artist = ''
 | 
						||
			title = ''
 | 
						||
			source = ''
 | 
						||
			track = ''
 | 
						||
			length = ''
 | 
						||
		elif hasattr(music_track_info, 'paused') and music_track_info.paused == 0:
 | 
						||
			artist = ''
 | 
						||
			title = ''
 | 
						||
			source = ''
 | 
						||
			track = ''
 | 
						||
			length = ''
 | 
						||
		else:
 | 
						||
			artist = music_track_info.artist
 | 
						||
			title = music_track_info.title
 | 
						||
			source = music_track_info.album
 | 
						||
		if account == '':
 | 
						||
			for account in accounts:
 | 
						||
				if not gajim.account_is_connected(account):
 | 
						||
					continue
 | 
						||
				if not gajim.connections[account].pep_supported:
 | 
						||
					continue
 | 
						||
				if gajim.connections[account].music_track_info == music_track_info:
 | 
						||
					continue
 | 
						||
				pep.user_send_tune(account, artist, title, source)
 | 
						||
				gajim.connections[account].music_track_info = music_track_info
 | 
						||
		elif gajim.connections[account].pep_supported:
 | 
						||
			if gajim.connections[account].music_track_info != music_track_info:
 | 
						||
				pep.user_send_tune(account, artist, title, source)
 | 
						||
				gajim.connections[account].music_track_info = music_track_info
 | 
						||
 | 
						||
	def connected_rooms(self, account):
 | 
						||
		if account in gajim.gc_connected[account].values():
 | 
						||
			return True
 | 
						||
		return False
 | 
						||
 | 
						||
	def auto_join_bookmarks(self, account):
 | 
						||
		'''autojoin bookmarks that have 'auto join' on for this account'''
 | 
						||
		for bm in gajim.connections[account].bookmarks:
 | 
						||
			if bm['autojoin'] in ('1', 'true'):
 | 
						||
				jid = bm['jid']
 | 
						||
				if not gajim.gc_connected[account].has_key(jid) or\
 | 
						||
					not gajim.gc_connected[account][jid]:
 | 
						||
					# we are not already connected
 | 
						||
					minimize = bm['minimize'] in ('1', 'true')
 | 
						||
					gajim.interface.join_gc_room(account, jid, bm['nick'],
 | 
						||
					bm['password'], minimize = minimize)
 | 
						||
 | 
						||
	def on_event_removed(self, event_list):
 | 
						||
		'''Remove contacts on last events removed.
 | 
						||
 | 
						||
		Only performed if removal was requested before 	but the contact
 | 
						||
		still had pending events
 | 
						||
		'''
 | 
						||
		contact_list = ((event.jid.split('/')[0], event.account) for event in \
 | 
						||
			event_list)
 | 
						||
 | 
						||
		for jid, account in contact_list:
 | 
						||
			self.draw_contact(jid, account)
 | 
						||
			# Remove contacts in roster if removal was requested
 | 
						||
			key = (jid, account)
 | 
						||
			if key in self.contacts_to_be_removed.keys():
 | 
						||
				del self.contacts_to_be_removed[key]
 | 
						||
				# Remove contact will delay removal if there are more events pending
 | 
						||
				self.remove_contact(jid, account, backend=True)
 | 
						||
		self.show_title()
 | 
						||
 | 
						||
	def open_event(self, account, jid, event):
 | 
						||
		'''If an event was handled, return True, else return False'''
 | 
						||
		data = event.parameters
 | 
						||
		ft = gajim.interface.instances['file_transfers']
 | 
						||
		event = gajim.events.get_first_event(account, jid, event.type_)
 | 
						||
		if event.type_ == 'normal':
 | 
						||
			dialogs.SingleMessageWindow(account, jid,
 | 
						||
				action='receive', from_whom=jid, subject=data[1], message=data[0],
 | 
						||
				resource=data[5], session=data[8], form_node=data[9])
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		elif event.type_ == 'file-request':
 | 
						||
			contact = gajim.contacts.get_contact_with_highest_priority(account,
 | 
						||
				jid)
 | 
						||
			ft.show_file_request(account, contact, data)
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		elif event.type_ in ('file-request-error', 'file-send-error'):
 | 
						||
			ft.show_send_error(data)
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		elif event.type_ in ('file-error', 'file-stopped'):
 | 
						||
			ft.show_stopped(jid, data)
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		elif event.type_ == 'file-completed':
 | 
						||
			ft.show_completed(jid, data)
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		elif event.type_ == 'gc-invitation':
 | 
						||
			dialogs.InvitationReceivedDialog(account, data[0], jid, data[2],
 | 
						||
				data[1])
 | 
						||
			gajim.events.remove_events(account, jid, event)
 | 
						||
			return True
 | 
						||
		return False
 | 
						||
 | 
						||
################################################################################
 | 
						||
### This and that... random.
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def show_roster_vbox(self, active):
 | 
						||
		if active:
 | 
						||
			self.xml.get_widget('roster_vbox2').show()
 | 
						||
		else:
 | 
						||
			self.xml.get_widget('roster_vbox2').hide()
 | 
						||
 | 
						||
 | 
						||
	def show_tooltip(self, contact):
 | 
						||
		pointer = self.tree.get_pointer()
 | 
						||
		props = self.tree.get_path_at_pos(pointer[0], pointer[1])
 | 
						||
		# check if the current pointer is at the same path
 | 
						||
		# as it was before setting the timeout
 | 
						||
		if props and self.tooltip.id == props[0]:
 | 
						||
			# bounding rectangle of coordinates for the cell within the treeview
 | 
						||
			rect = self.tree.get_cell_area(props[0], props[1])
 | 
						||
 | 
						||
			# position of the treeview on the screen
 | 
						||
			position = self.tree.window.get_origin()
 | 
						||
			self.tooltip.show_tooltip(contact, rect.height, position[1] + rect.y)
 | 
						||
		else:
 | 
						||
			self.tooltip.hide_tooltip()
 | 
						||
 | 
						||
 | 
						||
	def authorize(self, widget, jid, account):
 | 
						||
		'''Authorize a contact (by re-sending auth menuitem)'''
 | 
						||
		gajim.connections[account].send_authorization(jid)
 | 
						||
		dialogs.InformationDialog(_('Authorization has been sent'),
 | 
						||
			_('Now "%s" will know your status.') %jid)
 | 
						||
 | 
						||
	def req_sub(self, widget, jid, txt, account, groups = [], nickname = None,
 | 
						||
	auto_auth = False):
 | 
						||
		'''Request subscription to a contact'''
 | 
						||
		gajim.connections[account].request_subscription(jid, txt, nickname,
 | 
						||
			groups, auto_auth, gajim.nicks[account])
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if not contact:
 | 
						||
			keyID = ''
 | 
						||
			attached_keys = gajim.config.get_per('accounts', account,
 | 
						||
				'attached_gpg_keys').split()
 | 
						||
			if jid in attached_keys:
 | 
						||
				keyID = attached_keys[attached_keys.index(jid) + 1]
 | 
						||
			contact = gajim.contacts.create_contact(jid=jid, name=nickname,
 | 
						||
				groups=groups, show='requested', status='', ask='none',
 | 
						||
				sub='subscribe', keyID=keyID)
 | 
						||
			gajim.contacts.add_contact(account, contact)
 | 
						||
		else:
 | 
						||
			if not _('Not in Roster') in contact.get_shown_groups():
 | 
						||
				dialogs.InformationDialog(_('Subscription request has been sent'),
 | 
						||
					_('If "%s" accepts this request you will know his or her status.'
 | 
						||
					) % jid)
 | 
						||
				return
 | 
						||
			self.remove_contact(contact.jid, account)
 | 
						||
			contact.groups = groups
 | 
						||
			if nickname:
 | 
						||
				contact.name = nickname
 | 
						||
		self.add_contact(jid, account)
 | 
						||
 | 
						||
	def revoke_auth(self, widget, jid, account):
 | 
						||
		'''Revoke a contact's authorization'''
 | 
						||
		gajim.connections[account].refuse_authorization(jid)
 | 
						||
		dialogs.InformationDialog(_('Authorization has been removed'),
 | 
						||
			_('Now "%s" will always see you as offline.') %jid)
 | 
						||
 | 
						||
	def set_connecting_state(self, account):
 | 
						||
		child_iterA = self._get_account_iter(account, self.model)
 | 
						||
		if child_iterA:
 | 
						||
			self.model[child_iterA][0] = \
 | 
						||
				gajim.interface.jabber_state_images['16']['connecting']
 | 
						||
		if gajim.interface.systray_enabled:
 | 
						||
			gajim.interface.systray.change_status('connecting')
 | 
						||
 | 
						||
	def send_status(self, account, status, txt, auto = False, to = None):
 | 
						||
		child_iterA = self._get_account_iter(account, self.model)
 | 
						||
		if status != 'offline':
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				self.set_connecting_state(account)
 | 
						||
 | 
						||
				if not gajim.connections[account].password:
 | 
						||
					passphrase = ''
 | 
						||
					text = _('Enter your password for account %s') % account
 | 
						||
					if passwords.USER_HAS_GNOMEKEYRING and \
 | 
						||
					not passwords.USER_USES_GNOMEKEYRING:
 | 
						||
						text += '\n' + _('Gnome Keyring is installed but not \
 | 
						||
							correctly started (environment variable probably not \
 | 
						||
							correctly set)')
 | 
						||
					w = dialogs.PassphraseDialog(_('Password Required'), text,
 | 
						||
						_('Save password'))
 | 
						||
					passphrase, save = w.run()
 | 
						||
					if passphrase == -1:
 | 
						||
						if child_iterA:
 | 
						||
							self.model[child_iterA][0] = \
 | 
						||
								gajim.interface.jabber_state_images['16']['offline']
 | 
						||
						if gajim.interface.systray_enabled:
 | 
						||
							gajim.interface.systray.change_status('offline')
 | 
						||
						self.update_status_combobox()
 | 
						||
						return
 | 
						||
					gajim.connections[account].password = passphrase
 | 
						||
					if save:
 | 
						||
						gajim.config.set_per('accounts', account, 'savepass', True)
 | 
						||
						passwords.save_password(account, passphrase)
 | 
						||
 | 
						||
				keyid = gajim.config.get_per('accounts', account, 'keyid')
 | 
						||
				if keyid and not gajim.connections[account].gpg:
 | 
						||
					dialog = dialogs.WarningDialog(_('GPG is not usable'),
 | 
						||
						_('You will be connected to %s without OpenPGP.') % account)
 | 
						||
 | 
						||
		if gajim.account_is_connected(account):
 | 
						||
			if status == 'online' and gajim.interface.sleeper.getState() != \
 | 
						||
			common.sleepy.STATE_UNKNOWN:
 | 
						||
				gajim.sleeper_state[account] = 'online'
 | 
						||
			elif gajim.sleeper_state[account] not in ('autoaway', 'autoxa'):
 | 
						||
				gajim.sleeper_state[account] = 'off'
 | 
						||
 | 
						||
		if to:
 | 
						||
			gajim.connections[account].send_custom_status(status, txt, to)
 | 
						||
		else:
 | 
						||
			was_invisible = gajim.connections[account].connected == \
 | 
						||
				gajim.SHOW_LIST.index('invisible')
 | 
						||
			gajim.connections[account].change_status(status, txt, auto)
 | 
						||
 | 
						||
			if gajim.interface.status_sent_to_users.has_key(account):
 | 
						||
				gajim.interface.status_sent_to_users[account] = {}
 | 
						||
			if gajim.interface.status_sent_to_groups.has_key(account):
 | 
						||
				gajim.interface.status_sent_to_groups[account] = {}
 | 
						||
			for gc_control in gajim.interface.msg_win_mgr.get_controls(
 | 
						||
			message_control.TYPE_GC) + \
 | 
						||
			gajim.interface.minimized_controls[account].values():
 | 
						||
				if gc_control.account == account:
 | 
						||
					if gajim.gc_connected[account][gc_control.room_jid]:
 | 
						||
						gajim.connections[account].send_gc_status(gc_control.nick,
 | 
						||
							gc_control.room_jid, status, txt)
 | 
						||
					else:
 | 
						||
						# for some reason, we are not connected to the room even if
 | 
						||
						# tab is opened, send initial join_gc()
 | 
						||
						gajim.connections[account].join_gc(gc_control.nick,
 | 
						||
						gc_control.room_jid, None)
 | 
						||
			if was_invisible and status != 'offline':
 | 
						||
				# We come back from invisible, join bookmarks
 | 
						||
				gajim.interface.auto_join_bookmarks(account)
 | 
						||
 | 
						||
 | 
						||
	def chg_contact_status(self, contact, show, status, account):
 | 
						||
		'''When a contact changes his or her status'''
 | 
						||
		contact_instances = gajim.contacts.get_contacts(account, contact.jid)
 | 
						||
		contact.show = show
 | 
						||
		contact.status = status
 | 
						||
		# name is to show in conversation window
 | 
						||
		name = contact.get_shown_name()
 | 
						||
		
 | 
						||
		# The contact has several resources
 | 
						||
		if len(contact_instances) > 1:
 | 
						||
			if contact.resource != '':
 | 
						||
				name += '/' + contact.resource
 | 
						||
 | 
						||
			# Remove resource when going offline
 | 
						||
			if show in ('offline', 'error') and \
 | 
						||
			len(gajim.events.get_events(account, contact.get_full_jid())) == 0:
 | 
						||
				jid_with_resource = contact.jid + '/' + contact.resource
 | 
						||
				if gajim.interface.msg_win_mgr.has_window(jid_with_resource,
 | 
						||
				account):
 | 
						||
					win = gajim.interface.msg_win_mgr.get_window(jid_with_resource,
 | 
						||
						account)
 | 
						||
					ctrl = win.get_control(jid_with_resource, account)
 | 
						||
					if ctrl:
 | 
						||
						ctrl.update_ui()
 | 
						||
						win.redraw_tab(ctrl)
 | 
						||
				gajim.contacts.remove_contact(account, contact)
 | 
						||
 | 
						||
		elif contact.jid == gajim.get_jid_from_account(account) and \
 | 
						||
		show in ('offline', 'error'):
 | 
						||
			# SelfContact went offline. Remove him when last pending message was read
 | 
						||
			self.remove_contact(contact.jid, account, backend=True)
 | 
						||
 | 
						||
		# print status in chat window and update status/GPG image
 | 
						||
		if gajim.interface.msg_win_mgr.has_window(contact.jid, account):
 | 
						||
			win = gajim.interface.msg_win_mgr.get_window(contact.jid, account)
 | 
						||
			uf_show = helpers.get_uf_show(show)
 | 
						||
 | 
						||
			ctrl = win.get_control(contact.jid, account)
 | 
						||
			if ctrl:
 | 
						||
				ctrl.contact = gajim.contacts.get_contact_with_highest_priority(
 | 
						||
					account, contact.jid)
 | 
						||
				ctrl.update_ui()
 | 
						||
				win.redraw_tab(ctrl)
 | 
						||
 | 
						||
				ctrl.print_conversation(_('%s is now %s') % (name, uf_show),
 | 
						||
					'status')
 | 
						||
				if status:
 | 
						||
					ctrl.print_conversation(' (', 'status', simple=True)
 | 
						||
					ctrl.print_conversation('%s' % (status), 'status', simple=True)
 | 
						||
					ctrl.print_conversation(')', 'status', simple=True)
 | 
						||
 | 
						||
		# unset custom status
 | 
						||
		if gajim.interface.status_sent_to_users.has_key(account) and \
 | 
						||
			contact.jid in gajim.interface.status_sent_to_users[account]:
 | 
						||
			del gajim.interface.status_sent_to_users[account][contact.jid]
 | 
						||
 | 
						||
		# Redraw everything and select the sender
 | 
						||
		self.draw_completely_and_show_if_needed(contact.jid, account)
 | 
						||
 | 
						||
 | 
						||
	def on_status_changed(self, account, status):
 | 
						||
		'''the core tells us that our status has changed'''
 | 
						||
		if account not in gajim.contacts.get_accounts():
 | 
						||
			return
 | 
						||
		child_iterA = self._get_account_iter(account, self.model)
 | 
						||
		self.set_account_status_icon(account)
 | 
						||
		if status == 'offline':
 | 
						||
			if self.quit_on_next_offline > -1:
 | 
						||
				# we want to quit, we are waiting for all accounts to be offline
 | 
						||
				self.quit_on_next_offline -= 1
 | 
						||
				if self.quit_on_next_offline < 1:
 | 
						||
					# all accounts offline, quit
 | 
						||
					self.quit_gtkgui_interface()
 | 
						||
			else:
 | 
						||
				# No need to redraw contacts if we're quitting
 | 
						||
				if child_iterA:
 | 
						||
					self.model[child_iterA][C_AVATAR_PIXBUF] = None
 | 
						||
				if gajim.con_types.has_key(account):
 | 
						||
					gajim.con_types[account] = None
 | 
						||
				for jid in gajim.contacts.get_jid_list(account):
 | 
						||
					lcontact = gajim.contacts.get_contacts(account, jid)
 | 
						||
					for contact in [c for c in lcontact if (c.show != 'offline' or \
 | 
						||
					c.is_transport())]:
 | 
						||
						self.chg_contact_status(contact, 'offline', '', account)
 | 
						||
			self.actions_menu_needs_rebuild = True
 | 
						||
		self.update_status_combobox()
 | 
						||
		# Force the rebuild now since the on_activates on the menu itself does
 | 
						||
		# not work with the os/x top level menubar
 | 
						||
		if sys.platform == 'darwin':
 | 
						||
			self.make_menu(force = True)
 | 
						||
 | 
						||
	def get_status_message(self, show):
 | 
						||
		if show in gajim.config.get_per('defaultstatusmsg'):
 | 
						||
			if gajim.config.get_per('defaultstatusmsg', show, 'enabled'):
 | 
						||
				return gajim.config.get_per('defaultstatusmsg', show, 'message')
 | 
						||
		if (show == 'online' and not gajim.config.get('ask_online_status')) or \
 | 
						||
		(show in ('offline', 'invisible')
 | 
						||
		and not gajim.config.get('ask_offline_status')):
 | 
						||
			return ''
 | 
						||
		dlg = dialogs.ChangeStatusMessageDialog(show)
 | 
						||
		dlg.window.present() # show it on current workspace
 | 
						||
		message = dlg.run()
 | 
						||
		return message
 | 
						||
 | 
						||
	def change_status(self, widget, account, status):
 | 
						||
		def change(account, status):
 | 
						||
			message = self.get_status_message(status)
 | 
						||
			if message is None:
 | 
						||
				# user pressed Cancel to change status message dialog
 | 
						||
				return
 | 
						||
			self.send_status(account, status, message)
 | 
						||
 | 
						||
		if status == 'invisible' and self.connected_rooms(account):
 | 
						||
			dialogs.ConfirmationDialog(
 | 
						||
				_('You are participating in one or more group chats'),
 | 
						||
				_('Changing your status to invisible will result in disconnection '
 | 
						||
				'from those group chats. Are you sure you want to go invisible?'),
 | 
						||
				on_response_ok = (change, account, status))
 | 
						||
		else:
 | 
						||
			change(account, status)
 | 
						||
 | 
						||
	def update_status_combobox(self):
 | 
						||
		# table to change index in connection.connected to index in combobox
 | 
						||
		table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
 | 
						||
			'xa':3, 'dnd':4, 'invisible':5}
 | 
						||
 | 
						||
		# we check if there are more options in the combobox that it should
 | 
						||
		# if yes, we remove the first ones
 | 
						||
		while len(self.status_combobox.get_model()) > len(table)+2:
 | 
						||
			self.status_combobox.remove_text(0)
 | 
						||
 | 
						||
		show = helpers.get_global_show()
 | 
						||
		# temporarily block signal in order not to send status that we show
 | 
						||
		# in the combobox
 | 
						||
		self.combobox_callback_active = False
 | 
						||
		if helpers.statuses_unified():
 | 
						||
			self.status_combobox.set_active(table[show])
 | 
						||
		else:
 | 
						||
			uf_show = helpers.get_uf_show(show)
 | 
						||
			liststore = self.status_combobox.get_model()
 | 
						||
			liststore.prepend(['SEPARATOR', None, '', True])
 | 
						||
			status_combobox_text = uf_show + ' (' + _("desync'ed") +')'
 | 
						||
			liststore.prepend([status_combobox_text,
 | 
						||
				gajim.interface.jabber_state_images['16'][show], show, False])
 | 
						||
			self.status_combobox.set_active(0)
 | 
						||
		self._change_awn_icon_status(show)
 | 
						||
		self.combobox_callback_active = True
 | 
						||
		if gajim.interface.systray_enabled:
 | 
						||
			gajim.interface.systray.change_status(show)
 | 
						||
 | 
						||
	def get_show(self, lcontact):
 | 
						||
		prio = lcontact[0].priority
 | 
						||
		show = lcontact[0].show
 | 
						||
		for u in lcontact:
 | 
						||
			if u.priority > prio:
 | 
						||
				prio = u.priority
 | 
						||
				show = u.show
 | 
						||
		return show
 | 
						||
 | 
						||
	def on_message_window_delete(self, win_mgr, msg_win):
 | 
						||
		if gajim.config.get('one_message_window') == 'always_with_roster':
 | 
						||
			self.show_roster_vbox(True)
 | 
						||
			gtkgui_helpers.resize_window(self.window,
 | 
						||
				gajim.config.get('roster_width'),
 | 
						||
				gajim.config.get('roster_height'))
 | 
						||
 | 
						||
	def close_all_from_dict(self, dic):
 | 
						||
		'''close all the windows in the given dictionary'''
 | 
						||
		for w in dic.values():
 | 
						||
			if type(w) == type({}):
 | 
						||
				self.close_all_from_dict(w)
 | 
						||
			else:
 | 
						||
				w.window.destroy()
 | 
						||
 | 
						||
	def close_all(self, account, force = False):
 | 
						||
		'''close all the windows from an account
 | 
						||
		if force is True, do not ask confirmation before closing chat/gc windows
 | 
						||
		'''
 | 
						||
		if account in gajim.interface.instances:
 | 
						||
			self.close_all_from_dict(gajim.interface.instances[account])
 | 
						||
		for ctrl in gajim.interface.msg_win_mgr.get_controls(acct = account):
 | 
						||
			ctrl.parent_win.remove_tab(ctrl, ctrl.parent_win.CLOSE_CLOSE_BUTTON,
 | 
						||
				force = force)
 | 
						||
 | 
						||
	def on_roster_window_delete_event(self, widget, event):
 | 
						||
		'''Main window X button was clicked'''
 | 
						||
		if gajim.interface.systray_enabled and not gajim.config.get(
 | 
						||
		'quit_on_roster_x_button'):
 | 
						||
			self.tooltip.hide_tooltip()
 | 
						||
			self.window.hide()
 | 
						||
		else:
 | 
						||
			self.on_quit_request()
 | 
						||
		return True # do NOT destroy the window
 | 
						||
 | 
						||
	def quit_gtkgui_interface(self):
 | 
						||
		'''When we quit the gtk interface :
 | 
						||
		tell that to the core and exit gtk'''
 | 
						||
		msgwin_width_adjust = 0
 | 
						||
 | 
						||
		# in case show_roster_on_start is False and roster is never shown
 | 
						||
		# window.window is None
 | 
						||
		if self.window.window is not None:
 | 
						||
			x, y = self.window.window.get_root_origin()
 | 
						||
			gajim.config.set('roster_x-position', x)
 | 
						||
			gajim.config.set('roster_y-position', y)
 | 
						||
			width, height = self.window.get_size()
 | 
						||
			# For the width use the size of the vbox containing the tree and
 | 
						||
			# status combo, this will cancel out any hpaned width
 | 
						||
			width = self.xml.get_widget('roster_vbox2').allocation.width
 | 
						||
			gajim.config.set('roster_width', width)
 | 
						||
			gajim.config.set('roster_height', height)
 | 
						||
			if not self.xml.get_widget('roster_vbox2').get_property('visible'):
 | 
						||
				# The roster vbox is hidden, so the message window is larger
 | 
						||
				# then we want to save (i.e. the window will grow every startup)
 | 
						||
				# so adjust.
 | 
						||
				msgwin_width_adjust = -1 * width
 | 
						||
		gajim.config.set('show_roster_on_startup',
 | 
						||
			self.window.get_property('visible'))
 | 
						||
		gajim.interface.msg_win_mgr.shutdown(msgwin_width_adjust)
 | 
						||
 | 
						||
		gajim.config.set('collapsed_rows', '\t'.join(self.collapsed_rows))
 | 
						||
		gajim.interface.save_config()
 | 
						||
		for account in gajim.connections:
 | 
						||
			gajim.connections[account].quit(True)
 | 
						||
			self.close_all(account)
 | 
						||
		if gajim.interface.systray_enabled:
 | 
						||
			gajim.interface.hide_systray()
 | 
						||
		gtk.main_quit()
 | 
						||
 | 
						||
	def on_quit_request(self, widget = None):
 | 
						||
		''' user want to quit. Check if he should be warned about messages
 | 
						||
		pending. Terminate all sessions and send offline to all connected
 | 
						||
		account. We do NOT really quit gajim here '''
 | 
						||
		accounts = gajim.connections.keys()
 | 
						||
		get_msg = False
 | 
						||
		for acct in accounts:
 | 
						||
			if gajim.connections[acct].connected:
 | 
						||
				get_msg = True
 | 
						||
				break
 | 
						||
		if get_msg:
 | 
						||
			message = self.get_status_message('offline')
 | 
						||
			if message is None:
 | 
						||
				# user pressed Cancel to change status message dialog
 | 
						||
				return
 | 
						||
 | 
						||
		# check if we have unread messages
 | 
						||
		unread = gajim.events.get_nb_events()
 | 
						||
		if not gajim.config.get('notify_on_all_muc_messages'):
 | 
						||
			unread_not_to_notify = gajim.events.get_nb_events(['printed_gc_msg'])
 | 
						||
			unread -= unread_not_to_notify
 | 
						||
 | 
						||
		# check if we have recent messages
 | 
						||
		recent = False
 | 
						||
		for win in gajim.interface.msg_win_mgr.windows():
 | 
						||
			for ctrl in win.controls():
 | 
						||
				fjid = ctrl.get_full_jid()
 | 
						||
				if gajim.last_message_time[ctrl.account].has_key(fjid):
 | 
						||
					if time.time() - gajim.last_message_time[ctrl.account][fjid] < 2:
 | 
						||
						recent = True
 | 
						||
						break
 | 
						||
			if recent:
 | 
						||
				break
 | 
						||
 | 
						||
		if unread or recent:
 | 
						||
			dialog = dialogs.ConfirmationDialog(_('You have unread messages'),
 | 
						||
				_('Messages will only be available for reading them later if you'
 | 
						||
				' have history enabled and contact is in your roster.'))
 | 
						||
			if dialog.get_response() != gtk.RESPONSE_OK:
 | 
						||
				return
 | 
						||
 | 
						||
		self.quit_on_next_offline = 0
 | 
						||
		for acct in accounts:
 | 
						||
			if gajim.connections[acct].connected:
 | 
						||
				self.quit_on_next_offline += 1
 | 
						||
				self.send_status(acct, 'offline', message)
 | 
						||
 | 
						||
		if not self.quit_on_next_offline:
 | 
						||
			self.quit_gtkgui_interface()
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Menu and GUI callbacks
 | 
						||
### FIXME: order callbacks in itself...
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def on_actions_menuitem_activate(self, widget):
 | 
						||
		self.make_menu()
 | 
						||
 | 
						||
	def on_edit_menuitem_activate(self, widget):
 | 
						||
		'''need to call make_menu to build profile, avatar item'''
 | 
						||
		self.make_menu()
 | 
						||
 | 
						||
	def on_bookmark_menuitem_activate(self, widget, account, bookmark):
 | 
						||
		gajim.interface.join_gc_room(account, bookmark['jid'], bookmark['nick'],
 | 
						||
			bookmark['password'])
 | 
						||
 | 
						||
	def on_send_server_message_menuitem_activate(self, widget, account):
 | 
						||
		server = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
		server += '/announce/online'
 | 
						||
		dialogs.SingleMessageWindow(account, server, 'send')
 | 
						||
 | 
						||
	def on_xml_console_menuitem_activate(self, widget, account):
 | 
						||
		if gajim.interface.instances[account].has_key('xml_console'):
 | 
						||
			gajim.interface.instances[account]['xml_console'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances[account]['xml_console'] = \
 | 
						||
				dialogs.XMLConsoleWindow(account)
 | 
						||
 | 
						||
	def on_privacy_lists_menuitem_activate(self, widget, account):
 | 
						||
		if gajim.interface.instances[account].has_key('privacy_lists'):
 | 
						||
			gajim.interface.instances[account]['privacy_lists'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances[account]['privacy_lists'] = \
 | 
						||
				dialogs.PrivacyListsWindow(account)
 | 
						||
 | 
						||
	def on_set_motd_menuitem_activate(self, widget, account):
 | 
						||
		server = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
		server += '/announce/motd'
 | 
						||
		dialogs.SingleMessageWindow(account, server, 'send')
 | 
						||
 | 
						||
	def on_update_motd_menuitem_activate(self, widget, account):
 | 
						||
		server = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
		server += '/announce/motd/update'
 | 
						||
		dialogs.SingleMessageWindow(account, server, 'send')
 | 
						||
 | 
						||
	def on_delete_motd_menuitem_activate(self, widget, account):
 | 
						||
		server = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
		server += '/announce/motd/delete'
 | 
						||
		gajim.connections[account].send_motd(server)
 | 
						||
 | 
						||
	def on_history_manager_menuitem_activate(self, widget):
 | 
						||
		if os.name == 'nt':
 | 
						||
			if os.path.exists('history_manager.exe'): # user is running stable
 | 
						||
				helpers.exec_command('history_manager.exe')
 | 
						||
			else: # user is running svn
 | 
						||
				helpers.exec_command('python history_manager.py')
 | 
						||
		else: # Unix user
 | 
						||
			helpers.exec_command('python history_manager.py &')
 | 
						||
 | 
						||
	def on_info(self, widget, contact, account):
 | 
						||
		'''Call vcard_information_window class to display contact's information'''
 | 
						||
		if gajim.connections[account].is_zeroconf:
 | 
						||
			self.on_info_zeroconf(widget, contact, account)
 | 
						||
			return
 | 
						||
 | 
						||
		info = gajim.interface.instances[account]['infos']
 | 
						||
		if info.has_key(contact.jid):
 | 
						||
			info[contact.jid].window.present()
 | 
						||
		else:
 | 
						||
			info[contact.jid] = vcard.VcardWindow(contact, account)
 | 
						||
 | 
						||
	def on_info_zeroconf(self, widget, contact, account):
 | 
						||
		info = gajim.interface.instances[account]['infos']
 | 
						||
		if info.has_key(contact.jid):
 | 
						||
			info[contact.jid].window.present()
 | 
						||
		else:
 | 
						||
			contact = gajim.contacts.get_first_contact_from_jid(account,
 | 
						||
							contact.jid)
 | 
						||
			if contact.show in ('offline', 'error'):
 | 
						||
				# don't show info on offline contacts
 | 
						||
				return
 | 
						||
			info[contact.jid] = vcard.ZeroconfVcardWindow(contact, account)
 | 
						||
 | 
						||
	def on_roster_treeview_leave_notify_event(self, widget, event):
 | 
						||
		props = widget.get_path_at_pos(int(event.x), int(event.y))
 | 
						||
		if self.tooltip.timeout > 0:
 | 
						||
			if not props or self.tooltip.id == props[0]:
 | 
						||
				self.tooltip.hide_tooltip()
 | 
						||
 | 
						||
	def on_roster_treeview_motion_notify_event(self, widget, event):
 | 
						||
		model = widget.get_model()
 | 
						||
		props = widget.get_path_at_pos(int(event.x), int(event.y))
 | 
						||
		if self.tooltip.timeout > 0:
 | 
						||
			if not props or self.tooltip.id != props[0]:
 | 
						||
				self.tooltip.hide_tooltip()
 | 
						||
		if props:
 | 
						||
			[row, col, x, y] = props
 | 
						||
			titer = None
 | 
						||
			try:
 | 
						||
				titer = model.get_iter(row)
 | 
						||
			except:
 | 
						||
				self.tooltip.hide_tooltip()
 | 
						||
				return
 | 
						||
			if model[titer][C_TYPE] in ('contact', 'self_contact'):
 | 
						||
				# we're on a contact entry in the roster
 | 
						||
				account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
				jid = model[titer][C_JID].decode('utf-8')
 | 
						||
				if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
 | 
						||
					self.tooltip.id = row
 | 
						||
					contacts = gajim.contacts.get_contacts(account, jid)
 | 
						||
					connected_contacts = []
 | 
						||
					for c in contacts:
 | 
						||
						if c.show not in ('offline', 'error'):
 | 
						||
							connected_contacts.append(c)
 | 
						||
					if not connected_contacts:
 | 
						||
						# no connected contacts, show the ofline one
 | 
						||
						connected_contacts = contacts
 | 
						||
					self.tooltip.account = account
 | 
						||
					self.tooltip.timeout = gobject.timeout_add(500,
 | 
						||
						self.show_tooltip, connected_contacts)
 | 
						||
			elif model[titer][C_TYPE] == 'groupchat':
 | 
						||
				account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
				jid = model[titer][C_JID].decode('utf-8')
 | 
						||
				if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
 | 
						||
					self.tooltip.id = row
 | 
						||
					contact = gajim.contacts.get_contacts(account, jid)
 | 
						||
					self.tooltip.account = account
 | 
						||
					self.tooltip.timeout = gobject.timeout_add(500,
 | 
						||
						self.show_tooltip, contact)
 | 
						||
			elif model[titer][C_TYPE] == 'account':
 | 
						||
				# we're on an account entry in the roster
 | 
						||
				account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
				if account == 'all':
 | 
						||
					if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
 | 
						||
						self.tooltip.id = row
 | 
						||
						self.tooltip.account = None
 | 
						||
						self.tooltip.timeout = gobject.timeout_add(500,
 | 
						||
							self.show_tooltip, [])
 | 
						||
					return
 | 
						||
				jid = gajim.get_jid_from_account(account)
 | 
						||
				contacts = []
 | 
						||
				connection = gajim.connections[account]
 | 
						||
				# get our current contact info
 | 
						||
 | 
						||
				nbr_on, nbr_total = gajim.contacts.get_nb_online_total_contacts(
 | 
						||
					accounts = [account])
 | 
						||
				account_name = account
 | 
						||
				if gajim.account_is_connected(account):
 | 
						||
					account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
 | 
						||
				contact = gajim.contacts.create_contact(jid=jid, name=account_name,
 | 
						||
					show=connection.get_status(), sub='', status=connection.status,
 | 
						||
					resource=connection.server_resource,
 | 
						||
					priority=connection.priority, mood=connection.mood,
 | 
						||
					tune=connection.tune, activity=connection.activity)
 | 
						||
				if gajim.connections[account].gpg:
 | 
						||
					contact.keyID = gajim.config.get_per('accounts', connection.name,
 | 
						||
						'keyid')
 | 
						||
				contacts.append(contact)
 | 
						||
				# if we're online ...
 | 
						||
				if connection.connection:
 | 
						||
					roster = connection.connection.getRoster()
 | 
						||
					# in threadless connection when no roster stanza is sent,
 | 
						||
					# 'roster' is None
 | 
						||
					if roster and roster.getItem(jid):
 | 
						||
						resources = roster.getResources(jid)
 | 
						||
						# ...get the contact info for our other online resources
 | 
						||
						for resource in resources:
 | 
						||
							# Check if we already have this resource
 | 
						||
							found = False
 | 
						||
							for contact_ in contacts:
 | 
						||
								if contact_.resource == resource:
 | 
						||
									found = True
 | 
						||
									break
 | 
						||
							if found:
 | 
						||
								continue
 | 
						||
							show = roster.getShow(jid+'/'+resource)
 | 
						||
							if not show:
 | 
						||
								show = 'online'
 | 
						||
							contact = gajim.contacts.create_contact(jid=jid,
 | 
						||
								name=account, groups=['self_contact'], show=show,
 | 
						||
								status=roster.getStatus(jid + '/' + resource),
 | 
						||
								resource=resource,
 | 
						||
								priority=roster.getPriority(jid + '/' + resource))
 | 
						||
							contacts.append(contact)
 | 
						||
				if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
 | 
						||
					self.tooltip.id = row
 | 
						||
					self.tooltip.account = None
 | 
						||
					self.tooltip.timeout = gobject.timeout_add(500,
 | 
						||
						self.show_tooltip, contacts)
 | 
						||
 | 
						||
	def on_agent_logging(self, widget, jid, state, account):
 | 
						||
		'''When an agent is requested to log in or off'''
 | 
						||
		gajim.connections[account].send_agent_status(jid, state)
 | 
						||
 | 
						||
	def on_edit_agent(self, widget, contact, account):
 | 
						||
		'''When we want to modify the agent registration'''
 | 
						||
		gajim.connections[account].request_register_agent_info(contact.jid)
 | 
						||
 | 
						||
	def on_remove_agent(self, widget, list_):
 | 
						||
		'''When an agent is requested to be removed. list_ is a list of
 | 
						||
		(contact, account) tuple'''
 | 
						||
		for (contact, account) in list_:
 | 
						||
			if gajim.config.get_per('accounts', account, 'hostname') == \
 | 
						||
			contact.jid:
 | 
						||
				# We remove the server contact
 | 
						||
				# remove it from treeview
 | 
						||
				gajim.connections[account].unsubscribe(contact.jid)
 | 
						||
				self.remove_contact(contact.jid, account, backend=True)
 | 
						||
				return
 | 
						||
 | 
						||
		def remove(list_):
 | 
						||
			for (contact, account) in list_:
 | 
						||
				full_jid = contact.get_full_jid()
 | 
						||
				gajim.connections[account].unsubscribe_agent(full_jid)
 | 
						||
				# remove transport from treeview
 | 
						||
				self.remove_contact(contact.jid, account, backend=True)
 | 
						||
 | 
						||
		# Check if there are unread events from some contacts
 | 
						||
		has_unread_events = False
 | 
						||
		for (contact, account) in list_:
 | 
						||
			for jid in gajim.events.get_events(account):
 | 
						||
				if jid.endswith(contact.jid):
 | 
						||
					has_unread_events = True
 | 
						||
					break
 | 
						||
		if has_unread_events:
 | 
						||
			dialogs.ErrorDialog(_('You have unread messages'),
 | 
						||
				_('You must read them before removing this transport.'))
 | 
						||
			return
 | 
						||
		if len(list_) == 1:
 | 
						||
			pritext = _('Transport "%s" will be removed') % contact.jid
 | 
						||
			sectext = _('You will no longer be able to send and receive messages '
 | 
						||
				'from contacts using this transport.')
 | 
						||
		else:
 | 
						||
			pritext = _('Transports will be removed')
 | 
						||
			jids = ''
 | 
						||
			for (contact, account) in list_:
 | 
						||
				jids += '\n  ' + contact.get_shown_name() + ','
 | 
						||
			jids = jids[:-1] + '.'
 | 
						||
			sectext = _('You will no longer be able to send and receive messages '
 | 
						||
				'to contacts from these transports: %s') % jids
 | 
						||
		dialogs.ConfirmationDialog(pritext, sectext,
 | 
						||
			on_response_ok = (remove, list_))
 | 
						||
 | 
						||
	def on_block(self, widget, titer, group_list):
 | 
						||
		''' When clicked on the 'block' button in context menu. '''
 | 
						||
		model = self.modelfilter
 | 
						||
		accounts = []
 | 
						||
		msg = self.get_status_message('offline')
 | 
						||
		if group_list is None:
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			accounts.append(account)
 | 
						||
			self.send_status(account, 'offline', msg, to = jid)
 | 
						||
			new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
 | 
						||
				'value' : jid, 'child': [u'message', u'iq', u'presence-out']}
 | 
						||
			gajim.connections[account].blocked_list.append(new_rule)
 | 
						||
			# needed for draw_contact:
 | 
						||
			gajim.connections[account].blocked_contacts.append(jid)
 | 
						||
			self.draw_contact(jid, account)
 | 
						||
		else:
 | 
						||
			if titer is None:
 | 
						||
				for (contact, account) in group_list:
 | 
						||
					if account not in accounts:
 | 
						||
						if not gajim.connections[account].privacy_rules_supported:
 | 
						||
							continue
 | 
						||
						accounts.append(account)
 | 
						||
					self.send_status(account, 'offline', msg, to=contact.jid)
 | 
						||
					new_rule = {'order': u'1', 'type': u'jid',
 | 
						||
							'action': u'deny', 'value' : contact.jid,
 | 
						||
							'child': [u'message', u'iq', u'presence-out']}
 | 
						||
					gajim.connections[account].blocked_list.append(new_rule)
 | 
						||
					# needed for draw_contact:
 | 
						||
					gajim.connections[account].blocked_contacts.append(contact.jid)
 | 
						||
					self.draw_contact(contact.jid, account)
 | 
						||
			else:
 | 
						||
				group = model[titer][C_JID].decode('utf-8')
 | 
						||
				for (contact, account) in group_list:
 | 
						||
					if account not in accounts:
 | 
						||
						if not gajim.connections[account].privacy_rules_supported:
 | 
						||
							continue
 | 
						||
						accounts.append(account)
 | 
						||
						# needed for draw_group:
 | 
						||
						gajim.connections[account].blocked_groups.append(group)
 | 
						||
						self.draw_group(group, account)
 | 
						||
					self.send_status(account, 'offline', msg, to=contact.jid)
 | 
						||
					self.draw_contact(contact.jid, account)
 | 
						||
				new_rule = {'order': u'1', 'type': u'group', 'action': u'deny',
 | 
						||
					'value' : group, 'child': [u'message', u'iq', u'presence-out']}
 | 
						||
				gajim.connections[account].blocked_list.append(new_rule)
 | 
						||
		for account in accounts:
 | 
						||
			gajim.connections[account].set_privacy_list(
 | 
						||
			'block', gajim.connections[account].blocked_list)
 | 
						||
		if len(gajim.connections[account].blocked_list) == 1:
 | 
						||
			gajim.connections[account].set_active_list('block')
 | 
						||
			gajim.connections[account].set_default_list('block')
 | 
						||
		gajim.connections[account].get_privacy_list('block')
 | 
						||
 | 
						||
	def on_unblock(self, widget, titer, group_list):
 | 
						||
		''' When clicked on the 'unblock' button in context menu. '''
 | 
						||
		model = self.modelfilter
 | 
						||
		accounts = []
 | 
						||
		if group_list is None:
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			jid_account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			accounts.append(jid_account)
 | 
						||
			gajim.connections[jid_account].new_blocked_list = []
 | 
						||
			for rule in gajim.connections[jid_account].blocked_list:
 | 
						||
				if rule['action'] != 'deny' or rule['type'] != 'jid' \
 | 
						||
				or rule['value'] != jid:
 | 
						||
					gajim.connections[jid_account].new_blocked_list.append(rule)
 | 
						||
			# needed for draw_contact:
 | 
						||
			if jid in gajim.connections[jid_account].blocked_contacts:
 | 
						||
				gajim.connections[jid_account].blocked_contacts.remove(jid)
 | 
						||
			self.draw_contact(jid, jid_account)
 | 
						||
		else:
 | 
						||
			if titer is None:
 | 
						||
				for (contact, account) in group_list:
 | 
						||
					if account not in accounts:
 | 
						||
						if gajim.connections[account].privacy_rules_supported:
 | 
						||
							accounts.append(account)
 | 
						||
							gajim.connections[account].new_blocked_list = []
 | 
						||
							gajim.connections[account].to_unblock = []
 | 
						||
							gajim.connections[account].to_unblock.append(contact.jid)
 | 
						||
					else:
 | 
						||
						gajim.connections[account].to_unblock.append(contact.jid)
 | 
						||
					# needed for draw_contact:
 | 
						||
					if contact.jid in gajim.connections[account].blocked_contacts:
 | 
						||
						gajim.connections[account].blocked_contacts.remove(
 | 
						||
							contact.jid)
 | 
						||
					self.draw_contact(contact.jid, account)
 | 
						||
				for account in accounts:
 | 
						||
					for rule in gajim.connections[account].blocked_list:
 | 
						||
						if rule['action'] != 'deny' or rule['type'] != 'jid' \
 | 
						||
						or rule['value'] not in gajim.connections[account].to_unblock:
 | 
						||
							gajim.connections[account].new_blocked_list.append(rule)
 | 
						||
			else:
 | 
						||
				group = model[titer][C_JID].decode('utf-8')
 | 
						||
				for (contact, account) in group_list:
 | 
						||
					if account not in accounts:
 | 
						||
						if gajim.connections[account].privacy_rules_supported:
 | 
						||
							accounts.append(account)
 | 
						||
							# needed for draw_group:
 | 
						||
							if group in gajim.connections[account].blocked_groups:
 | 
						||
								gajim.connections[account].blocked_groups.remove(group)
 | 
						||
							self.draw_group(group, account)
 | 
						||
							gajim.connections[account].new_blocked_list = []
 | 
						||
							for rule in gajim.connections[account].blocked_list:
 | 
						||
								if rule['action'] != 'deny' or rule['type'] != 'group' \
 | 
						||
								or rule['value'] != group:
 | 
						||
									gajim.connections[account].new_blocked_list.append(
 | 
						||
										rule)
 | 
						||
					self.draw_contact(contact.jid, account)
 | 
						||
		for account in accounts:
 | 
						||
			gajim.connections[account].set_privacy_list(
 | 
						||
				'block', gajim.connections[account].new_blocked_list)
 | 
						||
			gajim.connections[account].get_privacy_list('block')
 | 
						||
			if len(gajim.connections[account].new_blocked_list) == 0:
 | 
						||
				gajim.connections[account].blocked_list = []
 | 
						||
				gajim.connections[account].blocked_contacts = []
 | 
						||
				gajim.connections[account].blocked_groups = []
 | 
						||
				gajim.connections[account].set_default_list('')
 | 
						||
				gajim.connections[account].set_active_list('')
 | 
						||
				gajim.connections[account].del_privacy_list('block')
 | 
						||
				if gajim.interface.instances[account].has_key('blocked_contacts'):
 | 
						||
					gajim.interface.instances[account]['blocked_contacts'].\
 | 
						||
						privacy_list_received([])
 | 
						||
		if group_list is None:
 | 
						||
			status = gajim.connections[jid_account].connected
 | 
						||
			if gajim.SHOW_LIST[status] == 'invisible':
 | 
						||
				# Don't send our presence if we're invisible
 | 
						||
				return
 | 
						||
			msg = gajim.connections[jid_account].status
 | 
						||
			if not self.regroup:
 | 
						||
				show = gajim.SHOW_LIST[status]
 | 
						||
			else:	# accounts merged
 | 
						||
				show = helpers.get_global_show()
 | 
						||
			self.send_status(jid_account, show, msg, to=jid)
 | 
						||
		else:
 | 
						||
			for (contact, account) in group_list:
 | 
						||
				if not self.regroup:
 | 
						||
					show = gajim.SHOW_LIST[gajim.connections[account].connected]
 | 
						||
				else:	# accounts merged
 | 
						||
					show = helpers.get_global_show()
 | 
						||
				if show == 'invisible':
 | 
						||
					# Don't send our presence if we're invisible
 | 
						||
					continue
 | 
						||
				if account not in accounts:
 | 
						||
					if gajim.connections[account].privacy_rules_supported:
 | 
						||
						accounts.append(account)
 | 
						||
						self.send_status(account, show,
 | 
						||
							gajim.connections[account].status, to=contact.jid)
 | 
						||
				else:
 | 
						||
					self.send_status(account, show,
 | 
						||
						gajim.connections[account].status, to=contact.jid)
 | 
						||
 | 
						||
	def on_rename(self, widget, titer, path):
 | 
						||
		# this function is called either by F2 or by Rename menuitem
 | 
						||
		if gajim.interface.instances.has_key('rename'):
 | 
						||
			gajim.interface.instances['rename'].dialog.present()
 | 
						||
			return
 | 
						||
		model = self.modelfilter
 | 
						||
 | 
						||
		row_type = model[titer][C_TYPE]
 | 
						||
		jid = model[titer][C_JID].decode('utf-8')
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
		# account is offline, don't allow to rename
 | 
						||
		if gajim.connections[account].connected < 2:
 | 
						||
			return
 | 
						||
		if row_type in ('contact', 'agent'):
 | 
						||
			# it's jid
 | 
						||
			title = _('Rename Contact')
 | 
						||
			message = _('Enter a new nickname for contact %s') % jid
 | 
						||
			old_text = gajim.contacts.get_contact_with_highest_priority(account,
 | 
						||
				jid).name
 | 
						||
		elif row_type == 'group':
 | 
						||
			if jid in helpers.special_groups + (_('General'),):
 | 
						||
				return
 | 
						||
			old_text = model[titer][C_JID].decode('utf-8')
 | 
						||
			title = _('Rename Group')
 | 
						||
			message = _('Enter a new name for group %s') % old_text
 | 
						||
 | 
						||
		def on_renamed(new_text, account, row_type, jid, old_text):
 | 
						||
			if gajim.interface.instances.has_key('rename'):
 | 
						||
				del gajim.interface.instances['rename']
 | 
						||
			if row_type in ('contact', 'agent'):
 | 
						||
				if old_text == new_text:
 | 
						||
					return
 | 
						||
				for contact in gajim.contacts.get_contacts(account, jid):
 | 
						||
					contact.name = new_text
 | 
						||
				gajim.connections[account].update_contact(jid, new_text, \
 | 
						||
					contact.groups)
 | 
						||
				self.draw_contact(jid, account)
 | 
						||
				# Update opened chats
 | 
						||
				for ctrl in gajim.interface.msg_win_mgr.get_controls(jid, account):
 | 
						||
					ctrl.update_ui()
 | 
						||
					win = gajim.interface.msg_win_mgr.get_window(jid, account)
 | 
						||
					win.redraw_tab(ctrl)
 | 
						||
					win.show_title()
 | 
						||
			elif row_type == 'group':
 | 
						||
				# in C_JID column, we hold the group name (which is not escaped)
 | 
						||
				if old_text == new_text:
 | 
						||
					return
 | 
						||
				# Groups may not change name from or to a special groups
 | 
						||
				for g in helpers.special_groups:
 | 
						||
					if g in (new_text, old_text):
 | 
						||
						return
 | 
						||
				# update all contacts in the given group
 | 
						||
				if self.regroup:
 | 
						||
					accounts = gajim.connections.keys()
 | 
						||
				else:
 | 
						||
					accounts = [account,]
 | 
						||
				for acc in accounts:
 | 
						||
					for jid in gajim.contacts.get_jid_list(acc):
 | 
						||
						contact = gajim.contacts.get_first_contact_from_jid(acc, jid)
 | 
						||
						if old_text in contact.groups:
 | 
						||
							self.remove_contact_from_groups(jid, acc, [old_text,])
 | 
						||
							self.add_contact_to_groups(jid, acc, [new_text,])
 | 
						||
 | 
						||
		def on_canceled():
 | 
						||
			if gajim.interface.instances.has_key('rename'):
 | 
						||
				del gajim.interface.instances['rename']
 | 
						||
 | 
						||
		gajim.interface.instances['rename'] = dialogs.InputDialog(title, message,
 | 
						||
			old_text, False, (on_renamed, account, row_type, jid, old_text),
 | 
						||
			on_canceled)
 | 
						||
 | 
						||
	def on_remove_group_item_activated(self, widget, group, account):
 | 
						||
		dlg = dialogs.ConfirmationDialogCheck(_('Remove Group'),
 | 
						||
			_('Do you want to remove group %s from the roster?' % group),
 | 
						||
			_('Remove also all contacts in this group from your roster'))
 | 
						||
		dlg.set_default_response(gtk.BUTTONS_OK_CANCEL)
 | 
						||
		response = dlg.run()
 | 
						||
		if response == gtk.RESPONSE_OK:
 | 
						||
			for contact in gajim.contacts.get_contacts_from_group(account, group):
 | 
						||
				if not dlg.is_checked():
 | 
						||
					self.remove_contact_from_groups(contact.jid,account, [group])
 | 
						||
				else:
 | 
						||
					gajim.connections[account].unsubscribe(contact.jid)
 | 
						||
					self.remove_contact(contact.jid, account, backend=True)
 | 
						||
 | 
						||
	def on_assign_pgp_key(self, widget, contact, account):
 | 
						||
		attached_keys = gajim.config.get_per('accounts', account,
 | 
						||
			'attached_gpg_keys').split()
 | 
						||
		keys = {}
 | 
						||
		#GPG Key
 | 
						||
		keyID = _('None')
 | 
						||
		for i in xrange(len(attached_keys)/2):
 | 
						||
			keys[attached_keys[2*i]] = attached_keys[2*i+1]
 | 
						||
			if attached_keys[2*i] == contact.jid:
 | 
						||
				keyID = attached_keys[2*i+1]
 | 
						||
		public_keys = gajim.connections[account].ask_gpg_keys()
 | 
						||
		#GPG Key
 | 
						||
		public_keys[_('None')] = _('None')
 | 
						||
		instance = dialogs.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
 | 
						||
			_('Select a key to apply to the contact'), public_keys, keyID)
 | 
						||
		keyID = instance.run()
 | 
						||
		if keyID is None:
 | 
						||
			return
 | 
						||
		#GPG Key
 | 
						||
		if keyID[0] == _('None'):
 | 
						||
			if contact.jid in keys:
 | 
						||
				del keys[contact.jid]
 | 
						||
			keyID = ''
 | 
						||
		else:
 | 
						||
			keyID = keyID[0]
 | 
						||
			keys[contact.jid] = keyID
 | 
						||
 | 
						||
		ctrl = gajim.interface.msg_win_mgr.get_control(contact.jid, account)
 | 
						||
		if ctrl:
 | 
						||
			ctrl.update_ui()
 | 
						||
 | 
						||
		keys_str = ''
 | 
						||
		for jid in keys:
 | 
						||
			keys_str += jid + ' ' + keys[jid] + ' '
 | 
						||
		gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str)
 | 
						||
		for u in gajim.contacts.get_contacts(account, contact.jid):
 | 
						||
			u.keyID = helpers.prepare_and_validate_gpg_keyID(account,
 | 
						||
					contact.jid, keyID)
 | 
						||
 | 
						||
	def on_set_custom_avatar_activate(self, widget, contact, account):
 | 
						||
		def on_ok(widget, path_to_file):
 | 
						||
			filesize = os.path.getsize(path_to_file) # in bytes
 | 
						||
			invalid_file = False
 | 
						||
			msg = ''
 | 
						||
			if os.path.isfile(path_to_file):
 | 
						||
				stat = os.stat(path_to_file)
 | 
						||
				if stat[6] == 0:
 | 
						||
					invalid_file = True
 | 
						||
					msg = _('File is empty')
 | 
						||
			else:
 | 
						||
				invalid_file = True
 | 
						||
				msg = _('File does not exist')
 | 
						||
			if invalid_file:
 | 
						||
				dialogs.ErrorDialog(_('Could not load image'), msg)
 | 
						||
				return
 | 
						||
			try:
 | 
						||
				pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_file)
 | 
						||
				if filesize > 16384: # 16 kb
 | 
						||
					# get the image at 'tooltip size'
 | 
						||
					# and hope that user did not specify in ACE crazy size
 | 
						||
					pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'tooltip')
 | 
						||
			except gobject.GError, msg: # unknown format
 | 
						||
				# msg should be string, not object instance
 | 
						||
				msg = str(msg)
 | 
						||
				dialogs.ErrorDialog(_('Could not load image'), msg)
 | 
						||
				return
 | 
						||
			gajim.interface.save_avatar_files(contact.jid, pixbuf, local = True)
 | 
						||
			dlg.destroy()
 | 
						||
			self.update_avatar_in_gui(contact.jid, account)
 | 
						||
 | 
						||
		def on_clear(widget):
 | 
						||
			dlg.destroy()
 | 
						||
			# Delete file:
 | 
						||
			gajim.interface.remove_avatar_files(contact.jid, local = True)
 | 
						||
			self.update_avatar_in_gui(contact.jid, account)
 | 
						||
 | 
						||
		dlg = dialogs.AvatarChooserDialog(on_response_ok = on_ok,
 | 
						||
			on_response_clear = on_clear)
 | 
						||
 | 
						||
	def on_edit_groups(self, widget, list_):
 | 
						||
		dlg = dialogs.EditGroupsDialog(list_)
 | 
						||
		dlg.run()
 | 
						||
 | 
						||
	def on_history(self, widget, contact, account):
 | 
						||
		'''When history menuitem is activated: call log window'''
 | 
						||
		if gajim.interface.instances.has_key('logs'):
 | 
						||
			gajim.interface.instances['logs'].window.present()
 | 
						||
			gajim.interface.instances['logs'].open_history(contact.jid, account)
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['logs'] = history_window.\
 | 
						||
				HistoryWindow(contact.jid, account)
 | 
						||
 | 
						||
	def on_disconnect(self, widget, jid, account):
 | 
						||
		'''When disconnect menuitem is activated: disconect from room'''
 | 
						||
		ctrl = gajim.interface.minimized_controls[account][jid]
 | 
						||
		del gajim.interface.minimized_controls[account][jid]
 | 
						||
		ctrl.shutdown()
 | 
						||
		self.remove_groupchat(jid, account)
 | 
						||
 | 
						||
	def on_send_single_message_menuitem_activate(self, widget, account,
 | 
						||
	contact = None):
 | 
						||
		if contact is None:
 | 
						||
			dialogs.SingleMessageWindow(account, action = 'send')
 | 
						||
		elif type(contact) == type([]):
 | 
						||
			dialogs.SingleMessageWindow(account, contact, 'send')
 | 
						||
		else:
 | 
						||
			jid = contact.jid
 | 
						||
			if contact.jid == gajim.get_jid_from_account(account):
 | 
						||
				jid += '/' + contact.resource
 | 
						||
			dialogs.SingleMessageWindow(account, jid, 'send')
 | 
						||
 | 
						||
	def on_send_file_menuitem_activate(self, widget, contact, account,
 | 
						||
	resource=None):
 | 
						||
		gajim.interface.instances['file_transfers'].show_file_send_request(
 | 
						||
			account, contact)
 | 
						||
 | 
						||
	def on_add_special_notification_menuitem_activate(self, widget, jid):
 | 
						||
		dialogs.AddSpecialNotificationDialog(jid)
 | 
						||
 | 
						||
	def on_invite_to_new_room(self, widget, list_, resource = None):
 | 
						||
		''' resource parameter MUST NOT be used if more than one contact in
 | 
						||
		list '''
 | 
						||
		account_list = []
 | 
						||
		jid_list = []
 | 
						||
		for (contact, account) in list_:
 | 
						||
			if contact.jid not in jid_list:
 | 
						||
				if resource: # we MUST have one contact only in list_
 | 
						||
					fjid = contact.jid + '/' + resource
 | 
						||
					jid_list.append(fjid)
 | 
						||
				else:
 | 
						||
					jid_list.append(contact.jid)
 | 
						||
			if account not in account_list:
 | 
						||
				account_list.append(account)
 | 
						||
		# transform None in 'jabber'
 | 
						||
		type_ = gajim.get_transport_name_from_jid(jid_list[0]) or 'jabber'
 | 
						||
		for account in account_list:
 | 
						||
			if gajim.connections[account].muc_jid[type_]:
 | 
						||
				# create the room on this muc server
 | 
						||
				if gajim.interface.instances[account].has_key('join_gc'):
 | 
						||
					gajim.interface.instances[account]['join_gc'].window.destroy()
 | 
						||
				try:
 | 
						||
					gajim.interface.instances[account]['join_gc'] = \
 | 
						||
						dialogs.JoinGroupchatWindow(account,
 | 
						||
							gajim.connections[account].muc_jid[type_],
 | 
						||
							automatic = {'invities': jid_list})
 | 
						||
				except GajimGeneralException:
 | 
						||
					continue
 | 
						||
				break
 | 
						||
 | 
						||
	def on_invite_to_room(self, widget, list_, room_jid, room_account,
 | 
						||
		resource = None):
 | 
						||
		''' resource parameter MUST NOT be used if more than one contact in
 | 
						||
		list '''
 | 
						||
		for (contact, acct) in list_:
 | 
						||
			contact_jid = contact.jid
 | 
						||
			if resource: # we MUST have one contact only in list_
 | 
						||
				contact_jid += '/' + resource
 | 
						||
			gajim.connections[room_account].send_invite(room_jid, contact_jid)
 | 
						||
 | 
						||
	def on_all_groupchat_maximized(self, widget, group_list):
 | 
						||
		for (contact, account) in group_list:
 | 
						||
			self.on_groupchat_maximized(widget, contact.jid, account)
 | 
						||
 | 
						||
	def on_groupchat_maximized(self, widget, jid, account):
 | 
						||
		'''When a groupchat is maximised'''
 | 
						||
		ctrl = gajim.interface.minimized_controls[account][jid]
 | 
						||
		mw = gajim.interface.msg_win_mgr.get_window(ctrl.contact.jid,
 | 
						||
			ctrl.account)
 | 
						||
		if not mw:
 | 
						||
			mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
 | 
						||
				ctrl.account, ctrl.type_id)
 | 
						||
		ctrl.parent_win = mw
 | 
						||
		mw.new_tab(ctrl)
 | 
						||
		mw.set_active_tab(ctrl)
 | 
						||
		del gajim.interface.minimized_controls[account][jid]
 | 
						||
 | 
						||
		self.remove_groupchat(jid, account)
 | 
						||
 | 
						||
	def on_edit_account(self, widget, account):
 | 
						||
		if gajim.interface.instances.has_key('accounts'):
 | 
						||
			gajim.interface.instances['accounts'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['accounts'] = config.AccountsWindow()
 | 
						||
		gajim.interface.instances['accounts'].select_account(account)
 | 
						||
 | 
						||
	def on_zeroconf_properties(self, widget, account):
 | 
						||
		if gajim.interface.instances.has_key('accounts'):
 | 
						||
			gajim.interface.instances['accounts'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['accounts'] = config.AccountsWindow()
 | 
						||
		gajim.interface.instances['accounts'].select_account(account)
 | 
						||
 | 
						||
	def on_open_gmail_inbox(self, widget, account):
 | 
						||
		url = gajim.connections[account].gmail_url
 | 
						||
		if url:
 | 
						||
			helpers.launch_browser_mailer('url', url)
 | 
						||
 | 
						||
	def on_change_activity_activate(self, widget, account):
 | 
						||
		dlg = dialogs.ChangeActivityDialog(account)
 | 
						||
 | 
						||
	def on_change_mood_activate(self, widget, account):
 | 
						||
		dlg = dialogs.ChangeMoodDialog(account)
 | 
						||
 | 
						||
	def on_change_status_message_activate(self, widget, account):
 | 
						||
		show = gajim.SHOW_LIST[gajim.connections[account].connected]
 | 
						||
		dlg = dialogs.ChangeStatusMessageDialog(show)
 | 
						||
		message = dlg.run()
 | 
						||
		if message is not None: # None is if user pressed Cancel
 | 
						||
			self.send_status(account, show, message)
 | 
						||
 | 
						||
	def on_add_to_roster(self, widget, contact, account):
 | 
						||
		dialogs.AddNewContactWindow(account, contact.jid, contact.name)
 | 
						||
 | 
						||
 | 
						||
	def on_roster_treeview_scroll_event(self, widget, event):
 | 
						||
		self.tooltip.hide_tooltip()
 | 
						||
 | 
						||
	def on_roster_treeview_key_press_event(self, widget, event):
 | 
						||
		'''when a key is pressed in the treeviews'''
 | 
						||
		self.tooltip.hide_tooltip()
 | 
						||
		if event.keyval == gtk.keysyms.Escape:
 | 
						||
			self.tree.get_selection().unselect_all()
 | 
						||
		elif event.keyval == gtk.keysyms.F2:
 | 
						||
			treeselection = self.tree.get_selection()
 | 
						||
			model, list_of_paths = treeselection.get_selected_rows()
 | 
						||
			if len(list_of_paths) != 1:
 | 
						||
				return
 | 
						||
			path = list_of_paths[0]
 | 
						||
			type_ = model[path][C_TYPE]
 | 
						||
			if type_ in ('contact', 'group', 'agent'):
 | 
						||
				titer = model.get_iter(path)
 | 
						||
				self.on_rename(widget, titer, path)
 | 
						||
 | 
						||
		elif event.keyval == gtk.keysyms.Delete:
 | 
						||
			treeselection = self.tree.get_selection()
 | 
						||
			model, list_of_paths = treeselection.get_selected_rows()
 | 
						||
			if not len(list_of_paths):
 | 
						||
				return
 | 
						||
			type_ = model[list_of_paths[0]][C_TYPE]
 | 
						||
			account = model[list_of_paths[0]][C_ACCOUNT]
 | 
						||
			list_ = []
 | 
						||
			for path in list_of_paths:
 | 
						||
				if model[path][C_TYPE] != type_:
 | 
						||
					return
 | 
						||
				jid = model[path][C_JID].decode('utf-8')
 | 
						||
				account = model[path][C_ACCOUNT].decode('utf-8')
 | 
						||
				contact = gajim.contacts.get_contact_with_highest_priority(account,
 | 
						||
					jid)
 | 
						||
				list_.append((contact, account))
 | 
						||
			if type_ in ('account', 'group', 'self_contact') or \
 | 
						||
			account == gajim.ZEROCONF_ACC_NAME:
 | 
						||
				return
 | 
						||
			if type_ == 'contact':
 | 
						||
				self.on_req_usub(widget, list_)
 | 
						||
			elif type_ == 'agent':
 | 
						||
				self.on_remove_agent(widget, list_)
 | 
						||
 | 
						||
	def on_roster_treeview_button_release_event(self, widget, event):
 | 
						||
		try:
 | 
						||
			path, column, x, y = self.tree.get_path_at_pos(int(event.x),
 | 
						||
				int(event.y))
 | 
						||
		except TypeError:
 | 
						||
			return False
 | 
						||
 | 
						||
		if event.button == 1: # Left click
 | 
						||
			if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
 | 
						||
			not event.state & gtk.gdk.CONTROL_MASK:
 | 
						||
				# Check if button has been pressed on the same row
 | 
						||
				if self.clicked_path == path:
 | 
						||
					self.on_row_activated(widget, path)
 | 
						||
				self.clicked_path = None
 | 
						||
 | 
						||
	def on_roster_treeview_button_press_event(self, widget, event):
 | 
						||
		# hide tooltip, no matter the button is pressed
 | 
						||
		self.tooltip.hide_tooltip()
 | 
						||
		try:
 | 
						||
			path, column, x, y = self.tree.get_path_at_pos(int(event.x),
 | 
						||
				int(event.y))
 | 
						||
		except TypeError:
 | 
						||
			self.tree.get_selection().unselect_all()
 | 
						||
			return False
 | 
						||
 | 
						||
		if event.button == 3: # Right click
 | 
						||
			try:
 | 
						||
				model, list_of_paths = self.tree.get_selection().get_selected_rows()
 | 
						||
			except TypeError:
 | 
						||
				list_of_paths = []
 | 
						||
			if path not in list_of_paths:
 | 
						||
				self.tree.get_selection().unselect_all()
 | 
						||
				self.tree.get_selection().select_path(path)
 | 
						||
			return self.show_treeview_menu(event)
 | 
						||
 | 
						||
		elif event.button == 2: # Middle click
 | 
						||
			try:
 | 
						||
				model, list_of_paths = self.tree.get_selection().get_selected_rows()
 | 
						||
			except TypeError:
 | 
						||
				list_of_paths = []
 | 
						||
			if list_of_paths != [path]:
 | 
						||
				self.tree.get_selection().unselect_all()
 | 
						||
				self.tree.get_selection().select_path(path)
 | 
						||
			type_ = model[path][C_TYPE]
 | 
						||
			if type_ in ('agent', 'contact', 'self_contact', 'groupchat'):
 | 
						||
				self.on_row_activated(widget, path)
 | 
						||
			elif type_ == 'account':
 | 
						||
				account = model[path][C_ACCOUNT].decode('utf-8')
 | 
						||
				if account != 'all':
 | 
						||
					show = gajim.connections[account].connected
 | 
						||
					if show > 1: # We are connected
 | 
						||
						self.on_change_status_message_activate(widget, account)
 | 
						||
					return True
 | 
						||
				show = helpers.get_global_show()
 | 
						||
				if show == 'offline':
 | 
						||
					return True
 | 
						||
				dlg = dialogs.ChangeStatusMessageDialog(show)
 | 
						||
				message = dlg.run()
 | 
						||
				if not message:
 | 
						||
					return True
 | 
						||
				for acct in gajim.connections:
 | 
						||
					if not gajim.config.get_per('accounts', acct,
 | 
						||
					'sync_with_global_status'):
 | 
						||
						continue
 | 
						||
					current_show = gajim.SHOW_LIST[gajim.connections[acct].connected]
 | 
						||
					self.send_status(acct, current_show, message)
 | 
						||
			return True
 | 
						||
 | 
						||
		elif event.button == 1: # Left click
 | 
						||
			model = self.modelfilter
 | 
						||
			type_ = model[path][C_TYPE]
 | 
						||
			# x_min is the x start position of status icon column
 | 
						||
			if gajim.config.get('avatar_position_in_roster') == 'left':
 | 
						||
				x_min = gajim.config.get('roster_avatar_width')
 | 
						||
			else:
 | 
						||
				x_min = 0
 | 
						||
			if gajim.single_click and not event.state & gtk.gdk.SHIFT_MASK and \
 | 
						||
			not event.state & gtk.gdk.CONTROL_MASK:
 | 
						||
				# Don't handle double click if we press icon of a metacontact
 | 
						||
				titer = model.get_iter(path)
 | 
						||
				if x > x_min and x < x_min + 27 and type_ == 'contact' and \
 | 
						||
				model.iter_has_child(titer):
 | 
						||
					if (self.tree.row_expanded(path)):
 | 
						||
						self.tree.collapse_row(path)
 | 
						||
					else:
 | 
						||
						self.tree.expand_row(path, False)
 | 
						||
					return
 | 
						||
				# We just save on which row we press button, and open chat window on
 | 
						||
				# button release to be able to do DND without opening chat window
 | 
						||
				self.clicked_path = path
 | 
						||
				return
 | 
						||
			else:
 | 
						||
				if type_ == 'group' and x < 27:
 | 
						||
					# first cell in 1st column (the arrow SINGLE clicked)
 | 
						||
					if (self.tree.row_expanded(path)):
 | 
						||
						self.tree.collapse_row(path)
 | 
						||
					else:
 | 
						||
						self.tree.expand_row(path, False)
 | 
						||
 | 
						||
				elif type_ == 'contact' and x > x_min and x < x_min + 27:
 | 
						||
					if (self.tree.row_expanded(path)):
 | 
						||
						self.tree.collapse_row(path)
 | 
						||
					else:
 | 
						||
						self.tree.expand_row(path, False)
 | 
						||
 | 
						||
	def on_req_usub(self, widget, list_):
 | 
						||
		'''Remove a contact. list_ is a list of (contact, account) tuples'''
 | 
						||
		def on_ok(is_checked, list_):
 | 
						||
			remove_auth = True
 | 
						||
			if len(list_) == 1:
 | 
						||
				contact = list_[0][0]
 | 
						||
				if contact.sub != 'to' and is_checked:
 | 
						||
					remove_auth = False
 | 
						||
			for (contact, account) in list_:
 | 
						||
				gajim.connections[account].unsubscribe(contact.jid, remove_auth)
 | 
						||
				self.remove_contact(contact.jid, account, backend=True)
 | 
						||
				if not remove_auth and contact.sub == 'both':
 | 
						||
					contact.name = ''
 | 
						||
					contact.groups = []
 | 
						||
					contact.sub = 'from'
 | 
						||
					# we can't see him, but have to set it manually in contact
 | 
						||
					contact.show = 'offline' 
 | 
						||
					gajim.contacts.add_contact(account, contact)
 | 
						||
					self.add_contact(contact.jid, account)
 | 
						||
		def on_ok2(list_):
 | 
						||
			on_ok(False, list_)
 | 
						||
 | 
						||
		if len(list_) == 1:
 | 
						||
			contact = list_[0][0]
 | 
						||
			account = list_[0][1]
 | 
						||
			pritext = _('Contact "%s" will be removed from your roster') % \
 | 
						||
				contact.get_shown_name()
 | 
						||
			if contact.sub == 'to':
 | 
						||
				dialogs.ConfirmationDialog(pritext,
 | 
						||
					_('By removing this contact you also remove authorization '
 | 
						||
					'resulting in him or her always seeing you as offline.'),
 | 
						||
					on_response_ok = (on_ok2, list_))
 | 
						||
			else:
 | 
						||
				dialogs.ConfirmationDialogCheck(pritext,
 | 
						||
					_('By removing this contact you also by default remove '
 | 
						||
					'authorization resulting in him or her always seeing you as '
 | 
						||
					'offline.'),
 | 
						||
					_('I want this contact to know my status after removal'),
 | 
						||
					on_response_ok = (on_ok, list_))
 | 
						||
		else:
 | 
						||
			# several contact to remove at the same time
 | 
						||
			pritext = _('Contacts will be removed from your roster')
 | 
						||
			jids = ''
 | 
						||
			for (contact, account) in list_:
 | 
						||
				jids += '\n  ' + contact.get_shown_name() + ','
 | 
						||
			sectext = _('By removing these contacts:%s\nyou also remove '
 | 
						||
				'authorization resulting in them always seeing you as offline.') % \
 | 
						||
				jids
 | 
						||
			dialogs.ConfirmationDialog(pritext, sectext,
 | 
						||
				on_response_ok = (on_ok2, list_))
 | 
						||
 | 
						||
	def on_send_custom_status(self, widget, contact_list, show, group=None):
 | 
						||
		'''send custom status'''
 | 
						||
		dlg = dialogs.ChangeStatusMessageDialog(show)
 | 
						||
		message = dlg.run()
 | 
						||
		if message is not None: # None if user pressed Cancel
 | 
						||
			for (contact, account) in contact_list:
 | 
						||
				our_jid = gajim.get_jid_from_account(account)
 | 
						||
				accounts = []
 | 
						||
				if group and account not in accounts:
 | 
						||
					if not gajim.interface.status_sent_to_groups.has_key(account):
 | 
						||
						gajim.interface.status_sent_to_groups[account] = {}
 | 
						||
					gajim.interface.status_sent_to_groups[account][group] = show
 | 
						||
					accounts.append(group)
 | 
						||
				jid = contact.jid
 | 
						||
				if jid == our_jid:
 | 
						||
					jid += '/' + contact.resource
 | 
						||
				self.send_status(account, show, message, to=jid)
 | 
						||
				if not gajim.interface.status_sent_to_users.has_key(account):
 | 
						||
					gajim.interface.status_sent_to_users[account] = {}
 | 
						||
				gajim.interface.status_sent_to_users[account][contact.jid] = show
 | 
						||
 | 
						||
	def on_status_combobox_changed(self, widget):
 | 
						||
		'''When we change our status via the combobox'''
 | 
						||
		model = self.status_combobox.get_model()
 | 
						||
		active = self.status_combobox.get_active()
 | 
						||
		if active == -1: # no active item
 | 
						||
			return
 | 
						||
		if not self.combobox_callback_active:
 | 
						||
			self.previous_status_combobox_active = active
 | 
						||
			return
 | 
						||
		accounts = gajim.connections.keys()
 | 
						||
		if len(accounts) == 0:
 | 
						||
			dialogs.ErrorDialog(_('No account available'),
 | 
						||
		_('You must create an account before you can chat with other contacts.'))
 | 
						||
			self.update_status_combobox()
 | 
						||
			return
 | 
						||
		status = model[active][2].decode('utf-8')
 | 
						||
		statuses_unified = helpers.statuses_unified() # status "desync'ed" or not
 | 
						||
		if (active == 7 and statuses_unified) or (active == 9 and \
 | 
						||
		not statuses_unified):
 | 
						||
			# 'Change status message' selected:
 | 
						||
			# do not change show, just show change status dialog
 | 
						||
			status = model[self.previous_status_combobox_active][2].decode('utf-8')
 | 
						||
			dlg = dialogs.ChangeStatusMessageDialog(status)
 | 
						||
			message = dlg.run()
 | 
						||
			if message is not None: # None if user pressed Cancel
 | 
						||
				for account in accounts:
 | 
						||
					if not gajim.config.get_per('accounts', account,
 | 
						||
						'sync_with_global_status'):
 | 
						||
						continue
 | 
						||
					current_show = gajim.SHOW_LIST[
 | 
						||
						gajim.connections[account].connected]
 | 
						||
					self.send_status(account, current_show, message)
 | 
						||
			self.combobox_callback_active = False
 | 
						||
			self.status_combobox.set_active(
 | 
						||
				self.previous_status_combobox_active)
 | 
						||
			self.combobox_callback_active = True
 | 
						||
			return
 | 
						||
		# we are about to change show, so save this new show so in case
 | 
						||
		# after user chooses "Change status message" menuitem
 | 
						||
		# we can return to this show
 | 
						||
		self.previous_status_combobox_active = active
 | 
						||
		connected_accounts = gajim.get_number_of_connected_accounts()
 | 
						||
		if status == 'invisible':
 | 
						||
			bug_user = False
 | 
						||
			for account in accounts:
 | 
						||
				if connected_accounts < 1 or gajim.account_is_connected(account):
 | 
						||
					if not gajim.config.get_per('accounts', account,
 | 
						||
							'sync_with_global_status'):
 | 
						||
						continue
 | 
						||
					# We're going to change our status to invisible
 | 
						||
					if self.connected_rooms(account):
 | 
						||
						bug_user = True
 | 
						||
						break
 | 
						||
			if bug_user:
 | 
						||
				dialog = dialogs.ConfirmationDialog(
 | 
						||
					_('You are participating in one or more group chats'),
 | 
						||
					_('Changing your status to invisible will result in '
 | 
						||
					'disconnection from those group chats. Are you sure you want to '
 | 
						||
					'go invisible?'))
 | 
						||
				if dialog.get_response() != gtk.RESPONSE_OK:
 | 
						||
					self.update_status_combobox()
 | 
						||
					return
 | 
						||
		message = self.get_status_message(status)
 | 
						||
		if message is None: # user pressed Cancel to change status message dialog
 | 
						||
			self.update_status_combobox()
 | 
						||
			return
 | 
						||
		global_sync_accounts = []
 | 
						||
		for acct in accounts:
 | 
						||
			if gajim.config.get_per('accounts', acct, 'sync_with_global_status'):
 | 
						||
				global_sync_accounts.append(acct)
 | 
						||
		global_sync_connected_accounts = gajim.get_number_of_connected_accounts(
 | 
						||
			global_sync_accounts)
 | 
						||
		for account in accounts:
 | 
						||
			if not gajim.config.get_per('accounts', account,
 | 
						||
			'sync_with_global_status'):
 | 
						||
				continue
 | 
						||
			# we are connected (so we wanna change show and status)
 | 
						||
			# or no account is connected and we want to connect with new show and
 | 
						||
			# status
 | 
						||
 | 
						||
			if not global_sync_connected_accounts > 0 or \
 | 
						||
			gajim.connections[account].connected > 0:
 | 
						||
				self.send_status(account, status, message)
 | 
						||
		self.update_status_combobox()
 | 
						||
 | 
						||
	def on_preferences_menuitem_activate(self, widget):
 | 
						||
		if gajim.interface.instances.has_key('preferences'):
 | 
						||
			gajim.interface.instances['preferences'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['preferences'] = config.PreferencesWindow()
 | 
						||
 | 
						||
	def on_publish_tune_toggled(self, widget, account):
 | 
						||
		act = widget.get_active()
 | 
						||
		gajim.config.set_per('accounts', account, 'publish_tune', act)
 | 
						||
		if act:
 | 
						||
			if not self.music_track_changed_signal:
 | 
						||
				listener = MusicTrackListener.get()
 | 
						||
				self.music_track_changed_signal = listener.connect(
 | 
						||
					'music-track-changed', self.music_track_changed)
 | 
						||
				track = listener.get_playing_track()
 | 
						||
				self.music_track_changed(listener, track)
 | 
						||
		else:
 | 
						||
			# disable it only if no other account use it
 | 
						||
			for acct in gajim.connections:
 | 
						||
				if gajim.config.get_per('accounts', acct, 'publish_tune'):
 | 
						||
					break
 | 
						||
			else:
 | 
						||
				listener = MusicTrackListener.get()
 | 
						||
				listener.disconnect(self.music_track_changed_signal)
 | 
						||
				self.music_track_changed_signal = None
 | 
						||
 | 
						||
			if gajim.connections[account].pep_supported:
 | 
						||
				# As many implementations don't support retracting items, we send a
 | 
						||
				# "Stopped" event first
 | 
						||
				from common import pep
 | 
						||
				pep.user_send_tune(account, '')
 | 
						||
				pep.user_retract_tune(account)
 | 
						||
		helpers.update_optional_features(account)
 | 
						||
 | 
						||
	def on_pep_services_menuitem_activate(self, widget, account):
 | 
						||
		if gajim.interface.instances[account].has_key('pep_services'):
 | 
						||
			gajim.interface.instances[account]['pep_services'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances[account]['pep_services'] = \
 | 
						||
				config.ManagePEPServicesWindow(account)
 | 
						||
 | 
						||
	def on_add_new_contact(self, widget, account):
 | 
						||
		dialogs.AddNewContactWindow(account)
 | 
						||
 | 
						||
	def on_join_gc_activate(self, widget, account):
 | 
						||
		'''when the join gc menuitem is clicked, show the join gc window'''
 | 
						||
		invisible_show = gajim.SHOW_LIST.index('invisible')
 | 
						||
		if gajim.connections[account].connected == invisible_show:
 | 
						||
			dialogs.ErrorDialog(_('You cannot join a group chat while you are '
 | 
						||
				'invisible'))
 | 
						||
			return
 | 
						||
		if gajim.interface.instances[account].has_key('join_gc'):
 | 
						||
			gajim.interface.instances[account]['join_gc'].window.present()
 | 
						||
		else:
 | 
						||
			# c http://nkour.blogspot.com/2005/05/pythons-init-return-none-doesnt-return.html
 | 
						||
			try:
 | 
						||
				gajim.interface.instances[account]['join_gc'] = \
 | 
						||
					dialogs.JoinGroupchatWindow(account)
 | 
						||
			except GajimGeneralException:
 | 
						||
				pass
 | 
						||
 | 
						||
	def on_new_chat_menuitem_activate(self, widget, account):
 | 
						||
		dialogs.NewChatDialog(account)
 | 
						||
 | 
						||
	def on_contents_menuitem_activate(self, widget):
 | 
						||
		helpers.launch_browser_mailer('url', 'http://trac.gajim.org/wiki')
 | 
						||
 | 
						||
	def on_faq_menuitem_activate(self, widget):
 | 
						||
		helpers.launch_browser_mailer('url',
 | 
						||
			'http://trac.gajim.org/wiki/GajimFaq')
 | 
						||
 | 
						||
	def on_features_menuitem_activate(self, widget):
 | 
						||
		features_window.FeaturesWindow()
 | 
						||
 | 
						||
	def on_about_menuitem_activate(self, widget):
 | 
						||
		dialogs.AboutDialog()
 | 
						||
 | 
						||
	def on_accounts_menuitem_activate(self, widget):
 | 
						||
		if gajim.interface.instances.has_key('accounts'):
 | 
						||
			gajim.interface.instances['accounts'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['accounts'] = config.AccountsWindow()
 | 
						||
 | 
						||
	def on_file_transfers_menuitem_activate(self, widget):
 | 
						||
		if gajim.interface.instances['file_transfers'].window.get_property(
 | 
						||
		'visible'):
 | 
						||
			gajim.interface.instances['file_transfers'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['file_transfers'].window.show_all()
 | 
						||
 | 
						||
	def on_history_menuitem_activate(self, widget):
 | 
						||
		if gajim.interface.instances.has_key('logs'):
 | 
						||
			gajim.interface.instances['logs'].window.present()
 | 
						||
		else:
 | 
						||
			gajim.interface.instances['logs'] = history_window.\
 | 
						||
				HistoryWindow()
 | 
						||
 | 
						||
	def on_show_transports_menuitem_activate(self, widget):
 | 
						||
		gajim.config.set('show_transports_group', widget.get_active())
 | 
						||
		self.refilter_shown_roster_items()
 | 
						||
 | 
						||
	def on_manage_bookmarks_menuitem_activate(self, widget):
 | 
						||
		config.ManageBookmarksWindow()
 | 
						||
 | 
						||
	def on_profile_avatar_menuitem_activate(self, widget, account):
 | 
						||
		gajim.interface.edit_own_details(account)
 | 
						||
 | 
						||
	def on_execute_command(self, widget, contact, account, resource=None):
 | 
						||
		'''Execute command. Full JID needed; if it is other contact,
 | 
						||
		resource is necessary. Widget is unnecessary, only to be
 | 
						||
		able to make this a callback.'''
 | 
						||
		jid = contact.jid
 | 
						||
		if resource is not None:
 | 
						||
			jid = jid + u'/' + resource
 | 
						||
		adhoc_commands.CommandWindow(account, jid)
 | 
						||
 | 
						||
	def on_roster_window_focus_in_event(self, widget, event):
 | 
						||
		# roster received focus, so if we had urgency REMOVE IT
 | 
						||
		# NOTE: we do not have to read the message to remove urgency
 | 
						||
		# so this functions does that
 | 
						||
		gtkgui_helpers.set_unset_urgency_hint(widget, False)
 | 
						||
 | 
						||
		# if a contact row is selected, update colors (eg. for status msg)
 | 
						||
		# because gtk engines may differ in bg when window is selected
 | 
						||
		# or not
 | 
						||
		if len(self._last_selected_contact):
 | 
						||
			for (jid, account) in self._last_selected_contact:
 | 
						||
				self.draw_contact(jid, account, selected = True,
 | 
						||
					focus = True)
 | 
						||
 | 
						||
	def on_roster_window_focus_out_event(self, widget, event):
 | 
						||
		# if a contact row is selected, update colors (eg. for status msg)
 | 
						||
		# because gtk engines may differ in bg when window is selected
 | 
						||
		# or not
 | 
						||
		if len(self._last_selected_contact):
 | 
						||
			for (jid, account) in self._last_selected_contact:
 | 
						||
				self.draw_contact(jid, account, selected = True,
 | 
						||
					focus = False)
 | 
						||
 | 
						||
	def on_roster_window_key_press_event(self, widget, event):
 | 
						||
		if event.keyval == gtk.keysyms.Escape:
 | 
						||
			if gajim.interface.msg_win_mgr.mode == \
 | 
						||
			MessageWindowMgr.ONE_MSG_WINDOW_ALWAYS_WITH_ROSTER and \
 | 
						||
			gajim.interface.msg_win_mgr.one_window_opened():
 | 
						||
				# let message window close the tab
 | 
						||
				return
 | 
						||
			model, list_of_paths = self.tree.get_selection().get_selected_rows()
 | 
						||
			if not len(list_of_paths) and gajim.interface.systray_enabled and \
 | 
						||
			not gajim.config.get('quit_on_roster_x_button'):
 | 
						||
				self.tooltip.hide_tooltip()
 | 
						||
				self.window.hide()
 | 
						||
 | 
						||
	def on_roster_window_popup_menu(self, widget):
 | 
						||
		event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
 | 
						||
		self.show_treeview_menu(event)
 | 
						||
 | 
						||
	def on_row_activated(self, widget, path):
 | 
						||
		'''When an iter is activated (double-click or single click if gnome is
 | 
						||
		set this way'''
 | 
						||
		model = self.modelfilter
 | 
						||
		account = model[path][C_ACCOUNT].decode('utf-8')
 | 
						||
		type_ = model[path][C_TYPE]
 | 
						||
		jid = model[path][C_JID].decode('utf-8')
 | 
						||
		resource = None
 | 
						||
		contact = None
 | 
						||
		titer = model.get_iter(path)
 | 
						||
		if type_ in ('group', 'account'):
 | 
						||
			if self.tree.row_expanded(path):
 | 
						||
				self.tree.collapse_row(path)
 | 
						||
			else:
 | 
						||
				self.tree.expand_row(path, False)
 | 
						||
		elif jid in gajim.interface.minimized_controls[account]:
 | 
						||
			self.on_groupchat_maximized(None, jid, account)
 | 
						||
		else:
 | 
						||
			first_ev = gajim.events.get_first_event(account, jid)
 | 
						||
			if not first_ev:
 | 
						||
				# look in other resources
 | 
						||
				for c in gajim.contacts.get_contacts(account, jid):
 | 
						||
					fjid = c.get_full_jid()
 | 
						||
					first_ev = gajim.events.get_first_event(account, fjid)
 | 
						||
					if first_ev:
 | 
						||
						resource = c.resource
 | 
						||
						break
 | 
						||
			if not first_ev and model.iter_has_child(titer):
 | 
						||
				child_iter = model.iter_children(titer)
 | 
						||
				while not first_ev and child_iter:
 | 
						||
					child_jid = model[child_iter][C_JID].decode('utf-8')
 | 
						||
					first_ev = gajim.events.get_first_event(account, child_jid)
 | 
						||
					if first_ev:
 | 
						||
						jid = child_jid
 | 
						||
					else:
 | 
						||
						child_iter = model.iter_next(child_iter)
 | 
						||
			session = None
 | 
						||
			if first_ev:
 | 
						||
				if first_ev.type_ in ('chat', 'normal'):
 | 
						||
					session = first_ev.parameters[8]
 | 
						||
				fjid = jid
 | 
						||
				if resource:
 | 
						||
					fjid += '/' + resource
 | 
						||
				if self.open_event(account, fjid, first_ev):
 | 
						||
					return
 | 
						||
				# else
 | 
						||
				contact = gajim.contacts.get_contact(account, jid, resource)
 | 
						||
			if not contact or isinstance(contact, list):
 | 
						||
				contact = \
 | 
						||
					gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
			if jid == gajim.get_jid_from_account(account):
 | 
						||
				resource = contact.resource
 | 
						||
 | 
						||
			gajim.interface.on_open_chat_window(None, contact, account, \
 | 
						||
				resource = resource, session = session)
 | 
						||
 | 
						||
	def on_roster_treeview_row_activated(self, widget, path, col = 0):
 | 
						||
		'''When an iter is double clicked: open the first event window'''
 | 
						||
		if not gajim.single_click:
 | 
						||
			self.on_row_activated(widget, path)
 | 
						||
 | 
						||
	def on_roster_treeview_row_expanded(self, widget, titer, path):
 | 
						||
		'''When a row is expanded change the icon of the arrow'''
 | 
						||
		self._toggeling_row = True
 | 
						||
		model = widget.get_model()
 | 
						||
		child_model = model.get_model()
 | 
						||
		child_iter =  model.convert_iter_to_child_iter(titer)
 | 
						||
 | 
						||
		if self.regroup: # merged accounts
 | 
						||
			accounts = gajim.connections.keys()
 | 
						||
		else:
 | 
						||
			accounts = [model[titer][C_ACCOUNT].decode('utf-8')]
 | 
						||
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if type_ == 'group':
 | 
						||
			child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images[
 | 
						||
				'16']['opened']
 | 
						||
			group = model[titer][C_JID].decode('utf-8')
 | 
						||
			for account in accounts:
 | 
						||
				if group in gajim.groups[account]: # This account has this group
 | 
						||
					gajim.groups[account][group]['expand'] = True
 | 
						||
					if account + group in self.collapsed_rows:
 | 
						||
						self.collapsed_rows.remove(account + group)
 | 
						||
		elif type_ == 'account':
 | 
						||
			account = accounts[0] # There is only one cause we don't use merge
 | 
						||
			if account in self.collapsed_rows:
 | 
						||
				self.collapsed_rows.remove(account)
 | 
						||
			self.draw_account(account)
 | 
						||
			# When we expand, groups are collapsed. Restore expand state
 | 
						||
			for group in gajim.groups[account]:
 | 
						||
				if gajim.groups[account][group]['expand']:
 | 
						||
					titer = self._get_group_iter(group, account)
 | 
						||
					if titer:
 | 
						||
						path = model.get_path(titer)
 | 
						||
						self.tree.expand_row(path, False)
 | 
						||
		elif type_ == 'contact':
 | 
						||
			# Metacontact got toggled, update icon
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
			nearby_family, bb_jid, bb_account = \
 | 
						||
				self._get_nearby_family_and_big_brother(family, account)
 | 
						||
			# Redraw all brothers to show pending events
 | 
						||
			for data in nearby_family:
 | 
						||
				self.draw_contact(data['jid'], data['account'])
 | 
						||
 | 
						||
		self._toggeling_row = False
 | 
						||
 | 
						||
	def on_roster_treeview_row_collapsed(self, widget, titer, path):
 | 
						||
		'''When a row is collapsed change the icon of the arrow'''
 | 
						||
		self._toggeling_row = True
 | 
						||
		model = widget.get_model()
 | 
						||
		child_model = model.get_model()
 | 
						||
		child_iter =  model.convert_iter_to_child_iter(titer)
 | 
						||
 | 
						||
		if self.regroup: # merged accounts
 | 
						||
			accounts = gajim.connections.keys()
 | 
						||
		else:
 | 
						||
			accounts = [model[titer][C_ACCOUNT].decode('utf-8')]
 | 
						||
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if type_ == 'group':
 | 
						||
			child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images[
 | 
						||
				'16']['closed']
 | 
						||
			group = model[titer][C_JID].decode('utf-8')
 | 
						||
			for account in accounts:
 | 
						||
				if group in gajim.groups[account]: # This account has this group
 | 
						||
					gajim.groups[account][group]['expand'] = False
 | 
						||
					if not account + group in self.collapsed_rows:
 | 
						||
						self.collapsed_rows.append(account + group)
 | 
						||
		elif type_ == 'account':
 | 
						||
			account = accounts[0] # There is only one cause we don't use merge
 | 
						||
			if not account in self.collapsed_rows:
 | 
						||
				self.collapsed_rows.append(account)
 | 
						||
			self.draw_account(account)
 | 
						||
		elif type_ == 'contact':
 | 
						||
			# Metacontact got toggled, update icon
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
						||
			nearby_family, bb_jid, bb_account = \
 | 
						||
				self._get_nearby_family_and_big_brother(family, account)
 | 
						||
			# Redraw all brothers to show pending events
 | 
						||
			for data in nearby_family:
 | 
						||
				self.draw_contact(data['jid'], data['account'])
 | 
						||
 | 
						||
		self._toggeling_row = False
 | 
						||
 | 
						||
	def on_modelfilter_row_has_child_toggled(self, model, path, titer):
 | 
						||
		'''Called when a row has gotten the first or lost its last child row.
 | 
						||
 | 
						||
		Expand Parent if necessary.
 | 
						||
		'''
 | 
						||
		if self._toggeling_row:
 | 
						||
			# Signal is emitted when we write to our model
 | 
						||
			return
 | 
						||
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		account = model[titer][C_ACCOUNT]
 | 
						||
		if not account:
 | 
						||
			return
 | 
						||
 | 
						||
		account = account.decode('utf-8')
 | 
						||
 | 
						||
		if type_ == 'contact':
 | 
						||
			child_iter = model.convert_iter_to_child_iter(titer)
 | 
						||
			if self.model.iter_has_child(child_iter):
 | 
						||
				# we are a bigbrother metacontact
 | 
						||
				# redraw us to show/hide expand icon
 | 
						||
				if self.filtering:
 | 
						||
					# Prevent endless loops
 | 
						||
					jid = model[titer][C_JID].decode('utf-8')
 | 
						||
					gobject.idle_add(self.draw_contact, jid, account)
 | 
						||
		elif type_ == 'group':
 | 
						||
			group = model[titer][C_JID].decode('utf-8')
 | 
						||
			self._adjust_group_expand_collapse_state(group, account)
 | 
						||
		elif type_ == 'account':
 | 
						||
			self._adjust_account_expand_collapse_state(account)
 | 
						||
 | 
						||
# Selection can change when the model is filtered
 | 
						||
# Only write to the model when filtering is finished!
 | 
						||
#
 | 
						||
# FIXME: When we are filtering our custom colors are somehow lost
 | 
						||
#
 | 
						||
#	def on_treeview_selection_changed(self, selection):
 | 
						||
#		'''Called when selection in TreeView has changed.
 | 
						||
#
 | 
						||
#		Redraw unselected rows to make status message readable
 | 
						||
#		on all possible backgrounds.
 | 
						||
#		'''
 | 
						||
#		model, list_of_paths = selection.get_selected_rows()
 | 
						||
#		if len(self._last_selected_contact):
 | 
						||
#			# update unselected rows
 | 
						||
#			for (jid, account) in self._last_selected_contact:
 | 
						||
#				gobject.idle_add(self.draw_contact, jid, account)
 | 
						||
#		self._last_selected_contact = []
 | 
						||
#		if len(list_of_paths) == 0:
 | 
						||
#			return
 | 
						||
#		for path in list_of_paths:
 | 
						||
#			row = model[path]
 | 
						||
#			if row[C_TYPE] != 'contact':
 | 
						||
#				self._last_selected_contact = []
 | 
						||
#				return
 | 
						||
#			jid = row[C_JID].decode('utf-8')
 | 
						||
#			account = row[C_ACCOUNT].decode('utf-8')
 | 
						||
#			self._last_selected_contact.append((jid, account))
 | 
						||
#			gobject.idle_add(self.draw_contact, jid, account, True)
 | 
						||
 | 
						||
	def on_service_disco_menuitem_activate(self, widget, account):
 | 
						||
		server_jid = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
		if gajim.interface.instances[account]['disco'].has_key(server_jid):
 | 
						||
			gajim.interface.instances[account]['disco'][server_jid].\
 | 
						||
				window.present()
 | 
						||
		else:
 | 
						||
			try:
 | 
						||
				# Object will add itself to the window dict
 | 
						||
				disco.ServiceDiscoveryWindow(account, address_entry = True)
 | 
						||
			except GajimGeneralException:
 | 
						||
				pass
 | 
						||
 | 
						||
	def on_show_offline_contacts_menuitem_activate(self, widget):
 | 
						||
		'''when show offline option is changed:
 | 
						||
		redraw the treeview'''
 | 
						||
		gajim.config.set('showoffline', not gajim.config.get('showoffline'))
 | 
						||
		self.refilter_shown_roster_items()
 | 
						||
		if gajim.config.get('showoffline'):
 | 
						||
			# We need to filter twice to show groups with no contacts inside
 | 
						||
			# in the correct expand state
 | 
						||
			self.refilter_shown_roster_items()
 | 
						||
 | 
						||
 | 
						||
	def on_view_menu_activate(self, widget):
 | 
						||
		# Hide the show roster menu if we are not in the right windowing mode.
 | 
						||
		if self.hpaned.get_child2() is not None:
 | 
						||
			self.xml.get_widget('show_roster_menuitem').show()
 | 
						||
		else:
 | 
						||
			self.xml.get_widget('show_roster_menuitem').hide()
 | 
						||
 | 
						||
	def on_show_roster_menuitem_toggled(self, widget):
 | 
						||
		# when num controls is 0 this menuitem is hidden, but still need to
 | 
						||
		# disable keybinding
 | 
						||
		if self.hpaned.get_child2() is not None:
 | 
						||
			self.show_roster_vbox(widget.get_active())
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Drag and Drop handling
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def drag_data_get_data(self, treeview, context, selection, target_id, etime):
 | 
						||
		model, list_of_paths = self.tree.get_selection().get_selected_rows()
 | 
						||
		if len(list_of_paths) != 1:
 | 
						||
			return
 | 
						||
		path = list_of_paths[0]
 | 
						||
		data = ''
 | 
						||
		if len(path) >= 3:
 | 
						||
			data = model[path][C_JID]
 | 
						||
		selection.set(selection.target, 8, data)
 | 
						||
 | 
						||
	def drag_begin(self, treeview, context):
 | 
						||
		self.dragging = True
 | 
						||
 | 
						||
	def drag_end(self, treeview, context):
 | 
						||
		self.dragging = False
 | 
						||
 | 
						||
	def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
 | 
						||
	c_dest, was_big_brother, context, etime):
 | 
						||
 | 
						||
		if not gajim.connections[account_source].private_storage_supported or not\
 | 
						||
		gajim.connections[account_dest].private_storage_supported:
 | 
						||
			dialogs.WarningDialog(_('Metacontacts storage not supported by your '
 | 
						||
				'server'),
 | 
						||
				_('Your server does not support storing metacontacts information. '
 | 
						||
				'So those information will not be saved on next reconnection.'))
 | 
						||
 | 
						||
		def merge_contacts(is_checked=None):
 | 
						||
			if is_checked is not None: # dialog has been shown
 | 
						||
				if is_checked: # user does not want to be asked again
 | 
						||
					gajim.config.set('confirm_metacontacts', 'no')
 | 
						||
				else:
 | 
						||
					gajim.config.set('confirm_metacontacts', 'yes')
 | 
						||
 | 
						||
			# We might have dropped on a metacontact.
 | 
						||
			# Remove it and readd later with updated family info
 | 
						||
			dest_family = gajim.contacts.get_metacontacts_family(account_dest,
 | 
						||
				c_dest.jid)
 | 
						||
			if dest_family:
 | 
						||
				self._remove_metacontact_family(dest_family, account_dest)
 | 
						||
			else:
 | 
						||
				self._remove_entity(c_dest, account_dest)
 | 
						||
 | 
						||
			old_family = gajim.contacts.get_metacontacts_family(account_source,
 | 
						||
				c_source.jid)
 | 
						||
			old_groups = c_source.groups
 | 
						||
 | 
						||
			# Remove old source contact(s)
 | 
						||
			if was_big_brother:
 | 
						||
				# We have got little brothers. Readd them all
 | 
						||
				self._remove_metacontact_family(old_family, account_source)
 | 
						||
			else:
 | 
						||
				# We are only a litle brother. Simply remove us from our big brother
 | 
						||
				if self._get_contact_iter(c_source.jid, account_source):
 | 
						||
					# When we have been in the group before.
 | 
						||
					# Do not try to remove us again
 | 
						||
					self._remove_entity(c_source, account_source)
 | 
						||
 | 
						||
				own_data = {}
 | 
						||
				own_data['jid'] = c_source.jid
 | 
						||
				own_data['account'] = account_source
 | 
						||
				# Don't touch the rest of the family
 | 
						||
				old_family = [own_data]
 | 
						||
 | 
						||
			# Apply new tag and update contact
 | 
						||
			for data in old_family:
 | 
						||
				if account_source != data['account'] and not self.regroup:
 | 
						||
					continue
 | 
						||
 | 
						||
				_account = data['account']
 | 
						||
				_jid = data['jid']
 | 
						||
				_contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
 | 
						||
 | 
						||
				_contact.groups = c_dest.groups[:]
 | 
						||
				gajim.contacts.add_metacontact(account_dest, c_dest.jid,
 | 
						||
					_account, _contact.jid)
 | 
						||
				gajim.connections[account_source].update_contact(_contact.jid,
 | 
						||
					_contact.name, _contact.groups)
 | 
						||
 | 
						||
			# Re-add all and update GUI
 | 
						||
			new_family = gajim.contacts.get_metacontacts_family(account_source,
 | 
						||
				c_source.jid)
 | 
						||
			brothers = self._add_metacontact_family(new_family, account_source)
 | 
						||
 | 
						||
			for c, acc in brothers:
 | 
						||
				self.draw_contact(c.jid, acc)
 | 
						||
				self.draw_avatar(c.jid, acc)
 | 
						||
 | 
						||
			old_groups.extend(c_dest.groups)
 | 
						||
			for g in old_groups:
 | 
						||
				self.draw_group(g, account_source)
 | 
						||
 | 
						||
			self.draw_account(account_source)
 | 
						||
			context.finish(True, True, etime)
 | 
						||
 | 
						||
		confirm_metacontacts = gajim.config.get('confirm_metacontacts')
 | 
						||
		if confirm_metacontacts == 'no':
 | 
						||
			merge_contacts()
 | 
						||
			return
 | 
						||
		pritext = _('You are about to create a metacontact. Are you sure you want'
 | 
						||
			' to continue?')
 | 
						||
		sectext = _('Metacontacts are a way to regroup several contacts in one '
 | 
						||
			'line. Generally it is used when the same person has several Jabber '
 | 
						||
			'accounts or transport accounts.')
 | 
						||
		dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
 | 
						||
			_('Do _not ask me again'), on_response_ok = merge_contacts)
 | 
						||
		if not confirm_metacontacts: # First time we see this window
 | 
						||
			dlg.checkbutton.set_active(True)
 | 
						||
 | 
						||
 | 
						||
	def on_drop_in_group(self, widget, account, c_source, grp_dest,
 | 
						||
	is_big_brother, context, etime, grp_source = None):
 | 
						||
		if is_big_brother:
 | 
						||
			# add whole metacontact to new group
 | 
						||
			self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
 | 
						||
			# remove afterwards so the contact is not moved to General in the
 | 
						||
			# meantime
 | 
						||
			if grp_dest != grp_source:
 | 
						||
				self.remove_contact_from_groups(c_source.jid, account, [grp_source])
 | 
						||
		else:
 | 
						||
			# Normal contact or little brother
 | 
						||
			family = gajim.contacts.get_metacontacts_family(account,
 | 
						||
				c_source.jid)
 | 
						||
			if family:
 | 
						||
				# Little brother
 | 
						||
				# Remove whole family. Remove us from the family.
 | 
						||
				# Then re-add other family members.
 | 
						||
				self._remove_metacontact_family(family, account)
 | 
						||
				gajim.contacts.remove_metacontact(account, c_source.jid)
 | 
						||
				for data in family:
 | 
						||
					if account != data['account'] and not self.regroup:
 | 
						||
						continue
 | 
						||
					if data['jid'] == c_source.jid and\
 | 
						||
					data['account'] == account:
 | 
						||
						continue
 | 
						||
					self.add_contact(data['jid'], data['account'])
 | 
						||
					break
 | 
						||
 | 
						||
				self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
 | 
						||
 | 
						||
			else:
 | 
						||
				# Normal contact
 | 
						||
				self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
 | 
						||
				# remove afterwards so the contact is not moved to General in the
 | 
						||
				# meantime
 | 
						||
				if grp_dest != grp_source:
 | 
						||
					self.remove_contact_from_groups(c_source.jid, account,
 | 
						||
						[grp_source])
 | 
						||
 | 
						||
		if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
 | 
						||
			context.finish(True, True, etime)
 | 
						||
 | 
						||
 | 
						||
	def drag_drop(self, treeview, context, x, y, timestamp):
 | 
						||
		target_list = treeview.drag_dest_get_target_list()
 | 
						||
		target = treeview.drag_dest_find_target(context, target_list)
 | 
						||
		selection = treeview.drag_get_data(context, target)
 | 
						||
		context.finish(False, True)
 | 
						||
		return True
 | 
						||
 | 
						||
	def drag_data_received_data(self, treeview, context, x, y, selection, info,
 | 
						||
	etime):
 | 
						||
		treeview.stop_emission('drag_data_received')
 | 
						||
		drop_info = treeview.get_dest_row_at_pos(x, y)
 | 
						||
		if not drop_info:
 | 
						||
			return
 | 
						||
		if not selection.data:
 | 
						||
			return # prevents tb when several entrys are dragged
 | 
						||
		model = treeview.get_model()
 | 
						||
		data = selection.data
 | 
						||
		path_dest, position = drop_info
 | 
						||
 | 
						||
		if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2 \
 | 
						||
			and path_dest[1] == 0: # dropped before the first group
 | 
						||
			return
 | 
						||
		if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
 | 
						||
			# dropped before a group: we drop it in the previous group every time
 | 
						||
			path_dest = (path_dest[0], path_dest[1]-1)
 | 
						||
		# destination: the row something got dropped on
 | 
						||
		iter_dest = model.get_iter(path_dest)
 | 
						||
		type_dest = model[iter_dest][C_TYPE].decode('utf-8')
 | 
						||
		jid_dest = model[iter_dest][C_JID].decode('utf-8')
 | 
						||
		account_dest = model[iter_dest][C_ACCOUNT].decode('utf-8')
 | 
						||
 | 
						||
		# drop on account row in merged mode, we cannot know the desired account
 | 
						||
		if account_dest == 'all':
 | 
						||
			return
 | 
						||
		# nothing can be done, if destination account is offline
 | 
						||
		if gajim.connections[account_dest].connected < 2:
 | 
						||
			return
 | 
						||
 | 
						||
		# A file got dropped on the roster
 | 
						||
		if info == self.TARGET_TYPE_URI_LIST:
 | 
						||
			if len(path_dest) < 3:
 | 
						||
				return
 | 
						||
			if type_dest != 'contact':
 | 
						||
				return
 | 
						||
			c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
 | 
						||
				jid_dest)
 | 
						||
			uri = data.strip()
 | 
						||
			uri_splitted = uri.split() # we may have more than one file dropped
 | 
						||
			try:
 | 
						||
				# This is always the last element in windows
 | 
						||
				uri_splitted.remove('\0')
 | 
						||
			except ValueError:
 | 
						||
				pass
 | 
						||
			nb_uri = len(uri_splitted)
 | 
						||
			# Check the URIs
 | 
						||
			bad_uris = []
 | 
						||
			for a_uri in uri_splitted:
 | 
						||
				path = helpers.get_file_path_from_dnd_dropped_uri(a_uri)
 | 
						||
				if not os.path.isfile(path):
 | 
						||
					bad_uris.append(a_uri)
 | 
						||
			if len(bad_uris):
 | 
						||
				dialogs.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
 | 
						||
				return
 | 
						||
			def _on_send_files(account, jid, uris):
 | 
						||
				c = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
				for uri in uris:
 | 
						||
					path = helpers.get_file_path_from_dnd_dropped_uri(uri)
 | 
						||
					if os.path.isfile(path): # is it file?
 | 
						||
						gajim.interface.instances['file_transfers'].send_file(
 | 
						||
							account, c, path)
 | 
						||
			# Popup dialog to confirm sending
 | 
						||
			prim_text = 'Send file?'
 | 
						||
			sec_text = i18n.ngettext('Do you want to send this file to %s:',
 | 
						||
				'Do you want to send those files to %s:', nb_uri) %\
 | 
						||
				c_dest.get_shown_name()
 | 
						||
			for uri in uri_splitted:
 | 
						||
				path = helpers.get_file_path_from_dnd_dropped_uri(uri)
 | 
						||
				sec_text += '\n' + os.path.basename(path)
 | 
						||
			dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
 | 
						||
				on_response_ok = (_on_send_files, account_dest, jid_dest,
 | 
						||
				uri_splitted))
 | 
						||
			dialog.popup()
 | 
						||
			return
 | 
						||
 | 
						||
		# a roster entry was dragged and dropped somewhere in the roster
 | 
						||
 | 
						||
		# source: the row that was dragged
 | 
						||
		path_source = treeview.get_selection().get_selected_rows()[1][0]
 | 
						||
		iter_source = model.get_iter(path_source)
 | 
						||
		type_source = model[iter_source][C_TYPE]
 | 
						||
		account_source = model[iter_source][C_ACCOUNT].decode('utf-8')
 | 
						||
 | 
						||
		# Only normal contacts can be dragged
 | 
						||
		if type_source != 'contact':
 | 
						||
			return
 | 
						||
		if gajim.config.get_per('accounts', account_source, 'is_zeroconf'):
 | 
						||
			return
 | 
						||
 | 
						||
		# A contact was dropped
 | 
						||
		if gajim.config.get_per('accounts', account_dest, 'is_zeroconf'):
 | 
						||
			# drop on zeroconf account, adding not possible
 | 
						||
			return
 | 
						||
		if type_dest == 'self_contact':
 | 
						||
			# drop on self contact row
 | 
						||
			return
 | 
						||
		if type_dest == 'account' and account_source == account_dest:
 | 
						||
			# drop on the account it was dragged from
 | 
						||
			return
 | 
						||
		if type_dest == 'groupchat':
 | 
						||
			# drop on a minimized groupchat
 | 
						||
			# TODO: Invite to groupchat
 | 
						||
			return
 | 
						||
 | 
						||
		# Get valid source group, jid and contact
 | 
						||
		it = iter_source
 | 
						||
		while model[it][C_TYPE] == 'contact':
 | 
						||
			it = model.iter_parent(it)
 | 
						||
		grp_source = model[it][C_JID].decode('utf-8')
 | 
						||
		if grp_source in helpers.special_groups and \
 | 
						||
			grp_source not in ('Not in Roster', 'Observers'):
 | 
						||
			# a transport or a minimized groupchat was dragged
 | 
						||
			# we can add it to other accounts but not move it to another group,
 | 
						||
			# see below
 | 
						||
			return
 | 
						||
		jid_source = data.decode('utf-8')
 | 
						||
		c_source = gajim.contacts.get_contact_with_highest_priority(
 | 
						||
			account_source, jid_source)
 | 
						||
 | 
						||
		# Get destination group
 | 
						||
		grp_dest = None
 | 
						||
		if type_dest == 'group':
 | 
						||
			grp_dest = model[iter_dest][C_JID].decode('utf-8')
 | 
						||
		elif type_dest in ('contact', 'agent'):
 | 
						||
			it = iter_dest
 | 
						||
			while model[it][C_TYPE] != 'group':
 | 
						||
				it = model.iter_parent(it)
 | 
						||
			grp_dest = model[it][C_JID].decode('utf-8')
 | 
						||
		if grp_dest in helpers.special_groups:
 | 
						||
			return
 | 
						||
 | 
						||
		if jid_source == jid_dest:
 | 
						||
			if grp_source == grp_dest and account_source == account_dest:
 | 
						||
				# Drop on self
 | 
						||
				return
 | 
						||
 | 
						||
		# contact drop somewhere in or on a foreign account
 | 
						||
		if (type_dest == 'account' or not self.regroup) and \
 | 
						||
				account_source != account_dest:
 | 
						||
			# add to account in specified group
 | 
						||
			dialogs.AddNewContactWindow(account = account_dest, jid = jid_source,
 | 
						||
				user_nick = c_source.name, group = grp_dest)
 | 
						||
			return
 | 
						||
 | 
						||
		# we may not add contacts from special_groups
 | 
						||
		if grp_source in helpers.special_groups :
 | 
						||
			return
 | 
						||
 | 
						||
		# Is the contact we drag a meta contact?
 | 
						||
		accounts = (self.regroup and gajim.contacts.get_accounts()) or account_source
 | 
						||
		is_big_brother = gajim.contacts.is_big_brother(account_source, jid_source, accounts)
 | 
						||
 | 
						||
		# Contact drop on group row or between two contacts
 | 
						||
		if type_dest == 'group' or position == gtk.TREE_VIEW_DROP_BEFORE or \
 | 
						||
				position == gtk.TREE_VIEW_DROP_AFTER:
 | 
						||
			self.on_drop_in_group(None, account_source, c_source, grp_dest,
 | 
						||
				is_big_brother, context, etime, grp_source)
 | 
						||
			return
 | 
						||
 | 
						||
		# Contact drop on another contact, make meta contacts
 | 
						||
		if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \
 | 
						||
				position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE:
 | 
						||
			c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
 | 
						||
				jid_dest)
 | 
						||
			if not c_dest:
 | 
						||
				# c_dest is None if jid_dest doesn't belong to account
 | 
						||
				return
 | 
						||
			self.on_drop_in_contact(treeview, account_source, c_source,
 | 
						||
				account_dest, c_dest, is_big_brother, context, etime)
 | 
						||
			return
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Everything about images and icons....
 | 
						||
### Cleanup assigned to Jim++ :-)
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def get_appropriate_state_images(self, jid, size = '16',
 | 
						||
	icon_name = 'online'):
 | 
						||
		'''check jid and return the appropriate state images dict for
 | 
						||
		the demanded size. icon_name is taken into account when jid is from
 | 
						||
		transport: transport iconset doesn't contain all icons, so we fall back
 | 
						||
		to jabber one'''
 | 
						||
		transport = gajim.get_transport_name_from_jid(jid)
 | 
						||
		if transport and self.transports_state_images.has_key(size):
 | 
						||
			if not self.transports_state_images[size].has_key(transport):
 | 
						||
				# we don't have iconset for this transport loaded yet. Let's do it
 | 
						||
				self.make_transport_state_images(transport)
 | 
						||
			if self.transports_state_images[size].has_key(transport) and \
 | 
						||
			icon_name in self.transports_state_images[size][transport]:
 | 
						||
				return self.transports_state_images[size][transport]
 | 
						||
		return gajim.interface.jabber_state_images[size]
 | 
						||
 | 
						||
	def make_transport_state_images(self, transport):
 | 
						||
		'''initialise opened and closed 'transport' iconset dict'''
 | 
						||
		if gajim.config.get('use_transports_iconsets'):
 | 
						||
			folder = os.path.join(helpers.get_transport_path(transport),
 | 
						||
				'16x16')
 | 
						||
			pixo, pixc = gtkgui_helpers.load_icons_meta()
 | 
						||
			self.transports_state_images['opened'][transport] = \
 | 
						||
				gtkgui_helpers.load_iconset(folder, pixo, transport=True)
 | 
						||
			self.transports_state_images['closed'][transport] = \
 | 
						||
				gtkgui_helpers.load_iconset(folder, pixc, transport=True)
 | 
						||
			folder = os.path.join(helpers.get_transport_path(transport), '32x32')
 | 
						||
			self.transports_state_images['32'][transport] = \
 | 
						||
				gtkgui_helpers.load_iconset(folder, transport=True)
 | 
						||
			folder = os.path.join(helpers.get_transport_path(transport), '16x16')
 | 
						||
			self.transports_state_images['16'][transport] = \
 | 
						||
				gtkgui_helpers.load_iconset(folder, transport=True)
 | 
						||
 | 
						||
	def update_jabber_state_images(self):
 | 
						||
		# Update the roster
 | 
						||
		self.setup_and_draw_roster()
 | 
						||
		# Update the status combobox
 | 
						||
		model = self.status_combobox.get_model()
 | 
						||
		titer = model.get_iter_root()
 | 
						||
		while titer:
 | 
						||
			if model[titer][2] != '':
 | 
						||
				# If it's not change status message iter
 | 
						||
				# eg. if it has show parameter not ''
 | 
						||
				model[titer][1] = gajim.interface.jabber_state_images['16'][model[
 | 
						||
					titer][2]]
 | 
						||
			titer = model.iter_next(titer)
 | 
						||
		# Update the systray
 | 
						||
		if gajim.interface.systray_enabled:
 | 
						||
			gajim.interface.systray.set_img()
 | 
						||
 | 
						||
		for win in gajim.interface.msg_win_mgr.windows():
 | 
						||
			for ctrl in win.controls():
 | 
						||
				ctrl.update_ui()
 | 
						||
				win.redraw_tab(ctrl)
 | 
						||
 | 
						||
		self.update_status_combobox()
 | 
						||
 | 
						||
	def set_account_status_icon(self, account):
 | 
						||
		status = gajim.connections[account].connected
 | 
						||
		child_iterA = self._get_account_iter(account, self.model)
 | 
						||
		if not child_iterA:
 | 
						||
			return
 | 
						||
		if not self.regroup:
 | 
						||
			show = gajim.SHOW_LIST[status]
 | 
						||
		else:	# accounts merged
 | 
						||
			show = helpers.get_global_show()
 | 
						||
		self.model[child_iterA][C_IMG] = gajim.interface.jabber_state_images[
 | 
						||
			'16'][show]
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Style and theme related methods
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def show_title(self):
 | 
						||
		change_title_allowed = gajim.config.get('change_roster_title')
 | 
						||
		if not change_title_allowed:
 | 
						||
			return
 | 
						||
 | 
						||
		if gajim.config.get('one_message_window') == 'always_with_roster':
 | 
						||
			# always_with_roster mode defers to the MessageWindow
 | 
						||
			if not gajim.interface.msg_win_mgr.one_window_opened():
 | 
						||
				# No MessageWindow to defer to
 | 
						||
				self.window.set_title('Gajim')
 | 
						||
			return
 | 
						||
 | 
						||
		nb_unread = 0
 | 
						||
		start = ''
 | 
						||
		for account in gajim.connections:
 | 
						||
			# Count events in roster title only if we don't auto open them
 | 
						||
			if not helpers.allow_popup_window(account):
 | 
						||
				nb_unread += gajim.events.get_nb_events(['chat', 'normal',
 | 
						||
					'file-request', 'file-error', 'file-completed',
 | 
						||
					'file-request-error', 'file-send-error', 'file-stopped',
 | 
						||
					'printed_chat'], account)
 | 
						||
		if nb_unread > 1:
 | 
						||
			start = '[' + str(nb_unread) + ']  '
 | 
						||
		elif nb_unread == 1:
 | 
						||
			start = '*  '
 | 
						||
 | 
						||
		self.window.set_title(start + 'Gajim')
 | 
						||
 | 
						||
		gtkgui_helpers.set_unset_urgency_hint(self.window, nb_unread)
 | 
						||
 | 
						||
	def _change_style(self, model, path, titer, option):
 | 
						||
		if option is None or model[titer][C_TYPE] == option:
 | 
						||
			# We changed style for this type of row
 | 
						||
			model[titer][C_NAME] = model[titer][C_NAME]
 | 
						||
 | 
						||
	def change_roster_style(self, option):
 | 
						||
		self.model.foreach(self._change_style, option)
 | 
						||
		for win in gajim.interface.msg_win_mgr.windows():
 | 
						||
			win.repaint_themed_widgets()
 | 
						||
 | 
						||
	def repaint_themed_widgets(self):
 | 
						||
		'''Notify windows that contain themed widgets to repaint them'''
 | 
						||
		for win in gajim.interface.msg_win_mgr.windows():
 | 
						||
			win.repaint_themed_widgets()
 | 
						||
		for account in gajim.connections:
 | 
						||
			for addr in gajim.interface.instances[account]['disco']:
 | 
						||
				gajim.interface.instances[account]['disco'][addr].paint_banner()
 | 
						||
			for ctrl in gajim.interface.minimized_controls[account].values():
 | 
						||
				ctrl.repaint_themed_widgets()
 | 
						||
 | 
						||
	def update_avatar_in_gui(self, jid, account):
 | 
						||
		# Update roster
 | 
						||
		self.draw_avatar(jid, account)
 | 
						||
		# Update chat window
 | 
						||
 | 
						||
		ctrl = gajim.interface.msg_win_mgr.get_control(jid, account)
 | 
						||
		if ctrl and ctrl.type_id != message_control.TYPE_GC:
 | 
						||
			ctrl.show_avatar()
 | 
						||
 | 
						||
	def on_roster_treeview_style_set(self, treeview, style):
 | 
						||
		'''When style (theme) changes, redraw all contacts'''
 | 
						||
		for contact in self._iter_contact_rows():
 | 
						||
			self.draw_contact(contact[C_JID].decode('utf-8'),
 | 
						||
				contact[C_ACCOUNT].decode('utf-8'))
 | 
						||
 | 
						||
	def set_renderer_color(self, renderer, style, set_background = True):
 | 
						||
		'''set style for treeview cell, using PRELIGHT system color'''
 | 
						||
		if set_background:
 | 
						||
			bgcolor = self.tree.style.bg[style]
 | 
						||
			renderer.set_property('cell-background-gdk', bgcolor)
 | 
						||
		else:
 | 
						||
			fgcolor = self.tree.style.fg[style]
 | 
						||
			renderer.set_property('foreground-gdk', fgcolor)
 | 
						||
 | 
						||
	def _iconCellDataFunc(self, column, renderer, model, titer, data = None):
 | 
						||
		'''When a row is added, set properties for icon renderer'''
 | 
						||
		theme = gajim.config.get('roster_theme')
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if type_ == 'account':
 | 
						||
			color = gajim.config.get_per('themes', theme, 'accountbgcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('cell-background', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
 | 
						||
			renderer.set_property('xalign', 0)
 | 
						||
		elif type_ == 'group':
 | 
						||
			color = gajim.config.get_per('themes', theme, 'groupbgcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('cell-background', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
 | 
						||
			renderer.set_property('xalign', 0.2)
 | 
						||
		elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
 | 
						||
			if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
 | 
						||
				# This can append when at the moment we add the row
 | 
						||
				return
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			if jid in gajim.newly_added[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_connected_bg_color'))
 | 
						||
			elif jid in gajim.to_be_removed[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_disconnected_bg_color'))
 | 
						||
			else:
 | 
						||
				color = gajim.config.get_per('themes', theme, 'contactbgcolor')
 | 
						||
				if color:
 | 
						||
					renderer.set_property('cell-background', color)
 | 
						||
				else:
 | 
						||
					renderer.set_property('cell-background', None)
 | 
						||
			parent_iter = model.iter_parent(titer)
 | 
						||
			if model[parent_iter][C_TYPE] == 'contact':
 | 
						||
				renderer.set_property('xalign', 1)
 | 
						||
			else:
 | 
						||
				renderer.set_property('xalign', 0.4)
 | 
						||
		renderer.set_property('width', 26)
 | 
						||
 | 
						||
	def _nameCellDataFunc(self, column, renderer, model, titer, data = None):
 | 
						||
		'''When a row is added, set properties for name renderer'''
 | 
						||
		theme = gajim.config.get('roster_theme')
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if type_ == 'account':
 | 
						||
			color = gajim.config.get_per('themes', theme, 'accounttextcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('foreground', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_ACTIVE, False)
 | 
						||
			color = gajim.config.get_per('themes', theme, 'accountbgcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('cell-background', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
 | 
						||
			renderer.set_property('font',
 | 
						||
				gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
 | 
						||
			renderer.set_property('xpad', 0)
 | 
						||
			renderer.set_property('width', 3)
 | 
						||
		elif type_ == 'group':
 | 
						||
			color = gajim.config.get_per('themes', theme, 'grouptextcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('foreground', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_PRELIGHT, False)
 | 
						||
			color = gajim.config.get_per('themes', theme, 'groupbgcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('cell-background', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_PRELIGHT)
 | 
						||
			renderer.set_property('font',
 | 
						||
				gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
 | 
						||
			renderer.set_property('xpad', 4)
 | 
						||
		elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
 | 
						||
			if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
 | 
						||
				# This can append when at the moment we add the row
 | 
						||
				return
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			color = gajim.config.get_per('themes', theme, 'contacttextcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('foreground', color)
 | 
						||
			else:
 | 
						||
				renderer.set_property('foreground', None)
 | 
						||
			if jid in gajim.newly_added[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_connected_bg_color'))
 | 
						||
			elif jid in gajim.to_be_removed[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_disconnected_bg_color'))
 | 
						||
			else:
 | 
						||
				color = gajim.config.get_per('themes', theme, 'contactbgcolor')
 | 
						||
				if color:
 | 
						||
					renderer.set_property('cell-background', color)
 | 
						||
				else:
 | 
						||
					renderer.set_property('cell-background', None)
 | 
						||
			renderer.set_property('font',
 | 
						||
				gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
 | 
						||
			parent_iter = model.iter_parent(titer)
 | 
						||
			if model[parent_iter][C_TYPE] == 'contact':
 | 
						||
				renderer.set_property('xpad', 16)
 | 
						||
			else:
 | 
						||
				renderer.set_property('xpad', 8)
 | 
						||
 | 
						||
	def _fill_avatar_pixbuf_rederer(self, column, renderer, model, titer,
 | 
						||
	data = None):
 | 
						||
		'''When a row is added, set properties for avatar renderer'''
 | 
						||
		theme = gajim.config.get('roster_theme')
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		if type_ in ('group', 'account'):
 | 
						||
			renderer.set_property('visible', False)
 | 
						||
			return
 | 
						||
 | 
						||
		# allocate space for the icon only if needed
 | 
						||
		if model[titer][C_AVATAR_PIXBUF] or \
 | 
						||
		gajim.config.get('avatar_position_in_roster') == 'left':
 | 
						||
			renderer.set_property('visible', True)
 | 
						||
		else:
 | 
						||
			renderer.set_property('visible', False)
 | 
						||
		if type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
 | 
						||
			if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
 | 
						||
				# This can append at the moment we add the row
 | 
						||
				return
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			if jid in gajim.newly_added[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_connected_bg_color'))
 | 
						||
			elif jid in gajim.to_be_removed[account]:
 | 
						||
				renderer.set_property('cell-background', gajim.config.get(
 | 
						||
					'just_disconnected_bg_color'))
 | 
						||
			else:
 | 
						||
				color = gajim.config.get_per('themes', theme, 'contactbgcolor')
 | 
						||
				if color:
 | 
						||
					renderer.set_property('cell-background', color)
 | 
						||
				else:
 | 
						||
					renderer.set_property('cell-background', None)
 | 
						||
		if gajim.config.get('avatar_position_in_roster') == 'left':
 | 
						||
			renderer.set_property('width', gajim.config.get('roster_avatar_width'))
 | 
						||
			renderer.set_property('xalign', 0.5)
 | 
						||
		else:
 | 
						||
			renderer.set_property('xalign', 1) # align pixbuf to the right
 | 
						||
 | 
						||
	def _fill_padlock_pixbuf_rederer(self, column, renderer, model, titer,
 | 
						||
	data = None):
 | 
						||
		'''When a row is added, set properties for padlock renderer'''
 | 
						||
		theme = gajim.config.get('roster_theme')
 | 
						||
		type_ = model[titer][C_TYPE]
 | 
						||
		# allocate space for the icon only if needed
 | 
						||
		if type_ == 'account' and model[titer][C_PADLOCK_PIXBUF]:
 | 
						||
			renderer.set_property('visible', True)
 | 
						||
			color = gajim.config.get_per('themes', theme, 'accountbgcolor')
 | 
						||
			if color:
 | 
						||
				renderer.set_property('cell-background', color)
 | 
						||
			else:
 | 
						||
				self.set_renderer_color(renderer, gtk.STATE_ACTIVE)
 | 
						||
			renderer.set_property('xalign', 1) # align pixbuf to the right
 | 
						||
		else:
 | 
						||
			renderer.set_property('visible', False)
 | 
						||
 | 
						||
################################################################################
 | 
						||
### Everything about building menus
 | 
						||
### FIXME: We really need to make it simpler! 1465 lines are a few to much....
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def make_menu(self, force = False):
 | 
						||
		'''create the main window\'s menus'''
 | 
						||
		if not force and not self.actions_menu_needs_rebuild:
 | 
						||
			return
 | 
						||
		new_chat_menuitem = self.xml.get_widget('new_chat_menuitem')
 | 
						||
		single_message_menuitem = self.xml.get_widget(
 | 
						||
			'send_single_message_menuitem')
 | 
						||
		join_gc_menuitem = self.xml.get_widget('join_gc_menuitem')
 | 
						||
		muc_icon = gtkgui_helpers.load_icon('muc_active')
 | 
						||
		if muc_icon:
 | 
						||
			join_gc_menuitem.set_image(muc_icon)
 | 
						||
		add_new_contact_menuitem = self.xml.get_widget('add_new_contact_menuitem')
 | 
						||
		service_disco_menuitem = self.xml.get_widget('service_disco_menuitem')
 | 
						||
		advanced_menuitem = self.xml.get_widget('advanced_menuitem')
 | 
						||
		profile_avatar_menuitem = self.xml.get_widget('profile_avatar_menuitem')
 | 
						||
 | 
						||
		# destroy old advanced menus
 | 
						||
		for m in self.advanced_menus:
 | 
						||
			m.destroy()
 | 
						||
 | 
						||
		# make it sensitive. it is insensitive only if no accounts are *available*
 | 
						||
		advanced_menuitem.set_sensitive(True)
 | 
						||
 | 
						||
		if self.add_new_contact_handler_id:
 | 
						||
			add_new_contact_menuitem.handler_disconnect(
 | 
						||
				self.add_new_contact_handler_id)
 | 
						||
			self.add_new_contact_handler_id = None
 | 
						||
 | 
						||
		if self.service_disco_handler_id:
 | 
						||
			service_disco_menuitem.handler_disconnect(
 | 
						||
				self.service_disco_handler_id)
 | 
						||
			self.service_disco_handler_id = None
 | 
						||
 | 
						||
		if self.new_chat_menuitem_handler_id:
 | 
						||
			new_chat_menuitem.handler_disconnect(
 | 
						||
				self.new_chat_menuitem_handler_id)
 | 
						||
			self.new_chat_menuitem_handler_id = None
 | 
						||
 | 
						||
		if self.single_message_menuitem_handler_id:
 | 
						||
			single_message_menuitem.handler_disconnect(
 | 
						||
				self.single_message_menuitem_handler_id)
 | 
						||
			self.single_message_menuitem_handler_id = None
 | 
						||
 | 
						||
		if self.profile_avatar_menuitem_handler_id:
 | 
						||
			profile_avatar_menuitem.handler_disconnect(
 | 
						||
				self.profile_avatar_menuitem_handler_id)
 | 
						||
			self.profile_avatar_menuitem_handler_id = None
 | 
						||
 | 
						||
		# remove the existing submenus
 | 
						||
		add_new_contact_menuitem.remove_submenu()
 | 
						||
		service_disco_menuitem.remove_submenu()
 | 
						||
		join_gc_menuitem.remove_submenu()
 | 
						||
		single_message_menuitem.remove_submenu()
 | 
						||
		new_chat_menuitem.remove_submenu()
 | 
						||
		advanced_menuitem.remove_submenu()
 | 
						||
		profile_avatar_menuitem.remove_submenu()
 | 
						||
 | 
						||
		# remove the existing accelerator
 | 
						||
		if self.have_new_chat_accel:
 | 
						||
			ag = gtk.accel_groups_from_object(self.window)[0]
 | 
						||
			new_chat_menuitem.remove_accelerator(ag, gtk.keysyms.n,
 | 
						||
				gtk.gdk.CONTROL_MASK)
 | 
						||
			self.have_new_chat_accel = False
 | 
						||
 | 
						||
		gc_sub_menu = gtk.Menu() # gc is always a submenu
 | 
						||
		join_gc_menuitem.set_submenu(gc_sub_menu)
 | 
						||
 | 
						||
		connected_accounts = gajim.get_number_of_connected_accounts()
 | 
						||
 | 
						||
		connected_accounts_with_private_storage = 0
 | 
						||
 | 
						||
		accounts_list = gajim.contacts.get_accounts()
 | 
						||
		accounts_list.sort()
 | 
						||
 | 
						||
		# items that get shown whether an account is zeroconf or not
 | 
						||
		if connected_accounts > 1: # 2 or more accounts? make submenus
 | 
						||
			new_chat_sub_menu = gtk.Menu()
 | 
						||
 | 
						||
			for account in accounts_list:
 | 
						||
				if gajim.connections[account].connected <= 1:
 | 
						||
					# if offline or connecting
 | 
						||
					continue
 | 
						||
 | 
						||
				# new chat
 | 
						||
				new_chat_item = gtk.MenuItem(_('using account %s') % account,
 | 
						||
					False)
 | 
						||
				new_chat_sub_menu.append(new_chat_item)
 | 
						||
				new_chat_item.connect('activate',
 | 
						||
					self.on_new_chat_menuitem_activate, account)
 | 
						||
 | 
						||
			new_chat_menuitem.set_submenu(new_chat_sub_menu)
 | 
						||
			new_chat_sub_menu.show_all()
 | 
						||
 | 
						||
		elif connected_accounts == 1: # user has only one account
 | 
						||
			for account in gajim.connections:
 | 
						||
				if gajim.account_is_connected(account): # THE connected account
 | 
						||
					# new chat
 | 
						||
					if not self.new_chat_menuitem_handler_id:
 | 
						||
						self.new_chat_menuitem_handler_id = new_chat_menuitem.\
 | 
						||
							connect('activate', self.on_new_chat_menuitem_activate,
 | 
						||
							account)
 | 
						||
 | 
						||
					break
 | 
						||
 | 
						||
		# menu items that don't apply to zeroconf connections
 | 
						||
		if connected_accounts == 1 or (connected_accounts == 2 and \
 | 
						||
		gajim.zeroconf_is_connected()):
 | 
						||
			# only one 'real' (non-zeroconf) account is connected, don't need submenus
 | 
						||
 | 
						||
			for account in accounts_list:
 | 
						||
				if gajim.account_is_connected(account) and \
 | 
						||
				not gajim.config.get_per('accounts', account, 'is_zeroconf'):
 | 
						||
					# gc
 | 
						||
					if gajim.connections[account].private_storage_supported:
 | 
						||
						connected_accounts_with_private_storage += 1
 | 
						||
					self.add_bookmarks_list(gc_sub_menu, account)
 | 
						||
					gc_sub_menu.show_all()
 | 
						||
					# add
 | 
						||
					if not self.add_new_contact_handler_id:
 | 
						||
						self.add_new_contact_handler_id =\
 | 
						||
							add_new_contact_menuitem.connect(
 | 
						||
							'activate', self.on_add_new_contact, account)
 | 
						||
					# disco
 | 
						||
					if not self.service_disco_handler_id:
 | 
						||
						self.service_disco_handler_id = service_disco_menuitem.\
 | 
						||
							connect('activate',
 | 
						||
							self.on_service_disco_menuitem_activate, account)
 | 
						||
 | 
						||
					# single message
 | 
						||
					if not self.single_message_menuitem_handler_id:
 | 
						||
						self.single_message_menuitem_handler_id = \
 | 
						||
						single_message_menuitem.connect('activate', \
 | 
						||
						self.on_send_single_message_menuitem_activate, account)
 | 
						||
 | 
						||
					# new chat accel
 | 
						||
					if not self.have_new_chat_accel:
 | 
						||
						ag = gtk.accel_groups_from_object(self.window)[0]
 | 
						||
						new_chat_menuitem.add_accelerator('activate', ag,
 | 
						||
							gtk.keysyms.n,	gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
 | 
						||
						self.have_new_chat_accel = True
 | 
						||
 | 
						||
					break # No other account connected
 | 
						||
		else:
 | 
						||
			# 2 or more 'real' accounts are connected, make submenus
 | 
						||
			single_message_sub_menu = gtk.Menu()
 | 
						||
			add_sub_menu = gtk.Menu()
 | 
						||
			disco_sub_menu = gtk.Menu()
 | 
						||
 | 
						||
			for account in accounts_list:
 | 
						||
				if gajim.connections[account].connected <= 1 or \
 | 
						||
				gajim.config.get_per('accounts', account, 'is_zeroconf'):
 | 
						||
					# skip account if it's offline or connecting or is zeroconf
 | 
						||
					continue
 | 
						||
 | 
						||
				# single message
 | 
						||
				single_message_item = gtk.MenuItem(_('using account %s') % account,
 | 
						||
					False)
 | 
						||
				single_message_sub_menu.append(single_message_item)
 | 
						||
				single_message_item.connect('activate',
 | 
						||
					self.on_send_single_message_menuitem_activate, account)
 | 
						||
 | 
						||
				# join gc
 | 
						||
				if gajim.connections[account].private_storage_supported:
 | 
						||
					connected_accounts_with_private_storage += 1
 | 
						||
				gc_item = gtk.MenuItem(_('using account %s') % account, False)
 | 
						||
				gc_sub_menu.append(gc_item)
 | 
						||
				gc_menuitem_menu = gtk.Menu()
 | 
						||
				self.add_bookmarks_list(gc_menuitem_menu, account)
 | 
						||
				gc_item.set_submenu(gc_menuitem_menu)
 | 
						||
 | 
						||
				# add
 | 
						||
				add_item = gtk.MenuItem(_('to %s account') % account, False)
 | 
						||
				add_sub_menu.append(add_item)
 | 
						||
				add_item.connect('activate', self.on_add_new_contact, account)
 | 
						||
 | 
						||
				# disco
 | 
						||
				disco_item = gtk.MenuItem(_('using %s account') % account, False)
 | 
						||
				disco_sub_menu.append(disco_item)
 | 
						||
				disco_item.connect('activate',
 | 
						||
					self.on_service_disco_menuitem_activate, account)
 | 
						||
 | 
						||
			single_message_menuitem.set_submenu(single_message_sub_menu)
 | 
						||
			single_message_sub_menu.show_all()
 | 
						||
			gc_sub_menu.show_all()
 | 
						||
			add_new_contact_menuitem.set_submenu(add_sub_menu)
 | 
						||
			add_sub_menu.show_all()
 | 
						||
			service_disco_menuitem.set_submenu(disco_sub_menu)
 | 
						||
			disco_sub_menu.show_all()
 | 
						||
 | 
						||
		if connected_accounts == 0:
 | 
						||
			# no connected accounts, make the menuitems insensitive
 | 
						||
			for item in [new_chat_menuitem, join_gc_menuitem,\
 | 
						||
					add_new_contact_menuitem, service_disco_menuitem,\
 | 
						||
					single_message_menuitem]:
 | 
						||
				item.set_sensitive(False)
 | 
						||
		else: # we have one or more connected accounts
 | 
						||
			for item in [new_chat_menuitem, join_gc_menuitem,\
 | 
						||
					add_new_contact_menuitem, service_disco_menuitem,\
 | 
						||
					single_message_menuitem]:
 | 
						||
				item.set_sensitive(True)
 | 
						||
			# disable some fields if only local account is there
 | 
						||
			if connected_accounts == 1:
 | 
						||
				for account in gajim.connections:
 | 
						||
					if gajim.account_is_connected(account) and \
 | 
						||
							gajim.connections[account].is_zeroconf:
 | 
						||
						for item in [join_gc_menuitem,\
 | 
						||
								add_new_contact_menuitem, service_disco_menuitem,
 | 
						||
								single_message_menuitem]:
 | 
						||
							item.set_sensitive(False)
 | 
						||
 | 
						||
		if connected_accounts_with_private_storage > 0:
 | 
						||
			# At least one account with private storage support
 | 
						||
			# show the 'manage gc bookmarks' item
 | 
						||
			newitem = gtk.SeparatorMenuItem() # separator
 | 
						||
			gc_sub_menu.append(newitem)
 | 
						||
 | 
						||
			newitem = gtk.ImageMenuItem(_('_Manage Bookmarks...'))
 | 
						||
			img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
 | 
						||
				gtk.ICON_SIZE_MENU)
 | 
						||
			newitem.set_image(img)
 | 
						||
			newitem.connect('activate',
 | 
						||
				self.on_manage_bookmarks_menuitem_activate)
 | 
						||
			gc_sub_menu.append(newitem)
 | 
						||
			gc_sub_menu.show_all()
 | 
						||
 | 
						||
		connected_accounts_with_vcard = []
 | 
						||
		for account in gajim.connections:
 | 
						||
			if gajim.account_is_connected(account) and \
 | 
						||
			gajim.connections[account].vcard_supported:
 | 
						||
				connected_accounts_with_vcard.append(account)
 | 
						||
		if len(connected_accounts_with_vcard) > 1:
 | 
						||
			# 2 or more accounts? make submenus
 | 
						||
			profile_avatar_sub_menu = gtk.Menu()
 | 
						||
			for account in connected_accounts_with_vcard:
 | 
						||
				# profile, avatar
 | 
						||
				profile_avatar_item = gtk.MenuItem(_('of account %s') % account,
 | 
						||
					False)
 | 
						||
				profile_avatar_sub_menu.append(profile_avatar_item)
 | 
						||
				profile_avatar_item.connect('activate',
 | 
						||
					self.on_profile_avatar_menuitem_activate, account)
 | 
						||
			profile_avatar_menuitem.set_submenu(profile_avatar_sub_menu)
 | 
						||
			profile_avatar_sub_menu.show_all()
 | 
						||
		elif len(connected_accounts_with_vcard) == 1: # user has only one account
 | 
						||
			account = connected_accounts_with_vcard[0]
 | 
						||
			# profile, avatar
 | 
						||
			if not self.profile_avatar_menuitem_handler_id:
 | 
						||
				self.profile_avatar_menuitem_handler_id = \
 | 
						||
					profile_avatar_menuitem.connect('activate',
 | 
						||
					self.on_profile_avatar_menuitem_activate, account)
 | 
						||
 | 
						||
		if len(connected_accounts_with_vcard) == 0:
 | 
						||
			profile_avatar_menuitem.set_sensitive(False)
 | 
						||
		else:
 | 
						||
			profile_avatar_menuitem.set_sensitive(True)
 | 
						||
 | 
						||
		# Advanced Actions
 | 
						||
		if len(gajim.connections) == 0: # user has no accounts
 | 
						||
			advanced_menuitem.set_sensitive(False)
 | 
						||
		elif len(gajim.connections) == 1: # we have one acccount
 | 
						||
			account = gajim.connections.keys()[0]
 | 
						||
			advanced_menuitem_menu = self.get_and_connect_advanced_menuitem_menu(
 | 
						||
				account)
 | 
						||
			self.advanced_menus.append(advanced_menuitem_menu)
 | 
						||
 | 
						||
			self.add_history_manager_menuitem(advanced_menuitem_menu)
 | 
						||
 | 
						||
			advanced_menuitem.set_submenu(advanced_menuitem_menu)
 | 
						||
			advanced_menuitem_menu.show_all()
 | 
						||
		else: # user has *more* than one account : build advanced submenus
 | 
						||
			advanced_sub_menu = gtk.Menu()
 | 
						||
			accounts = [] # Put accounts in a list to sort them
 | 
						||
			for account in gajim.connections:
 | 
						||
				accounts.append(account)
 | 
						||
			accounts.sort()
 | 
						||
			for account in accounts:
 | 
						||
				advanced_item = gtk.MenuItem(_('for account %s') % account, False)
 | 
						||
				advanced_sub_menu.append(advanced_item)
 | 
						||
				advanced_menuitem_menu = \
 | 
						||
					self.get_and_connect_advanced_menuitem_menu(account)
 | 
						||
				self.advanced_menus.append(advanced_menuitem_menu)
 | 
						||
				advanced_item.set_submenu(advanced_menuitem_menu)
 | 
						||
 | 
						||
			self.add_history_manager_menuitem(advanced_sub_menu)
 | 
						||
 | 
						||
			advanced_menuitem.set_submenu(advanced_sub_menu)
 | 
						||
			advanced_sub_menu.show_all()
 | 
						||
 | 
						||
		if sys.platform == 'darwin':
 | 
						||
			try:
 | 
						||
				syncmenu.takeover_menu(self.xml.get_widget('menubar'))
 | 
						||
			except NameError:
 | 
						||
				pass
 | 
						||
 | 
						||
		self.actions_menu_needs_rebuild = False
 | 
						||
 | 
						||
	def build_account_menu(self, account):
 | 
						||
		# we have to create our own set of icons for the menu
 | 
						||
		# using self.jabber_status_images is poopoo
 | 
						||
		iconset = gajim.config.get('iconset')
 | 
						||
		path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
		state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
 | 
						||
		if not gajim.config.get_per('accounts', account, 'is_zeroconf'):
 | 
						||
			xml = gtkgui_helpers.get_glade('account_context_menu.glade')
 | 
						||
			account_context_menu = xml.get_widget('account_context_menu')
 | 
						||
 | 
						||
			status_menuitem = xml.get_widget('status_menuitem')
 | 
						||
			start_chat_menuitem = xml.get_widget('start_chat_menuitem')
 | 
						||
			join_group_chat_menuitem = xml.get_widget('join_group_chat_menuitem')
 | 
						||
			muc_icon = gtkgui_helpers.load_icon('muc_active')
 | 
						||
			if muc_icon:
 | 
						||
				join_group_chat_menuitem.set_image(muc_icon)
 | 
						||
			open_gmail_inbox_menuitem = xml.get_widget('open_gmail_inbox_menuitem')
 | 
						||
			add_contact_menuitem = xml.get_widget('add_contact_menuitem')
 | 
						||
			service_discovery_menuitem = xml.get_widget(
 | 
						||
				'service_discovery_menuitem')
 | 
						||
			execute_command_menuitem = xml.get_widget('execute_command_menuitem')
 | 
						||
			edit_account_menuitem = xml.get_widget('edit_account_menuitem')
 | 
						||
			sub_menu = gtk.Menu()
 | 
						||
			status_menuitem.set_submenu(sub_menu)
 | 
						||
 | 
						||
			for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
 | 
						||
				uf_show = helpers.get_uf_show(show, use_mnemonic = True)
 | 
						||
				item = gtk.ImageMenuItem(uf_show)
 | 
						||
				icon = state_images[show]
 | 
						||
				item.set_image(icon)
 | 
						||
				sub_menu.append(item)
 | 
						||
				con = gajim.connections[account]
 | 
						||
				if show == 'invisible' and con.connected > 1 and \
 | 
						||
				not con.privacy_rules_supported:
 | 
						||
					item.set_sensitive(False)
 | 
						||
				else:
 | 
						||
					item.connect('activate', self.change_status, account, show)
 | 
						||
 | 
						||
			item = gtk.SeparatorMenuItem()
 | 
						||
			sub_menu.append(item)
 | 
						||
 | 
						||
			item = gtk.ImageMenuItem(_('_Change Status Message'))
 | 
						||
			path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
 | 
						||
			img = gtk.Image()
 | 
						||
			img.set_from_file(path)
 | 
						||
			item.set_image(img)
 | 
						||
			sub_menu.append(item)
 | 
						||
			item.connect('activate', self.on_change_status_message_activate,
 | 
						||
				account)
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				item.set_sensitive(False)
 | 
						||
 | 
						||
			item = gtk.SeparatorMenuItem()
 | 
						||
			sub_menu.append(item)
 | 
						||
 | 
						||
			uf_show = helpers.get_uf_show('offline', use_mnemonic = True)
 | 
						||
			item = gtk.ImageMenuItem(uf_show)
 | 
						||
			icon = state_images['offline']
 | 
						||
			item.set_image(icon)
 | 
						||
			sub_menu.append(item)
 | 
						||
			item.connect('activate', self.change_status, account, 'offline')
 | 
						||
 | 
						||
			pep_menuitem = xml.get_widget('pep_menuitem')
 | 
						||
			if gajim.connections[account].pep_supported:
 | 
						||
				have_mood = gajim.config.get_per('accounts', account,
 | 
						||
					'publish_mood')
 | 
						||
				have_activity = gajim.config.get_per('accounts', account,
 | 
						||
					'publish_activity')
 | 
						||
				have_tune = gajim.config.get_per('accounts', account,
 | 
						||
					'publish_tune')
 | 
						||
				pep_submenu = gtk.Menu()
 | 
						||
				pep_menuitem.set_submenu(pep_submenu)
 | 
						||
				item = gtk.CheckMenuItem(_('Publish Tune'))
 | 
						||
				pep_submenu.append(item)
 | 
						||
				item.set_active(have_tune)
 | 
						||
				item.connect('toggled', self.on_publish_tune_toggled, account)
 | 
						||
				if have_mood:
 | 
						||
					item = gtk.CheckMenuItem(_('Mood'))
 | 
						||
					pep_submenu.append(item)
 | 
						||
					item.set_active(len(gajim.connections[account].mood) > 0)
 | 
						||
					item.connect('activate', self.on_change_mood_activate, account)
 | 
						||
				if have_activity:
 | 
						||
					item = gtk.CheckMenuItem(_('Activity'))
 | 
						||
					pep_submenu.append(item)
 | 
						||
					item.set_active(len(gajim.connections[account].activity) > 0)
 | 
						||
					item.connect('activate', self.on_change_activity_activate,
 | 
						||
						account)
 | 
						||
 | 
						||
				pep_config = gtk.ImageMenuItem(_('Configure Services...'))
 | 
						||
				if have_mood or have_activity or have_tune:
 | 
						||
					item = gtk.SeparatorMenuItem()
 | 
						||
					pep_submenu.append(item)
 | 
						||
					pep_config.set_sensitive(True)
 | 
						||
				else:
 | 
						||
					pep_config.set_sensitive(False)
 | 
						||
				pep_submenu.append(pep_config)
 | 
						||
				pep_config.connect('activate',
 | 
						||
					self.on_pep_services_menuitem_activate, account)
 | 
						||
				img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
 | 
						||
					gtk.ICON_SIZE_MENU)
 | 
						||
				pep_config.set_image(img)
 | 
						||
 | 
						||
			else:
 | 
						||
				pep_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			if not gajim.connections[account].gmail_url:
 | 
						||
				open_gmail_inbox_menuitem.set_no_show_all(True)
 | 
						||
				open_gmail_inbox_menuitem.hide()
 | 
						||
			else:
 | 
						||
				open_gmail_inbox_menuitem.connect('activate',
 | 
						||
					self.on_open_gmail_inbox, account)
 | 
						||
 | 
						||
			edit_account_menuitem.connect('activate', self.on_edit_account,
 | 
						||
				account)
 | 
						||
			add_contact_menuitem.connect('activate', self.on_add_new_contact,
 | 
						||
				account)
 | 
						||
			service_discovery_menuitem.connect('activate',
 | 
						||
				self.on_service_disco_menuitem_activate, account)
 | 
						||
			hostname = gajim.config.get_per('accounts', account, 'hostname')
 | 
						||
			contact = gajim.contacts.create_contact(jid=hostname) # Fake contact
 | 
						||
			execute_command_menuitem.connect('activate',
 | 
						||
				self.on_execute_command, contact, account)
 | 
						||
 | 
						||
			start_chat_menuitem.connect('activate',
 | 
						||
				self.on_new_chat_menuitem_activate, account)
 | 
						||
 | 
						||
			gc_sub_menu = gtk.Menu() # gc is always a submenu
 | 
						||
			join_group_chat_menuitem.set_submenu(gc_sub_menu)
 | 
						||
			self.add_bookmarks_list(gc_sub_menu, account)
 | 
						||
 | 
						||
			# make some items insensitive if account is offline
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				for widget in [add_contact_menuitem, service_discovery_menuitem,
 | 
						||
				join_group_chat_menuitem, execute_command_menuitem, pep_menuitem,
 | 
						||
				start_chat_menuitem]:
 | 
						||
					widget.set_sensitive(False)
 | 
						||
		else:
 | 
						||
			xml = gtkgui_helpers.get_glade('zeroconf_context_menu.glade')
 | 
						||
			account_context_menu = xml.get_widget('zeroconf_context_menu')
 | 
						||
 | 
						||
			status_menuitem = xml.get_widget('status_menuitem')
 | 
						||
			zeroconf_properties_menuitem = xml.get_widget(
 | 
						||
				'zeroconf_properties_menuitem')
 | 
						||
			sub_menu = gtk.Menu()
 | 
						||
			status_menuitem.set_submenu(sub_menu)
 | 
						||
 | 
						||
			for show in ('online', 'away', 'dnd', 'invisible'):
 | 
						||
				uf_show = helpers.get_uf_show(show, use_mnemonic = True)
 | 
						||
				item = gtk.ImageMenuItem(uf_show)
 | 
						||
				icon = state_images[show]
 | 
						||
				item.set_image(icon)
 | 
						||
				sub_menu.append(item)
 | 
						||
				item.connect('activate', self.change_status, account, show)
 | 
						||
 | 
						||
			item = gtk.SeparatorMenuItem()
 | 
						||
			sub_menu.append(item)
 | 
						||
 | 
						||
			item = gtk.ImageMenuItem(_('_Change Status Message'))
 | 
						||
			path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
 | 
						||
			img = gtk.Image()
 | 
						||
			img.set_from_file(path)
 | 
						||
			item.set_image(img)
 | 
						||
			sub_menu.append(item)
 | 
						||
			item.connect('activate', self.on_change_status_message_activate,
 | 
						||
				account)
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				item.set_sensitive(False)
 | 
						||
 | 
						||
			uf_show = helpers.get_uf_show('offline', use_mnemonic = True)
 | 
						||
			item = gtk.ImageMenuItem(uf_show)
 | 
						||
			icon = state_images['offline']
 | 
						||
			item.set_image(icon)
 | 
						||
			sub_menu.append(item)
 | 
						||
			item.connect('activate', self.change_status, account, 'offline')
 | 
						||
 | 
						||
			zeroconf_properties_menuitem.connect('activate',
 | 
						||
				self.on_zeroconf_properties, account)
 | 
						||
			#gc_sub_menu = gtk.Menu() # gc is always a submenu
 | 
						||
			#join_group_chat_menuitem.set_submenu(gc_sub_menu)
 | 
						||
			#self.add_bookmarks_list(gc_sub_menu, account)
 | 
						||
			#new_message_menuitem.connect('activate',
 | 
						||
			#	self.on_new_message_menuitem_activate, account)
 | 
						||
 | 
						||
			# make some items insensitive if account is offline
 | 
						||
			#if gajim.connections[account].connected < 2:
 | 
						||
			#	for widget in [join_group_chat_menuitem, new_message_menuitem]:
 | 
						||
			#		widget.set_sensitive(False)
 | 
						||
			#	new_message_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
		return account_context_menu
 | 
						||
 | 
						||
	def make_account_menu(self, event, titer):
 | 
						||
		'''Make account's popup menu'''
 | 
						||
		model = self.modelfilter
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
 | 
						||
		if account != 'all': # not in merged mode
 | 
						||
			menu = self.build_account_menu(account)
 | 
						||
		else:
 | 
						||
			menu = gtk.Menu()
 | 
						||
			iconset = gajim.config.get('iconset')
 | 
						||
			path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
			accounts = [] # Put accounts in a list to sort them
 | 
						||
			for account in gajim.connections:
 | 
						||
				accounts.append(account)
 | 
						||
			accounts.sort()
 | 
						||
			for account in accounts:
 | 
						||
				state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
				item = gtk.ImageMenuItem(account)
 | 
						||
				show = gajim.SHOW_LIST[gajim.connections[account].connected]
 | 
						||
				icon = state_images[show]
 | 
						||
				item.set_image(icon)
 | 
						||
				account_menu = self.build_account_menu(account)
 | 
						||
				item.set_submenu(account_menu)
 | 
						||
				menu.append(item)
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		menu.attach_to_widget(self.tree, None)
 | 
						||
		menu.connect('selection-done', gtkgui_helpers.destroy_widget)
 | 
						||
		menu.show_all()
 | 
						||
		menu.popup(None, None, None, event_button, event.time)
 | 
						||
 | 
						||
	def make_group_menu(self, event, titer):
 | 
						||
		'''Make group's popup menu'''
 | 
						||
		model = self.modelfilter
 | 
						||
		path = model.get_path(titer)
 | 
						||
		group = model[titer][C_JID].decode('utf-8')
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
 | 
						||
		list_ = [] # list of (jid, account) tuples
 | 
						||
		list_online = [] # list of (jid, account) tuples
 | 
						||
 | 
						||
		group = model[titer][C_JID]
 | 
						||
		for jid in gajim.contacts.get_jid_list(account):
 | 
						||
			contact = gajim.contacts.get_contact_with_highest_priority(account,
 | 
						||
					jid)
 | 
						||
			if group in contact.get_shown_groups():
 | 
						||
				if contact.show not in ('offline', 'error'):
 | 
						||
					list_online.append((contact, account))
 | 
						||
				list_.append((contact, account))
 | 
						||
		menu = gtk.Menu()
 | 
						||
 | 
						||
		# Make special context menu if group is Groupchats
 | 
						||
		if group == _('Groupchats'):
 | 
						||
			maximize_menuitem = gtk.ImageMenuItem(_('_Maximize All'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
 | 
						||
			maximize_menuitem.set_image(icon)
 | 
						||
			maximize_menuitem.connect('activate', self.on_all_groupchat_maximized,\
 | 
						||
				list_)
 | 
						||
			menu.append(maximize_menuitem)
 | 
						||
		else:
 | 
						||
			# Send Group Message
 | 
						||
			send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
 | 
						||
			send_group_message_item.set_image(icon)
 | 
						||
 | 
						||
			send_group_message_submenu = gtk.Menu()
 | 
						||
			send_group_message_item.set_submenu(send_group_message_submenu)
 | 
						||
			menu.append(send_group_message_item)
 | 
						||
 | 
						||
			group_message_to_all_item = gtk.MenuItem(_('To all users'))
 | 
						||
			send_group_message_submenu.append(group_message_to_all_item)
 | 
						||
 | 
						||
			group_message_to_all_online_item = gtk.MenuItem(
 | 
						||
				_('To all online users'))
 | 
						||
			send_group_message_submenu.append(group_message_to_all_online_item)
 | 
						||
 | 
						||
			group_message_to_all_online_item.connect('activate',
 | 
						||
				self.on_send_single_message_menuitem_activate, account, list_online)
 | 
						||
			group_message_to_all_item.connect('activate',
 | 
						||
				self.on_send_single_message_menuitem_activate, account, list_)
 | 
						||
 | 
						||
			# Invite to
 | 
						||
			invite_menuitem = gtk.ImageMenuItem(_('In_vite to'))
 | 
						||
			muc_icon = gtkgui_helpers.load_icon('muc_active')
 | 
						||
			if muc_icon:
 | 
						||
				invite_menuitem.set_image(muc_icon)
 | 
						||
 | 
						||
			self.build_invite_submenu(invite_menuitem, list_online)
 | 
						||
			menu.append(invite_menuitem)
 | 
						||
 | 
						||
			# Send Custom Status
 | 
						||
			send_custom_status_menuitem = gtk.ImageMenuItem(
 | 
						||
				_('Send Cus_tom Status'))
 | 
						||
			# add a special img for this menuitem
 | 
						||
			if group in gajim.connections[account].blocked_groups:
 | 
						||
				send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
 | 
						||
					'offline'))
 | 
						||
				send_custom_status_menuitem.set_sensitive(False)
 | 
						||
			else:
 | 
						||
				icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
 | 
						||
					gtk.ICON_SIZE_MENU)
 | 
						||
				send_custom_status_menuitem.set_image(icon)
 | 
						||
			status_menuitems = gtk.Menu()
 | 
						||
			send_custom_status_menuitem.set_submenu(status_menuitems)
 | 
						||
			iconset = gajim.config.get('iconset')
 | 
						||
			path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
			for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
 | 
						||
				# icon MUST be different instance for every item
 | 
						||
				state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
				status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
 | 
						||
				status_menuitem.connect('activate', self.on_send_custom_status,
 | 
						||
					list_, s, group)
 | 
						||
				icon = state_images[s]
 | 
						||
				status_menuitem.set_image(icon)
 | 
						||
				status_menuitems.append(status_menuitem)
 | 
						||
			menu.append(send_custom_status_menuitem)
 | 
						||
 | 
						||
			# there is no singlemessage and custom status for zeroconf
 | 
						||
			if gajim.config.get_per('accounts', account, 'is_zeroconf'):
 | 
						||
				send_custom_status_menuitem.set_sensitive(False)
 | 
						||
				send_group_message_item.set_sensitive(False)
 | 
						||
 | 
						||
		if not group in helpers.special_groups:
 | 
						||
			item = gtk.SeparatorMenuItem() # separator
 | 
						||
			menu.append(item)
 | 
						||
 | 
						||
			# Rename
 | 
						||
			rename_item = gtk.ImageMenuItem(_('Re_name'))
 | 
						||
			# add a special img for rename menuitem
 | 
						||
			path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
 | 
						||
				'kbd_input.png')
 | 
						||
			img = gtk.Image()
 | 
						||
			img.set_from_file(path_to_kbd_input_img)
 | 
						||
			rename_item.set_image(img)
 | 
						||
			menu.append(rename_item)
 | 
						||
			rename_item.connect('activate', self.on_rename, titer, path)
 | 
						||
 | 
						||
			# Block group
 | 
						||
			is_blocked = False
 | 
						||
			if self.regroup:
 | 
						||
				for g_account in gajim.connections:
 | 
						||
					if group in gajim.connections[g_account].blocked_groups:
 | 
						||
						is_blocked = True
 | 
						||
			else:
 | 
						||
				if group in gajim.connections[account].blocked_groups:
 | 
						||
					is_blocked = True
 | 
						||
 | 
						||
			if is_blocked and gajim.connections[account].privacy_rules_supported:
 | 
						||
				unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
 | 
						||
				icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
 | 
						||
				unblock_menuitem.set_image(icon)
 | 
						||
				unblock_menuitem.connect('activate', self.on_unblock, titer, list_)
 | 
						||
				menu.append(unblock_menuitem)
 | 
						||
			else:
 | 
						||
				block_menuitem = gtk.ImageMenuItem(_('_Block'))
 | 
						||
				icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
 | 
						||
				block_menuitem.set_image(icon)
 | 
						||
				block_menuitem.connect('activate', self.on_block, titer, list_)
 | 
						||
				menu.append(block_menuitem)
 | 
						||
				if not gajim.connections[account].privacy_rules_supported:
 | 
						||
					block_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			# Remove group
 | 
						||
			remove_item = gtk.ImageMenuItem(_('_Remove'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
 | 
						||
			remove_item.set_image(icon)
 | 
						||
			menu.append(remove_item)
 | 
						||
			remove_item.connect('activate', self.on_remove_group_item_activated,
 | 
						||
				group, account)
 | 
						||
 | 
						||
			# unsensitive if account is not connected
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				rename_item.set_sensitive(False)
 | 
						||
 | 
						||
			# General group cannot be changed
 | 
						||
			if group == _('General'):
 | 
						||
				rename_item.set_sensitive(False)
 | 
						||
				block_menuitem.set_sensitive(False)
 | 
						||
				remove_item.set_sensitive(False)
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		menu.attach_to_widget(self.tree, None)
 | 
						||
		menu.connect('selection-done', gtkgui_helpers.destroy_widget)
 | 
						||
		menu.show_all()
 | 
						||
		menu.popup(None, None, None, event_button, event.time)
 | 
						||
 | 
						||
	def make_contact_menu(self, event, titer):
 | 
						||
		'''Make contact\'s popup menu'''
 | 
						||
		model = self.modelfilter
 | 
						||
		jid = model[titer][C_JID].decode('utf-8')
 | 
						||
		tree_path = model.get_path(titer)
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
		our_jid = jid == gajim.get_jid_from_account(account)
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		if not contact:
 | 
						||
			return
 | 
						||
 | 
						||
		# Zeroconf Account
 | 
						||
		if gajim.config.get_per('accounts', account, 'is_zeroconf'):
 | 
						||
			xml = gtkgui_helpers.get_glade('zeroconf_contact_context_menu.glade')
 | 
						||
			zeroconf_contact_context_menu = xml.get_widget(
 | 
						||
				'zeroconf_contact_context_menu')
 | 
						||
 | 
						||
			start_chat_menuitem = xml.get_widget('start_chat_menuitem')
 | 
						||
			rename_menuitem = xml.get_widget('rename_menuitem')
 | 
						||
			edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
 | 
						||
			send_file_menuitem = xml.get_widget('send_file_menuitem')
 | 
						||
			assign_openpgp_key_menuitem = xml.get_widget(
 | 
						||
				'assign_openpgp_key_menuitem')
 | 
						||
			add_special_notification_menuitem = xml.get_widget(
 | 
						||
				'add_special_notification_menuitem')
 | 
						||
 | 
						||
			if not our_jid:
 | 
						||
				# add a special img for rename menuitem
 | 
						||
				path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
 | 
						||
					'kbd_input.png')
 | 
						||
				img = gtk.Image()
 | 
						||
				img.set_from_file(path_to_kbd_input_img)
 | 
						||
				rename_menuitem.set_image(img)
 | 
						||
 | 
						||
			above_information_separator = xml.get_widget(
 | 
						||
				'above_information_separator')
 | 
						||
 | 
						||
			information_menuitem = xml.get_widget('information_menuitem')
 | 
						||
			history_menuitem = xml.get_widget('history_menuitem')
 | 
						||
 | 
						||
			contacts = gajim.contacts.get_contacts(account, jid)
 | 
						||
			if len(contacts) > 1: # several resources
 | 
						||
				sub_menu = gtk.Menu()
 | 
						||
				start_chat_menuitem.set_submenu(sub_menu)
 | 
						||
 | 
						||
				iconset = gajim.config.get('iconset')
 | 
						||
				path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
				for c in contacts:
 | 
						||
					# icon MUST be different instance for every item
 | 
						||
					state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
					item = gtk.ImageMenuItem('%s (%s)' % (c.resource,
 | 
						||
						str(c.priority)))
 | 
						||
					icon_name = helpers.get_icon_name_to_show(c, account)
 | 
						||
					icon = state_images[icon_name]
 | 
						||
					item.set_image(icon)
 | 
						||
					sub_menu.append(item)
 | 
						||
					item.connect('activate', gajim.interface.on_open_chat_window, \
 | 
						||
						c, account,	c.resource)
 | 
						||
 | 
						||
			else: # one resource
 | 
						||
				start_chat_menuitem.connect('activate',
 | 
						||
					self.on_roster_treeview_row_activated, tree_path)
 | 
						||
 | 
						||
			if gajim.capscache.is_supported(contact, NS_FILE):
 | 
						||
				send_file_menuitem.set_sensitive(True)
 | 
						||
				send_file_menuitem.connect('activate',
 | 
						||
					self.on_send_file_menuitem_activate, contact, account)
 | 
						||
			else:
 | 
						||
				send_file_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			rename_menuitem.connect('activate', self.on_rename, titer, tree_path)
 | 
						||
			if contact.show in ('offline', 'error'):
 | 
						||
				information_menuitem.set_sensitive(False)
 | 
						||
				send_file_menuitem.set_sensitive(False)
 | 
						||
			else:
 | 
						||
				information_menuitem.connect('activate', self.on_info_zeroconf,
 | 
						||
					contact, account)
 | 
						||
			history_menuitem.connect('activate', self.on_history, contact,
 | 
						||
				account)
 | 
						||
 | 
						||
			if _('Not in Roster') not in contact.get_shown_groups():
 | 
						||
				# contact is in normal group
 | 
						||
				edit_groups_menuitem.set_no_show_all(False)
 | 
						||
				assign_openpgp_key_menuitem.set_no_show_all(False)
 | 
						||
				edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
 | 
						||
					contact,account)])
 | 
						||
 | 
						||
				if gajim.connections[account].gpg:
 | 
						||
					assign_openpgp_key_menuitem.connect('activate',
 | 
						||
						self.on_assign_pgp_key, contact, account)
 | 
						||
				else:
 | 
						||
					assign_openpgp_key_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			else: # contact is in group 'Not in Roster'
 | 
						||
				edit_groups_menuitem.set_sensitive(False)
 | 
						||
				edit_groups_menuitem.set_no_show_all(True)
 | 
						||
				assign_openpgp_key_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			# Remove many items when it's self contact row
 | 
						||
			if our_jid:
 | 
						||
				for menuitem in (rename_menuitem, edit_groups_menuitem,
 | 
						||
				above_information_separator):
 | 
						||
					menuitem.set_no_show_all(True)
 | 
						||
					menuitem.hide()
 | 
						||
 | 
						||
			# Unsensitive many items when account is offline
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				for widget in [start_chat_menuitem,	rename_menuitem,
 | 
						||
				edit_groups_menuitem, send_file_menuitem]:
 | 
						||
					widget.set_sensitive(False)
 | 
						||
 | 
						||
			event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
			zeroconf_contact_context_menu.attach_to_widget(self.tree, None)
 | 
						||
			zeroconf_contact_context_menu.connect('selection-done',
 | 
						||
				gtkgui_helpers.destroy_widget)
 | 
						||
			zeroconf_contact_context_menu.show_all()
 | 
						||
			zeroconf_contact_context_menu.popup(None, None, None, event_button,
 | 
						||
				event.time)
 | 
						||
			return
 | 
						||
 | 
						||
		# normal account
 | 
						||
		xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
 | 
						||
		roster_contact_context_menu = xml.get_widget(
 | 
						||
			'roster_contact_context_menu')
 | 
						||
 | 
						||
		start_chat_menuitem = xml.get_widget('start_chat_menuitem')
 | 
						||
		send_custom_status_menuitem = xml.get_widget(
 | 
						||
			'send_custom_status_menuitem')
 | 
						||
		send_single_message_menuitem = xml.get_widget(
 | 
						||
			'send_single_message_menuitem')
 | 
						||
		invite_menuitem = xml.get_widget('invite_menuitem')
 | 
						||
		block_menuitem = xml.get_widget('block_menuitem')
 | 
						||
		unblock_menuitem = xml.get_widget('unblock_menuitem')
 | 
						||
		ignore_menuitem = xml.get_widget('ignore_menuitem')
 | 
						||
		unignore_menuitem = xml.get_widget('unignore_menuitem')
 | 
						||
		rename_menuitem = xml.get_widget('rename_menuitem')
 | 
						||
		edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
 | 
						||
		send_file_menuitem = xml.get_widget('send_file_menuitem')
 | 
						||
		assign_openpgp_key_menuitem = xml.get_widget(
 | 
						||
			'assign_openpgp_key_menuitem')
 | 
						||
		set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem')
 | 
						||
		add_special_notification_menuitem = xml.get_widget(
 | 
						||
			'add_special_notification_menuitem')
 | 
						||
		execute_command_menuitem = xml.get_widget(
 | 
						||
			'execute_command_menuitem')
 | 
						||
 | 
						||
		# send custom status icon
 | 
						||
		blocked = False
 | 
						||
		if jid in gajim.connections[account].blocked_contacts:
 | 
						||
			blocked = True
 | 
						||
		else:
 | 
						||
			for group in contact.get_shown_groups():
 | 
						||
				if group in gajim.connections[account].blocked_groups:
 | 
						||
					blocked = True
 | 
						||
					break
 | 
						||
		if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
 | 
						||
			# Transport contact, send custom status unavailable
 | 
						||
			send_custom_status_menuitem.set_sensitive(False)
 | 
						||
		elif blocked:
 | 
						||
			send_custom_status_menuitem.set_image( \
 | 
						||
				gtkgui_helpers.load_icon('offline'))
 | 
						||
			send_custom_status_menuitem.set_sensitive(False)
 | 
						||
		elif gajim.interface.status_sent_to_users.has_key(account) and \
 | 
						||
		jid in gajim.interface.status_sent_to_users[account]:
 | 
						||
			send_custom_status_menuitem.set_image(
 | 
						||
				gtkgui_helpers.load_icon( \
 | 
						||
					gajim.interface.status_sent_to_users[account][jid]))
 | 
						||
		else:
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU)
 | 
						||
			send_custom_status_menuitem.set_image(icon)
 | 
						||
 | 
						||
		if not our_jid:
 | 
						||
			# add a special img for rename menuitem
 | 
						||
			path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
 | 
						||
				'kbd_input.png')
 | 
						||
			img = gtk.Image()
 | 
						||
			img.set_from_file(path_to_kbd_input_img)
 | 
						||
			rename_menuitem.set_image(img)
 | 
						||
 | 
						||
		muc_icon = gtkgui_helpers.load_icon('muc_active')
 | 
						||
		if muc_icon:
 | 
						||
			invite_menuitem.set_image(muc_icon)
 | 
						||
 | 
						||
		self.build_invite_submenu(invite_menuitem, [(contact, account)])
 | 
						||
 | 
						||
		# Subscription submenu
 | 
						||
		subscription_menuitem = xml.get_widget('subscription_menuitem')
 | 
						||
		send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem =\
 | 
						||
			subscription_menuitem.get_submenu().get_children()
 | 
						||
		add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem')
 | 
						||
		remove_from_roster_menuitem = xml.get_widget(
 | 
						||
			'remove_from_roster_menuitem')
 | 
						||
 | 
						||
		information_menuitem = xml.get_widget('information_menuitem')
 | 
						||
		history_menuitem = xml.get_widget('history_menuitem')
 | 
						||
 | 
						||
		contacts = gajim.contacts.get_contacts(account, jid)
 | 
						||
 | 
						||
		# One or several resource, we do the same for send_custom_status
 | 
						||
		status_menuitems = gtk.Menu()
 | 
						||
		send_custom_status_menuitem.set_submenu(status_menuitems)
 | 
						||
		iconset = gajim.config.get('iconset')
 | 
						||
		path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
		for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
 | 
						||
			# icon MUST be different instance for every item
 | 
						||
			state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
			status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
 | 
						||
			status_menuitem.connect('activate', self.on_send_custom_status,
 | 
						||
				[(contact, account)], s)
 | 
						||
			icon = state_images[s]
 | 
						||
			status_menuitem.set_image(icon)
 | 
						||
			status_menuitems.append(status_menuitem)
 | 
						||
		if len(contacts) > 1: # several resources
 | 
						||
			start_chat_menuitem.set_submenu(self.build_resources_submenu(contacts,
 | 
						||
				account, gajim.interface.on_open_chat_window))
 | 
						||
			send_file_menuitem.set_submenu(self.build_resources_submenu(contacts,
 | 
						||
				account, self.on_send_file_menuitem_activate,
 | 
						||
				cap=NS_FILE))
 | 
						||
			execute_command_menuitem.set_submenu(self.build_resources_submenu(
 | 
						||
				contacts, account, self.on_execute_command,
 | 
						||
				cap=NS_COMMANDS))
 | 
						||
 | 
						||
		else: # one resource
 | 
						||
			start_chat_menuitem.connect('activate',
 | 
						||
				gajim.interface.on_open_chat_window, contact, account)
 | 
						||
			if gajim.capscache.is_supported(contact, NS_COMMANDS):
 | 
						||
				execute_command_menuitem.set_sensitive(True)
 | 
						||
				execute_command_menuitem.connect('activate', self.on_execute_command,
 | 
						||
					contact, account, contact.resource)
 | 
						||
			else:
 | 
						||
				execute_command_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			our_jid_other_resource = None
 | 
						||
			if our_jid:
 | 
						||
				# It's another resource of us, be sure to send invite to her
 | 
						||
				our_jid_other_resource = contact.resource
 | 
						||
			# Else this var is useless but harmless in next connect calls
 | 
						||
 | 
						||
			if gajim.capscache.is_supported(contact, NS_FILE):
 | 
						||
				send_file_menuitem.set_sensitive(True)
 | 
						||
				send_file_menuitem.connect('activate',
 | 
						||
					self.on_send_file_menuitem_activate, contact, account)
 | 
						||
			else:
 | 
						||
				send_file_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
		send_single_message_menuitem.connect('activate',
 | 
						||
			self.on_send_single_message_menuitem_activate, account, contact)
 | 
						||
 | 
						||
		rename_menuitem.connect('activate', self.on_rename, titer, tree_path)
 | 
						||
		remove_from_roster_menuitem.connect('activate', self.on_req_usub,
 | 
						||
			[(contact, account)])
 | 
						||
		information_menuitem.connect('activate', self.on_info, contact,
 | 
						||
			account)
 | 
						||
		history_menuitem.connect('activate', self.on_history, contact,
 | 
						||
			account)
 | 
						||
 | 
						||
		if _('Not in Roster') not in contact.get_shown_groups():
 | 
						||
			# contact is in normal group
 | 
						||
			add_to_roster_menuitem.hide()
 | 
						||
			add_to_roster_menuitem.set_no_show_all(True)
 | 
						||
			edit_groups_menuitem.connect('activate', self.on_edit_groups, [(
 | 
						||
				contact,account)])
 | 
						||
 | 
						||
			if gajim.connections[account].gpg:
 | 
						||
				assign_openpgp_key_menuitem.connect('activate',
 | 
						||
					self.on_assign_pgp_key, contact, account)
 | 
						||
			else:
 | 
						||
				assign_openpgp_key_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			if contact.sub in ('from', 'both'):
 | 
						||
				send_auth_menuitem.set_sensitive(False)
 | 
						||
			else:
 | 
						||
				send_auth_menuitem.connect('activate', self.authorize, jid, account)
 | 
						||
			if contact.sub in ('to', 'both'):
 | 
						||
				ask_auth_menuitem.set_sensitive(False)
 | 
						||
				add_special_notification_menuitem.connect('activate',
 | 
						||
					self.on_add_special_notification_menuitem_activate, jid)
 | 
						||
			else:
 | 
						||
				ask_auth_menuitem.connect('activate', self.req_sub, jid,
 | 
						||
					_('I would like to add you to my roster'), account,
 | 
						||
					contact.groups, contact.name)
 | 
						||
			if contact.sub in ('to', 'none') or gajim.get_transport_name_from_jid(
 | 
						||
			jid, use_config_setting=False):
 | 
						||
				revoke_auth_menuitem.set_sensitive(False)
 | 
						||
			else:
 | 
						||
				revoke_auth_menuitem.connect('activate', self.revoke_auth, jid,
 | 
						||
					account)
 | 
						||
 | 
						||
		else: # contact is in group 'Not in Roster'
 | 
						||
			add_to_roster_menuitem.set_no_show_all(False)
 | 
						||
			edit_groups_menuitem.set_sensitive(False)
 | 
						||
			assign_openpgp_key_menuitem.set_sensitive(False)
 | 
						||
			subscription_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
			add_to_roster_menuitem.connect('activate',
 | 
						||
				self.on_add_to_roster, contact, account)
 | 
						||
 | 
						||
		set_custom_avatar_menuitem.connect('activate',
 | 
						||
			self.on_set_custom_avatar_activate, contact, account)
 | 
						||
		# Hide items when it's self contact row
 | 
						||
		if our_jid:
 | 
						||
			menuitem = xml.get_widget('manage_contact')
 | 
						||
			menuitem.set_sensitive(False)
 | 
						||
 | 
						||
		# Unsensitive many items when account is offline
 | 
						||
		if gajim.connections[account].connected < 2:
 | 
						||
			for widget in [start_chat_menuitem, send_single_message_menuitem,
 | 
						||
			rename_menuitem, edit_groups_menuitem, send_file_menuitem,
 | 
						||
			subscription_menuitem, add_to_roster_menuitem,
 | 
						||
			remove_from_roster_menuitem, execute_command_menuitem,
 | 
						||
			send_custom_status_menuitem]:
 | 
						||
				widget.set_sensitive(False)
 | 
						||
 | 
						||
		if gajim.connections[account] and gajim.connections[account].\
 | 
						||
		privacy_rules_supported:
 | 
						||
			if jid in gajim.connections[account].blocked_contacts:
 | 
						||
				block_menuitem.set_no_show_all(True)
 | 
						||
				block_menuitem.hide()
 | 
						||
				if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
 | 
						||
					unblock_menuitem.set_no_show_all(True)
 | 
						||
					unblock_menuitem.hide()
 | 
						||
					unignore_menuitem.set_no_show_all(False)
 | 
						||
					unignore_menuitem.connect('activate', self.on_unblock, titer,
 | 
						||
						None)
 | 
						||
				else:
 | 
						||
					unblock_menuitem.connect('activate', self.on_unblock, titer,
 | 
						||
						None)
 | 
						||
			else:
 | 
						||
				unblock_menuitem.set_no_show_all(True)
 | 
						||
				unblock_menuitem.hide()
 | 
						||
				if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
 | 
						||
					block_menuitem.set_no_show_all(True)
 | 
						||
					block_menuitem.hide()
 | 
						||
					ignore_menuitem.set_no_show_all(False)
 | 
						||
					ignore_menuitem.connect('activate', self.on_block, titer, None)
 | 
						||
				else:
 | 
						||
					block_menuitem.connect('activate', self.on_block, titer, None)
 | 
						||
		else:
 | 
						||
			unblock_menuitem.set_no_show_all(True)
 | 
						||
			block_menuitem.set_sensitive(False)
 | 
						||
			unblock_menuitem.hide()
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		roster_contact_context_menu.attach_to_widget(self.tree, None)
 | 
						||
		roster_contact_context_menu.connect('selection-done',
 | 
						||
			gtkgui_helpers.destroy_widget)
 | 
						||
		roster_contact_context_menu.show_all()
 | 
						||
		roster_contact_context_menu.popup(None, None, None, event_button,
 | 
						||
			event.time)
 | 
						||
 | 
						||
	def make_multiple_contact_menu(self, event, iters):
 | 
						||
		'''Make group's popup menu'''
 | 
						||
		model = self.modelfilter
 | 
						||
		list_ = [] # list of (jid, account) tuples
 | 
						||
		one_account_offline = False
 | 
						||
		is_blocked = True
 | 
						||
		for titer in iters:
 | 
						||
			jid = model[titer][C_JID].decode('utf-8')
 | 
						||
			account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
			if gajim.connections[account].connected < 2:
 | 
						||
				one_account_offline = True
 | 
						||
			contact = gajim.contacts.get_contact_with_highest_priority(account,
 | 
						||
				jid)
 | 
						||
			if jid not in gajim.connections[account].blocked_contacts:
 | 
						||
				is_blocked = False
 | 
						||
			list_.append((contact, account))
 | 
						||
 | 
						||
		menu = gtk.Menu()
 | 
						||
		account = None
 | 
						||
		for (contact, current_account) in list_:
 | 
						||
			# check that we use the same account for every sender
 | 
						||
			if account is not None and account != current_account:
 | 
						||
				account = None
 | 
						||
				break
 | 
						||
			account = current_account
 | 
						||
		if account is not None:
 | 
						||
			send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
 | 
						||
			send_group_message_item.set_image(icon)
 | 
						||
			menu.append(send_group_message_item)
 | 
						||
			send_group_message_item.connect('activate',
 | 
						||
				self.on_send_single_message_menuitem_activate, account, list_)
 | 
						||
 | 
						||
		# Invite to Groupchat
 | 
						||
		invite_item = gtk.ImageMenuItem(_('In_vite to'))
 | 
						||
		muc_icon = gtkgui_helpers.load_icon('muc_active')
 | 
						||
		if muc_icon:
 | 
						||
			invite_item.set_image(muc_icon)
 | 
						||
 | 
						||
		self.build_invite_submenu(invite_item, list_)
 | 
						||
		menu.append(invite_item)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# Manage Transport submenu
 | 
						||
		item = gtk.ImageMenuItem(_('_Manage Contacts'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		manage_contacts_submenu = gtk.Menu()
 | 
						||
		item.set_submenu(manage_contacts_submenu)
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# Edit Groups
 | 
						||
		edit_groups_item = gtk.ImageMenuItem(_('Edit _Groups'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_EDIT, gtk.ICON_SIZE_MENU)
 | 
						||
		edit_groups_item.set_image(icon)
 | 
						||
		manage_contacts_submenu.append(edit_groups_item)
 | 
						||
		edit_groups_item.connect('activate', self.on_edit_groups, list_)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		manage_contacts_submenu.append(item)
 | 
						||
 | 
						||
		# Block
 | 
						||
		if is_blocked and gajim.connections[account].privacy_rules_supported:
 | 
						||
			unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
 | 
						||
			unblock_menuitem.set_image(icon)
 | 
						||
			unblock_menuitem.connect('activate', self.on_unblock, None, list_)
 | 
						||
			manage_contacts_submenu.append(unblock_menuitem)
 | 
						||
		else:
 | 
						||
			block_menuitem = gtk.ImageMenuItem(_('_Block'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
 | 
						||
			block_menuitem.set_image(icon)
 | 
						||
			block_menuitem.connect('activate', self.on_block, None, list_)
 | 
						||
			manage_contacts_submenu.append(block_menuitem)
 | 
						||
 | 
						||
			if not gajim.connections[account].privacy_rules_supported:
 | 
						||
				block_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
		# Remove
 | 
						||
		remove_item = gtk.ImageMenuItem(_('_Remove'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
 | 
						||
		remove_item.set_image(icon)
 | 
						||
		manage_contacts_submenu.append(remove_item)
 | 
						||
		remove_item.connect('activate', self.on_req_usub, list_)
 | 
						||
		# unsensitive remove if one account is not connected
 | 
						||
		if one_account_offline:
 | 
						||
			remove_item.set_sensitive(False)
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		menu.attach_to_widget(self.tree, None)
 | 
						||
		menu.connect('selection-done', gtkgui_helpers.destroy_widget)
 | 
						||
		menu.show_all()
 | 
						||
		menu.popup(None, None, None, event_button, event.time)
 | 
						||
 | 
						||
	def make_transport_menu(self, event, titer):
 | 
						||
		'''Make transport\'s popup menu'''
 | 
						||
		model = self.modelfilter
 | 
						||
		jid = model[titer][C_JID].decode('utf-8')
 | 
						||
		path = model.get_path(titer)
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		menu = gtk.Menu()
 | 
						||
 | 
						||
		# Send single message
 | 
						||
		item = gtk.ImageMenuItem(_('Send Single Message'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		item.connect('activate',
 | 
						||
			self.on_send_single_message_menuitem_activate, account, contact)
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		blocked = False
 | 
						||
		if jid in gajim.connections[account].blocked_contacts:
 | 
						||
			blocked = True
 | 
						||
 | 
						||
		# Send Custom Status
 | 
						||
		send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
 | 
						||
		# add a special img for this menuitem
 | 
						||
		if blocked:
 | 
						||
			send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
 | 
						||
				'offline'))
 | 
						||
			send_custom_status_menuitem.set_sensitive(False)
 | 
						||
		else:
 | 
						||
			if gajim.interface.status_sent_to_users.has_key(account) and \
 | 
						||
			jid in gajim.interface.status_sent_to_users[account]:
 | 
						||
				send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
 | 
						||
					gajim.interface.status_sent_to_users[account][jid]))
 | 
						||
			else:
 | 
						||
				icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
 | 
						||
					gtk.ICON_SIZE_MENU)
 | 
						||
				send_custom_status_menuitem.set_image(icon)
 | 
						||
			status_menuitems = gtk.Menu()
 | 
						||
			send_custom_status_menuitem.set_submenu(status_menuitems)
 | 
						||
			iconset = gajim.config.get('iconset')
 | 
						||
			path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
			for s in ['online', 'chat', 'away', 'xa', 'dnd', 'offline']:
 | 
						||
				# icon MUST be different instance for every item
 | 
						||
				state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
				status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
 | 
						||
				status_menuitem.connect('activate', self.on_send_custom_status,
 | 
						||
					[(contact, account)], s)
 | 
						||
				icon = state_images[s]
 | 
						||
				status_menuitem.set_image(icon)
 | 
						||
				status_menuitems.append(status_menuitem)
 | 
						||
		menu.append(send_custom_status_menuitem)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# Execute Command
 | 
						||
		item = gtk.ImageMenuItem(_('Execute Command...'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		menu.append(item)
 | 
						||
		item.connect('activate', self.on_execute_command, contact, account,
 | 
						||
			contact.resource)
 | 
						||
		if gajim.account_is_disconnected(account):
 | 
						||
			item.set_sensitive(False)
 | 
						||
 | 
						||
		# Manage Transport submenu
 | 
						||
		item = gtk.ImageMenuItem(_('_Manage Transport'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		manage_transport_submenu = gtk.Menu()
 | 
						||
		item.set_submenu(manage_transport_submenu)
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# Modify Transport
 | 
						||
		item = gtk.ImageMenuItem(_('_Modify Transport'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		manage_transport_submenu.append(item)
 | 
						||
		item.connect('activate', self.on_edit_agent, contact, account)
 | 
						||
		if gajim.account_is_disconnected(account):
 | 
						||
			item.set_sensitive(False)
 | 
						||
 | 
						||
		# Rename
 | 
						||
		item = gtk.ImageMenuItem(_('_Rename'))
 | 
						||
		# add a special img for rename menuitem
 | 
						||
		path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
 | 
						||
			'kbd_input.png')
 | 
						||
		img = gtk.Image()
 | 
						||
		img.set_from_file(path_to_kbd_input_img)
 | 
						||
		item.set_image(img)
 | 
						||
		manage_transport_submenu.append(item)
 | 
						||
		item.connect('activate', self.on_rename, titer, path)
 | 
						||
		if gajim.account_is_disconnected(account):
 | 
						||
			item.set_sensitive(False)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		manage_transport_submenu.append(item)
 | 
						||
 | 
						||
		# Block
 | 
						||
		if blocked:
 | 
						||
			item = gtk.ImageMenuItem(_('_Unblock'))
 | 
						||
			item.connect('activate', self.on_unblock, titer, None)
 | 
						||
		else:
 | 
						||
			item = gtk.ImageMenuItem(_('_Block'))
 | 
						||
			item.connect('activate', self.on_block, titer, None)
 | 
						||
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		manage_transport_submenu.append(item)
 | 
						||
		if gajim.account_is_disconnected(account):
 | 
						||
			item.set_sensitive(False)
 | 
						||
 | 
						||
		# Remove
 | 
						||
		item = gtk.ImageMenuItem(_('_Remove'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		manage_transport_submenu.append(item)
 | 
						||
		item.connect('activate', self.on_remove_agent, [(contact, account)])
 | 
						||
		if gajim.account_is_disconnected(account):
 | 
						||
			item.set_sensitive(False)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# Information
 | 
						||
		information_menuitem = gtk.ImageMenuItem(_('_Information'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU)
 | 
						||
		information_menuitem.set_image(icon)
 | 
						||
		menu.append(information_menuitem)
 | 
						||
		information_menuitem.connect('activate', self.on_info, contact, account)
 | 
						||
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		menu.attach_to_widget(self.tree, None)
 | 
						||
		menu.connect('selection-done', gtkgui_helpers.destroy_widget)
 | 
						||
		menu.show_all()
 | 
						||
		menu.popup(None, None, None, event_button, event.time)
 | 
						||
 | 
						||
	def make_groupchat_menu(self, event, titer):
 | 
						||
		model = self.modelfilter
 | 
						||
 | 
						||
		path = model.get_path(titer)
 | 
						||
		jid = model[titer][C_JID].decode('utf-8')
 | 
						||
		account = model[titer][C_ACCOUNT].decode('utf-8')
 | 
						||
		contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
 | 
						||
		menu = gtk.Menu()
 | 
						||
 | 
						||
		if jid in gajim.interface.minimized_controls[account]:
 | 
						||
			maximize_menuitem = gtk.ImageMenuItem(_('_Maximize'))
 | 
						||
			icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
 | 
						||
			maximize_menuitem.set_image(icon)
 | 
						||
			maximize_menuitem.connect('activate', self.on_groupchat_maximized, \
 | 
						||
				jid, account)
 | 
						||
			menu.append(maximize_menuitem)
 | 
						||
 | 
						||
		disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
 | 
						||
		disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
 | 
						||
			gtk.ICON_SIZE_MENU)
 | 
						||
		disconnect_menuitem.set_image(disconnect_icon)
 | 
						||
		disconnect_menuitem.connect('activate', self.on_disconnect, jid, account)
 | 
						||
		menu.append(disconnect_menuitem)
 | 
						||
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		history_menuitem = gtk.ImageMenuItem(_('_History'))
 | 
						||
		history_icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, \
 | 
						||
			gtk.ICON_SIZE_MENU)
 | 
						||
		history_menuitem.set_image(history_icon)
 | 
						||
		history_menuitem .connect('activate', self.on_history, \
 | 
						||
				contact, account)
 | 
						||
		menu.append(history_menuitem)
 | 
						||
 | 
						||
		event_button = gtkgui_helpers.get_possible_button_event(event)
 | 
						||
 | 
						||
		menu.attach_to_widget(self.tree, None)
 | 
						||
		menu.connect('selection-done', gtkgui_helpers.destroy_widget)
 | 
						||
		menu.show_all()
 | 
						||
		menu.popup(None, None, None, event_button, event.time)
 | 
						||
 | 
						||
	def build_resources_submenu(self, contacts, account, action, room_jid=None,
 | 
						||
	room_account=None, cap=None):
 | 
						||
		''' Build a submenu with contact's resources.
 | 
						||
		room_jid and room_account are for action self.on_invite_to_room '''
 | 
						||
		sub_menu = gtk.Menu()
 | 
						||
 | 
						||
		iconset = gajim.config.get('iconset')
 | 
						||
		if not iconset:
 | 
						||
			iconset = gajim.config.DEFAULT_ICONSET
 | 
						||
		path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
 | 
						||
		for c in contacts:
 | 
						||
			# icon MUST be different instance for every item
 | 
						||
			state_images = gtkgui_helpers.load_iconset(path)
 | 
						||
			item = gtk.ImageMenuItem('%s (%s)' % (c.resource, str(c.priority)))
 | 
						||
			icon_name = helpers.get_icon_name_to_show(c, account)
 | 
						||
			icon = state_images[icon_name]
 | 
						||
			item.set_image(icon)
 | 
						||
			sub_menu.append(item)
 | 
						||
			if action == self.on_invite_to_room:
 | 
						||
				item.connect('activate', action, [(c, account)],
 | 
						||
					room_jid, room_account, c.resource)
 | 
						||
			elif action == self.on_invite_to_new_room:
 | 
						||
				item.connect('activate', action, [(c, account)], c.resource)
 | 
						||
			else: # start_chat, execute_command, send_file
 | 
						||
				item.connect('activate', action, c, account, c.resource)
 | 
						||
			if cap and \
 | 
						||
			not gajim.capscache.is_supported(c, cap):
 | 
						||
				item.set_sensitive(False)
 | 
						||
		return sub_menu
 | 
						||
 | 
						||
	def build_invite_submenu(self, invite_menuitem, list_):
 | 
						||
		'''list_ in a list of (contact, account)'''
 | 
						||
		# used if we invite only one contact with several resources
 | 
						||
		contact_list = []
 | 
						||
		if len(list_) == 1:
 | 
						||
			contact, account = list_[0]
 | 
						||
			contact_list = gajim.contacts.get_contacts(account, contact.jid)
 | 
						||
		contacts_transport = -1
 | 
						||
		connected_accounts = []
 | 
						||
		# -1 is at start, False when not from the same, None when jabber
 | 
						||
		for (contact, account) in list_:
 | 
						||
			if not account in connected_accounts:
 | 
						||
				connected_accounts.append(account)
 | 
						||
			transport = gajim.get_transport_name_from_jid(contact.jid)
 | 
						||
			if contacts_transport == -1:
 | 
						||
				contacts_transport = transport
 | 
						||
			elif contacts_transport != transport:
 | 
						||
				contacts_transport = False
 | 
						||
 | 
						||
		if contacts_transport == False:
 | 
						||
			# they are not all from the same transport
 | 
						||
			invite_menuitem.set_sensitive(False)
 | 
						||
			return
 | 
						||
		invite_to_submenu = gtk.Menu()
 | 
						||
		invite_menuitem.set_submenu(invite_to_submenu)
 | 
						||
		invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
 | 
						||
		invite_to_new_room_menuitem.set_image(icon)
 | 
						||
		if len(contact_list) > 1: # several resources
 | 
						||
			invite_to_new_room_menuitem.set_submenu(self.build_resources_submenu(
 | 
						||
				contact_list, account, self.on_invite_to_new_room, cap=NS_MUC))
 | 
						||
		elif len(list_) == 1 and gajim.capscache.is_supported(contact, NS_MUC):
 | 
						||
			invite_menuitem.set_sensitive(True)
 | 
						||
			invite_to_new_room_menuitem.connect('activate',
 | 
						||
				self.on_invite_to_new_room, list_)
 | 
						||
		else:
 | 
						||
			invite_menuitem.set_sensitive(False)
 | 
						||
		# transform None in 'jabber'
 | 
						||
		c_t = contacts_transport or 'jabber'
 | 
						||
		muc_jid = {}
 | 
						||
		for account in connected_accounts:
 | 
						||
			for t in gajim.connections[account].muc_jid:
 | 
						||
				muc_jid[t] = gajim.connections[account].muc_jid[t]
 | 
						||
		if not muc_jid.has_key(c_t):
 | 
						||
			invite_to_new_room_menuitem.set_sensitive(False)
 | 
						||
		rooms = [] # a list of (room_jid, account) tuple
 | 
						||
		invite_to_submenu.append(invite_to_new_room_menuitem)
 | 
						||
		rooms = [] # a list of (room_jid, account) tuple
 | 
						||
		minimized_controls = []
 | 
						||
		for account in connected_accounts:
 | 
						||
			minimized_controls += \
 | 
						||
				gajim.interface.minimized_controls[account].values()
 | 
						||
		for gc_control in gajim.interface.msg_win_mgr.get_controls(
 | 
						||
		message_control.TYPE_GC) + minimized_controls:
 | 
						||
			acct = gc_control.account
 | 
						||
			room_jid = gc_control.room_jid
 | 
						||
			if gajim.gc_connected[acct].has_key(room_jid) and \
 | 
						||
			gajim.gc_connected[acct][room_jid] and \
 | 
						||
			contacts_transport == gajim.get_transport_name_from_jid(room_jid):
 | 
						||
				rooms.append((room_jid, acct))
 | 
						||
		if len(rooms):
 | 
						||
			item = gtk.SeparatorMenuItem() # separator
 | 
						||
			invite_to_submenu.append(item)
 | 
						||
			for (room_jid, account) in rooms:
 | 
						||
				menuitem = gtk.MenuItem(room_jid.split('@')[0])
 | 
						||
				if len(contact_list) > 1: # several resources
 | 
						||
					menuitem.set_submenu(self.build_resources_submenu(
 | 
						||
						contact_list, account, self.on_invite_to_room, room_jid,
 | 
						||
						account))
 | 
						||
				else:
 | 
						||
					menuitem.connect('activate', self.on_invite_to_room, list_,
 | 
						||
						room_jid, account)
 | 
						||
				invite_to_submenu.append(menuitem)
 | 
						||
 | 
						||
	def get_and_connect_advanced_menuitem_menu(self, account):
 | 
						||
		'''adds FOR ACCOUNT options'''
 | 
						||
		xml = gtkgui_helpers.get_glade('advanced_menuitem_menu.glade')
 | 
						||
		advanced_menuitem_menu = xml.get_widget('advanced_menuitem_menu')
 | 
						||
 | 
						||
		xml_console_menuitem = xml.get_widget('xml_console_menuitem')
 | 
						||
		privacy_lists_menuitem = xml.get_widget('privacy_lists_menuitem')
 | 
						||
		administrator_menuitem = xml.get_widget('administrator_menuitem')
 | 
						||
		send_server_message_menuitem = xml.get_widget(
 | 
						||
			'send_server_message_menuitem')
 | 
						||
		set_motd_menuitem = xml.get_widget('set_motd_menuitem')
 | 
						||
		update_motd_menuitem = xml.get_widget('update_motd_menuitem')
 | 
						||
		delete_motd_menuitem = xml.get_widget('delete_motd_menuitem')
 | 
						||
 | 
						||
		xml_console_menuitem.connect('activate',
 | 
						||
			self.on_xml_console_menuitem_activate, account)
 | 
						||
 | 
						||
		if gajim.connections[account] and gajim.connections[account].\
 | 
						||
		privacy_rules_supported:
 | 
						||
			privacy_lists_menuitem.connect('activate',
 | 
						||
				self.on_privacy_lists_menuitem_activate, account)
 | 
						||
		else:
 | 
						||
			privacy_lists_menuitem.set_sensitive(False)
 | 
						||
 | 
						||
		if gajim.connections[account].is_zeroconf:
 | 
						||
			administrator_menuitem.set_sensitive(False)
 | 
						||
			send_server_message_menuitem.set_sensitive(False)
 | 
						||
			set_motd_menuitem.set_sensitive(False)
 | 
						||
			update_motd_menuitem.set_sensitive(False)
 | 
						||
			delete_motd_menuitem.set_sensitive(False)
 | 
						||
		else:
 | 
						||
			send_server_message_menuitem.connect('activate',
 | 
						||
				self.on_send_server_message_menuitem_activate, account)
 | 
						||
 | 
						||
			set_motd_menuitem.connect('activate',
 | 
						||
				self.on_set_motd_menuitem_activate, account)
 | 
						||
 | 
						||
			update_motd_menuitem.connect('activate',
 | 
						||
				self.on_update_motd_menuitem_activate, account)
 | 
						||
 | 
						||
			delete_motd_menuitem.connect('activate',
 | 
						||
				self.on_delete_motd_menuitem_activate, account)
 | 
						||
 | 
						||
		advanced_menuitem_menu.show_all()
 | 
						||
 | 
						||
		return advanced_menuitem_menu
 | 
						||
 | 
						||
	def add_history_manager_menuitem(self, menu):
 | 
						||
		'''adds a seperator and History Manager menuitem BELOW for account
 | 
						||
		menuitems'''
 | 
						||
		item = gtk.SeparatorMenuItem() # separator
 | 
						||
		menu.append(item)
 | 
						||
 | 
						||
		# History manager
 | 
						||
		item = gtk.ImageMenuItem(_('History Manager'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL,
 | 
						||
			gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		menu.append(item)
 | 
						||
		item.connect('activate', self.on_history_manager_menuitem_activate)
 | 
						||
 | 
						||
	def add_bookmarks_list(self, gc_sub_menu, account):
 | 
						||
		'''Show join new group chat item and bookmarks list for an account'''
 | 
						||
		item = gtk.ImageMenuItem(_('_Join New Group Chat'))
 | 
						||
		icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
 | 
						||
		item.set_image(icon)
 | 
						||
		item.connect('activate', self.on_join_gc_activate, account)
 | 
						||
		gc_sub_menu.append(item)
 | 
						||
 | 
						||
		# user has at least one bookmark
 | 
						||
		if len(gajim.connections[account].bookmarks) > 0:
 | 
						||
			item = gtk.SeparatorMenuItem() # separator
 | 
						||
			gc_sub_menu.append(item)
 | 
						||
 | 
						||
		for bookmark in gajim.connections[account].bookmarks:
 | 
						||
			item = gtk.MenuItem(bookmark['name'], False) # Do not use underline
 | 
						||
			item.connect('activate', self.on_bookmark_menuitem_activate,
 | 
						||
				account, bookmark)
 | 
						||
			gc_sub_menu.append(item)
 | 
						||
 | 
						||
	def set_actions_menu_needs_rebuild(self):
 | 
						||
		self.actions_menu_needs_rebuild = True
 | 
						||
		# Force the rebuild now since the on_activates on the menu itself does
 | 
						||
		# not work with the os/x top level menubar
 | 
						||
		if sys.platform == 'darwin':
 | 
						||
			self.make_menu(force = True)
 | 
						||
		return
 | 
						||
 | 
						||
	def show_appropriate_context_menu(self, event, iters):
 | 
						||
		# iters must be all of the same type
 | 
						||
		model = self.modelfilter
 | 
						||
		type_ = model[iters[0]][C_TYPE]
 | 
						||
		for titer in iters[1:]:
 | 
						||
			if model[titer][C_TYPE] != type_:
 | 
						||
				return
 | 
						||
		if type_ == 'group' and len(iters) == 1:
 | 
						||
			self.make_group_menu(event, iters[0])
 | 
						||
		if type_ == 'groupchat' and len(iters) == 1:
 | 
						||
			self.make_groupchat_menu(event, iters[0])
 | 
						||
		elif type_ == 'agent' and len(iters) == 1:
 | 
						||
			self.make_transport_menu(event, iters[0])
 | 
						||
		elif type_ in ('contact', 'self_contact') and len(iters) == 1:
 | 
						||
			self.make_contact_menu(event, iters[0])
 | 
						||
		elif type_ == 'contact':
 | 
						||
			self.make_multiple_contact_menu(event, iters)
 | 
						||
		elif type_ == 'account' and len(iters) == 1:
 | 
						||
			self.make_account_menu(event, iters[0])
 | 
						||
 | 
						||
	def show_treeview_menu(self, event):
 | 
						||
		try:
 | 
						||
			model, list_of_paths = self.tree.get_selection().get_selected_rows()
 | 
						||
		except TypeError:
 | 
						||
			self.tree.get_selection().unselect_all()
 | 
						||
			return
 | 
						||
		if not len(list_of_paths):
 | 
						||
			# no row is selected
 | 
						||
			return
 | 
						||
		if len(list_of_paths) > 1:
 | 
						||
			iters = []
 | 
						||
			for path in list_of_paths:
 | 
						||
				iters.append(model.get_iter(path))
 | 
						||
		else:
 | 
						||
			path = list_of_paths[0]
 | 
						||
			iters = [model.get_iter(path)]
 | 
						||
		self.show_appropriate_context_menu(event, iters)
 | 
						||
 | 
						||
		return True
 | 
						||
 | 
						||
	def setup_for_osx(self):
 | 
						||
		# This is broken
 | 
						||
		return
 | 
						||
		'''Massage the GTK menu so it will match up to the OS/X nib style menu
 | 
						||
		when passed to sync-menu and merged'''
 | 
						||
		main_menu = self.xml.get_widget('menubar')
 | 
						||
		app_item = gtk.MenuItem('Gajim')
 | 
						||
		main_menu.insert(app_item, 0)
 | 
						||
		win_item = gtk.MenuItem('Window')
 | 
						||
		main_menu.insert(win_item, 4)
 | 
						||
		actions_menu = self.xml.get_widget('actions_menu_menu')
 | 
						||
		quit_item = self.xml.get_widget('quit_menuitem')
 | 
						||
		actions_menu.remove(quit_item)
 | 
						||
		actions_menu.remove(self.xml.get_widget('separator1'))
 | 
						||
		edit_menu = self.xml.get_widget('edit_menu_menu')
 | 
						||
		#edit_menu.remove(self.xml.get_widget('preferences_menuitem'))
 | 
						||
		edit_menu.remove(self.xml.get_widget('separator2'))
 | 
						||
		help_menu = self.xml.get_widget('help_menu_menu')
 | 
						||
		about_item = self.xml.get_widget('about_menuitem')
 | 
						||
		help_menu.remove(about_item)
 | 
						||
		# Build up App menu
 | 
						||
		app_menu = gtk.Menu()
 | 
						||
		app_item.set_submenu(app_menu)
 | 
						||
		app_menu.append(about_item)
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		prefs_item = gtk.MenuItem('Preferences...')
 | 
						||
		prefs_item.connect('activate', self.on_preferences_menuitem_activate)
 | 
						||
		accels = gtk.AccelGroup()
 | 
						||
		self.xml.get_widget('roster_window').add_accel_group(accels)
 | 
						||
		prefs_item.add_accelerator('activate', accels, ord(','),
 | 
						||
						gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
 | 
						||
		app_menu.append(prefs_item)
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(gtk.MenuItem('__SKIP__'))
 | 
						||
		app_menu.append(quit_item)
 | 
						||
		app_menu.show_all()
 | 
						||
		# Do the merge baby!
 | 
						||
		syncmenu.takeover_menu(main_menu)
 | 
						||
		self.make_menu(force = True)
 | 
						||
		# Hide the GTK menubar itself and let the OS/X menubar do its thing
 | 
						||
		#self.xml.get_widget('menubar').hide()
 | 
						||
		return
 | 
						||
 | 
						||
################################################################################
 | 
						||
###
 | 
						||
################################################################################
 | 
						||
 | 
						||
	def __init__(self):
 | 
						||
		self.filtering = False
 | 
						||
		self.xml = gtkgui_helpers.get_glade('roster_window.glade')
 | 
						||
		self.window = self.xml.get_widget('roster_window')
 | 
						||
		self.hpaned = self.xml.get_widget('roster_hpaned')
 | 
						||
		self.music_track_changed_signal = None
 | 
						||
		gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
 | 
						||
		gajim.interface.msg_win_mgr.connect('window-delete',
 | 
						||
			self.on_message_window_delete)
 | 
						||
		self.advanced_menus = [] # We keep them to destroy them
 | 
						||
		if gajim.config.get('roster_window_skip_taskbar'):
 | 
						||
			self.window.set_property('skip-taskbar-hint', True)
 | 
						||
		self.tree = self.xml.get_widget('roster_treeview')
 | 
						||
		sel = self.tree.get_selection()
 | 
						||
		sel.set_mode(gtk.SELECTION_MULTIPLE)
 | 
						||
		#sel.connect('changed',
 | 
						||
		#	self.on_treeview_selection_changed)
 | 
						||
 | 
						||
		self._last_selected_contact = [] # holds a list of (jid, account) tupples
 | 
						||
		self.transports_state_images = {'16': {}, '32': {}, 'opened': {},
 | 
						||
			'closed': {}}
 | 
						||
 | 
						||
		self.last_save_dir = None
 | 
						||
		self.editing_path = None # path of row with cell in edit mode
 | 
						||
		self.add_new_contact_handler_id = False
 | 
						||
		self.service_disco_handler_id = False
 | 
						||
		self.new_chat_menuitem_handler_id = False
 | 
						||
		self.single_message_menuitem_handler_id = False
 | 
						||
		self.profile_avatar_menuitem_handler_id = False
 | 
						||
		self.actions_menu_needs_rebuild = True
 | 
						||
		self.regroup = gajim.config.get('mergeaccounts')
 | 
						||
		self.clicked_path = None # Used remember on wich row we clicked
 | 
						||
		if len(gajim.connections) < 2: # Do not merge accounts if only one exists
 | 
						||
			self.regroup = False
 | 
						||
		#FIXME: When list_accel_closures will be wrapped in pygtk
 | 
						||
		# no need of this variable
 | 
						||
		self.have_new_chat_accel = False # Is the "Ctrl+N" shown ?
 | 
						||
		gtkgui_helpers.resize_window(self.window,
 | 
						||
			gajim.config.get('roster_width'),
 | 
						||
			gajim.config.get('roster_height'))
 | 
						||
		gtkgui_helpers.move_window(self.window,
 | 
						||
			gajim.config.get('roster_x-position'),
 | 
						||
			gajim.config.get('roster_y-position'))
 | 
						||
 | 
						||
		self.popups_notification_height = 0
 | 
						||
		self.popup_notification_windows = []
 | 
						||
 | 
						||
 		# Remove contact from roster when last event opened
 | 
						||
		# { (contact, account): { backend: boolean }
 | 
						||
		self.contacts_to_be_removed = {}
 | 
						||
		gajim.events.event_removed_subscribe(self.on_event_removed)
 | 
						||
 | 
						||
		# when this value become 0 we quit main application. If it's more than 0
 | 
						||
		# it means we are waiting for this number of accounts to disconnect before
 | 
						||
		# quitting
 | 
						||
		self.quit_on_next_offline = -1
 | 
						||
 | 
						||
		# uf_show, img, show, sensitive
 | 
						||
		liststore = gtk.ListStore(str, gtk.Image, str, bool)
 | 
						||
		self.status_combobox = self.xml.get_widget('status_combobox')
 | 
						||
 | 
						||
		cell = cell_renderer_image.CellRendererImage(0, 1)
 | 
						||
		self.status_combobox.pack_start(cell, False)
 | 
						||
 | 
						||
		# img to show is in in 2nd column of liststore
 | 
						||
		self.status_combobox.add_attribute(cell, 'image', 1)
 | 
						||
		# if it will be sensitive or not it is in the fourth column
 | 
						||
		# all items in the 'row' must have sensitive to False
 | 
						||
		# if we want False (so we add it for img_cell too)
 | 
						||
		self.status_combobox.add_attribute(cell, 'sensitive', 3)
 | 
						||
 | 
						||
		cell = gtk.CellRendererText()
 | 
						||
		cell.set_property('xpad', 5) # padding for status text
 | 
						||
		self.status_combobox.pack_start(cell, True)
 | 
						||
		# text to show is in in first column of liststore
 | 
						||
		self.status_combobox.add_attribute(cell, 'text', 0)
 | 
						||
		# if it will be sensitive or not it is in the fourth column
 | 
						||
		self.status_combobox.add_attribute(cell, 'sensitive', 3)
 | 
						||
 | 
						||
		self.status_combobox.set_row_separator_func(self._iter_is_separator)
 | 
						||
 | 
						||
		for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
 | 
						||
			uf_show = helpers.get_uf_show(show)
 | 
						||
			liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
 | 
						||
				show], show, True])
 | 
						||
		# Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
 | 
						||
		liststore.append(['SEPARATOR', None, '', True])
 | 
						||
 | 
						||
		path = os.path.join(gajim.DATA_DIR, 'pixmaps', 'kbd_input.png')
 | 
						||
		img = gtk.Image()
 | 
						||
		img.set_from_file(path)
 | 
						||
		# sensitivity to False because by default we're offline
 | 
						||
		self.status_message_menuitem_iter = liststore.append(
 | 
						||
			[_('Change Status Message...'), img, '', False])
 | 
						||
		# Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
 | 
						||
		liststore.append(['SEPARATOR', None, '', True])
 | 
						||
 | 
						||
		uf_show = helpers.get_uf_show('offline')
 | 
						||
		liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
 | 
						||
			'offline'], 'offline', True])
 | 
						||
 | 
						||
		status_combobox_items = ['online', 'chat', 'away', 'xa', 'dnd',
 | 
						||
			'invisible', 'separator1', 'change_status_msg', 'separator2',
 | 
						||
			'offline']
 | 
						||
		self.status_combobox.set_model(liststore)
 | 
						||
 | 
						||
		# default to offline
 | 
						||
		number_of_menuitem = status_combobox_items.index('offline')
 | 
						||
		self.status_combobox.set_active(number_of_menuitem)
 | 
						||
 | 
						||
		# holds index to previously selected item so if "change status message..."
 | 
						||
		# is selected we can fallback to previously selected item and not stay
 | 
						||
		# with that item selected
 | 
						||
		self.previous_status_combobox_active = number_of_menuitem
 | 
						||
 | 
						||
		showOffline = gajim.config.get('showoffline')
 | 
						||
		self.xml.get_widget('show_offline_contacts_menuitem').set_active(
 | 
						||
			showOffline)
 | 
						||
 | 
						||
		show_transports_group = gajim.config.get('show_transports_group')
 | 
						||
		self.xml.get_widget('show_transports_menuitem').set_active(
 | 
						||
			show_transports_group)
 | 
						||
 | 
						||
		self.xml.get_widget('show_roster_menuitem').set_active(True)
 | 
						||
 | 
						||
		# columns
 | 
						||
 | 
						||
		# this col has 3 cells:
 | 
						||
		# first one img, second one text, third is sec pixbuf
 | 
						||
		col = gtk.TreeViewColumn()
 | 
						||
 | 
						||
		def add_avatar_renderer():
 | 
						||
			render_pixbuf = gtk.CellRendererPixbuf() # avatar img
 | 
						||
			col.pack_start(render_pixbuf, expand = False)
 | 
						||
			col.add_attribute(render_pixbuf, 'pixbuf', C_AVATAR_PIXBUF)
 | 
						||
			col.set_cell_data_func(render_pixbuf,
 | 
						||
				self._fill_avatar_pixbuf_rederer, None)
 | 
						||
 | 
						||
		if gajim.config.get('avatar_position_in_roster') == 'left':
 | 
						||
			add_avatar_renderer()
 | 
						||
 | 
						||
		render_image = cell_renderer_image.CellRendererImage(0, 0)
 | 
						||
		# show img or +-
 | 
						||
		col.pack_start(render_image, expand = False)
 | 
						||
		col.add_attribute(render_image, 'image', C_IMG)
 | 
						||
		col.set_cell_data_func(render_image, self._iconCellDataFunc, None)
 | 
						||
 | 
						||
		render_text = gtk.CellRendererText() # contact or group or account name
 | 
						||
		render_text.set_property('ellipsize', pango.ELLIPSIZE_END)
 | 
						||
		col.pack_start(render_text, expand = True)
 | 
						||
		col.add_attribute(render_text, 'markup', C_NAME) # where we hold the name
 | 
						||
		col.set_cell_data_func(render_text, self._nameCellDataFunc, None)
 | 
						||
 | 
						||
		if gajim.config.get('avatar_position_in_roster') == 'right':
 | 
						||
			add_avatar_renderer()
 | 
						||
 | 
						||
		render_pixbuf = gtk.CellRendererPixbuf() # tls/ssl img
 | 
						||
		col.pack_start(render_pixbuf, expand = False)
 | 
						||
		col.add_attribute(render_pixbuf, 'pixbuf', C_PADLOCK_PIXBUF)
 | 
						||
		col.set_cell_data_func(render_pixbuf,
 | 
						||
			self._fill_padlock_pixbuf_rederer, None)
 | 
						||
		self.tree.append_column(col)
 | 
						||
 | 
						||
		# do not show gtk arrows workaround
 | 
						||
		col = gtk.TreeViewColumn()
 | 
						||
		render_pixbuf = gtk.CellRendererPixbuf()
 | 
						||
		col.pack_start(render_pixbuf, expand = False)
 | 
						||
		self.tree.append_column(col)
 | 
						||
		col.set_visible(False)
 | 
						||
		self.tree.set_expander_column(col)
 | 
						||
 | 
						||
		# set search function
 | 
						||
		self.tree.set_search_equal_func(self._search_roster_func)
 | 
						||
 | 
						||
		# signals
 | 
						||
		self.TARGET_TYPE_URI_LIST = 80
 | 
						||
		TARGETS = [('MY_TREE_MODEL_ROW',
 | 
						||
			gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, 0)]
 | 
						||
		TARGETS2 = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
 | 
						||
					('text/uri-list', 0, self.TARGET_TYPE_URI_LIST)]
 | 
						||
		self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, TARGETS,
 | 
						||
			gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_COPY)
 | 
						||
		self.tree.enable_model_drag_dest(TARGETS2, gtk.gdk.ACTION_DEFAULT)
 | 
						||
		self.tree.connect('drag_begin', self.drag_begin)
 | 
						||
		self.tree.connect('drag_end', self.drag_end)
 | 
						||
		self.tree.connect('drag_drop', self.drag_drop)
 | 
						||
		self.tree.connect('drag_data_get', self.drag_data_get_data)
 | 
						||
		self.tree.connect('drag_data_received', self.drag_data_received_data)
 | 
						||
		self.dragging = False
 | 
						||
		self.xml.signal_autoconnect(self)
 | 
						||
		self.combobox_callback_active = True
 | 
						||
 | 
						||
		self.collapsed_rows = gajim.config.get('collapsed_rows').split('\t')
 | 
						||
		self.tooltip = tooltips.RosterTooltip()
 | 
						||
		# Workaroung: For strange reasons signal is behaving like row-changed
 | 
						||
		self._toggeling_row = False
 | 
						||
		self.setup_and_draw_roster()
 | 
						||
 | 
						||
		for account in gajim.connections:
 | 
						||
			if gajim.config.get_per('accounts', account, 'publish_tune'):
 | 
						||
				listener = MusicTrackListener.get()
 | 
						||
				self.music_track_changed_signal = listener.connect(
 | 
						||
					'music-track-changed', self.music_track_changed)
 | 
						||
				track = listener.get_playing_track()
 | 
						||
				self.music_track_changed(listener, track)
 | 
						||
				break
 | 
						||
 | 
						||
		if gajim.config.get('show_roster_on_startup'):
 | 
						||
			self.window.show_all()
 | 
						||
		else:
 | 
						||
			if not gajim.config.get('trayicon') or not \
 | 
						||
			gajim.interface.systray_capabilities:
 | 
						||
				# cannot happen via GUI, but I put this incase user touches
 | 
						||
				# config. without trayicon, he or she should see the roster!
 | 
						||
				self.window.show_all()
 | 
						||
				gajim.config.set('show_roster_on_startup', True)
 | 
						||
 | 
						||
		if len(gajim.connections) == 0: # if we have no account
 | 
						||
			gajim.interface.instances['account_creation_wizard'] = \
 | 
						||
				config.AccountCreationWizardWindow()
 | 
						||
		if not gajim.ZEROCONF_ACC_NAME in gajim.config.get_per('accounts'):
 | 
						||
			# Create zeroconf in config file
 | 
						||
			zeroconf = common.zeroconf.connection_zeroconf.ConnectionZeroconf(
 | 
						||
				gajim.ZEROCONF_ACC_NAME)
 | 
						||
 | 
						||
		if sys.platform == 'darwin':
 | 
						||
			self.setup_for_osx()
 | 
						||
 |