Port application menu to GMenu

This commit is contained in:
Philipp Hörist 2017-01-26 21:34:54 +01:00
parent eec1a69efd
commit 6f8452667d
14 changed files with 660 additions and 869 deletions

View File

@ -1,84 +0,0 @@
<?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>

View File

@ -0,0 +1,100 @@
<?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>

View File

@ -25,304 +25,7 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkMenuBar" id="menubar"> <placeholder/>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="actions_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Actions</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_actions_menuitem_activate" swapped="no"/>
<child type="submenu">
<object class="GtkMenu" id="actions_menu_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="new_chat_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Start Chat...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="send_single_message_menuitem">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Send Single _Message...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="join_gc_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Join _Group Chat</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separatormenuitem1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="add_new_contact_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Add _Contact...</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="service_disco_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Discover Services</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="advanced_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Advanced</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separator1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="quit_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">_Quit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_quit_request" swapped="no"/>
<accelerator key="Q" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="edit_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Edit</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_edit_menuitem_activate" swapped="no"/>
<child type="submenu">
<object class="GtkMenu" id="edit_menu_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="accounts_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Accounts</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_accounts_menuitem_activate" swapped="no"/>
<accelerator key="A" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="profile_avatar_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Profile, A_vatar</property>
<property name="use_underline">True</property>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separator2">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="preferences_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">_Preferences</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_preferences_menuitem_activate" swapped="no"/>
<accelerator key="P" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="plugins_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">P_lugins</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_plugins_menuitem_activate" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="view_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_View</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_view_menu_activate" swapped="no"/>
<child type="submenu">
<object class="GtkMenu" id="view_menu_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkCheckMenuItem" id="show_offline_contacts_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show _Offline Contacts</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_show_offline_contacts_menuitem_activate" swapped="no"/>
<accelerator key="O" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="show_only_active_contacts_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show Only _Active Contacts</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_show_only_active_contacts_menuitem_activate" swapped="no"/>
<accelerator key="Y" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="show_transports_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Show T_ransports</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_show_transports_menuitem_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="show_roster_menuitem">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Show _Roster</property>
<property name="use_underline">True</property>
<property name="active">True</property>
<signal name="toggled" handler="on_show_roster_menuitem_toggled" swapped="no"/>
<accelerator key="R" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="separator3">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="file_transfers_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">File _Transfers</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_file_transfers_menuitem_activate" swapped="no"/>
<accelerator key="T" signal="activate" modifiers="GDK_CONTROL_MASK"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="history_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">_History</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_history_menuitem_activate" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkMenuItem" id="help_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
<child type="submenu">
<object class="GtkMenu" id="help_menu_menu">
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="contents_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Help online</property>
<property name="label" translatable="yes">_Contents</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_contents_menuitem_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="faq_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Frequently Asked Questions (online)</property>
<property name="label" translatable="yes">_FAQ</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_faq_menuitem_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="keyboard_shortcuts_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Keyboard Shortcuts</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_keyboard_shortcuts_menuitem_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem" id="menuitem1">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="features_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="label" translatable="yes">Fea_tures</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_features_menuitem_activate" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="about_menuitem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">_About</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_about_menuitem_activate" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkPaned" id="roster_hpaned"> <object class="GtkPaned" id="roster_hpaned">

223
src/app_actions.py Normal file
View File

@ -0,0 +1,223 @@
# -*- 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()

View File

@ -62,6 +62,8 @@ from common import exceptions
from common import check_X509 from common import check_X509
from common.connection_handlers import * from common.connection_handlers import *
from gtkgui_helpers import get_action
if gajim.HAVE_PYOPENSSL: if gajim.HAVE_PYOPENSSL:
import OpenSSL.crypto import OpenSSL.crypto
@ -2033,6 +2035,7 @@ class Connection(CommonConnection, ConnectionHandlers):
break break
if nbxmpp.NS_VCARD in obj.features: if nbxmpp.NS_VCARD in obj.features:
self.vcard_supported = True self.vcard_supported = True
get_action(self.name + '-profile').set_enabled(True)
if nbxmpp.NS_PUBSUB in obj.features: if nbxmpp.NS_PUBSUB in obj.features:
self.pubsub_supported = True self.pubsub_supported = True
if nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS in obj.features: if nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS in obj.features:
@ -2044,6 +2047,7 @@ class Connection(CommonConnection, ConnectionHandlers):
if nbxmpp.NS_MAM in obj.features: if nbxmpp.NS_MAM in obj.features:
self.archiving_supported = True self.archiving_supported = True
self.archiving_313_supported = True self.archiving_313_supported = True
get_action(self.name + '-archive').set_enabled(True)
if nbxmpp.NS_ARCHIVE in obj.features: if nbxmpp.NS_ARCHIVE in obj.features:
self.archiving_supported = True self.archiving_supported = True
self.archiving_136_supported = True self.archiving_136_supported = True
@ -2069,6 +2073,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq) self.connection.send(iq)
if nbxmpp.NS_PRIVACY in obj.features: if nbxmpp.NS_PRIVACY in obj.features:
self.privacy_rules_supported = True self.privacy_rules_supported = True
get_action(self.name + '-privacylists').set_enabled(True)
if nbxmpp.NS_BYTESTREAM in obj.features and \ if nbxmpp.NS_BYTESTREAM in obj.features and \
gajim.config.get_per('accounts', self.name, 'use_ft_proxies'): gajim.config.get_per('accounts', self.name, 'use_ft_proxies'):

View File

@ -43,6 +43,7 @@ config = config.Config()
version = config.get('version') version = config.get('version')
connections = {} # 'account name': 'account (connection.Connection) instance' connections = {} # 'account name': 'account (connection.Connection) instance'
ipython_window = None ipython_window = None
app = None # Gtk.Application
ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
nec = None # Network Events Controller nec = None # Network Events Controller

View File

@ -48,6 +48,7 @@ import message_control
from chat_control_base import ChatControlBase from chat_control_base import ChatControlBase
import dataforms_widget import dataforms_widget
import profile_window import profile_window
import gui_menu_builder
try: try:
import gtkspell import gtkspell
@ -2566,7 +2567,8 @@ class AccountsWindow:
else: else:
gajim.interface.roster.regroup = False gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster() 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): def _enable_account(self, account):
if account == gajim.ZEROCONF_ACC_NAME: if account == gajim.ZEROCONF_ACC_NAME:
@ -2612,7 +2614,8 @@ class AccountsWindow:
else: else:
gajim.interface.roster.regroup = False gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster() 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): def on_enable_zeroconf_checkbutton2_toggled(self, widget):
# don't do anything if there is an account with the local name but is a # don't do anything if there is an account with the local name but is a
@ -3154,7 +3157,8 @@ class RemoveAccountWindow:
else: else:
gajim.interface.roster.regroup = False gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster() 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: if 'accounts' in gajim.interface.instances:
gajim.interface.instances['accounts'].init_accounts() gajim.interface.instances['accounts'].init_accounts()
gajim.interface.instances['accounts'].init_account() gajim.interface.instances['accounts'].init_account()
@ -3356,7 +3360,7 @@ class ManageBookmarksWindow:
gajim.connections[acct].bookmarks.append(bmdict) gajim.connections[acct].bookmarks.append(bmdict)
gajim.connections[acct].store_bookmarks() gajim.connections[acct].store_bookmarks()
gajim.interface.roster.set_actions_menu_needs_rebuild() gui_menu_builder.build_bookmark_menu(acct)
self.window.destroy() self.window.destroy()
def on_cancel_button_clicked(self, widget): def on_cancel_button_clicked(self, widget):
@ -4091,7 +4095,8 @@ class AccountCreationWizardWindow:
else: else:
gajim.interface.roster.regroup = False gajim.interface.roster.regroup = False
gajim.interface.roster.setup_and_draw_roster() 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: class ManagePEPServicesWindow:
def __init__(self, account): def __init__(self, account):

View File

@ -57,6 +57,7 @@ import gtkgui_helpers
import groups import groups
import adhoc_commands import adhoc_commands
import search_window import search_window
import gui_menu_builder
from common import gajim from common import gajim
import nbxmpp import nbxmpp
@ -1852,7 +1853,7 @@ class MucBrowser(AgentBrowser):
gajim.connections[self.account].bookmarks.append(bm) gajim.connections[self.account].bookmarks.append(bm)
gajim.connections[self.account].store_bookmarks() gajim.connections[self.account].store_bookmarks()
gajim.interface.roster.set_actions_menu_needs_rebuild() gui_menu_builder.build_bookmark_menu(self.account)
dialogs.InformationDialog( dialogs.InformationDialog(
_('Bookmark has been added successfully'), _('Bookmark has been added successfully'),

View File

@ -233,11 +233,23 @@ class GajimApplication(Gtk.Application):
print("Encodings: d:{}, fs:{}, p:{}".format(sys.getdefaultencoding(), print("Encodings: d:{}, fs:{}, p:{}".format(sys.getdefaultencoding(),
sys.getfilesystemencoding(), locale.getpreferredencoding())) 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): def do_activate(self):
Gtk.Application.do_activate(self) Gtk.Application.do_activate(self)
from gui_interface import Interface from gui_interface import Interface
self.interface = Interface() self.interface = Interface()
self.interface.run(self) self.interface.run(self)
self.add_actions()
import gui_menu_builder
gui_menu_builder.build_accounts_menu()
def do_shutdown(self, *args): def do_shutdown(self, *args):
Gtk.Application.do_shutdown(self) Gtk.Application.do_shutdown(self)
@ -315,6 +327,91 @@ class GajimApplication(Gtk.Application):
sys.stderr = outerr sys.stderr = outerr
warnings.filterwarnings(action='ignore') 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 = GajimApplication()
app.run(sys.argv) app.run(sys.argv)

View File

@ -1085,3 +1085,6 @@ def __label_size_allocate(widget, allocation):
if lh_old != lh: if lh_old != lh:
widget.set_size_request (-1, lh / Pango.SCALE) widget.set_size_request (-1, lh / Pango.SCALE)
def get_action(action):
return gajim.app.lookup_action(action)

View File

@ -56,7 +56,7 @@ if dbus_support.supported:
import dbus import dbus
import gtkgui_helpers import gtkgui_helpers
import gui_menu_builder
import dialogs import dialogs
import notify import notify
import message_control import message_control
@ -843,7 +843,7 @@ class Interface:
# We received a bookmark item from the server (JEP48) # We received a bookmark item from the server (JEP48)
# Auto join GC windows if neccessary # 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') invisible_show = gajim.SHOW_LIST.index('invisible')
# do not autojoin if we are invisible # do not autojoin if we are invisible
if obj.conn.connected == invisible_show: if obj.conn.connected == invisible_show:
@ -2661,7 +2661,7 @@ class Interface:
else: else:
gajim.connections[account].bookmarks.append(bm) gajim.connections[account].bookmarks.append(bm)
gajim.connections[account].store_bookmarks() gajim.connections[account].store_bookmarks()
self.roster.set_actions_menu_needs_rebuild() gui_menu_builder.build_bookmark_menu(account)
dialogs.InformationDialog( dialogs.InformationDialog(
_('Bookmark has been added successfully'), _('Bookmark has been added successfully'),
_('You can manage your bookmarks via Actions menu in your roster.')) _('You can manage your bookmarks via Actions menu in your roster.'))

View File

@ -18,15 +18,17 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## 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 os
import gtkgui_helpers import gtkgui_helpers
import message_control import message_control
from common import gajim from common import gajim
from common import helpers 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_COMMANDS, NS_FILE, NS_MUC, NS_ESESSION
from nbxmpp.protocol import NS_JINGLE_FILE_TRANSFER, NS_CONFERENCE 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, def build_resources_submenu(contacts, account, action, room_jid=None,
room_account=None, cap=None): room_account=None, cap=None):
@ -636,3 +638,146 @@ def get_transport_menu(contact, account):
menu.show_all() menu.show_all()
return menu 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]
# Shorten long names
name = (name[:42] + '..') if len(name) > 42 else 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', bookmark['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-chat', _('Start Chat...')),
('-start-single-chat', _('Send Single Message...')),
('Advanced', [
('-archive', _('Archiving Preferences')),
('-privacylists', _('Privacy Lists')),
('-xml-console', _('XML Console'))
]),
('Admin', [
('-send-server-message', _('Send Server Message...')),
('-set-motd', _('Set MOTD...')),
('-update-motd', _('Update MOTD...')),
('-delete-motd', _('Delete MOTD...'))
]),
]
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)
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
return build_menu(account_menu)
def build_accounts_menu():
menubar = gajim.app.get_menubar()
# Accounts Submenu
acc_menu = menubar.get_item_link(0, 'submenu')
acc_menu.remove_all()
accounts_list = sorted(gajim.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:
acc_menu.append_submenu(
acc, get_account_menu(acc))
else:
acc_menu = get_account_menu(accounts_list[0])
menubar.remove(0)
menubar.insert_submenu(0, 'Accounts', acc_menu)
def build_bookmark_menu(account):
menubar = gajim.app.get_menubar()
bookmark_menu = get_bookmarks_menu(account)
if not bookmark_menu:
return
# Accounts Submenu
acc_menu = menubar.get_item_link(0, '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')
if label.get_string() == 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)

View File

@ -41,6 +41,7 @@ import dialogs
from chat_control_base import ChatControlBase from chat_control_base import ChatControlBase
from common import gajim from common import gajim
from gtkgui_helpers import get_action
#################### ####################
@ -97,8 +98,7 @@ class MessageWindow(object):
else: else:
self.parent_paned.add(self.notebook) self.parent_paned.add(self.notebook)
self.parent_paned.pack2(self.notebook, resize=True, shrink=True) self.parent_paned.pack2(self.notebook, resize=True, shrink=True)
gajim.interface.roster.xml.get_object('show_roster_menuitem').\ get_action('show-roster').set_enabled(True)
set_sensitive(True)
orig_window.destroy() orig_window.destroy()
del orig_window del orig_window
@ -626,8 +626,7 @@ class MessageWindow(object):
# Don't close parent window, just remove the child # Don't close parent window, just remove the child
child = self.parent_paned.get_child2() child = self.parent_paned.get_child2()
self.parent_paned.remove(child) self.parent_paned.remove(child)
gajim.interface.roster.xml.get_object('show_roster_menuitem').\ get_action('show-roster').set_enabled(False)
set_sensitive(False)
else: else:
self.window.destroy() self.window.destroy()
return # don't show_title, we are dead return # don't show_title, we are dead
@ -1273,8 +1272,7 @@ class MessageWindowMgr(GObject.GObject):
# Don't close parent window, just remove the child # Don't close parent window, just remove the child
child = w.parent_paned.get_child2() child = w.parent_paned.get_child2()
w.parent_paned.remove(child) w.parent_paned.remove(child)
gajim.interface.roster.xml.get_object('show_roster_menuitem').\ get_action('show-roster').set_enabled(False)
set_sensitive(False)
gtkgui_helpers.resize_window(w.window, gtkgui_helpers.resize_window(w.window,
gajim.config.get('roster_width'), gajim.config.get('roster_width'),
gajim.config.get('roster_height')) gajim.config.get('roster_height'))

View File

@ -38,8 +38,8 @@ from gi.repository import GdkPixbuf
from gi.repository import Pango from gi.repository import Pango
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
from gi.repository import Gio
import os import os
import sys
import time import time
import locale import locale
@ -57,10 +57,6 @@ import cell_renderer_image
import tooltips import tooltips
import message_control import message_control
import adhoc_commands import adhoc_commands
import features_window
import shortcuts_window
import plugins
import plugins.gui
from common import gajim from common import gajim
from common import helpers from common import helpers
@ -69,11 +65,10 @@ from common import i18n
from common import location_listener from common import location_listener
from common import ged from common import ged
from common import dbus_support from common import dbus_support
from message_window import MessageWindowMgr from message_window import MessageWindowMgr
from nbxmpp.protocol import NS_FILE, NS_ROSTERX, NS_CONFERENCE from nbxmpp.protocol import NS_FILE, NS_ROSTERX, NS_CONFERENCE
class Column(IntEnum): class Column(IntEnum):
IMG = 0 # image to show state (online, new message etc) IMG = 0 # image to show state (online, new message etc)
NAME = 1 # cellrenderer text that holds contact nickame NAME = 1 # cellrenderer text that holds contact nickame
@ -2297,7 +2292,6 @@ class RosterWindow:
for contact in [c for c in lcontact if ( for contact in [c for c in lcontact if (
(c.show != 'offline' or c.is_transport()) and not ctrl)]: (c.show != 'offline' or c.is_transport()) and not ctrl)]:
self.chg_contact_status(contact, 'offline', '', account) self.chg_contact_status(contact, 'offline', '', account)
self.set_actions_menu_needs_rebuild()
self.update_status_combobox() self.update_status_combobox()
def get_status_message(self, show, on_response, show_pep=True, def get_status_message(self, show, on_response, show_pep=True,
@ -2676,6 +2670,7 @@ class RosterWindow:
# sensitivity for this menuitem # sensitivity for this menuitem
if gajim.get_number_of_connected_accounts() == 0: if gajim.get_number_of_connected_accounts() == 0:
model[self.status_message_menuitem_iter][3] = False model[self.status_message_menuitem_iter][3] = False
self.application.set_account_actions_state(obj.conn.name)
else: else:
# sensitivity for this menuitem # sensitivity for this menuitem
model[self.status_message_menuitem_iter][3] = True model[self.status_message_menuitem_iter][3] = True
@ -2714,7 +2709,7 @@ class RosterWindow:
self.redraw_metacontacts(obj.conn.name) self.redraw_metacontacts(obj.conn.name)
def _nec_signed_in(self, obj): def _nec_signed_in(self, obj):
self.set_actions_menu_needs_rebuild() self.application.set_account_actions_state(obj.conn.name, True)
self.draw_account(obj.conn.name) self.draw_account(obj.conn.name)
def _nec_decrypted_message_received(self, obj): def _nec_decrypted_message_received(self, obj):
@ -2776,74 +2771,10 @@ class RosterWindow:
### FIXME: order callbacks in itself... ### FIXME: order callbacks in itself...
################################################################################ ################################################################################
def on_actions_menuitem_activate(self, widget):
self.make_menu()
def on_edit_menuitem_activate(self, widget):
"""
Need to call make_menu to build profile, avatar item
"""
self.make_menu()
def on_bookmark_menuitem_activate(self, widget, account, bookmark): def on_bookmark_menuitem_activate(self, widget, account, bookmark):
gajim.interface.join_gc_room(account, bookmark['jid'], bookmark['nick'], gajim.interface.join_gc_room(account, bookmark['jid'], bookmark['nick'],
bookmark['password']) bookmark['password'])
def on_send_server_message_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/online'
dialogs.SingleMessageWindow(account, server, 'send')
def on_xml_console_menuitem_activate(self, widget, account):
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)
def on_archiving_preferences_menuitem_activate(self, widget, account):
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_menuitem_activate(self, widget, account):
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_set_motd_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd'
dialogs.SingleMessageWindow(account, server, 'send')
def on_update_motd_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd/update'
dialogs.SingleMessageWindow(account, server, 'send')
def on_delete_motd_menuitem_activate(self, widget, account):
server = gajim.config.get_per('accounts', account, 'hostname')
server += '/announce/motd/delete'
gajim.connections[account].send_motd(server)
def on_history_manager_menuitem_activate(self, widget):
if os.name == 'nt':
if os.path.exists('history_manager.exe'): # user is running stable
helpers.exec_command('history_manager.exe')
else: # user is running svn
helpers.exec_command('%s history_manager.py' % sys.executable)
else: # Unix user
helpers.exec_command('%s history_manager.py' % sys.executable)
def on_info(self, widget, contact, account): def on_info(self, widget, contact, account):
""" """
Call vcard_information_window class to display contact's information Call vcard_information_window class to display contact's information
@ -3765,19 +3696,6 @@ class RosterWindow:
self.get_status_message(status, on_continue) self.get_status_message(status, on_continue)
def on_preferences_menuitem_activate(self, widget):
if 'preferences' in gajim.interface.instances:
gajim.interface.instances['preferences'].window.present()
else:
gajim.interface.instances['preferences'] = config.PreferencesWindow(
)
def on_plugins_menuitem_activate(self, widget):
if 'plugins' in gajim.interface.instances:
gajim.interface.instances['plugins'].window.present()
else:
gajim.interface.instances['plugins'] = plugins.gui.PluginsWindow()
def on_publish_tune_toggled(self, widget, account): def on_publish_tune_toggled(self, widget, account):
active = widget.get_active() active = widget.get_active()
gajim.config.set_per('accounts', account, 'publish_tune', active) gajim.config.set_per('accounts', account, 'publish_tune', active)
@ -3841,52 +3759,14 @@ class RosterWindow:
def on_new_chat_menuitem_activate(self, widget, account): def on_new_chat_menuitem_activate(self, widget, account):
dialogs.NewChatDialog(account) dialogs.NewChatDialog(account)
def on_contents_menuitem_activate(self, widget): def on_show_transports_action(self, action, param):
helpers.launch_browser_mailer('url', 'http://trac.gajim.org/wiki') gajim.config.set('show_transports_group', param.get_boolean())
action.set_state(param)
def on_faq_menuitem_activate(self, widget):
helpers.launch_browser_mailer('url',
'http://trac.gajim.org/wiki/GajimFaq')
def on_keyboard_shortcuts_menuitem_activate(self, widget):
shortcuts_window.show(self.window)
def on_features_menuitem_activate(self, widget):
features_window.FeaturesWindow()
def on_about_menuitem_activate(self, widget):
dialogs.AboutDialog()
def on_accounts_menuitem_activate(self, widget):
if 'accounts' in gajim.interface.instances:
gajim.interface.instances['accounts'].window.present()
else:
gajim.interface.instances['accounts'] = config.AccountsWindow()
def on_file_transfers_menuitem_activate(self, widget):
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_menuitem_activate(self, widget):
if 'logs' in gajim.interface.instances:
gajim.interface.instances['logs'].window.present()
else:
gajim.interface.instances['logs'] = history_window.\
HistoryWindow()
def on_show_transports_menuitem_activate(self, widget):
gajim.config.set('show_transports_group', widget.get_active())
self.refilter_shown_roster_items() self.refilter_shown_roster_items()
def on_manage_bookmarks_menuitem_activate(self, widget): def on_manage_bookmarks_menuitem_activate(self, widget):
config.ManageBookmarksWindow() config.ManageBookmarksWindow()
def on_profile_avatar_menuitem_activate(self, widget, account):
gajim.interface.edit_own_details(account)
def on_execute_command(self, widget, contact, account, resource=None): def on_execute_command(self, widget, contact, account, resource=None):
""" """
Execute command. Full JID needed; if it is other contact, resource is Execute command. Full JID needed; if it is other contact, resource is
@ -4217,6 +4097,7 @@ class RosterWindow:
# self._last_selected_contact.append((jid, account)) # self._last_selected_contact.append((jid, account))
# GLib.idle_add(self.draw_contact, jid, account, True) # GLib.idle_add(self.draw_contact, jid, account, True)
def on_service_disco_menuitem_activate(self, widget, account): def on_service_disco_menuitem_activate(self, widget, account):
server_jid = gajim.config.get_per('accounts', account, 'hostname') server_jid = gajim.config.get_per('accounts', account, 'hostname')
if server_jid in gajim.interface.instances[account]['disco']: if server_jid in gajim.interface.instances[account]['disco']:
@ -4229,50 +4110,43 @@ class RosterWindow:
except GajimGeneralException: except GajimGeneralException:
pass pass
def on_show_offline_contacts_menuitem_activate(self, widget): def on_show_offline_contacts_action(self, action, param):
""" """
When show offline option is changed: redraw the treeview When show offline option is changed: redraw the treeview
""" """
gajim.config.set('showoffline', not gajim.config.get('showoffline')) action.set_state(param)
gajim.config.set('showoffline', param.get_boolean())
self.refilter_shown_roster_items() self.refilter_shown_roster_items()
w = self.xml.get_object('show_only_active_contacts_menuitem') if param.get_boolean():
if gajim.config.get('showoffline'):
# We need to filter twice to show groups with no contacts inside # We need to filter twice to show groups with no contacts inside
# in the correct expand state # in the correct expand state
self.refilter_shown_roster_items() self.refilter_shown_roster_items()
w.set_sensitive(False) self.window.lookup_action('show-active').set_enabled(False)
else: else:
w.set_sensitive(True) self.window.lookup_action('show-active').set_enabled(True)
def on_show_only_active_contacts_menuitem_activate(self, widget): def on_show_active_contacts_action(self, action, param):
""" """
When show only active contact option is changed: redraw the treeview When show only active contact option is changed: redraw the treeview
""" """
gajim.config.set('show_only_chat_and_online', not gajim.config.get( action.set_state(param)
'show_only_chat_and_online')) gajim.config.set('show_only_chat_and_online', param.get_boolean())
self.refilter_shown_roster_items() self.refilter_shown_roster_items()
w = self.xml.get_object('show_offline_contacts_menuitem')
if gajim.config.get('show_only_chat_and_online'): if param.get_boolean():
# We need to filter twice to show groups with no contacts inside # We need to filter twice to show groups with no contacts inside
# in the correct expand state # in the correct expand state
self.refilter_shown_roster_items() self.refilter_shown_roster_items()
w.set_sensitive(False) self.window.lookup_action('show-offline').set_enabled(False)
else: else:
w.set_sensitive(True) self.window.lookup_action('show-offline').set_enabled(True)
def on_view_menu_activate(self, widget): def on_show_roster_action(self, action, param):
self.make_menu()
# Hide the show roster menu if we are not in the right windowing mode.
if self.hpaned.get_child2() is not None:
self.xml.get_object('show_roster_menuitem').show()
else:
self.xml.get_object('show_roster_menuitem').hide()
def on_show_roster_menuitem_toggled(self, widget):
# when num controls is 0 this menuitem is hidden, but still need to # when num controls is 0 this menuitem is hidden, but still need to
# disable keybinding # disable keybinding
action.set_state(param)
if self.hpaned.get_child2() is not None: if self.hpaned.get_child2() is not None:
self.show_roster_vbox(widget.get_active()) self.show_roster_vbox(param.get_boolean())
def on_rfilter_entry_changed(self, widget): def on_rfilter_entry_changed(self, widget):
""" When we update the content of the filter """ """ When we update the content of the filter """
@ -4337,7 +4211,9 @@ class RosterWindow:
self._readjust_expand_collapse_state() self._readjust_expand_collapse_state()
# If roster was hidden before enable_rfilter was called, hide it back. # If roster was hidden before enable_rfilter was called, hide it back.
self.on_show_roster_menuitem_toggled(self.xml.get_object('show_roster_menuitem')) state = self.window.lookup_action('show-roster').get_state().get_boolean()
if state is False and self.hpaned.get_child2() is not None:
self.show_roster_vbox(False)
def on_roster_hpaned_notify(self, pane, gparamspec): def on_roster_hpaned_notify(self, pane, gparamspec):
""" """
@ -5164,276 +5040,6 @@ class RosterWindow:
### FIXME: We really need to make it simpler! 1465 lines are a few to much.... ### FIXME: We really need to make it simpler! 1465 lines are a few to much....
################################################################################ ################################################################################
def make_menu(self, force=False):
"""
Create the main window's menus
"""
if not force and not self.actions_menu_needs_rebuild:
return
history_menuitem = self.xml.get_object('history_menuitem')
new_chat_menuitem = self.xml.get_object('new_chat_menuitem')
single_message_menuitem = self.xml.get_object(
'send_single_message_menuitem')
join_gc_menuitem = self.xml.get_object('join_gc_menuitem')
add_new_contact_menuitem = self.xml.get_object(
'add_new_contact_menuitem')
service_disco_menuitem = self.xml.get_object('service_disco_menuitem')
advanced_menuitem = self.xml.get_object('advanced_menuitem')
profile_avatar_menuitem = self.xml.get_object('profile_avatar_menuitem')
# destroy old advanced menus
for m in self.advanced_menus:
m.destroy()
# make it sensitive. it is insensitive only if no accounts are
# *available*
advanced_menuitem.set_sensitive(True)
if self.add_new_contact_handler_id:
add_new_contact_menuitem.handler_disconnect(
self.add_new_contact_handler_id)
self.add_new_contact_handler_id = None
if self.service_disco_handler_id:
service_disco_menuitem.handler_disconnect(
self.service_disco_handler_id)
self.service_disco_handler_id = None
if self.single_message_menuitem_handler_id:
single_message_menuitem.handler_disconnect(
self.single_message_menuitem_handler_id)
self.single_message_menuitem_handler_id = None
if self.profile_avatar_menuitem_handler_id:
profile_avatar_menuitem.handler_disconnect(
self.profile_avatar_menuitem_handler_id)
self.profile_avatar_menuitem_handler_id = None
# remove the existing submenus
add_new_contact_menuitem.set_submenu(None)
service_disco_menuitem.set_submenu(None)
join_gc_menuitem.set_submenu(None)
single_message_menuitem.set_submenu(None)
advanced_menuitem.set_submenu(None)
profile_avatar_menuitem.set_submenu(None)
gc_sub_menu = Gtk.Menu() # gc is always a submenu
join_gc_menuitem.set_submenu(gc_sub_menu)
connected_accounts = gajim.get_number_of_connected_accounts()
connected_accounts_with_private_storage = 0
# items that get shown whether an account is zeroconf or not
accounts_list = sorted(gajim.contacts.get_accounts())
if connected_accounts > 2 or \
(connected_accounts > 1 and not gajim.zeroconf_is_connected()):
# 2 or more "real" (no zeroconf) accounts? make submenus
new_chat_sub_menu = Gtk.Menu()
for account in accounts_list:
if gajim.connections[account].connected <= 1 or \
gajim.config.get_per('accounts', account, 'is_zeroconf'):
# if offline or connecting or zeroconf
continue
# new chat
new_chat_item = Gtk.MenuItem.new_with_label(
_('using account %s') % account)
new_chat_item.set_use_underline(False)
new_chat_sub_menu.append(new_chat_item)
new_chat_item.connect('activate',
self.on_new_chat_menuitem_activate, account)
new_chat_menuitem.set_submenu(new_chat_sub_menu)
new_chat_sub_menu.show_all()
# menu items that don't apply to zeroconf connections
if connected_accounts == 1 or (connected_accounts == 2 and \
gajim.zeroconf_is_connected()):
# only one 'real' (non-zeroconf) account is connected, don't need
# submenus
for account in accounts_list:
if gajim.account_is_connected(account) and \
not gajim.config.get_per('accounts', account, 'is_zeroconf'):
# gc
if gajim.connections[account].private_storage_supported:
connected_accounts_with_private_storage += 1
self.add_bookmarks_list(gc_sub_menu, account)
gc_sub_menu.show_all()
# add
if not self.add_new_contact_handler_id:
self.add_new_contact_handler_id = \
add_new_contact_menuitem.connect(
'activate', self.on_add_new_contact, account)
# disco
if not self.service_disco_handler_id:
self.service_disco_handler_id = service_disco_menuitem.\
connect('activate',
self.on_service_disco_menuitem_activate, account)
# single message
if not self.single_message_menuitem_handler_id:
self.single_message_menuitem_handler_id = \
single_message_menuitem.connect('activate', \
self.on_send_single_message_menuitem_activate, account)
break # No other account connected
else:
# 2 or more 'real' accounts are connected, make submenus
single_message_sub_menu = Gtk.Menu()
add_sub_menu = Gtk.Menu()
disco_sub_menu = Gtk.Menu()
for account in accounts_list:
if gajim.connections[account].connected <= 1 or \
gajim.config.get_per('accounts', account, 'is_zeroconf'):
# skip account if it's offline or connecting or is zeroconf
continue
# single message
single_message_item = Gtk.MenuItem.new_with_label(
_('using account %s') % account)
single_message_item.set_use_underline(False)
single_message_sub_menu.append(single_message_item)
single_message_item.connect('activate',
self.on_send_single_message_menuitem_activate, account)
# join gc
if gajim.connections[account].private_storage_supported:
connected_accounts_with_private_storage += 1
gc_item = Gtk.MenuItem.new_with_label(
_('using account %s') % account)
gc_item.set_use_underline(False)
gc_sub_menu.append(gc_item)
gc_menuitem_menu = Gtk.Menu()
self.add_bookmarks_list(gc_menuitem_menu, account)
gc_item.set_submenu(gc_menuitem_menu)
# add
add_item = Gtk.MenuItem.new_with_label(
_('to %s account') % account)
add_item.set_use_underline(False)
add_sub_menu.append(add_item)
add_item.connect('activate', self.on_add_new_contact, account)
# disco
disco_item = Gtk.MenuItem.new_with_label(
_('using %s account') % account)
disco_item.set_use_underline(False)
disco_sub_menu.append(disco_item)
disco_item.connect('activate',
self.on_service_disco_menuitem_activate, account)
single_message_menuitem.set_submenu(single_message_sub_menu)
single_message_sub_menu.show_all()
gc_sub_menu.show_all()
add_new_contact_menuitem.set_submenu(add_sub_menu)
add_sub_menu.show_all()
service_disco_menuitem.set_submenu(disco_sub_menu)
disco_sub_menu.show_all()
if connected_accounts == 0:
# no connected accounts, make the menuitems insensitive
for item in (new_chat_menuitem, join_gc_menuitem,
add_new_contact_menuitem, service_disco_menuitem,
single_message_menuitem):
item.set_sensitive(False)
else: # we have one or more connected accounts
for item in (new_chat_menuitem, join_gc_menuitem,
add_new_contact_menuitem, service_disco_menuitem,
single_message_menuitem):
item.set_sensitive(True)
# disable some fields if only local account is there
if connected_accounts == 1:
for account in gajim.connections:
if gajim.account_is_connected(account) and \
gajim.connections[account].is_zeroconf:
for item in (new_chat_menuitem, join_gc_menuitem,
add_new_contact_menuitem, service_disco_menuitem,
single_message_menuitem):
item.set_sensitive(False)
# Manage GC bookmarks
newitem = Gtk.SeparatorMenuItem.new() # separator
gc_sub_menu.append(newitem)
newitem = Gtk.MenuItem.new_with_mnemonic(_('_Manage Bookmarks…'))
newitem.connect('activate', self.on_manage_bookmarks_menuitem_activate)
gc_sub_menu.append(newitem)
gc_sub_menu.show_all()
if connected_accounts_with_private_storage == 0:
newitem.set_sensitive(False)
connected_accounts_with_vcard = []
for account in gajim.connections:
if gajim.account_is_connected(account) and \
gajim.connections[account].vcard_supported:
connected_accounts_with_vcard.append(account)
if len(connected_accounts_with_vcard) > 1:
# 2 or more accounts? make submenus
profile_avatar_sub_menu = Gtk.Menu()
for account in connected_accounts_with_vcard:
# profile, avatar
profile_avatar_item = Gtk.MenuItem.new_with_label(
_('of account %s') % account)
profile_avatar_item.set_use_underline(False)
profile_avatar_sub_menu.append(profile_avatar_item)
profile_avatar_item.connect('activate',
self.on_profile_avatar_menuitem_activate, account)
profile_avatar_menuitem.set_submenu(profile_avatar_sub_menu)
profile_avatar_sub_menu.show_all()
elif len(connected_accounts_with_vcard) == 1:
# user has only one account
account = connected_accounts_with_vcard[0]
# profile, avatar
if not self.profile_avatar_menuitem_handler_id:
self.profile_avatar_menuitem_handler_id = \
profile_avatar_menuitem.connect('activate',
self.on_profile_avatar_menuitem_activate, account)
if len(connected_accounts_with_vcard) == 0:
profile_avatar_menuitem.set_sensitive(False)
else:
profile_avatar_menuitem.set_sensitive(True)
# Advanced Actions
if len(gajim.connections) == 0: # user has no accounts
advanced_menuitem.set_sensitive(False)
elif len(gajim.connections) == 1: # we have one acccount
account = list(gajim.connections.keys())[0]
advanced_menuitem_menu = \
self.get_and_connect_advanced_menuitem_menu(account)
self.advanced_menus.append(advanced_menuitem_menu)
self.add_history_manager_menuitem(advanced_menuitem_menu)
advanced_menuitem.set_submenu(advanced_menuitem_menu)
advanced_menuitem_menu.show_all()
else: # user has *more* than one account : build advanced submenus
advanced_sub_menu = Gtk.Menu()
accounts = [] # Put accounts in a list to sort them
for account in gajim.connections:
accounts.append(account)
accounts.sort()
for account in accounts:
advanced_item = Gtk.MenuItem.new_with_label(
_('for account %s') % account)
advanced_item.set_use_underline(False)
advanced_sub_menu.append(advanced_item)
advanced_menuitem_menu = \
self.get_and_connect_advanced_menuitem_menu(account)
self.advanced_menus.append(advanced_menuitem_menu)
advanced_item.set_submenu(advanced_menuitem_menu)
self.add_history_manager_menuitem(advanced_sub_menu)
advanced_menuitem.set_submenu(advanced_sub_menu)
advanced_sub_menu.show_all()
self.actions_menu_needs_rebuild = False
def build_account_menu(self, account): def build_account_menu(self, account):
# we have to create our own set of icons for the menu # we have to create our own set of icons for the menu
# using self.jabber_status_images is poopoo # using self.jabber_status_images is poopoo
@ -6032,34 +5638,6 @@ class RosterWindow:
account, bookmark) account, bookmark)
gc_sub_menu.append(item) gc_sub_menu.append(item)
def set_actions_menu_needs_rebuild(self):
self.actions_menu_needs_rebuild = True
# Just handle new_chat_menuitem to have ctrl+N working even if we don't
# open the menu
new_chat_menuitem = self.xml.get_object('new_chat_menuitem')
ag = Gtk.accel_groups_from_object(self.window)#[0]
if self.new_chat_menuitem_handler_id:
new_chat_menuitem.handler_disconnect(
self.new_chat_menuitem_handler_id)
self.new_chat_menuitem_handler_id = None
new_chat_menuitem.set_submenu(None)
connected_accounts = gajim.get_number_of_connected_accounts()
if connected_accounts == 1 or (connected_accounts == 2 and \
gajim.zeroconf_is_connected()):
# only one 'real' (non-zeroconf) account is connected, don't need
# submenus
accounts_list = sorted(gajim.contacts.get_accounts())
for account in accounts_list:
if gajim.account_is_connected(account) and \
not gajim.config.get_per('accounts', account, 'is_zeroconf'):
if not self.new_chat_menuitem_handler_id:
self.new_chat_menuitem_handler_id = new_chat_menuitem.\
connect('activate',
self.on_new_chat_menuitem_activate, account)
def show_appropriate_context_menu(self, event, iters): def show_appropriate_context_menu(self, event, iters):
# iters must be all of the same type # iters must be all of the same type
model = self.modelfilter model = self.modelfilter
@ -6170,6 +5748,36 @@ class RosterWindow:
tooltip.populate(connected_contacts, account, typ) tooltip.populate(connected_contacts, account, typ)
return True return True
def add_actions(self):
action = Gio.SimpleAction.new_stateful(
"show-roster", None,
GLib.Variant.new_boolean(
not self.xml.get_object('roster_vbox2').get_no_show_all()))
action.connect("change-state",
self.on_show_roster_action)
self.window.add_action(action)
action = Gio.SimpleAction.new_stateful(
"show-offline", None,
GLib.Variant.new_boolean(gajim.config.get('showoffline')))
action.connect("change-state",
self.on_show_offline_contacts_action)
self.window.add_action(action)
action = Gio.SimpleAction.new_stateful(
"show-active", None,
GLib.Variant.new_boolean(
gajim.config.get('show_only_chat_and_online')))
action.connect("change-state",
self.on_show_active_contacts_action)
self.window.add_action(action)
action = Gio.SimpleAction.new_stateful(
"show-transports", None,
GLib.Variant.new_boolean(gajim.config.get('show_transports_group')))
action.connect("change-state", self.on_show_transports_action)
self.window.add_action(action)
################################################################################ ################################################################################
### ###
################################################################################ ################################################################################
@ -6192,6 +5800,7 @@ class RosterWindow:
self.xml = gtkgui_helpers.get_gtk_builder('roster_window.ui') self.xml = gtkgui_helpers.get_gtk_builder('roster_window.ui')
self.window = self.xml.get_object('roster_window') self.window = self.xml.get_object('roster_window')
app.add_window(self.window) app.add_window(self.window)
self.add_actions()
self.hpaned = self.xml.get_object('roster_hpaned') self.hpaned = self.xml.get_object('roster_hpaned')
gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned) gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
gajim.interface.msg_win_mgr.connect('window-delete', gajim.interface.msg_win_mgr.connect('window-delete',
@ -6223,7 +5832,6 @@ class RosterWindow:
#FIXME: When list_accel_closures will be wrapped in pygtk #FIXME: When list_accel_closures will be wrapped in pygtk
# no need of this variable # no need of this variable
self.have_new_chat_accel = False # Is the "Ctrl+N" shown ? self.have_new_chat_accel = False # Is the "Ctrl+N" shown ?
self.set_actions_menu_needs_rebuild()
self.regroup = gajim.config.get('mergeaccounts') self.regroup = gajim.config.get('mergeaccounts')
self.clicked_path = None # Used remember on wich row we clicked self.clicked_path = None # Used remember on wich row we clicked
if len(gajim.connections) < 2: if len(gajim.connections) < 2:
@ -6314,24 +5922,15 @@ class RosterWindow:
# selected item and not stay with that item selected # selected item and not stay with that item selected
self.previous_status_combobox_active = number_of_menuitem self.previous_status_combobox_active = number_of_menuitem
showOffline = gajim.config.get('showoffline') # Enable/Disable checkboxes at start
showOnlyChatAndOnline = gajim.config.get('show_only_chat_and_online') if gajim.config.get('showoffline'):
self.window.lookup_action('show-active').set_enabled(False)
w = self.xml.get_object('show_offline_contacts_menuitem') if gajim.config.get('show_only_chat_and_online'):
w.set_active(showOffline) self.window.lookup_action('show-offline').set_enabled(False)
if showOnlyChatAndOnline:
w.set_sensitive(False)
w = self.xml.get_object('show_only_active_contacts_menuitem') if self.hpaned.get_child2() is None:
w.set_active(showOnlyChatAndOnline) self.window.lookup_action('show-roster').set_enabled(False)
if showOffline:
w.set_sensitive(False)
show_transports_group = gajim.config.get('show_transports_group')
self.xml.get_object('show_transports_menuitem').set_active(
show_transports_group)
self.xml.get_object('show_roster_menuitem').set_active(True)
# columns # columns
col = Gtk.TreeViewColumn() col = Gtk.TreeViewColumn()
@ -6460,11 +6059,6 @@ class RosterWindow:
accel_group.connect(Gdk.KEY_j, Gdk.ModifierType.CONTROL_MASK, accel_group.connect(Gdk.KEY_j, Gdk.ModifierType.CONTROL_MASK,
Gtk.AccelFlags.MASK, self.on_ctrl_j) Gtk.AccelFlags.MASK, self.on_ctrl_j)
# Setting CTRL+N to be the shortcut for show Start chat dialog
new_chat_menuitem = self.xml.get_object('new_chat_menuitem')
new_chat_menuitem.add_accelerator('activate', accel_group,
Gdk.KEY_n, Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE)
# Setting CTRL+S to be the shortcut to change status message # Setting CTRL+S to be the shortcut to change status message
accel_group = Gtk.AccelGroup() accel_group = Gtk.AccelGroup()
keyval, mod = Gtk.accelerator_parse('<Control>s') keyval, mod = Gtk.accelerator_parse('<Control>s')