Commit 6f845266 authored by Philipp Hörist's avatar Philipp Hörist

Port application menu to GMenu

parent eec1a69e
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<object class="GtkMenu" id="advanced_menuitem_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="xml_console_menuitem">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show _XML Console</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="archiving_preferences_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Edit Archi_ving Preferences</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="privacy_lists_menuitem">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Edit _Privacy Lists...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separator8">
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="administrator_menuitem">
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Administrator</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="administrator_menuitem_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="send_server_message_menuitem">
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Sends a message to users currently connected to this server</property>
<property name="label" translatable="yes">_Send Server Message...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separator9">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="set_motd_menuitem">
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Sets Message of the Day</property>
<property name="label" translatable="yes">Set MOTD...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="update_motd_menuitem">
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Updates Message of the Day</property>
<property name="label" translatable="yes">Update MOTD...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="delete_motd_menuitem">
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Deletes Message of the Day</property>
<property name="label" translatable="yes">Delete MOTD</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="menubar">
<submenu>
<attribute name="label" translatable="yes">Accounts</attribute>
</submenu>
<submenu>
<attribute name="label" translatable="yes">View</attribute>
<section>
<item>
<attribute name="label" translatable="yes">Show Roster</attribute>
<attribute name="action">win.show-roster</attribute>
<attribute name="accel">&lt;Primary&gt;R</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Show Offline Contacts</attribute>
<attribute name="action">win.show-offline</attribute>
<attribute name="accel">&lt;Primary&gt;O</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Show Active Contacts</attribute>
<attribute name="action">win.show-active</attribute>
<attribute name="accel">&lt;Primary&gt;Y</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Show Transports</attribute>
<attribute name="action">win.show-transports</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">File Transfer</attribute>
<attribute name="action">app.file-transfer</attribute>
<attribute name="accel">&lt;Primary&gt;T</attribute>
</item>
<item>
<attribute name="label" translatable="yes">History</attribute>
<attribute name="action">app.history</attribute>
</item>
</section>
</submenu>
<submenu>
<attribute name="label" translatable="yes">Help</attribute>
<section>
<item>
<attribute name="label" translatable="yes">Contents</attribute>
<attribute name="action">app.content</attribute>
</item>
<item>
<attribute name="label" translatable="yes">FAQ</attribute>
<attribute name="action">app.faq</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Keyboard Shortcuts</attribute>
<attribute name="action">app.shortcuts</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Features</attribute>
<attribute name="action">app.features</attribute>
</item>
<item>
<attribute name="label" translatable="yes">About</attribute>
<attribute name="action">app.about</attribute>
</item>
</section>
</submenu>
</menu>
<menu id="appmenu">
<section>
<item>
<attribute name="label" translatable="yes">Accounts</attribute>
<attribute name="action">app.accounts</attribute>
<attribute name="accel">&lt;Primary&gt;&lt;Shift&gt;A</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Bookmarks</attribute>
<attribute name="action">app.bookmarks</attribute>
<attribute name="accel">&lt;Primary&gt;B</attribute>
</item>
<item>
<attribute name="label" translatable="yes">History Manager</attribute>
<attribute name="action">app.history-manager</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Plugins</attribute>
<attribute name="action">app.plugins</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Preferences</attribute>
<attribute name="action">app.preferences</attribute>
<attribute name="accel">&lt;Primary&gt;P</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Quit</attribute>
<attribute name="action">app.quit</attribute>
<attribute name="accel">&lt;Primary&gt;Q</attribute>
</item>
</section>
</menu>
</interface>
\ No newline at end of file
This diff is collapsed.
# -*- coding: utf-8 -*-
## src/app_actions.py
##
## Copyright (C) 2017 Philipp Hörist <philipp AT hoerist.com>
##
## 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 common import helpers
from common import gajim
from common.exceptions import GajimGeneralException
from gi.repository import Gtk
import sys
import os
import config
import dialogs
import features_window
import shortcuts_window
import plugins.gui
import history_window
import disco
class AppActions():
''' Action Callbacks '''
def __init__(self, app: Gtk.Application):
self.application = app
# Application Menu Actions
def on_preferences(self, action, param):
if 'preferences' in gajim.interface.instances:
gajim.interface.instances['preferences'].window.present()
else:
gajim.interface.instances['preferences'] = \
config.PreferencesWindow()
def on_plugins(self, action, param):
if 'plugins' in gajim.interface.instances:
gajim.interface.instances['plugins'].window.present()
else:
gajim.interface.instances['plugins'] = plugins.gui.PluginsWindow()
def on_accounts(self, action, param):
if 'accounts' in gajim.interface.instances:
gajim.interface.instances['accounts'].window.present()
else:
gajim.interface.instances['accounts'] = config.AccountsWindow()
def on_history_manager(self, action, param):
if os.name == 'nt':
if os.path.exists('history_manager.exe'):
# user is running frozen application
helpers.exec_command('history_manager.exe')
else:
# user is running from source
helpers.exec_command('%s history_manager.py' % sys.executable)
else:
# Unix user
helpers.exec_command('%s history_manager.py' % sys.executable)
def on_manage_bookmarks(self, action, param):
config.ManageBookmarksWindow()
def on_quit(self, action, param):
gajim.interface.roster.on_quit_request()
# Accounts Actions
def on_profile(self, action, param):
gajim.interface.edit_own_details(param.get_string())
def on_activate_bookmark(self, action, param):
dict_ = param.unpack()
account, jid, nick, password = \
dict_['account'], dict_['jid'], None, None
if 'nick' in dict_:
nick = dict_['nick']
if 'password' in dict_:
password = dict_['password']
gajim.interface.join_gc_room(account, jid, nick, password)
def on_send_server_message(self, action, param):
account = param.get_string()
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/online'
dialogs.SingleMessageWindow(account, server, 'send')
def on_service_disco(self, action, param):
account = param.get_string()
server_jid = gajim.config.get_per('accounts', account, 'hostname')
if server_jid in gajim.interface.instances[account]['disco']:
gajim.interface.instances[account]['disco'][server_jid].\
window.present()
else:
try:
# Object will add itself to the window dict
disco.ServiceDiscoveryWindow(account, address_entry=True)
except GajimGeneralException:
pass
def on_join_gc(self, action, param):
account = param.get_string()
invisible_show = gajim.SHOW_LIST.index('invisible')
if gajim.connections[account].connected == invisible_show:
dialogs.ErrorDialog(_(
'You cannot join a group chat while you are invisible'))
return
if 'join_gc' in gajim.interface.instances[account]:
gajim.interface.instances[account]['join_gc'].window.present()
else:
try:
gajim.interface.instances[account]['join_gc'] = \
dialogs.JoinGroupchatWindow(account)
except GajimGeneralException:
pass
def on_add_contact(self, action, param):
dialogs.AddNewContactWindow(param.get_string())
def on_new_chat(self, action, param):
dialogs.NewChatDialog(param.get_string())
def on_single_message(self, action, param):
dialogs.SingleMessageWindow(param.get_string(), action='send')
# Advanced Actions
def on_archiving_preferences(self, action, param):
account = param.get_string()
if 'archiving_preferences' in gajim.interface.instances[account]:
gajim.interface.instances[account]['archiving_preferences'].window.\
present()
else:
if gajim.connections[account].archiving_313_supported:
gajim.interface.instances[account]['archiving_preferences'] = \
dialogs.Archiving313PreferencesWindow(account)
else:
gajim.interface.instances[account]['archiving_preferences'] = \
dialogs.ArchivingPreferencesWindow(account)
def on_privacy_lists(self, action, param):
account = param.get_string()
if 'privacy_lists' in gajim.interface.instances[account]:
gajim.interface.instances[account]['privacy_lists'].window.present()
else:
gajim.interface.instances[account]['privacy_lists'] = \
dialogs.PrivacyListsWindow(account)
def on_xml_console(self, action, param):
account = param.get_string()
if 'xml_console' in gajim.interface.instances[account]:
gajim.interface.instances[account]['xml_console'].window.present()
else:
gajim.interface.instances[account]['xml_console'] = \
dialogs.XMLConsoleWindow(account)
# Admin Actions
def on_set_motd(self, action, param):
account = param.get_string()
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd'
dialogs.SingleMessageWindow(account, server, 'send')
def on_update_motd(self, action, param):
account = param.get_string()
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd/update'
dialogs.SingleMessageWindow(account, server, 'send')
def on_delete_motd(self, action, param):
account = param.get_string()
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd/delete'
gajim.connections[account].send_motd(server)
# Help Actions
def on_contents(self, action, param):
helpers.launch_browser_mailer(
'url', 'https://dev.gajim.org/gajim/gajim/wikis')
def on_faq(self, action, param):
helpers.launch_browser_mailer(
'url', 'https://dev.gajim.org/gajim/gajim/wikis/help/gajimfaq')
def on_keyboard_shortcuts(self, action, param):
shortcuts_window.show(self.application.get_active_window())
def on_features(self, action, param):
features_window.FeaturesWindow()
def on_about(self, action, param):
dialogs.AboutDialog()
# View Actions
def on_file_transfers(self, action, param):
if gajim.interface.instances['file_transfers']. \
window.get_property('visible'):
gajim.interface.instances['file_transfers'].window.present()
else:
gajim.interface.instances['file_transfers'].window.show_all()
def on_history(self, action, param):
if 'logs' in gajim.interface.instances:
gajim.interface.instances['logs'].window.present()
else:
gajim.interface.instances['logs'] = history_window.\
HistoryWindow()
......@@ -62,6 +62,8 @@ from common import exceptions
from common import check_X509
from common.connection_handlers import *
from gtkgui_helpers import get_action
if gajim.HAVE_PYOPENSSL:
import OpenSSL.crypto
......@@ -2033,6 +2035,7 @@ class Connection(CommonConnection, ConnectionHandlers):
break
if nbxmpp.NS_VCARD in obj.features:
self.vcard_supported = True
get_action(self.name + '-profile').set_enabled(True)
if nbxmpp.NS_PUBSUB in obj.features:
self.pubsub_supported = True
if nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS in obj.features:
......@@ -2044,6 +2047,7 @@ class Connection(CommonConnection, ConnectionHandlers):
if nbxmpp.NS_MAM in obj.features:
self.archiving_supported = True
self.archiving_313_supported = True
get_action(self.name + '-archive').set_enabled(True)
if nbxmpp.NS_ARCHIVE in obj.features:
self.archiving_supported = True
self.archiving_136_supported = True
......@@ -2069,6 +2073,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
if nbxmpp.NS_PRIVACY in obj.features:
self.privacy_rules_supported = True
get_action(self.name + '-privacylists').set_enabled(True)
if nbxmpp.NS_BYTESTREAM in obj.features and \
gajim.config.get_per('accounts', self.name, 'use_ft_proxies'):
......
......@@ -43,6 +43,7 @@ config = config.Config()
version = config.get('version')
connections = {} # 'account name': 'account (connection.Connection) instance'
ipython_window = None
app = None # Gtk.Application
ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
nec = None # Network Events Controller
......
......@@ -48,6 +48,7 @@ import message_control
from chat_control_base import ChatControlBase
import dataforms_widget
import profile_window
import gui_menu_builder
try:
import gtkspell
......@@ -2566,7 +2567,8 @@ class AccountsWindow:
else:
gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.app.remove_account_actions(account)
gui_menu_builder.build_accounts_menu()
def _enable_account(self, account):
if account == gajim.ZEROCONF_ACC_NAME:
......@@ -2612,7 +2614,8 @@ class AccountsWindow:
else:
gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.app.add_account_actions(account)
gui_menu_builder.build_accounts_menu()
def on_enable_zeroconf_checkbutton2_toggled(self, widget):
# don't do anything if there is an account with the local name but is a
......@@ -3154,7 +3157,8 @@ class RemoveAccountWindow:
else:
gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.app.remove_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
if 'accounts' in gajim.interface.instances:
gajim.interface.instances['accounts'].init_accounts()
gajim.interface.instances['accounts'].init_account()
......@@ -3356,7 +3360,7 @@ class ManageBookmarksWindow:
gajim.connections[acct].bookmarks.append(bmdict)
gajim.connections[acct].store_bookmarks()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gui_menu_builder.build_bookmark_menu(acct)
self.window.destroy()
def on_cancel_button_clicked(self, widget):
......@@ -4091,7 +4095,8 @@ class AccountCreationWizardWindow:
else:
gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gajim.app.add_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
class ManagePEPServicesWindow:
def __init__(self, account):
......
......@@ -57,6 +57,7 @@ import gtkgui_helpers
import groups
import adhoc_commands
import search_window
import gui_menu_builder
from common import gajim
import nbxmpp
......@@ -1852,7 +1853,7 @@ class MucBrowser(AgentBrowser):
gajim.connections[self.account].bookmarks.append(bm)
gajim.connections[self.account].store_bookmarks()
gajim.interface.roster.set_actions_menu_needs_rebuild()
gui_menu_builder.build_bookmark_menu(self.account)
dialogs.InformationDialog(
_('Bookmark has been added successfully'),
......
......@@ -233,11 +233,23 @@ class GajimApplication(Gtk.Application):
print("Encodings: d:{}, fs:{}, p:{}".format(sys.getdefaultencoding(),
sys.getfilesystemencoding(), locale.getpreferredencoding()))
# Set Application Menu
gajim.app = self
path = os.path.join(configpaths.get('GUI'), 'application_menu.ui')
builder = Gtk.Builder()
builder.set_translation_domain(i18n.APP)
builder.add_from_file(path)
self.set_menubar(builder.get_object("menubar"))
self.set_app_menu(builder.get_object("appmenu"))
def do_activate(self):
Gtk.Application.do_activate(self)
from gui_interface import Interface
self.interface = Interface()
self.interface.run(self)
self.add_actions()
import gui_menu_builder
gui_menu_builder.build_accounts_menu()
def do_shutdown(self, *args):
Gtk.Application.do_shutdown(self)
......@@ -315,6 +327,91 @@ class GajimApplication(Gtk.Application):
sys.stderr = outerr
warnings.filterwarnings(action='ignore')
def add_actions(self):
''' Build Application Actions '''
from app_actions import AppActions
action = AppActions(self)
self.account_actions = [
('-start-single-chat', action.on_single_message, 'online', 's'),
('-start-chat', action.on_new_chat, 'online', 's'),
('-join-groupchat', action.on_join_gc, 'online', 's'),
('-add-contact', action.on_add_contact, 'online', 's'),
('-services', action.on_service_disco, 'online', 's'),
('-profile', action.on_profile, 'feature', 's'),
('-xml-console', action.on_xml_console, 'always', 's'),
('-archive', action.on_archiving_preferences, 'feature', 's'),
('-privacylists', action.on_privacy_lists, 'feature', 's'),
('-send-server-message',
action.on_send_server_message, 'online', 's'),
('-set-motd', action.on_set_motd, 'online', 's'),
('-update-motd', action.on_update_motd, 'online', 's'),
('-delete-motd', action.on_delete_motd, 'online', 's'),
('-activate-bookmark',
action.on_activate_bookmark, 'online', 'a{sv}')
]
self.general_actions = [
('quit', action.on_quit),
('accounts', action.on_accounts),
('bookmarks', action.on_manage_bookmarks),
('history-manager', action.on_history_manager),
('preferences', action.on_preferences),
('plugins', action.on_plugins),
('file-transfer', action.on_file_transfers),
('history', action.on_history),
('shortcuts', action.on_keyboard_shortcuts),
('features', action.on_features),
('content', action.on_contents),
('about', action.on_about),
('faq', action.on_faq)
]
for action in self.general_actions:
action_name, func = action
act = Gio.SimpleAction.new(action_name, None)
act.connect("activate", func)
self.add_action(act)
from common import gajim
accounts_list = sorted(gajim.contacts.get_accounts())
if not accounts_list:
return
if len(accounts_list) > 1:
for acc in accounts_list:
self.add_account_actions(acc)
else:
self.add_account_actions(accounts_list[0])
def add_account_actions(self, account):
for action in self.account_actions:
action_name, func, state, type_ = action
action_name = account + action_name
if self.lookup_action(action_name):
# We already added this action
continue
act = Gio.SimpleAction.new(
action_name, GLib.VariantType.new(type_))
act.connect("activate", func)
if state != 'always':
act.set_enabled(False)
self.add_action(act)
def remove_account_actions(self, account):
for action in self.account_actions:
action_name = account + action[0]
self.remove_action(action_name)
def set_account_actions_state(self, account, new_state=False):
for action in self.account_actions:
action_name, _, state, _ = action
if not new_state and state in ('online', 'feature'):
# We go offline
self.lookup_action(account + action_name).set_enabled(False)
elif new_state and state == 'online':
# We go online
self.lookup_action(account + action_name).set_enabled(True)
app = GajimApplication()
app.run(sys.argv)
......@@ -1085,3 +1085,6 @@ def __label_size_allocate(widget, allocation):
if lh_old != lh:
widget.set_size_request (-1, lh / Pango.SCALE)
def get_action(action):
return gajim.app.lookup_action(action)
......@@ -56,7 +56,7 @@ if dbus_support.supported:
import dbus
import gtkgui_helpers
import gui_menu_builder
import dialogs
import notify
import message_control
......@@ -843,7 +843,7 @@ class Interface:
# We received a bookmark item from the server (JEP48)
# Auto join GC windows if neccessary
self.roster.set_actions_menu_needs_rebuild()
gui_menu_builder.build_bookmark_menu(obj.conn.name)
invisible_show = gajim.SHOW_LIST.index('invisible')
# do not autojoin if we are invisible
if obj.conn.connected == invisible_show:
......@@ -2661,7 +2661,7 @@ class Interface:
else:
gajim.connections[account].bookmarks.append(bm)
gajim.connections[account].store_bookmarks()
self.roster.set_actions_menu_needs_rebuild()
gui_menu_builder.build_bookmark_menu(account)
dialogs.InformationDialog(
_('Bookmark has been added successfully'),
_('You can manage your bookmarks via Actions menu in your roster.'))
......
......@@ -18,15 +18,17 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
from gi.repository import Gtk
from gi.repository import Gtk, Gio, GLib
import os
import gtkgui_helpers
import message_control
from common import gajim
from common import helpers
from common import i18n
from nbxmpp.protocol import NS_COMMANDS, NS_FILE, NS_MUC, NS_ESESSION
from nbxmpp.protocol import NS_JINGLE_FILE_TRANSFER, NS_CONFERENCE
from gtkgui_helpers import get_action
def build_resources_submenu(contacts, account, action, room_jid=None,
room_account=None, cap=None):
......@@ -636,3 +638,146 @@ def get_transport_menu(contact, account):
menu.show_all()
return menu
'''
Build dynamic Application Menus
'''
def get_bookmarks_menu(account, rebuild=False):
if not gajim.connections[account].bookmarks:
return None
menu = Gio.Menu()
# Build Join Groupchat
action = 'app.{}-join-groupchat'.format(account)
menuitem = Gio.MenuItem.new(_('Join Group Chat'), action)
variant = GLib.Variant('s', account)
menuitem.set_action_and_target_value(action, variant)
menu.append_item(menuitem)
# Build Bookmarks
section = Gio.Menu()
for bookmark in gajim.connections[account].bookmarks:
name = bookmark['name']
if not name:
# No name was given for this bookmark.
# Use the first part of JID instead...
name = bookmark['jid'].split("@")[0]