When there are no bookmarks on the server the join groupchat menu was disabled, preventing the user to open the join groupchat dialog
966 lines
36 KiB
Python
966 lines
36 KiB
Python
# Copyright (C) 2009-2014 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/>.
|
|
|
|
from gi.repository import Gtk, Gio, GLib
|
|
from nbxmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC
|
|
from nbxmpp.protocol import NS_JINGLE_FILE_TRANSFER_5, NS_CONFERENCE
|
|
|
|
from gajim import gtkgui_helpers
|
|
from gajim import message_control
|
|
from gajim.gtkgui_helpers import get_action
|
|
from gajim.common import app
|
|
from gajim.common import helpers
|
|
from gajim.common.i18n import ngettext
|
|
from gajim.common.i18n import _
|
|
|
|
from gajim.gtk.util import get_builder
|
|
|
|
|
|
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
|
|
"""
|
|
roster = app.interface.roster
|
|
sub_menu = Gtk.Menu()
|
|
|
|
for c in contacts:
|
|
item = Gtk.MenuItem.new_with_label(
|
|
'%s (%s)' % (c.resource, str(c.priority)))
|
|
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 c.supports(cap):
|
|
item.set_sensitive(False)
|
|
|
|
return sub_menu
|
|
|
|
def build_invite_submenu(invite_menuitem, list_, ignore_rooms=None,
|
|
show_bookmarked=False, force_resource=False):
|
|
"""
|
|
list_ in a list of (contact, account)
|
|
force_resource means we want to send invitation even if there is only one
|
|
resource
|
|
"""
|
|
if ignore_rooms is None:
|
|
ignore_rooms = []
|
|
roster = app.interface.roster
|
|
# used if we invite only one contact with several resources
|
|
contact_list = []
|
|
if len(list_) == 1:
|
|
contact, account = list_[0]
|
|
contact_list = app.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 = app.get_transport_name_from_jid(contact.jid)
|
|
if transport == 'jabber':
|
|
transport = None
|
|
if contacts_transport == -1:
|
|
contacts_transport = transport
|
|
elif contacts_transport != transport:
|
|
contacts_transport = False
|
|
|
|
if contacts_transport is 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.MenuItem.new_with_mnemonic(_(
|
|
'_New Group Chat'))
|
|
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 contact.supports(NS_MUC):
|
|
invite_menuitem.set_sensitive(True)
|
|
# use resource if it's self contact
|
|
if contact.jid == app.get_jid_from_account(account) or force_resource:
|
|
resource = contact.resource
|
|
else:
|
|
resource = None
|
|
invite_to_new_room_menuitem.connect('activate',
|
|
roster.on_invite_to_new_room, list_, resource)
|
|
elif len(list_) > 1:
|
|
list2 = []
|
|
for (c, a) in list_:
|
|
if c.supports(NS_MUC):
|
|
list2.append((c, a))
|
|
if list2:
|
|
invite_to_new_room_menuitem.connect('activate',
|
|
roster.on_invite_to_new_room, list2, None)
|
|
else:
|
|
invite_menuitem.set_sensitive(False)
|
|
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 app.connections[account].muc_jid:
|
|
muc_jid[t] = app.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)
|
|
minimized_controls = []
|
|
for account in connected_accounts:
|
|
minimized_controls += \
|
|
list(app.interface.minimized_controls[account].values())
|
|
for gc_control in app.interface.msg_win_mgr.get_controls(
|
|
message_control.TYPE_GC) + minimized_controls:
|
|
acct = gc_control.account
|
|
if acct not in connected_accounts:
|
|
continue
|
|
room_jid = gc_control.room_jid
|
|
if room_jid in ignore_rooms:
|
|
continue
|
|
if room_jid in app.gc_connected[acct] and \
|
|
app.gc_connected[acct][room_jid] and \
|
|
contacts_transport in ['jabber', None]:
|
|
rooms.append((room_jid, acct))
|
|
if rooms:
|
|
item = Gtk.SeparatorMenuItem.new() # separator
|
|
invite_to_submenu.append(item)
|
|
for (room_jid, account) in rooms:
|
|
menuitem = Gtk.MenuItem.new_with_label(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 == app.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)
|
|
|
|
if not show_bookmarked:
|
|
return
|
|
rooms2 = [] # a list of (room_jid, account) tuple
|
|
r_jids = [] # list of room jids
|
|
for account in connected_accounts:
|
|
con = app.connections[account]
|
|
boomarks = con.get_module('Bookmarks').bookmarks
|
|
for jid in boomarks.keys():
|
|
if jid in r_jids:
|
|
continue
|
|
if jid not in app.gc_connected[account] or not \
|
|
app.gc_connected[account][jid]:
|
|
rooms2.append((jid, account))
|
|
r_jids.append(jid)
|
|
|
|
if not rooms2:
|
|
return
|
|
item = Gtk.SeparatorMenuItem.new() # separator
|
|
invite_to_submenu.append(item)
|
|
for (room_jid, account) in rooms2:
|
|
menuitem = Gtk.MenuItem.new_with_label(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 == app.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,
|
|
show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
|
|
control=None, gc_contact=None, is_anonymous=True):
|
|
"""
|
|
Build contact popup menu for roster and chat window. If control is not set,
|
|
we hide invite_contacts_menuitem
|
|
"""
|
|
if not contact:
|
|
return
|
|
|
|
jid = contact.jid
|
|
our_jid = jid == app.get_jid_from_account(account)
|
|
roster = app.interface.roster
|
|
|
|
xml = get_builder('contact_context_menu.ui')
|
|
contact_context_menu = xml.get_object('contact_context_menu')
|
|
|
|
start_chat_menuitem = xml.get_object('start_chat_menuitem')
|
|
execute_command_menuitem = xml.get_object('execute_command_menuitem')
|
|
rename_menuitem = xml.get_object('rename_menuitem')
|
|
edit_groups_menuitem = xml.get_object('edit_groups_menuitem')
|
|
send_file_menuitem = xml.get_object('send_file_menuitem')
|
|
assign_openpgp_key_menuitem = xml.get_object('assign_openpgp_key_menuitem')
|
|
information_menuitem = xml.get_object('information_menuitem')
|
|
history_menuitem = xml.get_object('history_menuitem')
|
|
send_custom_status_menuitem = xml.get_object('send_custom_status_menuitem')
|
|
send_single_message_menuitem = xml.get_object('send_single_message_menuitem')
|
|
invite_menuitem = xml.get_object('invite_menuitem')
|
|
block_menuitem = xml.get_object('block_menuitem')
|
|
unblock_menuitem = xml.get_object('unblock_menuitem')
|
|
ignore_menuitem = xml.get_object('ignore_menuitem')
|
|
unignore_menuitem = xml.get_object('unignore_menuitem')
|
|
# Subscription submenu
|
|
subscription_menuitem = xml.get_object('subscription_menuitem')
|
|
send_auth_menuitem, ask_auth_menuitem, revoke_auth_menuitem = \
|
|
subscription_menuitem.get_submenu().get_children()
|
|
add_to_roster_menuitem = xml.get_object('add_to_roster_menuitem')
|
|
remove_from_roster_menuitem = xml.get_object(
|
|
'remove_from_roster_menuitem')
|
|
manage_contact_menuitem = xml.get_object('manage_contact')
|
|
convert_to_gc_menuitem = xml.get_object('convert_to_groupchat_menuitem')
|
|
last_separator = xml.get_object('last_separator')
|
|
|
|
items_to_hide = []
|
|
|
|
contacts = app.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, app.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',
|
|
app.interface.on_open_chat_window, contact, account)
|
|
if contact.supports(NS_FILE) or contact.supports(NS_JINGLE_FILE_TRANSFER_5):
|
|
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 contact.supports(NS_COMMANDS):
|
|
execute_command_menuitem.set_sensitive(True)
|
|
if gc_contact and gc_contact.jid and not is_anonymous:
|
|
execute_command_menuitem.connect('activate',
|
|
roster.on_execute_command, gc_contact, account,
|
|
gc_contact.resource)
|
|
else:
|
|
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.set_action_name('app.browse-history')
|
|
dict_ = {'jid': GLib.Variant('s', contact.jid),
|
|
'account': GLib.Variant('s', account)}
|
|
variant = GLib.Variant('a{sv}', dict_)
|
|
history_menuitem.set_action_target_value(variant)
|
|
|
|
if control:
|
|
convert_to_gc_menuitem.connect('activate',
|
|
control._on_convert_to_gc_menuitem_activate)
|
|
else:
|
|
items_to_hide.append(convert_to_gc_menuitem)
|
|
|
|
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 app.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:
|
|
items_to_hide += [rename_menuitem, edit_groups_menuitem]
|
|
|
|
# Unsensitive many items when account is offline
|
|
if app.account_is_disconnected(account):
|
|
for widget in (start_chat_menuitem, rename_menuitem,
|
|
edit_groups_menuitem, send_file_menuitem, convert_to_gc_menuitem,
|
|
information_menuitem):
|
|
widget.set_sensitive(False)
|
|
|
|
if not show_start_chat:
|
|
items_to_hide.append(start_chat_menuitem)
|
|
|
|
if not show_buttonbar_items:
|
|
items_to_hide += [history_menuitem, send_file_menuitem,
|
|
information_menuitem, convert_to_gc_menuitem, last_separator]
|
|
|
|
if not control:
|
|
items_to_hide.append(convert_to_gc_menuitem)
|
|
|
|
# Hide items when it's a pm
|
|
if gc_contact:
|
|
items_to_hide += [rename_menuitem, edit_groups_menuitem,
|
|
subscription_menuitem, remove_from_roster_menuitem]
|
|
|
|
for item in items_to_hide:
|
|
item.set_no_show_all(True)
|
|
item.hide()
|
|
|
|
# Zeroconf Account
|
|
if app.config.get_per('accounts', account, 'is_zeroconf'):
|
|
for item in (send_custom_status_menuitem, send_single_message_menuitem,
|
|
invite_menuitem, block_menuitem, unblock_menuitem, ignore_menuitem,
|
|
unignore_menuitem, subscription_menuitem,
|
|
manage_contact_menuitem, convert_to_gc_menuitem):
|
|
item.set_no_show_all(True)
|
|
item.hide()
|
|
|
|
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 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
|
|
transport = app.get_transport_name_from_jid(jid, use_config_setting=False)
|
|
if transport and transport != 'jabber':
|
|
# Transport contact, send custom status unavailable
|
|
send_custom_status_menuitem.set_sensitive(False)
|
|
elif blocked:
|
|
send_custom_status_menuitem.set_sensitive(False)
|
|
|
|
if gc_contact:
|
|
if not gc_contact.jid:
|
|
# it's a pm and we don't know real JID
|
|
invite_menuitem.set_sensitive(False)
|
|
else:
|
|
bookmarked = False
|
|
c_ = app.contacts.get_contact(account, gc_contact.jid,
|
|
gc_contact.resource)
|
|
if c_ and c_.supports(NS_CONFERENCE):
|
|
bookmarked = True
|
|
build_invite_submenu(invite_menuitem, [(gc_contact, account)],
|
|
show_bookmarked=bookmarked)
|
|
else:
|
|
force_resource = False
|
|
if control and control.resource:
|
|
force_resource = True
|
|
build_invite_submenu(invite_menuitem, [(contact, account)],
|
|
show_bookmarked=contact.supports(NS_CONFERENCE),
|
|
force_resource=force_resource)
|
|
|
|
if app.account_is_disconnected(account):
|
|
invite_menuitem.set_sensitive(False)
|
|
|
|
# One or several resource, we do the same for send_custom_status
|
|
status_menuitems = Gtk.Menu()
|
|
send_custom_status_menuitem.set_submenu(status_menuitems)
|
|
for s in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
|
|
# icon MUST be different instance for every item
|
|
status_menuitem = Gtk.MenuItem.new_with_label(helpers.get_uf_show(s))
|
|
status_menuitem.connect('activate', roster.on_send_custom_status,
|
|
[(contact, account)], s)
|
|
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)
|
|
else:
|
|
ask_auth_menuitem.connect('activate', roster.req_sub, jid,
|
|
_('I would like to add you to my roster'), account,
|
|
contact.groups, contact.name)
|
|
transport = app.get_transport_name_from_jid(jid,
|
|
use_config_setting=False)
|
|
if contact.sub in ('to', 'none') or transport not in ['jabber', None]:
|
|
revoke_auth_menuitem.set_sensitive(False)
|
|
else:
|
|
revoke_auth_menuitem.connect('activate', roster.revoke_auth, jid,
|
|
account)
|
|
|
|
elif app.connections[account].roster_supported:
|
|
# 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)
|
|
else:
|
|
add_to_roster_menuitem.hide()
|
|
add_to_roster_menuitem.set_no_show_all(True)
|
|
subscription_menuitem.set_sensitive(False)
|
|
|
|
# Hide items when it's self contact row
|
|
if our_jid:
|
|
manage_contact_menuitem.set_sensitive(False)
|
|
|
|
# Unsensitive items when account is offline
|
|
if app.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)
|
|
|
|
|
|
con = app.connections[account]
|
|
if con and (con.get_module('PrivacyLists').supported or
|
|
con.get_module('Blocking').supported):
|
|
if helpers.jid_is_blocked(account, jid):
|
|
block_menuitem.set_no_show_all(True)
|
|
block_menuitem.hide()
|
|
if app.get_transport_name_from_jid(jid, use_config_setting=False)\
|
|
and transport != 'jabber':
|
|
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 app.get_transport_name_from_jid(jid, use_config_setting=False)\
|
|
and transport != 'jabber':
|
|
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
|
|
|
|
def get_transport_menu(contact, account):
|
|
roster = app.interface.roster
|
|
jid = contact.jid
|
|
|
|
menu = Gtk.Menu()
|
|
|
|
# Send single message
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('Send Single _Message…'))
|
|
item.connect('activate', roster.on_send_single_message_menuitem_activate,
|
|
account, contact)
|
|
menu.append(item)
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
blocked = False
|
|
if helpers.jid_is_blocked(account, jid):
|
|
blocked = True
|
|
|
|
# Send Custom Status
|
|
send_custom_status_menuitem = Gtk.MenuItem.new_with_mnemonic(
|
|
_('Send Cus_tom Status'))
|
|
if blocked:
|
|
send_custom_status_menuitem.set_sensitive(False)
|
|
else:
|
|
status_menuitems = Gtk.Menu()
|
|
send_custom_status_menuitem.set_submenu(status_menuitems)
|
|
for s in ('online', 'chat', 'away', 'xa', 'dnd', 'offline'):
|
|
status_menuitem = Gtk.MenuItem.new_with_label(helpers.get_uf_show(
|
|
s))
|
|
status_menuitem.connect('activate', roster.on_send_custom_status,
|
|
[(contact, account)], s)
|
|
status_menuitems.append(status_menuitem)
|
|
menu.append(send_custom_status_menuitem)
|
|
if app.account_is_disconnected(account):
|
|
send_custom_status_menuitem.set_sensitive(False)
|
|
|
|
item = Gtk.SeparatorMenuItem.new() # separator
|
|
menu.append(item)
|
|
|
|
# Execute Command
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('E_xecute Command…'))
|
|
menu.append(item)
|
|
item.connect('activate', roster.on_execute_command, contact, account,
|
|
contact.resource)
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
# Manage Transport submenu
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('_Manage Transport'))
|
|
manage_transport_submenu = Gtk.Menu()
|
|
item.set_submenu(manage_transport_submenu)
|
|
menu.append(item)
|
|
|
|
# Modify Transport
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('_Modify Transport'))
|
|
manage_transport_submenu.append(item)
|
|
item.connect('activate', roster.on_edit_agent, contact, account)
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
# Rename
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('_Rename…'))
|
|
manage_transport_submenu.append(item)
|
|
item.connect('activate', roster.on_rename, 'agent', jid, account)
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
item = Gtk.SeparatorMenuItem.new() # separator
|
|
manage_transport_submenu.append(item)
|
|
|
|
# Block
|
|
if blocked:
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('_Unblock'))
|
|
item.connect('activate', roster.on_unblock, [(contact, account)])
|
|
else:
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('_Block'))
|
|
item.connect('activate', roster.on_block, [(contact, account)])
|
|
|
|
manage_transport_submenu.append(item)
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
# Remove
|
|
item = Gtk.MenuItem.new_with_mnemonic(_('Remo_ve'))
|
|
manage_transport_submenu.append(item)
|
|
item.connect('activate', roster.on_remove_agent, [(contact, account)])
|
|
if app.account_is_disconnected(account):
|
|
item.set_sensitive(False)
|
|
|
|
item = Gtk.SeparatorMenuItem.new() # separator
|
|
menu.append(item)
|
|
|
|
# Information
|
|
information_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Information'))
|
|
menu.append(information_menuitem)
|
|
information_menuitem.connect('activate', roster.on_info, contact, account)
|
|
if app.account_is_disconnected(account):
|
|
information_menuitem.set_sensitive(False)
|
|
|
|
menu.connect('selection-done', gtkgui_helpers.destroy_widget)
|
|
menu.show_all()
|
|
return menu
|
|
|
|
|
|
def show_save_as_menu(sha, name):
|
|
menu = Gtk.Menu()
|
|
menuitem = Gtk.MenuItem.new_with_mnemonic(_('Save _As'))
|
|
menuitem.connect(
|
|
'activate',
|
|
gtkgui_helpers.on_avatar_save_as_menuitem_activate, sha, name)
|
|
menu.append(menuitem)
|
|
menu.connect('selection-done', lambda w: w.destroy())
|
|
menu.show_all()
|
|
menu.popup_at_pointer()
|
|
|
|
|
|
def get_singlechat_menu(control_id, account, jid):
|
|
singlechat_menu = [
|
|
(_('Send File…'), [
|
|
('win.send-file-httpupload-', _('Upload File…')),
|
|
('win.send-file-jingle-', _('Send File Directly…')),
|
|
]),
|
|
('win.invite-contacts-', _('Invite Contacts')),
|
|
('win.add-to-roster-', _('Add to Roster')),
|
|
('win.toggle-audio-', _('Audio Session')),
|
|
('win.toggle-video-', _('Video Session')),
|
|
('win.information-', _('Information')),
|
|
('app.browse-history', _('History')),
|
|
]
|
|
|
|
def build_menu(preset):
|
|
menu = Gio.Menu()
|
|
for item in preset:
|
|
if isinstance(item[1], str):
|
|
action_name, label = item
|
|
if action_name == 'app.browse-history':
|
|
menuitem = Gio.MenuItem.new(label, action_name)
|
|
dict_ = {'account': GLib.Variant('s', account),
|
|
'jid': GLib.Variant('s', jid)}
|
|
variant_dict = GLib.Variant('a{sv}', dict_)
|
|
menuitem.set_action_and_target_value(action_name,
|
|
variant_dict)
|
|
menu.append_item(menuitem)
|
|
else:
|
|
menu.append(label, action_name + control_id)
|
|
else:
|
|
label, sub_menu = item
|
|
# This is a submenu
|
|
submenu = build_menu(sub_menu)
|
|
menu.append_submenu(label, submenu)
|
|
return menu
|
|
|
|
return build_menu(singlechat_menu)
|
|
|
|
|
|
def get_groupchat_menu(control_id, account, jid):
|
|
groupchat_menu = [
|
|
(_('Manage Room'), [
|
|
('win.change-subject-', _('Change Subject')),
|
|
('win.configure-', _('Configure Room')),
|
|
('win.upload-avatar-', _('Upload Avatar…')),
|
|
('win.destroy-', _('Destroy Room')),
|
|
]),
|
|
(_('Sync Threshold'), []),
|
|
('win.change-nick-', _('Change Nick')),
|
|
('win.bookmark-', _('Bookmark Room')),
|
|
('win.request-voice-', _('Request Voice')),
|
|
('win.notify-on-message-', _('Notify on all messages')),
|
|
('win.minimize-', _('Minimize on close')),
|
|
('win.execute-command-', _('Execute command')),
|
|
('app.browse-history', _('History')),
|
|
('win.disconnect-', _('Disconnect')),
|
|
]
|
|
|
|
def build_menu(preset):
|
|
menu = Gio.Menu()
|
|
for item in preset:
|
|
if isinstance(item[1], str):
|
|
action_name, label = item
|
|
if action_name == 'app.browse-history':
|
|
menuitem = Gio.MenuItem.new(label, action_name)
|
|
dict_ = {'account': GLib.Variant('s', account),
|
|
'jid': GLib.Variant('s', jid)}
|
|
variant_dict = GLib.Variant('a{sv}', dict_)
|
|
menuitem.set_action_and_target_value(action_name,
|
|
variant_dict)
|
|
menu.append_item(menuitem)
|
|
else:
|
|
menu.append(label, action_name + control_id)
|
|
else:
|
|
label, sub_menu = item
|
|
if not sub_menu:
|
|
# Sync threshold menu
|
|
submenu = build_sync_menu()
|
|
else:
|
|
# This is a submenu
|
|
submenu = build_menu(sub_menu)
|
|
menu.append_submenu(label, submenu)
|
|
return menu
|
|
|
|
def build_sync_menu():
|
|
menu = Gio.Menu()
|
|
days = app.config.get('threshold_options').split(',')
|
|
days = [int(day) for day in days]
|
|
action_name = 'win.choose-sync-%s::' % control_id
|
|
for day in days:
|
|
if day == 0:
|
|
label = _('No threshold')
|
|
else:
|
|
label = ngettext('%i day', '%i days', day, day, day)
|
|
menu.append(label, '%s%s' % (action_name, day))
|
|
return menu
|
|
|
|
return build_menu(groupchat_menu)
|
|
|
|
|
|
def get_bookmarks_menu(account, rebuild=False):
|
|
con = app.connections[account]
|
|
boomarks = con.get_module('Bookmarks').get_sorted_bookmarks(short_name=True)
|
|
|
|
menu = Gio.Menu()
|
|
|
|
# Build Join Groupchat
|
|
action = 'app.{}-join-groupchat'.format(account)
|
|
menuitem = Gio.MenuItem.new(_('Join Group Chat'), action)
|
|
variant = GLib.Variant('as', [account, ''])
|
|
menuitem.set_action_and_target_value(action, variant)
|
|
menu.append_item(menuitem)
|
|
|
|
# Build Bookmarks
|
|
section = Gio.Menu()
|
|
for jid, bookmark in boomarks.items():
|
|
name = bookmark['name']
|
|
|
|
action = 'app.{}-activate-bookmark'.format(account)
|
|
menuitem = Gio.MenuItem.new(name, action)
|
|
|
|
# Create Variant Dict
|
|
dict_ = {'account': GLib.Variant('s', account),
|
|
'jid': GLib.Variant('s', jid)}
|
|
if bookmark['nick']:
|
|
dict_['nick'] = GLib.Variant('s', bookmark['nick'])
|
|
if bookmark['password']:
|
|
dict_['password'] = GLib.Variant('s', bookmark['password'])
|
|
variant_dict = GLib.Variant('a{sv}', dict_)
|
|
|
|
menuitem.set_action_and_target_value(action, variant_dict)
|
|
section.append_item(menuitem)
|
|
menu.append_section(None, section)
|
|
if not rebuild:
|
|
get_action(account + '-activate-bookmark').set_enabled(True)
|
|
|
|
return menu
|
|
|
|
|
|
def get_account_menu(account):
|
|
'''
|
|
[(action, label/sub_menu)]
|
|
action: string
|
|
label: string
|
|
sub menu: list
|
|
'''
|
|
account_menu = [
|
|
('-add-contact', _('Add Contact…')),
|
|
('-join-groupchat', _('Join Group Chat')),
|
|
('-profile', _('Profile')),
|
|
('-services', _('Discover Services')),
|
|
('-start-single-chat', _('Send Single Message…')),
|
|
(_('Advanced'), [
|
|
('-archive', _('Archiving Preferences')),
|
|
('-sync-history', _('Synchronise History')),
|
|
('-privacylists', _('Privacy Lists')),
|
|
('-server-info', _('Server Info')),
|
|
('-xml-console', _('XML Console'))
|
|
]),
|
|
(_('Admin'), [
|
|
('-send-server-message', _('Send Server Message…')),
|
|
('-set-motd', _('Set MOTD…')),
|
|
('-update-motd', _('Update MOTD…')),
|
|
('-delete-motd', _('Delete MOTD…'))
|
|
]),
|
|
]
|
|
|
|
zeroconf_menu = [
|
|
('-xml-console', _('XML Console')),
|
|
]
|
|
|
|
def build_menu(preset):
|
|
menu = Gio.Menu()
|
|
for item in preset:
|
|
if isinstance(item[1], str):
|
|
action, label = item
|
|
if action == '-join-groupchat':
|
|
bookmark_menu = get_bookmarks_menu(account, True)
|
|
if bookmark_menu:
|
|
menu.append_submenu(label, bookmark_menu)
|
|
continue
|
|
action = 'app.{}{}'.format(account, action)
|
|
menuitem = Gio.MenuItem.new(label, action)
|
|
if 'add-contact' in action:
|
|
variant = GLib.Variant('as', [account, ''])
|
|
else:
|
|
variant = GLib.Variant('s', account)
|
|
menuitem.set_action_and_target_value(action, variant)
|
|
menu.append_item(menuitem)
|
|
else:
|
|
label, sub_menu = item
|
|
# This is a submenu
|
|
submenu = build_menu(sub_menu)
|
|
menu.append_submenu(label, submenu)
|
|
return menu
|
|
|
|
if account == 'Local':
|
|
return build_menu(zeroconf_menu)
|
|
return build_menu(account_menu)
|
|
|
|
|
|
def build_accounts_menu():
|
|
menubar = app.app.get_menubar()
|
|
# Accounts Submenu
|
|
menu_position = 1
|
|
if app.prefers_app_menu():
|
|
menu_position = 0
|
|
|
|
acc_menu = menubar.get_item_link(menu_position, 'submenu')
|
|
acc_menu.remove_all()
|
|
accounts_list = sorted(app.contacts.get_accounts())
|
|
if not accounts_list:
|
|
no_accounts = _('No Accounts available')
|
|
acc_menu.append_item(Gio.MenuItem.new(no_accounts, None))
|
|
return
|
|
if len(accounts_list) > 1:
|
|
for acc in accounts_list:
|
|
label = escape_mnemonic(app.get_account_label(acc))
|
|
acc_menu.append_submenu(
|
|
label, get_account_menu(acc))
|
|
else:
|
|
acc_menu = get_account_menu(accounts_list[0])
|
|
menubar.remove(menu_position)
|
|
menubar.insert_submenu(menu_position, _('Accounts'), acc_menu)
|
|
|
|
|
|
def build_bookmark_menu(account):
|
|
menubar = app.app.get_menubar()
|
|
bookmark_menu = get_bookmarks_menu(account)
|
|
if not bookmark_menu:
|
|
return
|
|
|
|
menu_position = 1
|
|
if app.prefers_app_menu():
|
|
menu_position = 0
|
|
|
|
# Accounts Submenu
|
|
acc_menu = menubar.get_item_link(menu_position, 'submenu')
|
|
|
|
# We have more than one Account active
|
|
if acc_menu.get_item_link(0, 'submenu'):
|
|
for i in range(acc_menu.get_n_items()):
|
|
label = acc_menu.get_item_attribute_value(i, 'label')
|
|
account_label = escape_mnemonic(
|
|
app.config.get_per('accounts', account, 'account_label'))
|
|
if label.get_string() in (account_label, account):
|
|
menu = acc_menu.get_item_link(i, 'submenu')
|
|
else:
|
|
# We have only one Account active
|
|
menu = acc_menu
|
|
label = menu.get_item_attribute_value(1, 'label').get_string()
|
|
menu.remove(1)
|
|
menu.insert_submenu(1, label, bookmark_menu)
|
|
|
|
|
|
def get_encryption_menu(control_id, type_id, zeroconf=False):
|
|
menu = Gio.Menu()
|
|
menu.append(
|
|
'Disabled', 'win.set-encryption-{}::{}'.format(control_id, 'disabled'))
|
|
for name, plugin in app.plugin_manager.encryption_plugins.items():
|
|
if type_id == 'gc':
|
|
if not hasattr(plugin, 'allow_groupchat'):
|
|
continue
|
|
if type_id == 'pm':
|
|
if not hasattr(plugin, 'allow_privatechat'):
|
|
continue
|
|
if zeroconf:
|
|
if not hasattr(plugin, 'allow_zeroconf'):
|
|
continue
|
|
menu_action = 'win.set-encryption-{}::{}'.format(
|
|
control_id, name)
|
|
menu.append(name, menu_action)
|
|
if menu.get_n_items() == 1:
|
|
return None
|
|
return menu
|
|
|
|
|
|
def get_conv_context_menu(account, kind, text):
|
|
if kind == 'xmpp':
|
|
if '?join' in text:
|
|
context_menu = [
|
|
('copy-link', _('Copy JID')),
|
|
('-join-groupchat', _('Join Groupchat')),
|
|
]
|
|
else:
|
|
context_menu = [
|
|
('copy-link', _('Copy JID')),
|
|
('-start-chat', _('Start Chat')),
|
|
('-add-contact', _('Add to Roster…')),
|
|
]
|
|
|
|
elif kind == 'url':
|
|
context_menu = [
|
|
('copy-link', _('Copy Link Location')),
|
|
('open-link', _('Open Link in Browser')),
|
|
]
|
|
|
|
elif kind == 'mail':
|
|
context_menu = [
|
|
('copy-link', _('Copy Email Address')),
|
|
('open-link', _('Open Email Composer')),
|
|
]
|
|
|
|
elif kind == 'sth_at_sth':
|
|
context_menu = [
|
|
('copy-link', _('Copy JID/Email')),
|
|
('open-link', _('Open Email Composer')),
|
|
('-start-chat', _('Start Chat')),
|
|
('-join-groupchat', _('Join Groupchat')),
|
|
('-add-contact', _('Add to Roster…')),
|
|
]
|
|
else:
|
|
return
|
|
|
|
menu = Gtk.Menu()
|
|
for item in context_menu:
|
|
action, label = item
|
|
menuitem = Gtk.MenuItem()
|
|
menuitem.set_label(label)
|
|
|
|
if action.startswith('-'):
|
|
action = 'app.%s%s' % (account, action)
|
|
else:
|
|
action = 'app.%s' % action
|
|
menuitem.set_action_name(action)
|
|
|
|
if 'join-groupchat' in action:
|
|
text = text.replace('xmpp:', '')
|
|
text = text.split('?')[0]
|
|
|
|
if 'add-contact' in action:
|
|
text = text.replace('xmpp:', '')
|
|
text = text.split('?')[0]
|
|
|
|
if action == 'app.open-link':
|
|
value = GLib.Variant.new_strv([kind, text])
|
|
elif action == 'app.copy-link':
|
|
value = GLib.Variant.new_string(text)
|
|
else:
|
|
value = GLib.Variant.new_strv([account, text])
|
|
menuitem.set_action_target_value(value)
|
|
menuitem.show()
|
|
menu.append(menuitem)
|
|
return menu
|
|
|
|
|
|
def escape_mnemonic(label):
|
|
if label is None:
|
|
return
|
|
# Underscore inside a label means the next letter is a keyboard
|
|
# shortcut. To show an underscore we have to use double underscore
|
|
return label.replace('_', '__')
|