gajim-plural/gajim/gui_menu_builder.py

965 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')),
]),
(_('Chat Settings'), [
('win.print-join-left-', _('Show join/leave')),
('win.print-status-', _('Show status changes')),
('win.notify-on-message-', _('Notify on all messages')),
('win.minimize-', _('Minimize on close')),
]),
(_('Sync Threshold'), []),
('win.change-nick-', _('Change Nick')),
('win.bookmark-', _('Bookmark Room')),
('win.request-voice-', _('Request Voice')),
('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('-'):
text = text.replace('xmpp:', '')
text = text.split('?')[0]
action = 'app.%s%s' % (account, action)
else:
action = 'app.%s' % action
menuitem.set_action_name(action)
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('_', '__')