lighter import
[gajim.git] / src / gui_menu_builder.py
blob1eed94dd58181f19a36d0f374a5c07efaf4075fd
1 # -*- coding:utf-8 -*-
2 ## src/gui_menu_builder.py
3 ##
4 ## Copyright (C) 2009-2010 Yann Leboulanger <asterix AT lagaule.org>
5 ##
6 ## This file is part of Gajim.
7 ##
8 ## Gajim is free software; you can redistribute it and/or modify
9 ## it under the terms of the GNU General Public License as published
10 ## by the Free Software Foundation; version 3 only.
12 ## Gajim is distributed in the hope that it will be useful,
13 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ## GNU General Public License for more details.
17 ## You should have received a copy of the GNU General Public License
18 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
21 import gtk
22 import os
23 import gtkgui_helpers
24 import message_control
26 from common import gajim
27 from common import helpers
28 from common.xmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC, NS_ESESSION
30 def build_resources_submenu(contacts, account, action, room_jid=None,
31 room_account=None, cap=None):
32 """
33 Build a submenu with contact's resources. room_jid and room_account are for
34 action self.on_invite_to_room
35 """
36 roster = gajim.interface.roster
37 sub_menu = gtk.Menu()
39 iconset = gajim.config.get('iconset')
40 if not iconset:
41 iconset = gajim.config.DEFAULT_ICONSET
42 path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
43 for c in contacts:
44 # icon MUST be different instance for every item
45 state_images = gtkgui_helpers.load_iconset(path)
46 item = gtk.ImageMenuItem('%s (%s)' % (c.resource, str(c.priority)))
47 icon_name = helpers.get_icon_name_to_show(c, account)
48 icon = state_images[icon_name]
49 item.set_image(icon)
50 sub_menu.append(item)
52 if action == roster.on_invite_to_room:
53 item.connect('activate', action, [(c, account)], room_jid,
54 room_account, c.resource)
55 elif action == roster.on_invite_to_new_room:
56 item.connect('activate', action, [(c, account)], c.resource)
57 else: # start_chat, execute_command, send_file
58 item.connect('activate', action, c, account, c.resource)
60 if cap and not c.supports(cap):
61 item.set_sensitive(False)
63 return sub_menu
65 def build_invite_submenu(invite_menuitem, list_, ignore_rooms=[]):
66 """
67 list_ in a list of (contact, account)
68 """
69 roster = gajim.interface.roster
70 # used if we invite only one contact with several resources
71 contact_list = []
72 if len(list_) == 1:
73 contact, account = list_[0]
74 contact_list = gajim.contacts.get_contacts(account, contact.jid)
75 contacts_transport = -1
76 connected_accounts = []
77 # -1 is at start, False when not from the same, None when jabber
78 for (contact, account) in list_:
79 if not account in connected_accounts:
80 connected_accounts.append(account)
81 transport = gajim.get_transport_name_from_jid(contact.jid)
82 if contacts_transport == -1:
83 contacts_transport = transport
84 elif contacts_transport != transport:
85 contacts_transport = False
87 if contacts_transport == False:
88 # they are not all from the same transport
89 invite_menuitem.set_sensitive(False)
90 return
91 invite_to_submenu = gtk.Menu()
92 invite_menuitem.set_submenu(invite_to_submenu)
93 invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat'))
94 icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
95 invite_to_new_room_menuitem.set_image(icon)
96 if len(contact_list) > 1: # several resources
97 invite_to_new_room_menuitem.set_submenu(build_resources_submenu(
98 contact_list, account, roster.on_invite_to_new_room, cap=NS_MUC))
99 elif len(list_) == 1 and contact.supports(NS_MUC):
100 invite_menuitem.set_sensitive(True)
101 # use resource if it's self contact
102 if contact.jid == gajim.get_jid_from_account(account):
103 resource = contact.resource
104 else:
105 resource = None
106 invite_to_new_room_menuitem.connect('activate',
107 roster.on_invite_to_new_room, list_, resource)
108 else:
109 invite_menuitem.set_sensitive(True)
110 # transform None in 'jabber'
111 c_t = contacts_transport or 'jabber'
112 muc_jid = {}
113 for account in connected_accounts:
114 for t in gajim.connections[account].muc_jid:
115 muc_jid[t] = gajim.connections[account].muc_jid[t]
116 if c_t not in muc_jid:
117 invite_to_new_room_menuitem.set_sensitive(False)
118 rooms = [] # a list of (room_jid, account) tuple
119 invite_to_submenu.append(invite_to_new_room_menuitem)
120 rooms = [] # a list of (room_jid, account) tuple
121 minimized_controls = []
122 for account in connected_accounts:
123 minimized_controls += \
124 gajim.interface.minimized_controls[account].values()
125 for gc_control in gajim.interface.msg_win_mgr.get_controls(
126 message_control.TYPE_GC) + minimized_controls:
127 acct = gc_control.account
128 room_jid = gc_control.room_jid
129 if room_jid in ignore_rooms:
130 continue
131 if room_jid in gajim.gc_connected[acct] and \
132 gajim.gc_connected[acct][room_jid] and \
133 contacts_transport == gajim.get_transport_name_from_jid(room_jid):
134 rooms.append((room_jid, acct))
135 if len(rooms):
136 item = gtk.SeparatorMenuItem() # separator
137 invite_to_submenu.append(item)
138 for (room_jid, account) in rooms:
139 menuitem = gtk.MenuItem(room_jid.split('@')[0])
140 if len(contact_list) > 1: # several resources
141 menuitem.set_submenu(build_resources_submenu(
142 contact_list, account, roster.on_invite_to_room, room_jid,
143 account))
144 else:
145 # use resource if it's self contact
146 if contact.jid == gajim.get_jid_from_account(account):
147 resource = contact.resource
148 else:
149 resource = None
150 menuitem.connect('activate', roster.on_invite_to_room, list_,
151 room_jid, account, resource)
152 invite_to_submenu.append(menuitem)
154 def get_contact_menu(contact, account, use_multiple_contacts=True,
155 show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
156 control=None, gc_contact=None):
158 Build contact popup menu for roster and chat window. If control is not set,
159 we hide invite_contacts_menuitem
161 if not contact:
162 return
164 jid = contact.jid
165 our_jid = jid == gajim.get_jid_from_account(account)
166 roster = gajim.interface.roster
168 xml = gtkgui_helpers.get_gtk_builder('contact_context_menu.ui')
169 contact_context_menu = xml.get_object('contact_context_menu')
171 start_chat_menuitem = xml.get_object('start_chat_menuitem')
172 execute_command_menuitem = xml.get_object('execute_command_menuitem')
173 rename_menuitem = xml.get_object('rename_menuitem')
174 edit_groups_menuitem = xml.get_object('edit_groups_menuitem')
175 send_file_menuitem = xml.get_object('send_file_menuitem')
176 assign_openpgp_key_menuitem = xml.get_object('assign_openpgp_key_menuitem')
177 add_special_notification_menuitem = xml.get_object(
178 'add_special_notification_menuitem')
179 information_menuitem = xml.get_object('information_menuitem')
180 history_menuitem = xml.get_object('history_menuitem')
181 send_custom_status_menuitem = xml.get_object('send_custom_status_menuitem')
182 send_single_message_menuitem = xml.get_object('send_single_message_menuitem')
183 invite_menuitem = xml.get_object('invite_menuitem')
184 block_menuitem = xml.get_object('block_menuitem')
185 unblock_menuitem = xml.get_object('unblock_menuitem')
186 ignore_menuitem = xml.get_object('ignore_menuitem')
187 unignore_menuitem = xml.get_object('unignore_menuitem')
188 set_custom_avatar_menuitem = xml.get_object('set_custom_avatar_menuitem')
189 # Subscription submenu
190 subscription_menuitem = xml.get_object('subscription_menuitem')
191 send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem = \
192 subscription_menuitem.get_submenu().get_children()
193 add_to_roster_menuitem = xml.get_object('add_to_roster_menuitem')
194 remove_from_roster_menuitem = xml.get_object(
195 'remove_from_roster_menuitem')
196 manage_contact_menuitem = xml.get_object('manage_contact')
197 convert_to_gc_menuitem = xml.get_object('convert_to_groupchat_menuitem')
198 encryption_separator = xml.get_object('encryption_separator')
199 toggle_gpg_menuitem = xml.get_object('toggle_gpg_menuitem')
200 toggle_e2e_menuitem = xml.get_object('toggle_e2e_menuitem')
201 last_separator = xml.get_object('last_separator')
203 items_to_hide = []
205 # add a special img for send file menuitem
206 path_to_upload_img = gtkgui_helpers.get_icon_path('gajim-upload')
207 img = gtk.Image()
208 img.set_from_file(path_to_upload_img)
209 send_file_menuitem.set_image(img)
211 if not our_jid:
212 # add a special img for rename menuitem
213 gtkgui_helpers.add_image_to_menuitem(rename_menuitem, 'gajim-kbd_input')
215 muc_icon = gtkgui_helpers.load_icon('muc_active')
216 if muc_icon:
217 convert_to_gc_menuitem.set_image(muc_icon)
219 contacts = gajim.contacts.get_contacts(account, jid)
220 if len(contacts) > 1 and use_multiple_contacts: # several resources
221 start_chat_menuitem.set_submenu(build_resources_submenu(contacts,
222 account, gajim.interface.on_open_chat_window))
223 send_file_menuitem.set_submenu(build_resources_submenu(contacts,
224 account, roster.on_send_file_menuitem_activate, cap=NS_FILE))
225 execute_command_menuitem.set_submenu(build_resources_submenu(
226 contacts, account, roster.on_execute_command, cap=NS_COMMANDS))
227 else:
228 start_chat_menuitem.connect('activate',
229 gajim.interface.on_open_chat_window, contact, account)
230 if contact.supports(NS_FILE):
231 send_file_menuitem.set_sensitive(True)
232 send_file_menuitem.connect('activate',
233 roster.on_send_file_menuitem_activate, contact, account)
234 else:
235 send_file_menuitem.set_sensitive(False)
237 if contact.supports(NS_COMMANDS):
238 execute_command_menuitem.set_sensitive(True)
239 execute_command_menuitem.connect('activate', roster.on_execute_command,
240 contact, account, contact.resource)
241 else:
242 execute_command_menuitem.set_sensitive(False)
244 rename_menuitem.connect('activate', roster.on_rename, 'contact', jid,
245 account)
246 history_menuitem.connect('activate', roster.on_history, contact, account)
248 if control:
249 convert_to_gc_menuitem.connect('activate',
250 control._on_convert_to_gc_menuitem_activate)
251 else:
252 items_to_hide.append(convert_to_gc_menuitem)
254 if _('Not in Roster') not in contact.get_shown_groups():
255 # contact is in normal group
256 edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
257 account)])
259 if gajim.connections[account].gpg:
260 assign_openpgp_key_menuitem.connect('activate',
261 roster.on_assign_pgp_key, contact, account)
262 else:
263 assign_openpgp_key_menuitem.set_sensitive(False)
264 else:
265 # contact is in group 'Not in Roster'
266 edit_groups_menuitem.set_sensitive(False)
267 assign_openpgp_key_menuitem.set_sensitive(False)
269 # Hide items when it's self contact row
270 if our_jid:
271 items_to_hide += [rename_menuitem, edit_groups_menuitem]
273 # Unsensitive many items when account is offline
274 if gajim.account_is_disconnected(account):
275 for widget in (start_chat_menuitem, rename_menuitem,
276 edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem):
277 widget.set_sensitive(False)
279 if not show_start_chat:
280 items_to_hide.append(start_chat_menuitem)
282 if not show_encryption or not control:
283 items_to_hide += [encryption_separator, toggle_gpg_menuitem,
284 toggle_e2e_menuitem]
285 else:
286 e2e_is_active = control.session is not None and \
287 control.session.enable_encryption
289 # check if we support and use gpg
290 if not gajim.config.get_per('accounts', account, 'keyid') or \
291 not gajim.connections[account].USE_GPG or gajim.jid_is_transport(
292 contact.jid):
293 toggle_gpg_menuitem.set_sensitive(False)
294 else:
295 toggle_gpg_menuitem.set_sensitive(control.gpg_is_active or \
296 not e2e_is_active)
297 toggle_gpg_menuitem.set_active(control.gpg_is_active)
298 toggle_gpg_menuitem.connect('activate',
299 control._on_toggle_gpg_menuitem_activate)
301 # disable esessions if we or the other client don't support them
302 if not gajim.HAVE_PYCRYPTO or not contact.supports(NS_ESESSION) or \
303 not gajim.config.get_per('accounts', account, 'enable_esessions'):
304 toggle_e2e_menuitem.set_sensitive(False)
305 else:
306 toggle_e2e_menuitem.set_active(e2e_is_active)
307 toggle_e2e_menuitem.set_sensitive(e2e_is_active or \
308 not control.gpg_is_active)
309 toggle_e2e_menuitem.connect('activate',
310 control._on_toggle_e2e_menuitem_activate)
312 if not show_buttonbar_items:
313 items_to_hide += [history_menuitem, send_file_menuitem,
314 information_menuitem, convert_to_gc_menuitem, last_separator]
316 if not control:
317 items_to_hide.append(convert_to_gc_menuitem)
319 for item in items_to_hide:
320 item.set_no_show_all(True)
321 item.hide()
323 # Zeroconf Account
324 if gajim.config.get_per('accounts', account, 'is_zeroconf'):
325 for item in (send_custom_status_menuitem, send_single_message_menuitem,
326 invite_menuitem, block_menuitem, unblock_menuitem, ignore_menuitem,
327 unignore_menuitem, set_custom_avatar_menuitem, subscription_menuitem,
328 manage_contact_menuitem, convert_to_gc_menuitem):
329 item.set_no_show_all(True)
330 item.hide()
332 if contact.show in ('offline', 'error'):
333 information_menuitem.set_sensitive(False)
334 send_file_menuitem.set_sensitive(False)
335 else:
336 information_menuitem.connect('activate', roster.on_info_zeroconf,
337 contact, account)
339 contact_context_menu.connect('selection-done',
340 gtkgui_helpers.destroy_widget)
341 contact_context_menu.show_all()
342 return contact_context_menu
344 # normal account
346 # send custom status icon
347 blocked = False
348 if helpers.jid_is_blocked(account, jid):
349 blocked = True
350 else:
351 for group in contact.get_shown_groups():
352 if helpers.group_is_blocked(account, group):
353 blocked = True
354 break
355 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
356 # Transport contact, send custom status unavailable
357 send_custom_status_menuitem.set_sensitive(False)
358 elif blocked:
359 send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
360 send_custom_status_menuitem.set_sensitive(False)
361 elif account in gajim.interface.status_sent_to_users and \
362 jid in gajim.interface.status_sent_to_users[account]:
363 send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
364 gajim.interface.status_sent_to_users[account][jid]))
365 else:
366 icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU)
367 send_custom_status_menuitem.set_image(icon)
369 muc_icon = gtkgui_helpers.load_icon('muc_active')
370 if muc_icon:
371 invite_menuitem.set_image(muc_icon)
373 if gc_contact:
374 if not gc_contact.jid:
375 # it's a pm and we don't know real JID
376 invite_menuitem.set_sensitive(False)
377 else:
378 build_invite_submenu(invite_menuitem, [(gc_contact, account)])
379 else:
380 build_invite_submenu(invite_menuitem, [(contact, account)])
382 # One or several resource, we do the same for send_custom_status
383 status_menuitems = gtk.Menu()
384 send_custom_status_menuitem.set_submenu(status_menuitems)
385 iconset = gajim.config.get('iconset')
386 path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
387 for s in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
388 # icon MUST be different instance for every item
389 state_images = gtkgui_helpers.load_iconset(path)
390 status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
391 status_menuitem.connect('activate', roster.on_send_custom_status,
392 [(contact, account)], s)
393 icon = state_images[s]
394 status_menuitem.set_image(icon)
395 status_menuitems.append(status_menuitem)
397 send_single_message_menuitem.connect('activate',
398 roster.on_send_single_message_menuitem_activate, account, contact)
400 remove_from_roster_menuitem.connect('activate', roster.on_req_usub,
401 [(contact, account)])
402 information_menuitem.connect('activate', roster.on_info, contact, account)
404 if _('Not in Roster') not in contact.get_shown_groups():
405 # contact is in normal group
406 add_to_roster_menuitem.hide()
407 add_to_roster_menuitem.set_no_show_all(True)
409 if contact.sub in ('from', 'both'):
410 send_auth_menuitem.set_sensitive(False)
411 else:
412 send_auth_menuitem.connect('activate', roster.authorize, jid, account)
413 if contact.sub in ('to', 'both'):
414 ask_auth_menuitem.set_sensitive(False)
415 add_special_notification_menuitem.connect('activate',
416 roster.on_add_special_notification_menuitem_activate, jid)
417 else:
418 ask_auth_menuitem.connect('activate', roster.req_sub, jid,
419 _('I would like to add you to my roster'), account,
420 contact.groups, contact.name)
421 if contact.sub in ('to', 'none') or gajim.get_transport_name_from_jid(
422 jid, use_config_setting=False):
423 revoke_auth_menuitem.set_sensitive(False)
424 else:
425 revoke_auth_menuitem.connect('activate', roster.revoke_auth, jid,
426 account)
428 else:
429 # contact is in group 'Not in Roster'
430 add_to_roster_menuitem.set_no_show_all(False)
431 subscription_menuitem.set_sensitive(False)
433 add_to_roster_menuitem.connect('activate', roster.on_add_to_roster,
434 contact, account)
436 set_custom_avatar_menuitem.connect('activate',
437 roster.on_set_custom_avatar_activate, contact, account)
439 # Hide items when it's self contact row
440 if our_jid:
441 manage_contact_menuitem.set_sensitive(False)
443 # Unsensitive items when account is offline
444 if gajim.account_is_disconnected(account):
445 for widget in (send_single_message_menuitem, subscription_menuitem,
446 add_to_roster_menuitem, remove_from_roster_menuitem,
447 execute_command_menuitem, send_custom_status_menuitem):
448 widget.set_sensitive(False)
450 if gajim.connections[account] and gajim.connections[account].\
451 privacy_rules_supported:
452 if helpers.jid_is_blocked(account, jid):
453 block_menuitem.set_no_show_all(True)
454 block_menuitem.hide()
455 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
456 unblock_menuitem.set_no_show_all(True)
457 unblock_menuitem.hide()
458 unignore_menuitem.set_no_show_all(False)
459 unignore_menuitem.connect('activate', roster.on_unblock, [(contact,
460 account)])
461 else:
462 unblock_menuitem.connect('activate', roster.on_unblock, [(contact,
463 account)])
464 else:
465 unblock_menuitem.set_no_show_all(True)
466 unblock_menuitem.hide()
467 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
468 block_menuitem.set_no_show_all(True)
469 block_menuitem.hide()
470 ignore_menuitem.set_no_show_all(False)
471 ignore_menuitem.connect('activate', roster.on_block, [(contact,
472 account)])
473 else:
474 block_menuitem.connect('activate', roster.on_block, [(contact,
475 account)])
476 else:
477 unblock_menuitem.set_no_show_all(True)
478 block_menuitem.set_sensitive(False)
479 unblock_menuitem.hide()
481 contact_context_menu.connect('selection-done', gtkgui_helpers.destroy_widget)
482 contact_context_menu.show_all()
483 return contact_context_menu