[David Flatz] show transports even if show transports is false when they have unread...
[gajim.git] / src / gui_menu_builder.py
blobef8a58ab398cff8ba8ff85b8e71f07416eb2d675
1 # -*- coding:utf-8 -*-
2 ## src/gui_menu_builder.py
3 ##
4 ## Copyright (C) 2009 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 ''' Build a submenu with contact's resources.
33 room_jid and room_account are for action self.on_invite_to_room '''
34 roster = gajim.interface.roster
35 sub_menu = gtk.Menu()
37 iconset = gajim.config.get('iconset')
38 if not iconset:
39 iconset = gajim.config.DEFAULT_ICONSET
40 path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
41 for c in contacts:
42 # icon MUST be different instance for every item
43 state_images = gtkgui_helpers.load_iconset(path)
44 item = gtk.ImageMenuItem('%s (%s)' % (c.resource, str(c.priority)))
45 icon_name = helpers.get_icon_name_to_show(c, account)
46 icon = state_images[icon_name]
47 item.set_image(icon)
48 sub_menu.append(item)
50 if action == roster.on_invite_to_room:
51 item.connect('activate', action, [(c, account)], room_jid,
52 room_account, c.resource)
53 elif action == roster.on_invite_to_new_room:
54 item.connect('activate', action, [(c, account)], c.resource)
55 else: # start_chat, execute_command, send_file
56 item.connect('activate', action, c, account, c.resource)
58 if cap and not gajim.capscache.is_supported(c, cap):
59 item.set_sensitive(False)
61 return sub_menu
63 def build_invite_submenu(invite_menuitem, list_):
64 '''list_ in a list of (contact, account)'''
65 roster = gajim.interface.roster
66 # used if we invite only one contact with several resources
67 contact_list = []
68 if len(list_) == 1:
69 contact, account = list_[0]
70 contact_list = gajim.contacts.get_contacts(account, contact.jid)
71 contacts_transport = -1
72 connected_accounts = []
73 # -1 is at start, False when not from the same, None when jabber
74 for (contact, account) in list_:
75 if not account in connected_accounts:
76 connected_accounts.append(account)
77 transport = gajim.get_transport_name_from_jid(contact.jid)
78 if contacts_transport == -1:
79 contacts_transport = transport
80 elif contacts_transport != transport:
81 contacts_transport = False
83 if contacts_transport == False:
84 # they are not all from the same transport
85 invite_menuitem.set_sensitive(False)
86 return
87 invite_to_submenu = gtk.Menu()
88 invite_menuitem.set_submenu(invite_to_submenu)
89 invite_to_new_room_menuitem = gtk.ImageMenuItem(_('_New Group Chat'))
90 icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
91 invite_to_new_room_menuitem.set_image(icon)
92 if len(contact_list) > 1: # several resources
93 invite_to_new_room_menuitem.set_submenu(build_resources_submenu(
94 contact_list, account, roster.on_invite_to_new_room, cap=NS_MUC))
95 elif len(list_) == 1 and gajim.capscache.is_supported(contact, NS_MUC):
96 invite_menuitem.set_sensitive(True)
97 # use resource if it's self contact
98 if contact.jid == gajim.get_jid_from_account(account):
99 resource = contact.resource
100 else:
101 resource = None
102 invite_to_new_room_menuitem.connect('activate',
103 roster.on_invite_to_new_room, list_, resource)
104 else:
105 invite_menuitem.set_sensitive(False)
106 # transform None in 'jabber'
107 c_t = contacts_transport or 'jabber'
108 muc_jid = {}
109 for account in connected_accounts:
110 for t in gajim.connections[account].muc_jid:
111 muc_jid[t] = gajim.connections[account].muc_jid[t]
112 if c_t not in muc_jid:
113 invite_to_new_room_menuitem.set_sensitive(False)
114 rooms = [] # a list of (room_jid, account) tuple
115 invite_to_submenu.append(invite_to_new_room_menuitem)
116 rooms = [] # a list of (room_jid, account) tuple
117 minimized_controls = []
118 for account in connected_accounts:
119 minimized_controls += gajim.interface.minimized_controls[account].values()
120 for gc_control in gajim.interface.msg_win_mgr.get_controls(
121 message_control.TYPE_GC) + minimized_controls:
122 acct = gc_control.account
123 room_jid = gc_control.room_jid
124 if room_jid in gajim.gc_connected[acct] and \
125 gajim.gc_connected[acct][room_jid] and \
126 contacts_transport == gajim.get_transport_name_from_jid(room_jid):
127 rooms.append((room_jid, acct))
128 if len(rooms):
129 item = gtk.SeparatorMenuItem() # separator
130 invite_to_submenu.append(item)
131 for (room_jid, account) in rooms:
132 menuitem = gtk.MenuItem(room_jid.split('@')[0])
133 if len(contact_list) > 1: # several resources
134 menuitem.set_submenu(build_resources_submenu(
135 contact_list, account, roster.on_invite_to_room, room_jid,
136 account))
137 else:
138 # use resource if it's self contact
139 if contact.jid == gajim.get_jid_from_account(account):
140 resource = contact.resource
141 else:
142 resource = None
143 menuitem.connect('activate', roster.on_invite_to_room, list_,
144 room_jid, account, resource)
145 invite_to_submenu.append(menuitem)
147 def get_contact_menu(contact, account, use_multiple_contacts=True,
148 show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
149 control=None):
150 ''' Build contact popup menu for roster and chat window.
151 If control is not set, we hide invite_contacts_menuitem'''
152 if not contact:
153 return
155 jid = contact.jid
156 our_jid = jid == gajim.get_jid_from_account(account)
157 roster = gajim.interface.roster
159 xml = gtkgui_helpers.get_glade('contact_context_menu.glade')
160 contact_context_menu = xml.get_widget('contact_context_menu')
162 start_chat_menuitem = xml.get_widget('start_chat_menuitem')
163 execute_command_menuitem = xml.get_widget('execute_command_menuitem')
164 rename_menuitem = xml.get_widget('rename_menuitem')
165 edit_groups_menuitem = xml.get_widget('edit_groups_menuitem')
166 send_file_menuitem = xml.get_widget('send_file_menuitem')
167 assign_openpgp_key_menuitem = xml.get_widget('assign_openpgp_key_menuitem')
168 add_special_notification_menuitem = xml.get_widget(
169 'add_special_notification_menuitem')
170 information_menuitem = xml.get_widget('information_menuitem')
171 history_menuitem = xml.get_widget('history_menuitem')
172 send_custom_status_menuitem = xml.get_widget('send_custom_status_menuitem')
173 send_single_message_menuitem = xml.get_widget('send_single_message_menuitem')
174 invite_menuitem = xml.get_widget('invite_menuitem')
175 block_menuitem = xml.get_widget('block_menuitem')
176 unblock_menuitem = xml.get_widget('unblock_menuitem')
177 ignore_menuitem = xml.get_widget('ignore_menuitem')
178 unignore_menuitem = xml.get_widget('unignore_menuitem')
179 set_custom_avatar_menuitem = xml.get_widget('set_custom_avatar_menuitem')
180 # Subscription submenu
181 subscription_menuitem = xml.get_widget('subscription_menuitem')
182 send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem = \
183 subscription_menuitem.get_submenu().get_children()
184 add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem')
185 remove_from_roster_menuitem = xml.get_widget(
186 'remove_from_roster_menuitem')
187 manage_contact_menuitem = xml.get_widget('manage_contact')
188 convert_to_gc_menuitem = xml.get_widget('convert_to_groupchat_menuitem')
189 encryption_separator = xml.get_widget('encryption_separator')
190 toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem')
191 toggle_e2e_menuitem = xml.get_widget('toggle_e2e_menuitem')
192 last_separator = xml.get_widget('last_separator')
194 items_to_hide = []
196 # add a special img for send file menuitem
197 path_to_upload_img = os.path.join(gajim.DATA_DIR, 'pixmaps', 'upload.png')
198 img = gtk.Image()
199 img.set_from_file(path_to_upload_img)
200 send_file_menuitem.set_image(img)
202 if not our_jid:
203 # add a special img for rename menuitem
204 path_to_kbd_input_img = os.path.join(gajim.DATA_DIR, 'pixmaps',
205 'kbd_input.png')
206 img = gtk.Image()
207 img.set_from_file(path_to_kbd_input_img)
208 rename_menuitem.set_image(img)
210 muc_icon = gtkgui_helpers.load_icon('muc_active')
211 if muc_icon:
212 convert_to_gc_menuitem.set_image(muc_icon)
214 contacts = gajim.contacts.get_contacts(account, jid)
215 if len(contacts) > 1 and use_multiple_contacts: # several resources
216 start_chat_menuitem.set_submenu(build_resources_submenu(contacts,
217 account, gajim.interface.on_open_chat_window))
218 send_file_menuitem.set_submenu(build_resources_submenu(contacts,
219 account, roster.on_send_file_menuitem_activate, cap=NS_FILE))
220 execute_command_menuitem.set_submenu(build_resources_submenu(
221 contacts, account, roster.on_execute_command, cap=NS_COMMANDS))
222 else:
223 start_chat_menuitem.connect('activate',
224 gajim.interface.on_open_chat_window, contact, account)
225 if gajim.capscache.is_supported(contact, NS_FILE):
226 send_file_menuitem.set_sensitive(True)
227 send_file_menuitem.connect('activate',
228 roster.on_send_file_menuitem_activate, contact, account)
229 else:
230 send_file_menuitem.set_sensitive(False)
232 if gajim.capscache.is_supported(contact, NS_COMMANDS):
233 execute_command_menuitem.set_sensitive(True)
234 execute_command_menuitem.connect('activate', roster.on_execute_command,
235 contact, account, contact.resource)
236 else:
237 execute_command_menuitem.set_sensitive(False)
239 rename_menuitem.connect('activate', roster.on_rename, 'contact', jid,
240 account)
241 history_menuitem.connect('activate', roster.on_history, contact, account)
243 if control:
244 convert_to_gc_menuitem.connect('activate',
245 control._on_convert_to_gc_menuitem_activate)
246 else:
247 items_to_hide.append(convert_to_gc_menuitem)
249 if _('Not in Roster') not in contact.get_shown_groups():
250 # contact is in normal group
251 edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
252 account)])
254 if gajim.connections[account].gpg:
255 assign_openpgp_key_menuitem.connect('activate',
256 roster.on_assign_pgp_key, contact, account)
257 else:
258 assign_openpgp_key_menuitem.set_sensitive(False)
259 else:
260 # contact is in group 'Not in Roster'
261 edit_groups_menuitem.set_sensitive(False)
262 assign_openpgp_key_menuitem.set_sensitive(False)
264 # Hide items when it's self contact row
265 if our_jid:
266 items_to_hide += [rename_menuitem, edit_groups_menuitem]
268 # Unsensitive many items when account is offline
269 if gajim.account_is_disconnected(account):
270 for widget in (start_chat_menuitem, rename_menuitem,
271 edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem):
272 widget.set_sensitive(False)
274 if not show_start_chat:
275 items_to_hide.append(start_chat_menuitem)
277 if not show_encryption or not control:
278 items_to_hide += [encryption_separator, toggle_gpg_menuitem,
279 toggle_e2e_menuitem]
280 else:
281 e2e_is_active = control.session is not None and \
282 control.session.enable_encryption
284 # check if we support and use gpg
285 if not gajim.config.get_per('accounts', account, 'keyid') or \
286 not gajim.connections[account].USE_GPG or gajim.jid_is_transport(
287 contact.jid):
288 toggle_gpg_menuitem.set_sensitive(False)
289 else:
290 toggle_gpg_menuitem.set_sensitive(control.gpg_is_active or \
291 not e2e_is_active)
292 toggle_gpg_menuitem.set_active(control.gpg_is_active)
293 toggle_gpg_menuitem.connect('activate',
294 control._on_toggle_gpg_menuitem_activate)
296 # disable esessions if we or the other client don't support them
297 # XXX: Once we have fallback to disco, remove notexistant check
298 if not gajim.HAVE_PYCRYPTO or \
299 not gajim.capscache.is_supported(contact, NS_ESESSION) or \
300 gajim.capscache.is_supported(contact, 'notexistant') or \
301 not gajim.config.get_per('accounts', account, 'enable_esessions'):
302 toggle_e2e_menuitem.set_sensitive(False)
303 else:
304 toggle_e2e_menuitem.set_active(e2e_is_active)
305 toggle_e2e_menuitem.set_sensitive(e2e_is_active or \
306 not control.gpg_is_active)
307 toggle_e2e_menuitem.connect('activate',
308 control._on_toggle_e2e_menuitem_activate)
310 if not show_buttonbar_items:
311 items_to_hide += [history_menuitem, send_file_menuitem,
312 information_menuitem, convert_to_gc_menuitem, last_separator]
314 if not control:
315 items_to_hide.append(convert_to_gc_menuitem)
317 for item in items_to_hide:
318 item.set_no_show_all(True)
319 item.hide()
321 # Zeroconf Account
322 if gajim.config.get_per('accounts', account, 'is_zeroconf'):
323 for item in (send_custom_status_menuitem, send_single_message_menuitem,
324 invite_menuitem, block_menuitem, unblock_menuitem, ignore_menuitem,
325 unignore_menuitem, set_custom_avatar_menuitem, subscription_menuitem,
326 manage_contact_menuitem, convert_to_gc_menuitem):
327 item.set_no_show_all(True)
328 item.hide()
330 if contact.show in ('offline', 'error'):
331 information_menuitem.set_sensitive(False)
332 send_file_menuitem.set_sensitive(False)
333 else:
334 information_menuitem.connect('activate', roster.on_info_zeroconf,
335 contact, account)
337 contact_context_menu.connect('selection-done',
338 gtkgui_helpers.destroy_widget)
339 contact_context_menu.show_all()
340 return contact_context_menu
342 # normal account
344 # send custom status icon
345 blocked = False
346 if helpers.jid_is_blocked(account, jid):
347 blocked = True
348 else:
349 for group in contact.get_shown_groups():
350 if helpers.group_is_blocked(account, group):
351 blocked = True
352 break
353 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
354 # Transport contact, send custom status unavailable
355 send_custom_status_menuitem.set_sensitive(False)
356 elif blocked:
357 send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon('offline'))
358 send_custom_status_menuitem.set_sensitive(False)
359 elif account in gajim.interface.status_sent_to_users and \
360 jid in gajim.interface.status_sent_to_users[account]:
361 send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
362 gajim.interface.status_sent_to_users[account][jid]))
363 else:
364 icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU)
365 send_custom_status_menuitem.set_image(icon)
367 muc_icon = gtkgui_helpers.load_icon('muc_active')
368 if muc_icon:
369 invite_menuitem.set_image(muc_icon)
371 build_invite_submenu(invite_menuitem, [(contact, account)])
373 # One or several resource, we do the same for send_custom_status
374 status_menuitems = gtk.Menu()
375 send_custom_status_menuitem.set_submenu(status_menuitems)
376 iconset = gajim.config.get('iconset')
377 path = os.path.join(helpers.get_iconset_path(iconset), '16x16')
378 for s in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
379 # icon MUST be different instance for every item
380 state_images = gtkgui_helpers.load_iconset(path)
381 status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
382 status_menuitem.connect('activate', roster.on_send_custom_status,
383 [(contact, account)], s)
384 icon = state_images[s]
385 status_menuitem.set_image(icon)
386 status_menuitems.append(status_menuitem)
388 send_single_message_menuitem.connect('activate',
389 roster.on_send_single_message_menuitem_activate, account, contact)
391 remove_from_roster_menuitem.connect('activate', roster.on_req_usub,
392 [(contact, account)])
393 information_menuitem.connect('activate', roster.on_info, contact, account)
395 if _('Not in Roster') not in contact.get_shown_groups():
396 # contact is in normal group
397 add_to_roster_menuitem.hide()
398 add_to_roster_menuitem.set_no_show_all(True)
400 if contact.sub in ('from', 'both'):
401 send_auth_menuitem.set_sensitive(False)
402 else:
403 send_auth_menuitem.connect('activate', roster.authorize, jid, account)
404 if contact.sub in ('to', 'both'):
405 ask_auth_menuitem.set_sensitive(False)
406 add_special_notification_menuitem.connect('activate',
407 roster.on_add_special_notification_menuitem_activate, jid)
408 else:
409 ask_auth_menuitem.connect('activate', roster.req_sub, jid,
410 _('I would like to add you to my roster'), account,
411 contact.groups, contact.name)
412 if contact.sub in ('to', 'none') or gajim.get_transport_name_from_jid(
413 jid, use_config_setting=False):
414 revoke_auth_menuitem.set_sensitive(False)
415 else:
416 revoke_auth_menuitem.connect('activate', roster.revoke_auth, jid,
417 account)
419 else:
420 # contact is in group 'Not in Roster'
421 add_to_roster_menuitem.set_no_show_all(False)
422 subscription_menuitem.set_sensitive(False)
424 add_to_roster_menuitem.connect('activate', roster.on_add_to_roster,
425 contact, account)
427 set_custom_avatar_menuitem.connect('activate',
428 roster.on_set_custom_avatar_activate, contact, account)
430 # Hide items when it's self contact row
431 if our_jid:
432 manage_contact_menuitem.set_sensitive(False)
434 # Unsensitive items when account is offline
435 if gajim.account_is_disconnected(account):
436 for widget in (send_single_message_menuitem, subscription_menuitem,
437 add_to_roster_menuitem, remove_from_roster_menuitem,
438 execute_command_menuitem, send_custom_status_menuitem):
439 widget.set_sensitive(False)
441 if gajim.connections[account] and gajim.connections[account].\
442 privacy_rules_supported:
443 if helpers.jid_is_blocked(account, jid):
444 block_menuitem.set_no_show_all(True)
445 block_menuitem.hide()
446 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
447 unblock_menuitem.set_no_show_all(True)
448 unblock_menuitem.hide()
449 unignore_menuitem.set_no_show_all(False)
450 unignore_menuitem.connect('activate', roster.on_unblock, [(contact,
451 account)])
452 else:
453 unblock_menuitem.connect('activate', roster.on_unblock, [(contact,
454 account)])
455 else:
456 unblock_menuitem.set_no_show_all(True)
457 unblock_menuitem.hide()
458 if gajim.get_transport_name_from_jid(jid, use_config_setting=False):
459 block_menuitem.set_no_show_all(True)
460 block_menuitem.hide()
461 ignore_menuitem.set_no_show_all(False)
462 ignore_menuitem.connect('activate', roster.on_block, [(contact,
463 account)])
464 else:
465 block_menuitem.connect('activate', roster.on_block, [(contact,
466 account)])
467 else:
468 unblock_menuitem.set_no_show_all(True)
469 block_menuitem.set_sensitive(False)
470 unblock_menuitem.hide()
472 contact_context_menu.connect('selection-done', gtkgui_helpers.destroy_widget)
473 contact_context_menu.show_all()
474 return contact_context_menu
476 # vim: se ts=3: