diff --git a/src/gui_menu_builder.py b/src/gui_menu_builder.py
new file mode 100644
index 0000000000000000000000000000000000000000..09fd5efd3c51162712efa4931a95e45550757da5
--- /dev/null
+++ b/src/gui_menu_builder.py
@@ -0,0 +1,407 @@
+# -*- coding:utf-8 -*-
+## src/gui_menu_builder.py
+##
+## Copyright (C) 2009 Yann Leboulanger <asterix AT lagaule.org>
+##
+## This file is part of Gajim.
+##
+## Gajim is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## Gajim is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
+##
+
+import gtk
+import os
+import gtkgui_helpers
+import message_control
+
+from common import gajim
+from common import helpers
+from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
+
+def build_resources_submenu(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 == roster.on_invite_to_room:
+			item.connect('activate', action, [(c, account)], room_jid,
+				room_account, c.resource)
+		elif action == roster.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(invite_menuitem, list_):
+	'''list_ in a list of (contact, account)'''
+	roster = gajim.interface.roster
+	# 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(build_resources_submenu(
+			contact_list, account, roster.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)
+		# use resource if it's self contact
+		if contact.jid == gajim.get_jid_from_account(account):
+			resource = contact.resource
+		else:
+			resource = None
+		invite_to_new_room_menuitem.connect('activate',
+			roster.on_invite_to_new_room, list_, resource)
+	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 c_t not in muc_jid:
+		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 room_jid in gajim.gc_connected[acct] 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(build_resources_submenu(
+					contact_list, account, roster.on_invite_to_room, room_jid,
+					account))
+			else:
+				# use resource if it's self contact
+				if contact.jid == gajim.get_jid_from_account(account):
+					resource = contact.resource
+				else:
+					resource = None
+				menuitem.connect('activate', roster.on_invite_to_room, list_,
+					room_jid, account, resource)
+			invite_to_submenu.append(menuitem)
+
+def get_contact_menu(contact, account, use_multiple_contacts=True):
+	if not contact:
+		return
+
+	jid = contact.jid
+	our_jid = jid == gajim.get_jid_from_account(account)
+	roster = gajim.interface.roster
+
+	if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+		xml = gtkgui_helpers.get_glade('zeroconf_contact_context_menu.glade')
+		contact_context_menu = xml.get_widget('zeroconf_contact_context_menu')
+	else:
+		xml = gtkgui_helpers.get_glade('roster_contact_context_menu.glade')
+		contact_context_menu = xml.get_widget('roster_contact_context_menu')
+
+	start_chat_menuitem = xml.get_widget('start_chat_menuitem')
+	execute_command_menuitem = xml.get_widget('execute_command_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')
+	information_menuitem = xml.get_widget('information_menuitem')
+	history_menuitem = xml.get_widget('history_menuitem')
+
+	# add a special img for send file menuitem
+	path_to_upload_img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'upload.png')
+	img = gtk.Image()
+	img.set_from_file(path_to_upload_img)
+	send_file_menuitem.set_image(img)
+
+	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)
+
+	contacts = gajim.contacts.get_contacts(account, jid)
+	if len(contacts) > 1 and use_multiple_contacts: # several resources
+		start_chat_menuitem.set_submenu(build_resources_submenu(contacts,
+			account, gajim.interface.on_open_chat_window))
+		send_file_menuitem.set_submenu(build_resources_submenu(contacts,
+			account, roster.on_send_file_menuitem_activate, cap=NS_FILE))
+		execute_command_menuitem.set_submenu(build_resources_submenu(
+			contacts, account, roster.on_execute_command, cap=NS_COMMANDS))
+	else:
+		start_chat_menuitem.connect('activate',
+			gajim.interface.on_open_chat_window, contact, account)
+		if gajim.capscache.is_supported(contact, NS_FILE):
+			send_file_menuitem.set_sensitive(True)
+			send_file_menuitem.connect('activate',
+				roster.on_send_file_menuitem_activate, contact, account)
+		else:
+			send_file_menuitem.set_sensitive(False)
+
+		if gajim.capscache.is_supported(contact, NS_COMMANDS):
+			execute_command_menuitem.set_sensitive(True)
+			execute_command_menuitem.connect('activate', roster.on_execute_command,
+				contact, account, contact.resource)
+		else:
+			execute_command_menuitem.set_sensitive(False)
+
+	rename_menuitem.connect('activate', roster.on_rename, 'contact', jid,
+		account)
+	history_menuitem.connect('activate', roster.on_history, contact, account)
+
+	if _('Not in Roster') not in contact.get_shown_groups():
+		# contact is in normal group
+		edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
+			account)])
+
+		if gajim.connections[account].gpg:
+			assign_openpgp_key_menuitem.connect('activate',
+				roster.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)
+		assign_openpgp_key_menuitem.set_sensitive(False)
+
+	# Hide items when it's self contact row
+	if our_jid:
+		for menuitem in (rename_menuitem, edit_groups_menuitem):
+			menuitem.set_no_show_all(True)
+			menuitem.hide()
+
+	# Unsensitive many items when account is offline
+	if gajim.account_is_disconnected(account):
+		for widget in (start_chat_menuitem,	rename_menuitem,
+		edit_groups_menuitem, send_file_menuitem):
+			widget.set_sensitive(False)
+
+	# Zeroconf Account
+	if gajim.config.get_per('accounts', account, 'is_zeroconf'):
+		if contact.show in ('offline', 'error'):
+			information_menuitem.set_sensitive(False)
+			send_file_menuitem.set_sensitive(False)
+		else:
+			information_menuitem.connect('activate', roster.on_info_zeroconf,
+				contact, account)
+
+		contact_context_menu.connect('selection-done',
+			gtkgui_helpers.destroy_widget)
+		contact_context_menu.show_all()
+		return contact_context_menu
+
+	# normal account
+	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')
+	set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem')
+
+	# send custom status icon
+	blocked = False
+	if helpers.jid_is_blocked(account, jid):
+		blocked = True
+	else:
+		for group in contact.get_shown_groups():
+			if helpers.group_is_blocked(account, group):
+				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 account in gajim.interface.status_sent_to_users 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)
+
+	muc_icon = gtkgui_helpers.load_icon('muc_active')
+	if muc_icon:
+		invite_menuitem.set_image(muc_icon)
+
+	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')
+
+	# 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', roster.on_send_custom_status,
+			[(contact, account)], s)
+		icon = state_images[s]
+		status_menuitem.set_image(icon)
+		status_menuitems.append(status_menuitem)
+
+	send_single_message_menuitem.connect('activate',
+		roster.on_send_single_message_menuitem_activate, account, contact)
+
+	remove_from_roster_menuitem.connect('activate', roster.on_req_usub,
+		[(contact, account)])
+	information_menuitem.connect('activate', roster.on_info, 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)
+
+		if contact.sub in ('from', 'both'):
+			send_auth_menuitem.set_sensitive(False)
+		else:
+			send_auth_menuitem.connect('activate', roster.authorize, jid, account)
+		if contact.sub in ('to', 'both'):
+			ask_auth_menuitem.set_sensitive(False)
+			add_special_notification_menuitem.connect('activate',
+				roster.on_add_special_notification_menuitem_activate, jid)
+		else:
+			ask_auth_menuitem.connect('activate', roster.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', roster.revoke_auth, jid,
+				account)
+
+	else:
+		# contact is in group 'Not in Roster'
+		add_to_roster_menuitem.set_no_show_all(False)
+		subscription_menuitem.set_sensitive(False)
+
+		add_to_roster_menuitem.connect('activate', roster.on_add_to_roster,
+			contact, account)
+
+	set_custom_avatar_menuitem.connect('activate',
+		roster.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 items when account is offline
+	if gajim.account_is_disconnected(account):
+		for widget in (send_single_message_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 helpers.jid_is_blocked(account, jid):
+			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', roster.on_unblock, [(contact,
+					account)])
+			else:
+				unblock_menuitem.connect('activate', roster.on_unblock, [(contact,
+					account)])
+		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', roster.on_block, [(contact,
+					account)])
+			else:
+				block_menuitem.connect('activate', roster.on_block, [(contact,
+					account)])
+	else:
+		unblock_menuitem.set_no_show_all(True)
+		block_menuitem.set_sensitive(False)
+		unblock_menuitem.hide()
+
+	contact_context_menu.connect('selection-done', gtkgui_helpers.destroy_widget)
+	contact_context_menu.show_all()
+	return contact_context_menu
+
+# vim: se ts=3:
diff --git a/src/roster_window.py b/src/roster_window.py
index 9043a9b9fc1e5a8e8c4171642c9f3cc4993f5956..d0d2a5fe1ca9f2a32f2b2b2198f75177597884c6 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -46,6 +46,7 @@ import vcard
 import config
 import disco
 import gtkgui_helpers
+import gui_menu_builder
 import cell_renderer_image
 import tooltips
 import message_control
@@ -64,7 +65,7 @@ from common import dbus_support
 if dbus_support.supported:
 	import dbus
 
-from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
+from common.xmpp.protocol import NS_FILE
 from common.pep import MOODS, ACTIVITIES
 
 #(icon, name, type, jid, account, editable, second pixbuf)
@@ -5250,7 +5251,7 @@ class RosterWindow:
 			if muc_icon:
 				invite_menuitem.set_image(muc_icon)
 
-			self.build_invite_submenu(invite_menuitem, list_online)
+			gui_menu_builder.build_invite_submenu(invite_menuitem, list_online)
 			menu.append(invite_menuitem)
 
 			# Send Custom Status
@@ -5357,373 +5358,11 @@ class RosterWindow:
 		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')
-			execute_command_menuitem = xml.get_widget('execute_command_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')
-
-			# add a special img for send file menuitem
-			path_to_upload_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
-				'upload.png')
-			img = gtk.Image()
-			img.set_from_file(path_to_upload_img)
-			send_file_menuitem.set_image(img)
-
-			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)
-
-			execute_command_menuitem.connect('activate',
-				self.on_execute_command, contact, account)
-
-			rename_menuitem.connect('activate', self.on_rename, 'contact', jid,
-				account)
-			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')
-
-		# add a special img for send file menuitem
-		path_to_upload_img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'upload.png')
-		img = gtk.Image()
-		img.set_from_file(path_to_upload_img)
-		send_file_menuitem.set_image(img)
-
-		# send custom status icon
-		blocked = False
-		if helpers.jid_is_blocked(account, jid):
-			blocked = True
-		else:
-			for group in contact.get_shown_groups():
-				if helpers.group_is_blocked(account, group):
-					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 account in gajim.interface.status_sent_to_users 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)
-
-			# This does nothing:
-			# 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, 'contact', jid,
-			account)
-		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 helpers.jid_is_blocked(account, jid):
-				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, [(contact,
-						account)])
-				else:
-					unblock_menuitem.connect('activate', self.on_unblock, [(contact,
-						account)])
-			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, [(contact,
-						account)])
-				else:
-					block_menuitem.connect('activate', self.on_block, [(contact,
-						account)])
-		else:
-			unblock_menuitem.set_no_show_all(True)
-			block_menuitem.set_sensitive(False)
-			unblock_menuitem.hide()
-
+		menu = gui_menu_builder.get_contact_menu(contact, account)
 		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)
+		menu.attach_to_widget(self.tree, None)
+		menu.popup(None, None, None, event_button, event.time)
 
 	def make_multiple_contact_menu(self, event, iters):
 		'''Make group's popup menu'''
@@ -5767,7 +5406,7 @@ class RosterWindow:
 		if muc_icon:
 			invite_item.set_image(muc_icon)
 
-		self.build_invite_submenu(invite_item, list_)
+		gui_menu_builder.build_invite_submenu(invite_item, list_)
 		menu.append(invite_item)
 
 		item = gtk.SeparatorMenuItem() # separator
@@ -6012,120 +5651,6 @@ class RosterWindow:
 		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)
-			# use resource if it's self contact
-			if contact.jid == gajim.get_jid_from_account(account):
-				resource = contact.resource
-			else:
-				resource = None
-			invite_to_new_room_menuitem.connect('activate',
-				self.on_invite_to_new_room, list_, resource)
-		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 c_t not in muc_jid:
-			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 room_jid in gajim.gc_connected[acct] 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:
-					# use resource if it's self contact
-					if contact.jid == gajim.get_jid_from_account(account):
-						resource = contact.resource
-					else:
-						resource = None
-					menuitem.connect('activate', self.on_invite_to_room, list_,
-						room_jid, account, resource)
-				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')