diff --git a/gajim/accounts_window.py b/gajim/accounts_window.py
index df5d553ad..118143619 100644
--- a/gajim/accounts_window.py
+++ b/gajim/accounts_window.py
@@ -6,7 +6,6 @@ from gajim.common import app
from gajim import gtkgui_helpers
from gajim import gui_menu_builder
from gajim.common import passwords
-from gajim import dialogs
from gajim import config
from gajim.common import helpers
from gajim.common import ged
@@ -14,6 +13,8 @@ from gajim.common.connection import Connection
from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
from gajim.options_dialog import OptionsDialog, OptionsBox
from gajim.common.const import Option, OptionKind, OptionType
+from gajim.gtk import ConfirmationDialog
+from gajim.gtk import YesNoDialog
class AccountsWindow(Gtk.ApplicationWindow):
@@ -143,7 +144,7 @@ class AccountsWindow(Gtk.ApplicationWindow):
account, 'offline', _('Be right back.'))
GLib.timeout_add(500, login, account, show_before, status_before)
- dialogs.YesNoDialog(
+ YesNoDialog(
_('Relogin now?'),
_('If you want all the changes to apply instantly, '
'you must relogin.'),
@@ -197,7 +198,7 @@ class AccountsWindow(Gtk.ApplicationWindow):
app.interface.instances[account]['remove_account'] = \
config.RemoveAccountWindow(account)
if win_opened:
- dialogs.ConfirmationDialog(
+ ConfirmationDialog(
_('You have opened chat in account %s') % account,
_('All chat and groupchat windows will be closed. '
'Do you want to continue?'),
diff --git a/gajim/adhoc_commands.py b/gajim/adhoc_commands.py
index 0db00eeee..b74e8c9b7 100644
--- a/gajim/adhoc_commands.py
+++ b/gajim/adhoc_commands.py
@@ -33,7 +33,7 @@ from gajim.common import dataforms
from gajim.common import ged
from gajim import gtkgui_helpers
-from gajim import dialogs
+from gajim.gtk import HigDialog
from gajim import dataforms_widget
class CommandWindow:
@@ -322,7 +322,7 @@ class CommandWindow:
dialog.destroy()
cb()
- dialog = dialogs.HigDialog(self.window, Gtk.MessageType.WARNING,
+ dialog = HigDialog(self.window, Gtk.MessageType.WARNING,
Gtk.ButtonsType.YES_NO, _('Cancel confirmation'),
_('You are in process of executing command. Do you really want to '
'cancel it?'), on_response_yes=on_yes)
diff --git a/gajim/app_actions.py b/gajim/app_actions.py
index cc7b9763f..b733ff939 100644
--- a/gajim/app_actions.py
+++ b/gajim/app_actions.py
@@ -28,14 +28,21 @@ import gajim.plugins.gui
from gajim import history_window
from gajim import disco
from gajim.gtk.history_sync import HistorySyncAssistant
-from gajim.server_info import ServerInfoDialog
+from gajim.gtk.server_info import ServerInfoDialog
from gajim.gtk.mam_preferences import MamPreferences
+from gajim.gtk import JoinGroupchatWindow
+from gajim.gtk import StartChatDialog
+from gajim.gtk import AddNewContactWindow
+from gajim.gtk import SingleMessageWindow
+from gajim.gtk import XMLConsoleWindow
+from gajim.gtk import AboutDialog
+from gajim.gtk import PrivacyListsWindow
# General Actions
def on_add_contact_jid(action, param):
- dialogs.AddNewContactWindow(None, param.get_string())
+ AddNewContactWindow(None, param.get_string())
# Application Menu Actions
@@ -79,7 +86,7 @@ def on_new_chat(action, param):
if 'start_chat' in app.interface.instances:
app.interface.instances['start_chat'].present()
else:
- app.interface.instances['start_chat'] = dialogs.StartChatDialog()
+ app.interface.instances['start_chat'] = StartChatDialog()
# Accounts Actions
@@ -103,7 +110,7 @@ def on_send_server_message(action, param):
account = param.get_string()
server = app.config.get_per('accounts', account, 'hostname')
server += '/announce/online'
- dialogs.SingleMessageWindow(account, server, 'send')
+ SingleMessageWindow(account, server, 'send')
def on_service_disco(action, param):
@@ -127,23 +134,23 @@ def on_join_gc(action, param):
return
else:
account = param.get_string()
- window = app.get_app_window(dialogs.JoinGroupchatWindow)
+ window = app.get_app_window(JoinGroupchatWindow)
if window is None:
- dialogs.JoinGroupchatWindow(account, None)
+ JoinGroupchatWindow(account, None)
else:
window.present()
def on_add_contact(action, param):
- window = app.get_app_window(dialogs.AddNewContactWindow)
+ window = app.get_app_window(AddNewContactWindow)
if window is None:
- dialogs.AddNewContactWindow(param.get_string())
+ AddNewContactWindow(param.get_string())
else:
window.present()
def on_single_message(action, param):
- dialogs.SingleMessageWindow(param.get_string(), action='send')
+ SingleMessageWindow(param.get_string(), action='send')
def on_merge_accounts(action, param):
@@ -206,7 +213,7 @@ def on_privacy_lists(action, param):
interface.instances[account]['privacy_lists'].window.present()
else:
interface.instances[account]['privacy_lists'] = \
- dialogs.PrivacyListsWindow(account)
+ PrivacyListsWindow(account)
def on_server_info(action, param):
@@ -224,7 +231,7 @@ def on_xml_console(action, param):
interface.instances[account]['xml_console'].present()
else:
interface.instances[account]['xml_console'] = \
- dialogs.XMLConsoleWindow(account)
+ XMLConsoleWindow(account)
def on_manage_proxies(action, param):
@@ -241,14 +248,14 @@ def on_set_motd(action, param):
account = param.get_string()
server = app.config.get_per('accounts', account, 'hostname')
server += '/announce/motd'
- dialogs.SingleMessageWindow(account, server, 'send')
+ SingleMessageWindow(account, server, 'send')
def on_update_motd(action, param):
account = param.get_string()
server = app.config.get_per('accounts', account, 'hostname')
server += '/announce/motd/update'
- dialogs.SingleMessageWindow(account, server, 'send')
+ SingleMessageWindow(account, server, 'send')
def on_delete_motd(action, param):
@@ -279,7 +286,7 @@ def on_features(action, param):
def on_about(action, param):
- dialogs.AboutDialog()
+ AboutDialog()
# View Actions
diff --git a/gajim/chat_control.py b/gajim/chat_control.py
index 386c272b9..ef8d2d7e3 100644
--- a/gajim/chat_control.py
+++ b/gajim/chat_control.py
@@ -37,6 +37,8 @@ from gajim import gtkgui_helpers
from gajim import gui_menu_builder
from gajim import message_control
from gajim import dialogs
+from gajim.gtk import ConfirmationDialog
+from gajim.gtk import AddNewContactWindow
from gajim.common import app
from gajim.common import helpers
@@ -337,7 +339,7 @@ class ChatControl(ChatControlBase):
'information-' + self.control_id).set_enabled(online)
def _on_add_to_roster(self, action, param):
- dialogs.AddNewContactWindow(self.account, self.contact.jid)
+ AddNewContactWindow(self.account, self.contact.jid)
def _on_information(self, action, param):
app.interface.roster.on_info(None, self.contact, self.account)
@@ -1208,7 +1210,7 @@ class ChatControl(ChatControlBase):
def on_cancel():
on_no(self)
- dialogs.ConfirmationDialog(
+ ConfirmationDialog(
#%s is being replaced in the code with JID
_('You just received a new message from "%s"') % \
self.contact.jid,
diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py
index 61bb51ac0..9cb8f2b2e 100644
--- a/gajim/chat_control_base.py
+++ b/gajim/chat_control_base.py
@@ -37,7 +37,7 @@ from gi.repository import Gio
from gajim import gtkgui_helpers
from gajim import message_control
-from gajim import dialogs
+from gajim.gtk import NonModalConfirmationDialog
from gajim import history_window
from gajim import notify
import re
@@ -1160,7 +1160,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
prim_text = _('Really send file?')
sec_text = _('If you send a file to %s, your real JID will '
'be revealed.') % gc_contact.name
- dialog = dialogs.NonModalConfirmationDialog(prim_text,
+ dialog = NonModalConfirmationDialog(prim_text,
sec_text, on_response_ok=(_on_ok, gc_contact))
dialog.popup()
return
diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py
index ad44d5a7f..42b1ab36d 100644
--- a/gajim/common/connection_handlers_events.py
+++ b/gajim/common/connection_handlers_events.py
@@ -955,7 +955,7 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
if self.msg_obj.form_node:
# It could be a voice request. See
# http://www.xmpp.org/extensions/xep-0045.html#voiceapprove
- from gajim.dialogs import SingleMessageWindow
+ from gajim.gtk import SingleMessageWindow
SingleMessageWindow(
self.conn.name, self.fjid,
action='receive', from_whom=self.fjid,
diff --git a/gajim/config.py b/gajim/config.py
index e203edfd2..e5c874bb8 100644
--- a/gajim/config.py
+++ b/gajim/config.py
@@ -52,6 +52,12 @@ from gajim.gajim_themes_window import GajimThemesWindow
from gajim.advanced_configuration_window import AdvancedConfigurationWindow
from gajim import dataforms_widget
from gajim import gui_menu_builder
+from gajim.gtk import AspellDictError
+from gajim.gtk import ConfirmationDialog
+from gajim.gtk import ConfirmationDialogDoubleRadio
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InputDialog
+from gajim.gtk import WarningDialog
from gajim.common import helpers
from gajim.common import app
@@ -653,7 +659,7 @@ class PreferencesWindow:
if gspell_lang is None:
gspell_lang = Gspell.language_get_default()
if gspell_lang is None:
- dialogs.AspellDictError(lang)
+ AspellDictError(lang)
app.config.set('use_speller', False)
widget.set_active(False)
else:
@@ -1682,7 +1688,7 @@ class GroupchatConfigWindow:
return
model = self.affiliation_treeview[affiliation].get_model()
model.append((jid, '', '', ''))
- dialogs.InputDialog(title, prompt, ok_handler=on_ok)
+ InputDialog(title, prompt, ok_handler=on_ok)
def on_remove_button_clicked(self, widget, affiliation):
selection = self.affiliation_treeview[affiliation].get_selection()
@@ -1785,7 +1791,7 @@ class RemoveAccountWindow:
app.connections[self.account].change_status('offline', 'offline')
if self.remove_and_unregister_radiobutton.get_active():
if not self.account in app.connections:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Account is disabled'),
_('To unregister from a server, account must be '
'enabled.'),
@@ -1813,7 +1819,7 @@ class RemoveAccountWindow:
if self.account in app.connections and \
app.connections[self.account].connected:
- dialogs.ConfirmationDialog(
+ ConfirmationDialog(
_('Account "%s" is connected to the server') % self.account,
_('If you remove it, the connection will be lost.'),
on_response_ok=remove,
@@ -1829,7 +1835,7 @@ class RemoveAccountWindow:
# action of unregistration has failed, we don't remove the account
# Error message is send by connect_and_auth()
if not res:
- dialogs.ConfirmationDialogDoubleRadio(
+ ConfirmationDialogDoubleRadio(
_('Connection to server %s failed') % self.account,
_('What would you like to do?'),
_('Remove only from Gajim'),
@@ -2036,7 +2042,7 @@ class ManageBookmarksWindow:
if self.server_entry.get_text() == '' or \
self.room_entry.get_text() == '':
- dialogs.ErrorDialog(_('This bookmark has invalid data'),
+ ErrorDialog(_('This bookmark has invalid data'),
_('Please be sure to fill out server and room fields or remove this'
' bookmark.'))
return False
@@ -2166,7 +2172,7 @@ class ManageBookmarksWindow:
try:
nick = helpers.parse_resource(nick)
except helpers.InvalidFormat:
- dialogs.ErrorDialog(_('Invalid nickname'),
+ ErrorDialog(_('Invalid nickname'),
_('Character not allowed'), transient_for=self.window)
self.nick_entry.set_text(model[iter_][6])
return True
@@ -2182,7 +2188,7 @@ class ManageBookmarksWindow:
if not server:
return
if '@' in server:
- dialogs.ErrorDialog(_('Invalid server'),
+ ErrorDialog(_('Invalid server'),
_('Character not allowed'), transient_for=self.window)
widget.set_text(server.replace('@', ''))
@@ -2193,7 +2199,7 @@ class ManageBookmarksWindow:
try:
room_jid = helpers.parse_jid(room_jid)
except helpers.InvalidFormat as e:
- dialogs.ErrorDialog(_('Invalid server'),
+ ErrorDialog(_('Invalid server'),
_('Character not allowed'), transient_for=self.window)
self.server_entry.set_text(model[iter_][2].split('@')[1])
return True
@@ -2221,7 +2227,7 @@ class ManageBookmarksWindow:
try:
room_jid = helpers.parse_jid(room_jid)
except helpers.InvalidFormat:
- dialogs.ErrorDialog(_('Invalid room'),
+ ErrorDialog(_('Invalid room'),
_('Character not allowed'), transient_for=self.window)
return True
model[iter_][2] = room_jid
@@ -2439,7 +2445,7 @@ class AccountCreationWizardWindow:
pritext = _('Invalid username')
sectext = _(
'You must provide a username to configure this account.')
- dialogs.ErrorDialog(pritext, sectext)
+ ErrorDialog(pritext, sectext)
return
server = self.xml.get_object('server_comboboxtext_entry').\
get_text().strip()
@@ -2457,7 +2463,7 @@ class AccountCreationWizardWindow:
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat as s:
pritext = _('Invalid JID')
- dialogs.ErrorDialog(pritext, str(s))
+ ErrorDialog(pritext, str(s))
return
self.account = server
@@ -2478,7 +2484,7 @@ class AccountCreationWizardWindow:
get_text()
if not server:
- dialogs.ErrorDialog(_('Invalid server'),
+ ErrorDialog(_('Invalid server'),
_('Please provide a server on which you want to register.'))
return
self.account = server
@@ -2502,7 +2508,7 @@ class AccountCreationWizardWindow:
try:
custom_port = int(custom_port)
except Exception:
- dialogs.ErrorDialog(_('Invalid entry'),
+ ErrorDialog(_('Invalid entry'),
_('Custom port must be a port number.'))
return
config['custom_port'] = custom_port
@@ -2535,7 +2541,7 @@ class AccountCreationWizardWindow:
with open(my_ca_certs) as f:
certs = f.read()
if self.ssl_cert in certs:
- dialogs.ErrorDialog(_('Certificate Already in File'),
+ ErrorDialog(_('Certificate Already in File'),
_('This certificate is already in file %s, so it\'s '
'not added again.') % my_ca_certs)
else:
@@ -2766,7 +2772,7 @@ class AccountCreationWizardWindow:
def save_account(self, login, server, savepass, password, anonymous=False):
if self.account in app.connections:
- dialogs.ErrorDialog(_('Account name is in use'),
+ ErrorDialog(_('Account name is in use'),
_('You already have an account using this name.'))
return
con = connection.Connection(self.account)
@@ -2899,7 +2905,7 @@ class ManagePEPServicesWindow:
def node_not_removed(self, jid, node, msg):
if jid != app.get_jid_from_account(self.account):
return
- dialogs.WarningDialog(_('PEP node was not removed'),
+ WarningDialog(_('PEP node was not removed'),
_('PEP node %(node)s was not removed: %(message)s') % {'node': node,
'message': msg})
diff --git a/gajim/conversation_textview.py b/gajim/conversation_textview.py
index 85457b217..7034c0062 100644
--- a/gajim/conversation_textview.py
+++ b/gajim/conversation_textview.py
@@ -38,7 +38,11 @@ from gajim import dialogs
import queue
import urllib
-from gajim import gtkgui_helpers
+from gajim.gtk import AddNewContactWindow
+from gajim.gtk import util
+from gajim.gtk.util import load_icon
+from gajim.gtk.util import get_builder
+from gajim.gtk.util import get_cursor
from gajim.common import app
from gajim.common import helpers
from gajim.common import i18n
@@ -164,11 +168,6 @@ class ConversationTextview(GObject.GObject):
)
)
- MESSAGE_CORRECTED_PIXBUF = gtkgui_helpers.get_icon_pixmap(
- 'document-edit-symbolic')
- MESSAGE_ENCRYPTED_PIXBUF = gtkgui_helpers.get_icon_pixmap(
- 'channel-secure-croped-symbolic')
-
def __init__(self, account, used_in_history_window = False):
"""
If used_in_history_window is True, then we do not show Clear menuitem in
@@ -330,11 +329,11 @@ class ConversationTextview(GObject.GObject):
if len(text) > 50:
text = text[:47] + '…'
tooltip.set_text(text)
- window.set_cursor(gtkgui_helpers.get_cursor('HAND2'))
+ window.set_cursor(get_cursor('HAND2'))
self.cursor_changed = True
return True
if tag_name in ('url', 'mail', 'xmpp', 'sth_at_sth'):
- window.set_cursor(gtkgui_helpers.get_cursor('HAND2'))
+ window.set_cursor(get_cursor('HAND2'))
self.cursor_changed = True
return False
try:
@@ -344,7 +343,7 @@ class ConversationTextview(GObject.GObject):
except KeyError:
pass
if self.cursor_changed:
- window.set_cursor(gtkgui_helpers.get_cursor('XTERM'))
+ window.set_cursor(get_cursor('XTERM'))
self.cursor_changed = False
return False
@@ -370,7 +369,7 @@ class ConversationTextview(GObject.GObject):
def scroll_to_end(self, force=False):
if self.autoscroll or force:
- gtkgui_helpers.scroll_to_end(self.tv.get_parent())
+ util.scroll_to_end(self.tv.get_parent())
def correct_message(self, correct_id, kind, name):
allowed = True
@@ -674,10 +673,10 @@ class ConversationTextview(GObject.GObject):
app.interface.join_gc_minimal(self.account, room_jid)
def on_add_to_roster_activate(self, widget, jid):
- dialogs.AddNewContactWindow(self.account, jid)
+ AddNewContactWindow(self.account, jid)
def make_link_menu(self, event, kind, text):
- xml = gtkgui_helpers.get_gtk_builder('chat_context_menu.ui')
+ xml = get_builder('chat_context_menu.ui')
menu = xml.get_object('chat_context_menu')
childs = menu.get_children()
if kind == 'url':
@@ -1142,8 +1141,9 @@ class ConversationTextview(GObject.GObject):
self.print_time(text, kind, tim, simple, direction_mark,
other_tags_for_time, iter_)
+ icon = load_icon('channel-secure-croped-symbolic', self.tv, pixbuf=True)
if encrypted:
- buffer_.insert_pixbuf(iter_, self.MESSAGE_ENCRYPTED_PIXBUF)
+ buffer_.insert_pixbuf(iter_, icon)
# If there's a displaymarking, print it here.
if displaymarking:
@@ -1185,8 +1185,9 @@ class ConversationTextview(GObject.GObject):
# Show Correction Icon
buffer_.create_tag(tag_name=msg_stanza_id)
buffer_.insert(iter_, ' ')
+ icon = load_icon('document-edit-symbolic', self.tv, pixbuf=True)
buffer_.insert_pixbuf(
- iter_, ConversationTextview.MESSAGE_CORRECTED_PIXBUF)
+ iter_, icon)
tag_start_iter = iter_.copy()
tag_start_iter.backward_chars(2)
buffer_.apply_tag_by_name(msg_stanza_id, tag_start_iter, iter_)
diff --git a/gajim/data/style/gajim.css b/gajim/data/style/gajim.css
index 1f81ee7af..fc0baf96d 100644
--- a/gajim/data/style/gajim.css
+++ b/gajim/data/style/gajim.css
@@ -128,3 +128,8 @@ button.flat.link { padding: 0; border: 0; }
/*SendFileDialog*/
#SendFileDialog grid {padding: 12px}
#SendFileDialog grid list { background-color: @theme_bg_color}
+
+/*Icon colors*/
+.success-color { color: @success_color; }
+.error-color { color: @error_color; }
+.warning-color { color: @warning_color; }
diff --git a/gajim/dialog_messages.py b/gajim/dialog_messages.py
index ffa5eff15..eec5a4bfd 100644
--- a/gajim/dialog_messages.py
+++ b/gajim/dialog_messages.py
@@ -22,8 +22,8 @@ from collections import namedtuple
from gi.repository import GLib
from gajim.common.app import app
-from gajim.dialogs import ErrorDialog
-from gajim.dialogs import InformationDialog
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InformationDialog
Message = namedtuple('Message', ['title', 'text', 'dialog'])
diff --git a/gajim/dialogs.py b/gajim/dialogs.py
index ff116f7eb..e86585a79 100644
--- a/gajim/dialogs.py
+++ b/gajim/dialogs.py
@@ -31,42 +31,33 @@
from gi.repository import Gtk
from gi.repository import Gdk
-from gi.repository import GdkPixbuf
from gi.repository import GObject
from gi.repository import GLib
-from gi.repository import Pango
import os
import nbxmpp
-import time
-import locale
+import logging
from gajim import gtkgui_helpers
from gajim import vcard
-from gajim import conversation_textview
from gajim import dataforms_widget
from random import randrange
-from gajim.common import pep
from gajim.common import ged
from gajim.common import const
-from gajim.options_dialog import OptionsDialog
-from gajim.common.const import Option, OptionKind, OptionType, ACTIVITIES
+from gajim.common.const import ACTIVITIES
from gajim.common.const import MOODS
from gajim.common import app
from gajim.common import helpers
from gajim.common import i18n
from gajim.common import dataforms
-from gajim.common.const import AvatarSize
-from gajim.common.caps_cache import muc_caps_cache
from gajim.common.exceptions import GajimGeneralException
-from gajim.common.connection_handlers_events import MessageOutgoingEvent
-if app.is_installed('GSPELL'):
- from gi.repository import Gspell
+# Compat with Gajim 1.0.3 for plugins
+from gajim.gtk import *
+
-import logging
log = logging.getLogger('gajim.dialogs')
@@ -845,1388 +836,6 @@ class ChangeStatusMessageDialog(TimeoutDialog):
ChangeMoodDialog(on_response, self.pep_dict['mood'],
self.pep_dict['mood_text'])
-class AddNewContactWindow(Gtk.ApplicationWindow):
- """
- Class for AddNewContactWindow
- """
-
- uid_labels = {'jabber': _('Jabber ID'),
- 'gadu-gadu': _('GG Number'),
- 'icq': _('ICQ Number')}
-
- def __init__(self, account=None, jid=None, user_nick=None, group=None):
- Gtk.ApplicationWindow.__init__(self)
- self.set_application(app.app)
- self.set_position(Gtk.WindowPosition.CENTER)
- self.set_show_menubar(False)
- self.set_resizable(False)
- self.set_title(_('Add Contact'))
-
- self.connect('destroy', self.on_add_new_contact_window_destroy)
- self.connect('key-press-event', self.on_add_new_contact_window_key_press_event)
-
- self.account = account
- self.adding_jid = False
-
- # fill accounts with active accounts
- accounts = app.get_enabled_accounts_with_labels()
-
- if not accounts:
- return
-
- if not account:
- self.account = accounts[0][0]
-
- self.xml = gtkgui_helpers.get_gtk_builder('add_new_contact_window.ui')
- self.add(self.xml.get_object('add_contact_box'))
- self.xml.connect_signals(self)
-
- for w in ('account_combobox', 'account_label', 'prompt_label',
- 'uid_label', 'uid_entry', 'protocol_combobox', 'protocol_jid_combobox',
- 'protocol_label', 'nickname_entry', 'message_scrolledwindow',
- 'save_message_checkbutton', 'register_hbox',
- 'add_button', 'message_textview', 'connected_label',
- 'group_comboboxentry', 'auto_authorize_checkbutton',
- 'save_message_revealer', 'nickname_label', 'group_label'):
- self.__dict__[w] = self.xml.get_object(w)
-
- self.subscription_table = [self.uid_label, self.uid_entry,
- self.nickname_label, self.nickname_entry,
- self.group_label, self.group_comboboxentry]
-
- self.add_button.grab_default()
-
- self.agents = {'jabber': []}
- self.gateway_prompt = {}
- # types to which we are not subscribed but account has an agent for it
- self.available_types = []
- for acct in accounts:
- for j in app.contacts.get_jid_list(acct[0]):
- if app.jid_is_transport(j):
- type_ = app.get_transport_name_from_jid(j, False)
- if not type_:
- continue
- if type_ in self.agents:
- self.agents[type_].append(j)
- else:
- self.agents[type_] = [j]
- self.gateway_prompt[j] = {'desc': None, 'prompt': None}
- # Now add the one to which we can register
- for acct in accounts:
- for type_ in app.connections[acct[0]].available_transports:
- if type_ in self.agents:
- continue
- self.agents[type_] = []
- for jid_ in app.connections[acct[0]].available_transports[type_]:
- if not jid_ in self.agents[type_]:
- self.agents[type_].append(jid_)
- self.gateway_prompt[jid_] = {'desc': None,
- 'prompt': None}
- self.available_types.append(type_)
-
- uf_type = {'jabber': 'XMPP', 'gadu-gadu': 'Gadu Gadu', 'icq': 'ICQ'}
- # Jabber as first
- liststore = self.protocol_combobox.get_model()
- liststore.append(['XMPP', 'xmpp', 'jabber'])
- for type_ in self.agents:
- if type_ == 'jabber':
- continue
- if type_ in uf_type:
- liststore.append([uf_type[type_], type_ + '-online', type_])
- else:
- liststore.append([type_, type_ + '-online', type_])
-
- if account:
- for service in self.agents[type_]:
- app.connections[account].request_gateway_prompt(service)
- self.protocol_combobox.set_active(0)
- self.auto_authorize_checkbutton.show()
-
- if jid:
- self.jid_escaped = True
- type_ = app.get_transport_name_from_jid(jid)
- if not type_:
- type_ = 'jabber'
- if type_ == 'jabber':
- self.uid_entry.set_text(jid)
- else:
- uid, transport = app.get_name_and_server_from_jid(jid)
- self.uid_entry.set_text(uid.replace('%', '@', 1))
- # set protocol_combobox
- model = self.protocol_combobox.get_model()
- iter_ = model.get_iter_first()
- i = 0
- while iter_:
- if model[iter_][2] == type_:
- self.protocol_combobox.set_active(i)
- break
- iter_ = model.iter_next(iter_)
- i += 1
-
- # set protocol_jid_combobox
- self.protocol_jid_combobox.set_active(0)
- model = self.protocol_jid_combobox.get_model()
- iter_ = model.get_iter_first()
- i = 0
- while iter_:
- if model[iter_][0] == transport:
- self.protocol_jid_combobox.set_active(i)
- break
- iter_ = model.iter_next(iter_)
- i += 1
- if user_nick:
- self.nickname_entry.set_text(user_nick)
- self.nickname_entry.grab_focus()
- else:
- self.jid_escaped = False
- self.uid_entry.grab_focus()
- group_names = []
- for acct in accounts:
- for g in app.groups[acct[0]].keys():
- if g not in helpers.special_groups and g not in group_names:
- group_names.append(g)
- group_names.sort()
- i = 0
- for g in group_names:
- self.group_comboboxentry.append_text(g)
- if group == g:
- self.group_comboboxentry.set_active(i)
- i += 1
-
- self.show_all()
-
- self.prompt_label.hide()
- self.save_message_revealer.hide()
-
- if len(accounts) > 1:
- liststore = self.account_combobox.get_model()
- for acc in accounts:
- liststore.append(acc)
-
- self.account_combobox.set_active_id(self.account)
- else:
- self.account_label.hide()
- self.account_combobox.hide()
-
- if len(self.agents) == 1:
- self.protocol_label.hide()
- self.protocol_combobox.hide()
- self.protocol_jid_combobox.hide()
-
- if self.account:
- message_buffer = self.message_textview.get_buffer()
- msg = helpers.from_one_line(helpers.get_subscription_request_msg(
- self.account))
- message_buffer.set_text(msg)
-
- app.ged.register_event_handler('gateway-prompt-received', ged.GUI1,
- self._nec_gateway_prompt_received)
- app.ged.register_event_handler('presence-received', ged.GUI1,
- self._nec_presence_received)
-
- def on_add_new_contact_window_destroy(self, widget):
- app.ged.remove_event_handler('presence-received', ged.GUI1,
- self._nec_presence_received)
- app.ged.remove_event_handler('gateway-prompt-received', ged.GUI1,
- self._nec_gateway_prompt_received)
-
- def on_register_button_clicked(self, widget):
- model = self.protocol_jid_combobox.get_model()
- row = self.protocol_jid_combobox.get_active()
- jid = model[row][0]
- app.connections[self.account].request_register_agent_info(jid)
-
- def on_add_new_contact_window_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape: # ESCAPE
- self.destroy()
-
- def on_cancel_button_clicked(self, widget):
- """
- When Cancel button is clicked
- """
- self.destroy()
-
- def on_message_textbuffer_changed(self, widget):
- self.save_message_revealer.show()
- self.save_message_revealer.set_reveal_child(True)
-
- def on_add_button_clicked(self, widget):
- """
- When Subscribe button is clicked
- """
- jid = self.uid_entry.get_text().strip()
- if not jid:
- ErrorDialog(
- _('%s Missing') % self.uid_label.get_text(),
- _('You must supply the %s of the new contact.' %
- self.uid_label.get_text())
- )
- return
-
- model = self.protocol_combobox.get_model()
- row = self.protocol_combobox.get_active_iter()
- type_ = model[row][2]
- if type_ != 'jabber':
- model = self.protocol_jid_combobox.get_model()
- row = self.protocol_jid_combobox.get_active()
- transport = model[row][0]
- if self.account and not self.jid_escaped:
- self.adding_jid = (jid, transport, type_)
- app.connections[self.account].request_gateway_prompt(
- transport, jid)
- else:
- jid = jid.replace('@', '%') + '@' + transport
- self._add_jid(jid, type_)
- else:
- self._add_jid(jid, type_)
-
- def _add_jid(self, jid, type_):
- # check if jid is conform to RFC and stringprep it
- try:
- jid = helpers.parse_jid(jid)
- except helpers.InvalidFormat as s:
- pritext = _('Invalid User ID')
- ErrorDialog(pritext, str(s))
- return
-
- # No resource in jid
- if jid.find('/') >= 0:
- pritext = _('Invalid User ID')
- ErrorDialog(pritext, _('The user ID must not contain a resource.'))
- return
-
- if jid == app.get_jid_from_account(self.account):
- pritext = _('Invalid User ID')
- ErrorDialog(pritext, _('You cannot add yourself to your roster.'))
- return
-
- if not app.account_is_connected(self.account):
- ErrorDialog(
- _('Account Offline'),
- _('Your account must be online to add new contacts.')
- )
- return
-
- nickname = self.nickname_entry.get_text() or ''
- # get value of account combobox, if account was not specified
- if not self.account:
- model = self.account_combobox.get_model()
- index = self.account_combobox.get_active()
- self.account = model[index][1]
-
- # Check if jid is already in roster
- if jid in app.contacts.get_jid_list(self.account):
- c = app.contacts.get_first_contact_from_jid(self.account, jid)
- if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
- ErrorDialog(_('Contact already in roster'),
- _('This contact is already listed in your roster.'))
- return
-
- if type_ == 'jabber':
- message_buffer = self.message_textview.get_buffer()
- start_iter = message_buffer.get_start_iter()
- end_iter = message_buffer.get_end_iter()
- message = message_buffer.get_text(start_iter, end_iter, True)
- if self.save_message_checkbutton.get_active():
- msg = helpers.to_one_line(message)
- app.config.set_per('accounts', self.account,
- 'subscription_request_msg', msg)
- else:
- message= ''
- group = self.group_comboboxentry.get_child().get_text()
- groups = []
- if group:
- groups = [group]
- auto_auth = self.auto_authorize_checkbutton.get_active()
- app.interface.roster.req_sub(self, jid, message, self.account,
- groups=groups, nickname=nickname, auto_auth=auto_auth)
- self.destroy()
-
- def on_account_combobox_changed(self, widget):
- account = widget.get_active_id()
- message_buffer = self.message_textview.get_buffer()
- message_buffer.set_text(helpers.get_subscription_request_msg(account))
- self.account = account
-
- def on_protocol_jid_combobox_changed(self, widget):
- model = widget.get_model()
- iter_ = widget.get_active_iter()
- if not iter_:
- return
- jid_ = model[iter_][0]
- model = self.protocol_combobox.get_model()
- iter_ = self.protocol_combobox.get_active_iter()
- type_ = model[iter_][2]
-
- desc = None
- if self.agents[type_] and jid_ in self.gateway_prompt:
- desc = self.gateway_prompt[jid_]['desc']
-
- if desc:
- self.prompt_label.set_markup(desc)
- self.prompt_label.show()
- else:
- self.prompt_label.hide()
-
- prompt = None
- if self.agents[type_] and jid_ in self.gateway_prompt:
- prompt = self.gateway_prompt[jid_]['prompt']
- if not prompt:
- if type_ in self.uid_labels:
- prompt = self.uid_labels[type_]
- else:
- prompt = _('User ID:')
- self.uid_label.set_text(prompt)
-
- def on_protocol_combobox_changed(self, widget):
- model = widget.get_model()
- iter_ = widget.get_active_iter()
- type_ = model[iter_][2]
- model = self.protocol_jid_combobox.get_model()
- model.clear()
- if len(self.agents[type_]):
- for jid_ in self.agents[type_]:
- model.append([jid_])
- self.protocol_jid_combobox.set_active(0)
- desc = None
- if self.agents[type_]:
- jid_ = self.agents[type_][0]
- if jid_ in self.gateway_prompt:
- desc = self.gateway_prompt[jid_]['desc']
-
- if desc:
- self.prompt_label.set_markup(desc)
- self.prompt_label.show()
- else:
- self.prompt_label.hide()
-
- if len(self.agents[type_]) > 1:
- self.protocol_jid_combobox.show()
- else:
- self.protocol_jid_combobox.hide()
- prompt = None
- if self.agents[type_]:
- jid_ = self.agents[type_][0]
- if jid_ in self.gateway_prompt:
- prompt = self.gateway_prompt[jid_]['prompt']
- if not prompt:
- if type_ in self.uid_labels:
- prompt = self.uid_labels[type_]
- else:
- prompt = _('User ID:')
- self.uid_label.set_text(prompt)
-
- if type_ == 'jabber':
- self.message_scrolledwindow.show()
- self.save_message_checkbutton.show()
- else:
- self.message_scrolledwindow.hide()
- self.save_message_checkbutton.hide()
- if type_ in self.available_types:
- self.register_hbox.show()
- self.auto_authorize_checkbutton.hide()
- self.connected_label.hide()
- self._subscription_table_hide()
- self.add_button.set_sensitive(False)
- else:
- self.register_hbox.hide()
- if type_ != 'jabber':
- model = self.protocol_jid_combobox.get_model()
- row = self.protocol_jid_combobox.get_active()
- jid = model[row][0]
- contact = app.contacts.get_first_contact_from_jid(
- self.account, jid)
- if contact is None or contact.show in ('offline', 'error'):
- self._subscription_table_hide()
- self.connected_label.show()
- self.add_button.set_sensitive(False)
- self.auto_authorize_checkbutton.hide()
- return
- self._subscription_table_show()
- self.auto_authorize_checkbutton.show()
- self.connected_label.hide()
- self.add_button.set_sensitive(True)
-
- def transport_signed_in(self, jid):
- model = self.protocol_jid_combobox.get_model()
- row = self.protocol_jid_combobox.get_active()
- _jid = model[row][0]
- if _jid == jid:
- self.register_hbox.hide()
- self.connected_label.hide()
- self._subscription_table_show()
- self.auto_authorize_checkbutton.show()
- self.add_button.set_sensitive(True)
-
- def transport_signed_out(self, jid):
- model = self.protocol_jid_combobox.get_model()
- row = self.protocol_jid_combobox.get_active()
- _jid = model[row][0]
- if _jid == jid:
- self._subscription_table_hide()
- self.auto_authorize_checkbutton.hide()
- self.connected_label.show()
- self.add_button.set_sensitive(False)
-
- def _nec_presence_received(self, obj):
- if app.jid_is_transport(obj.jid):
- if obj.old_show == 0 and obj.new_show > 1:
- self.transport_signed_in(obj.jid)
- elif obj.old_show > 1 and obj.new_show == 0:
- self.transport_signed_out(obj.jid)
-
- def _nec_gateway_prompt_received(self, obj):
- if self.adding_jid:
- jid, transport, type_ = self.adding_jid
- if obj.stanza.getError():
- ErrorDialog(_('Error while adding transport contact'),
- _('This error occured while adding a contact for transport '
- '%(transport)s:\n\n%(error)s') % {'transport': transport,
- 'error': obj.stanza.getErrorMsg()})
- return
- if obj.prompt_jid:
- self._add_jid(obj.prompt_jid, type_)
- else:
- jid = jid.replace('@', '%') + '@' + transport
- self._add_jid(jid, type_)
- elif obj.jid in self.gateway_prompt:
- if obj.desc:
- self.gateway_prompt[obj.jid]['desc'] = obj.desc
- if obj.prompt:
- self.gateway_prompt[obj.jid]['prompt'] = obj.prompt
-
- def _subscription_table_hide(self):
- for widget in self.subscription_table:
- widget.hide()
-
- def _subscription_table_show(self):
- for widget in self.subscription_table:
- widget.show()
-
-class AboutDialog(Gtk.AboutDialog):
- def __init__(self):
- Gtk.AboutDialog.__init__(self)
- self.set_transient_for(app.interface.roster.window)
- self.set_name('Gajim')
- self.set_version(app.version)
- self.set_copyright('Copyright © 2003-2018 Gajim Team')
- self.set_license_type(Gtk.License.GPL_3_0_ONLY)
- self.set_website('https://gajim.org/')
-
- gtk_ver = '%i.%i.%i' % (
- Gtk.get_major_version(),
- Gtk.get_minor_version(),
- Gtk.get_micro_version())
- gobject_ver = '.'.join(map(str, GObject.pygobject_version))
-
- comments = []
- comments.append(_('A GTK+ XMPP client'))
- comments.append(_('GTK+ Version: %s' % gtk_ver))
- comments.append(_('PyGObject Version: %s') % gobject_ver)
- comments.append(_('python-nbxmpp Version: %s') % nbxmpp.__version__)
- self.set_comments("\n".join(comments))
-
- self.add_credit_section(_('Current Developers'), const.DEVS_CURRENT)
- self.add_credit_section(_('Past Developers'), const.DEVS_PAST)
- self.add_credit_section(_('Artists'), const.ARTISTS)
-
- thanks = const.THANKS[:]
- thanks.append('')
- thanks.append(_('Last but not least'))
- thanks.append(_('we would like to thank all the package maintainers.'))
- self.add_credit_section(_('Thanks'), thanks)
-
- self.set_translator_credits(_('translator-credits'))
- self.set_logo_icon_name('org.gajim.Gajim')
-
- self.connect(
- 'response', lambda dialog, *args: Gtk.AboutDialog.do_close(dialog))
-
- self.show()
-
-
-class Dialog(Gtk.Dialog):
- def __init__(self, parent, title, buttons, default=None,
- on_response_ok=None, on_response_cancel=None):
- Gtk.Dialog.__init__(self, title, parent,
- Gtk.DialogFlags.DESTROY_WITH_PARENT)
-
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
- self.set_border_width(6)
- self.get_content_area().set_spacing(12)
- self.set_resizable(False)
-
- for stock, response in buttons:
- b = self.add_button(stock, response)
-
- if default is not None:
- self.set_default_response(default)
- else:
- self.set_default_response(buttons[-1][1])
-
- self.connect('response', self.on_response)
-
- def on_response(self, widget, response_id):
- if response_id == Gtk.ResponseType.OK:
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](*self.user_response_ok[1:])
- else:
- self.user_response_ok()
- self.destroy()
- elif response_id == Gtk.ResponseType.CANCEL:
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](*self.user_response_ok[1:])
- else:
- self.user_response_cancel()
- self.destroy()
-
- def just_destroy(self, widget):
- self.destroy()
-
- def get_button(self, index):
- buttons = self.get_action_area().get_children()
- return index < len(buttons) and buttons[index] or None
-
-
-class HigDialog(Gtk.MessageDialog):
- def __init__(self, parent, type_, buttons, pritext, sectext,
- on_response_ok=None, on_response_cancel=None, on_response_yes=None,
- on_response_no=None):
- self.call_cancel_on_destroy = True
- Gtk.MessageDialog.__init__(self, transient_for=parent,
- modal=True, destroy_with_parent=True,
- message_type=type_, buttons=buttons, text=pritext)
-
- self.format_secondary_markup(sectext)
-
- self.possible_responses = {Gtk.ResponseType.OK: on_response_ok,
- Gtk.ResponseType.CANCEL: on_response_cancel,
- Gtk.ResponseType.YES: on_response_yes,
- Gtk.ResponseType.NO: on_response_no}
-
- self.connect('response', self.on_response)
- self.connect('destroy', self.on_dialog_destroy)
-
- def on_response(self, dialog, response_id):
- if not response_id in self.possible_responses:
- return
- if not self.possible_responses[response_id]:
- self.destroy()
- elif isinstance(self.possible_responses[response_id], tuple):
- if len(self.possible_responses[response_id]) == 1:
- self.possible_responses[response_id][0](dialog)
- else:
- self.possible_responses[response_id][0](dialog,
- *self.possible_responses[response_id][1:])
- else:
- self.possible_responses[response_id](dialog)
-
-
- def on_dialog_destroy(self, widget):
- if not self.call_cancel_on_destroy:
- return
- cancel_handler = self.possible_responses[Gtk.ResponseType.CANCEL]
- if not cancel_handler:
- return False
- if isinstance(cancel_handler, tuple):
- cancel_handler[0](None, *cancel_handler[1:])
- else:
- cancel_handler(None)
-
- def popup(self):
- """
- Show dialog
- """
- vb = self.get_children()[0].get_children()[0] # Give focus to top vbox
-# vb.set_flags(Gtk.CAN_FOCUS)
- vb.grab_focus()
- self.show_all()
-
-class AspellDictError:
- def __init__(self, lang):
- ErrorDialog(
- _('Dictionary for language "%s" not available') % lang,
- _('You have to install the dictionary "%s" to use spellchecking, '
- 'or choose another language by setting the speller_language '
- 'option.\n\n'
- 'Highlighting misspelled words feature will not be used') % lang)
-
-class ConfirmationDialog(HigDialog):
- """
- HIG compliant confirmation dialog
- """
-
- def __init__(self, pritext, sectext='', on_response_ok=None,
- on_response_cancel=None, transient_for=None):
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
- HigDialog.__init__(self, transient_for,
- Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, pritext, sectext,
- self.on_response_ok, self.on_response_cancel)
- self.popup()
-
- def on_response_ok(self, widget):
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](*self.user_response_ok[1:])
- else:
- self.user_response_ok()
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_cancel(self, widget):
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](*self.user_response_ok[1:])
- else:
- self.user_response_cancel()
- self.call_cancel_on_destroy = False
- self.destroy()
-
-class NonModalConfirmationDialog(HigDialog):
- """
- HIG compliant non modal confirmation dialog
- """
-
- def __init__(self, pritext, sectext='', on_response_ok=None,
- on_response_cancel=None):
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
- if hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
- self.on_response_cancel)
- self.set_modal(False)
-
- def on_response_ok(self, widget):
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](*self.user_response_ok[1:])
- else:
- self.user_response_ok()
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_cancel(self, widget):
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](*self.user_response_cancel[1:])
- else:
- self.user_response_cancel()
- self.call_cancel_on_destroy = False
- self.destroy()
-
-class WarningDialog(HigDialog):
- """
- HIG compliant warning dialog
- """
-
- def __init__(self, pritext, sectext='', transient_for=None):
- if not transient_for and hasattr(app.interface, 'roster') and \
- app.interface.roster:
- transient_for = app.interface.roster.window
- HigDialog.__init__(self, transient_for, Gtk.MessageType.WARNING,
- Gtk.ButtonsType.OK, pritext, sectext)
- self.set_modal(False)
- self.popup()
-
-class InformationDialog(HigDialog):
- """
- HIG compliant info dialog
- """
-
- def __init__(self, pritext, sectext='', transient_for=None):
- if transient_for:
- parent = transient_for
- elif hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
- pritext, sectext)
- self.set_modal(False)
- self.popup()
-
-class ErrorDialog(HigDialog):
- """
- HIG compliant error dialog
- """
-
- def __init__(self, pritext, sectext='', on_response_ok=None,
- on_response_cancel=None, transient_for=None):
- if transient_for:
- parent = transient_for
- elif hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
- pritext, sectext, on_response_ok=on_response_ok,
- on_response_cancel=on_response_cancel)
- self.popup()
-
-class YesNoDialog(HigDialog):
- """
- HIG compliant YesNo dialog
- """
-
- def __init__(self, pritext, sectext='', checktext='', text_label=None,
- on_response_yes=None, on_response_no=None, type_=Gtk.MessageType.QUESTION,
- transient_for=None):
- self.user_response_yes = on_response_yes
- self.user_response_no = on_response_no
- if transient_for:
- parent = transient_for
- elif hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, type_, Gtk.ButtonsType.YES_NO, pritext,
- sectext, on_response_yes=self.on_response_yes,
- on_response_no=self.on_response_no)
-
- vbox = self.get_content_area()
- if checktext:
- self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
- vbox.pack_start(self.checkbutton, False, True, 0)
- else:
- self.checkbutton = None
- if text_label:
- label = Gtk.Label(label=text_label)
- vbox.pack_start(label, False, True, 0)
- buff = Gtk.TextBuffer()
- self.textview = Gtk.TextView.new_with_buffer(buff)
- frame = Gtk.Frame()
- frame.set_shadow_type(Gtk.ShadowType.IN)
- frame.add(self.textview)
- vbox.pack_start(frame, False, True, 0)
- else:
- self.textview = None
- self.set_modal(False)
- self.popup()
-
- def on_response_yes(self, widget):
- if self.user_response_yes:
- if self.textview:
- buff = self.textview.get_buffer()
- start, end = buff.get_bounds()
- txt = self.textview.get_buffer().get_text(start, end, True)
-
- if isinstance(self.user_response_yes, tuple):
- if self.textview:
- self.user_response_yes[0](self.is_checked(), txt,
- *self.user_response_yes[1:])
- else:
- self.user_response_yes[0](self.is_checked(),
- *self.user_response_yes[1:])
- else:
- if self.textview:
- self.user_response_yes(self.is_checked(), txt)
- else:
- self.user_response_yes(self.is_checked())
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_no(self, widget):
- if self.user_response_no:
- if self.textview:
- buff = self.textview.get_buffer()
- start, end = buff.get_bounds()
- txt = self.textview.get_buffer().get_text(start, end, True)
-
- if isinstance(self.user_response_no, tuple):
- if self.textview:
- self.user_response_no[0](txt, *self.user_response_no[1:])
- else:
- self.user_response_no[0](*self.user_response_no[1:])
- else:
- if self.textview:
- self.user_response_no(txt)
- else:
- self.user_response_no()
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def is_checked(self):
- """
- Get active state of the checkbutton
- """
- if not self.checkbutton:
- return False
- return self.checkbutton.get_active()
-
-class ConfirmationDialogCheck(ConfirmationDialog):
- """
- HIG compliant confirmation dialog with checkbutton
- """
-
- def __init__(self, pritext, sectext='', checktext='', on_response_ok=None,
- on_response_cancel=None, is_modal=True, transient_for=None):
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
-
- if transient_for:
- parent = transient_for
- elif hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
- self.on_response_cancel)
-
- self.set_default_response(Gtk.ResponseType.OK)
-
- ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
- ok_button.grab_focus()
-
- self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
- self.get_content_area().pack_start(self.checkbutton, False, True, 0)
- self.set_modal(is_modal)
- self.popup()
-
- def on_response_ok(self, widget):
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](self.is_checked(),
- *self.user_response_ok[1:])
- else:
- self.user_response_ok(self.is_checked())
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_cancel(self, widget):
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](self.is_checked(),
- *self.user_response_cancel[1:])
- else:
- self.user_response_cancel(self.is_checked())
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def is_checked(self):
- """
- Get active state of the checkbutton
- """
- return self.checkbutton.get_active()
-
-class ConfirmationDialogDoubleCheck(ConfirmationDialog):
- """
- HIG compliant confirmation dialog with 2 checkbuttons
- """
-
- def __init__(self, pritext, sectext='', checktext1='', checktext2='',
- tooltip1='', tooltip2='', on_response_ok=None, on_response_cancel=None,
- is_modal=True):
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
-
- if hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
- self.on_response_cancel)
-
- self.set_default_response(Gtk.ResponseType.OK)
-
- ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
- ok_button.grab_focus()
-
- vbox = self.get_content_area()
- if checktext1:
- self.checkbutton1 = Gtk.CheckButton.new_with_mnemonic(checktext1)
- if tooltip1:
- self.checkbutton1.set_tooltip_text(tooltip1)
- vbox.pack_start(self.checkbutton1, False, True, 0)
- else:
- self.checkbutton1 = None
- if checktext2:
- self.checkbutton2 = Gtk.CheckButton.new_with_mnemonic(checktext2)
- if tooltip2:
- self.checkbutton2.set_tooltip_text(tooltip2)
- vbox.pack_start(self.checkbutton2, False, True, 0)
- else:
- self.checkbutton2 = None
-
- self.set_modal(is_modal)
- self.popup()
-
- def on_response_ok(self, widget):
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](self.is_checked(),
- *self.user_response_ok[1:])
- else:
- self.user_response_ok(self.is_checked())
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_cancel(self, widget):
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](*self.user_response_cancel[1:])
- else:
- self.user_response_cancel()
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def is_checked(self):
- ''' Get active state of the checkbutton '''
- if self.checkbutton1:
- is_checked_1 = self.checkbutton1.get_active()
- else:
- is_checked_1 = False
- if self.checkbutton2:
- is_checked_2 = self.checkbutton2.get_active()
- else:
- is_checked_2 = False
- return [is_checked_1, is_checked_2]
-
-class PlainConnectionDialog(ConfirmationDialogDoubleCheck):
- """
- Dialog that is shown when using an insecure connection
- """
- def __init__(self, account, on_ok, on_cancel):
- pritext = _('Insecure connection')
- sectext = _('You are about to connect to the account %(account)s '
- '(%(server)s) insecurely. This means conversations will not be '
- 'encrypted, and is strongly discouraged.\nAre you sure you want '
- 'to do that?') % {'account': account,
- 'server': app.get_hostname_from_account(account)}
- checktext1 = _('Yes, I really want to connect insecurely')
- tooltip1 = _('Gajim will NOT connect unless you check this box')
- checktext2 = _('_Do not ask me again')
- ConfirmationDialogDoubleCheck.__init__(self, pritext, sectext,
- checktext1, checktext2, tooltip1=tooltip1, on_response_ok=on_ok,
- on_response_cancel=on_cancel, is_modal=False)
- self.ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
- self.ok_button.set_sensitive(False)
- self.checkbutton1.connect('clicked', self.on_checkbutton_clicked)
- self.set_title(_('Insecure connection'))
-
- def on_checkbutton_clicked(self, widget):
- self.ok_button.set_sensitive(widget.get_active())
-
-class ConfirmationDialogDoubleRadio(ConfirmationDialog):
- """
- HIG compliant confirmation dialog with 2 radios
- """
-
- def __init__(self, pritext, sectext='', radiotext1='', radiotext2='',
- on_response_ok=None, on_response_cancel=None, is_modal=True, transient_for=None):
- self.user_response_ok = on_response_ok
- self.user_response_cancel = on_response_cancel
-
- if transient_for is None:
- transient_for = app.app.get_active_window()
- HigDialog.__init__(self, transient_for, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
- self.on_response_cancel)
-
- self.set_default_response(Gtk.ResponseType.OK)
-
- ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
- ok_button.grab_focus()
-
- vbox = self.get_content_area()
- self.radiobutton1 = Gtk.RadioButton(label=radiotext1)
- vbox.pack_start(self.radiobutton1, False, True, 0)
-
- self.radiobutton2 = Gtk.RadioButton(group=self.radiobutton1,
- label=radiotext2)
- vbox.pack_start(self.radiobutton2, False, True, 0)
-
- self.set_modal(is_modal)
- self.popup()
-
- def on_response_ok(self, widget):
- if self.user_response_ok:
- if isinstance(self.user_response_ok, tuple):
- self.user_response_ok[0](self.is_checked(),
- *self.user_response_ok[1:])
- else:
- self.user_response_ok(self.is_checked())
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def on_response_cancel(self, widget):
- if self.user_response_cancel:
- if isinstance(self.user_response_cancel, tuple):
- self.user_response_cancel[0](*self.user_response_cancel[1:])
- else:
- self.user_response_cancel()
- self.call_cancel_on_destroy = False
- self.destroy()
-
- def is_checked(self):
- ''' Get active state of the checkbutton '''
- if self.radiobutton1:
- is_checked_1 = self.radiobutton1.get_active()
- else:
- is_checked_1 = False
- if self.radiobutton2:
- is_checked_2 = self.radiobutton2.get_active()
- else:
- is_checked_2 = False
- return [is_checked_1, is_checked_2]
-
-class FTOverwriteConfirmationDialog(ConfirmationDialog):
- """
- HIG compliant confirmation dialog to overwrite or resume a file transfert
- """
-
- def __init__(self, pritext, sectext='', propose_resume=True,
- on_response=None, transient_for=None):
- if transient_for:
- parent = transient_for
- elif hasattr(app.interface, 'roster') and app.interface.roster:
- parent = app.interface.roster.window
- else:
- parent = None
- HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
- Gtk.ButtonsType.CANCEL, pritext, sectext)
-
- self.on_response = on_response
-
- if propose_resume:
- b = Gtk.Button(label='', stock=Gtk.STOCK_REFRESH)
- align = b.get_children()[0]
- hbox = align.get_children()[0]
- label = hbox.get_children()[1]
- label.set_text(_('_Resume'))
- label.set_use_underline(True)
- self.add_action_widget(b, 100)
-
- b = Gtk.Button(label='', stock=Gtk.STOCK_SAVE_AS)
- align = b.get_children()[0]
- hbox = align.get_children()[0]
- label = hbox.get_children()[1]
- label.set_text(_('Re_place'))
- label.set_use_underline(True)
- self.add_action_widget(b, 200)
-
- self.connect('response', self.on_dialog_response)
- self.show_all()
-
- def on_dialog_response(self, dialog, response):
- if self.on_response:
- if isinstance(self.on_response, tuple):
- self.on_response[0](response, *self.on_response[1:])
- else:
- self.on_response(response)
- self.call_cancel_on_destroy = False
- self.destroy()
-
-class CommonInputDialog:
- """
- Common Class for Input dialogs
- """
-
- def __init__(self, title, label_str, is_modal, ok_handler, cancel_handler,
- transient_for=None):
- self.dialog = self.xml.get_object('input_dialog')
- label = self.xml.get_object('label')
- self.dialog.set_title(title)
- label.set_markup(label_str)
- self.cancel_handler = cancel_handler
- self.vbox = self.xml.get_object('vbox')
- if transient_for:
- self.dialog.set_transient_for(transient_for)
- else:
- self.dialog.set_transient_for(app.interface.roster.window)
-
- self.ok_handler = ok_handler
- okbutton = self.xml.get_object('okbutton')
- okbutton.connect('clicked', self.on_okbutton_clicked)
- cancelbutton = self.xml.get_object('cancelbutton')
- cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
- self.xml.connect_signals(self)
- self.dialog.show_all()
-
- def on_input_dialog_destroy(self, widget):
- if self.cancel_handler:
- self.cancel_handler()
-
- def on_okbutton_clicked(self, widget):
- user_input = self.get_text()
- if user_input:
- user_input = user_input
- self.cancel_handler = None
- self.dialog.destroy()
- if isinstance(self.ok_handler, tuple):
- self.ok_handler[0](user_input, *self.ok_handler[1:])
- else:
- self.ok_handler(user_input)
-
- def on_cancelbutton_clicked(self, widget):
- self.dialog.destroy()
-
- def destroy(self):
- self.dialog.destroy()
-
-class InputDialog(CommonInputDialog):
- """
- Class for Input dialog
- """
-
- def __init__(self, title, label_str, input_str=None, is_modal=True,
- ok_handler=None, cancel_handler=None, transient_for=None):
- self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
- CommonInputDialog.__init__(self, title, label_str, is_modal,
- ok_handler, cancel_handler,
- transient_for=transient_for)
- self.input_entry = self.xml.get_object('input_entry')
- if input_str:
- self.set_entry(input_str)
-
- def on_input_dialog_delete_event(self, widget, event):
- '''
- may be implemented by subclasses
- '''
- pass
-
- def set_entry(self, value):
- self.input_entry.set_text(value)
- self.input_entry.select_region(0, -1) # select all
-
- def get_text(self):
- return self.input_entry.get_text()
-
-class InputDialogCheck(InputDialog):
- """
- Class for Input dialog
- """
-
- def __init__(self, title, label_str, checktext='', input_str=None,
- is_modal=True, ok_handler=None, cancel_handler=None,
- transient_for=None):
- self.xml = gtkgui_helpers.get_gtk_builder('input_dialog.ui')
- InputDialog.__init__(self, title, label_str, input_str=input_str,
- is_modal=is_modal, ok_handler=ok_handler,
- cancel_handler=cancel_handler,
- transient_for=transient_for)
- self.input_entry = self.xml.get_object('input_entry')
- if input_str:
- self.input_entry.set_text(input_str)
- self.input_entry.select_region(0, -1) # select all
-
- if checktext:
- self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
- self.vbox.pack_start(self.checkbutton, False, True, 0)
- self.checkbutton.show()
-
- def on_okbutton_clicked(self, widget):
- user_input = self.get_text()
- if user_input:
- user_input = user_input
- self.cancel_handler = None
- self.dialog.destroy()
- if isinstance(self.ok_handler, tuple):
- self.ok_handler[0](user_input, self.is_checked(), *self.ok_handler[1:])
- else:
- self.ok_handler(user_input, self.is_checked())
-
- def get_text(self):
- return self.input_entry.get_text()
-
- def is_checked(self):
- """
- Get active state of the checkbutton
- """
- try:
- return self.checkbutton.get_active()
- except Exception:
- # There is no checkbutton
- return False
-
-class ChangeNickDialog(InputDialogCheck):
- """
- Class for changing room nickname in case of conflict
- """
-
- def __init__(self, account, room_jid, title, prompt, check_text=None,
- change_nick=False, transient_for=None):
- """
- change_nick must be set to True when we are already occupant of the room
- and we are changing our nick
- """
- InputDialogCheck.__init__(self, title, '', checktext=check_text,
- input_str='', is_modal=True, ok_handler=None,
- cancel_handler=None,
- transient_for=transient_for)
- self.room_queue = [(account, room_jid, prompt, change_nick)]
- self.check_next()
-
- def on_input_dialog_delete_event(self, widget, event):
- self.on_cancelbutton_clicked(widget)
- return True
-
- def setup_dialog(self):
- self.gc_control = app.interface.msg_win_mgr.get_gc_control(
- self.room_jid, self.account)
- if not self.gc_control and \
- self.room_jid in app.interface.minimized_controls[self.account]:
- self.gc_control = \
- app.interface.minimized_controls[self.account][self.room_jid]
- if not self.gc_control:
- self.check_next()
- return
- label = self.xml.get_object('label')
- label.set_markup(self.prompt)
- self.set_entry(self.gc_control.nick + \
- app.config.get('gc_proposed_nick_char'))
-
- def check_next(self):
- if len(self.room_queue) == 0:
- self.cancel_handler = None
- self.dialog.destroy()
- if 'change_nick_dialog' in app.interface.instances:
- del app.interface.instances['change_nick_dialog']
- return
- self.account, self.room_jid, self.prompt, self.change_nick = \
- self.room_queue.pop(0)
- self.setup_dialog()
-
- if app.new_room_nick is not None and not app.gc_connected[
- self.account][self.room_jid] and self.gc_control.nick != \
- app.new_room_nick:
- self.dialog.hide()
- self.on_ok(app.new_room_nick, True)
- else:
- self.dialog.show()
-
- def on_okbutton_clicked(self, widget):
- nick = self.get_text()
- if nick:
- nick = nick
- # send presence to room
- try:
- nick = helpers.parse_resource(nick)
- except Exception:
- # invalid char
- ErrorDialog(_('Invalid nickname'),
- _('The nickname contains invalid characters.'))
- return
- self.on_ok(nick, self.is_checked())
-
- def on_ok(self, nick, is_checked):
- if is_checked:
- app.new_room_nick = nick
- app.connections[self.account].join_gc(nick, self.room_jid, None,
- change_nick=self.change_nick)
- if app.gc_connected[self.account][self.room_jid]:
- # We are changing nick, we will change self.nick when we receive
- # presence that inform that it works
- self.gc_control.new_nick = nick
- else:
- # We are connecting, we will not get a changed nick presence so
- # change it NOW. We don't already have a nick so it's harmless
- self.gc_control.nick = nick
- self.check_next()
-
- def on_cancelbutton_clicked(self, widget):
- self.gc_control.new_nick = ''
- self.check_next()
-
- def add_room(self, account, room_jid, prompt, change_nick=False):
- if (account, room_jid, prompt, change_nick) not in self.room_queue:
- self.room_queue.append((account, room_jid, prompt, change_nick))
-
-class InputTextDialog(CommonInputDialog):
- """
- Class for multilines Input dialog (more place than InputDialog)
- """
-
- def __init__(self, title, label_str, input_str=None, is_modal=True,
- ok_handler=None, cancel_handler=None, transient_for=None):
- self.xml = gtkgui_helpers.get_gtk_builder('input_text_dialog.ui')
- CommonInputDialog.__init__(self, title, label_str, is_modal,
- ok_handler, cancel_handler,
- transient_for=transient_for)
- self.input_buffer = self.xml.get_object('input_textview').get_buffer()
- if input_str:
- self.input_buffer.set_text(input_str)
- start_iter, end_iter = self.input_buffer.get_bounds()
- self.input_buffer.select_range(start_iter, end_iter) # select all
-
- def get_text(self):
- start_iter, end_iter = self.input_buffer.get_bounds()
- return self.input_buffer.get_text(start_iter, end_iter, True)
-
-class DoubleInputDialog:
- """
- Class for Double Input dialog
- """
-
- def __init__(self, title, label_str1, label_str2, input_str1=None,
- input_str2=None, is_modal=True, ok_handler=None, cancel_handler=None,
- transient_for=None):
- self.xml = gtkgui_helpers.get_gtk_builder('dubbleinput_dialog.ui')
- self.dialog = self.xml.get_object('dubbleinput_dialog')
- label1 = self.xml.get_object('label1')
- self.input_entry1 = self.xml.get_object('input_entry1')
- label2 = self.xml.get_object('label2')
- self.input_entry2 = self.xml.get_object('input_entry2')
- self.dialog.set_title(title)
- label1.set_markup(label_str1)
- label2.set_markup(label_str2)
- self.cancel_handler = cancel_handler
- if input_str1:
- self.input_entry1.set_text(input_str1)
- self.input_entry1.select_region(0, -1) # select all
- if input_str2:
- self.input_entry2.set_text(input_str2)
- self.input_entry2.select_region(0, -1) # select all
- if transient_for:
- self.dialog.set_transient_for(transient_for)
-
- self.dialog.set_modal(is_modal)
-
- self.ok_handler = ok_handler
- okbutton = self.xml.get_object('okbutton')
- okbutton.connect('clicked', self.on_okbutton_clicked)
- cancelbutton = self.xml.get_object('cancelbutton')
- cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
- self.xml.connect_signals(self)
- self.dialog.show_all()
-
- def on_dubbleinput_dialog_destroy(self, widget):
- if not self.cancel_handler:
- return False
- if isinstance(self.cancel_handler, tuple):
- self.cancel_handler[0](*self.cancel_handler[1:])
- else:
- self.cancel_handler()
-
- def on_okbutton_clicked(self, widget):
- user_input1 = self.input_entry1.get_text()
- user_input2 = self.input_entry2.get_text()
- self.cancel_handler = None
- self.dialog.destroy()
- if not self.ok_handler:
- return
- if isinstance(self.ok_handler, tuple):
- self.ok_handler[0](user_input1, user_input2, *self.ok_handler[1:])
- else:
- self.ok_handler(user_input1, user_input2)
-
- def on_cancelbutton_clicked(self, widget):
- self.dialog.destroy()
- if not self.cancel_handler:
- return
- if isinstance(self.cancel_handler, tuple):
- self.cancel_handler[0](*self.cancel_handler[1:])
- else:
- self.cancel_handler()
class SubscriptionRequestWindow(Gtk.ApplicationWindow):
def __init__(self, jid, text, account, user_nick=None):
@@ -2311,306 +920,6 @@ class SubscriptionRequestWindow(Gtk.ApplicationWindow):
app.interface.roster.remove_contact(self.jid, self.account)
self.destroy()
-class JoinGroupchatWindow(Gtk.ApplicationWindow):
- def __init__(self, account, room_jid, password=None, automatic=None,
- transient_for=None):
- Gtk.ApplicationWindow.__init__(self)
- self.set_name('JoinGroupchat')
- self.set_application(app.app)
- self.set_show_menubar(False)
- self.set_resizable(False)
- self.set_position(Gtk.WindowPosition.CENTER)
- self.set_title(_('Join Group Chat'))
- if transient_for:
- self.set_transient_for(transient_for)
-
- self.automatic = automatic
- self.password = password
- self.requested_jid = None
- self.room_jid = room_jid
- self.account = account
-
- if self.room_jid is None:
- self.minimal_mode = False
- else:
- self.minimal_mode = True
-
- glade_objects = ['main_box', 'account_label', 'account_combo',
- 'server_label', 'server_combo', 'room_entry',
- 'recently_button', 'recently_popover',
- 'recently_treeview', 'search_button', 'password_label',
- 'password_entry', 'nick_entry', 'bookmark_switch',
- 'autojoin_switch']
-
- self.builder = gtkgui_helpers.get_gtk_builder(
- 'join_groupchat_window.ui')
- for obj in glade_objects:
- setattr(self, obj, self.builder.get_object(obj))
-
- self.add(self.main_box)
-
- # Show widgets depending on the mode the window is in
- if not self.minimal_mode:
- self.recently_button.show()
- self.search_button.show()
-
- accounts = app.get_enabled_accounts_with_labels()
- account_liststore = self.account_combo.get_model()
- for acc in accounts:
- account_liststore.append(acc)
-
- if not accounts:
- return
-
- if not self.account:
- self.account = accounts[0][0]
-
- self.builder.connect_signals(self)
- self.connect('key-press-event', self._on_key_press_event)
- self.connect('destroy', self._on_destroy)
-
- if not self.minimal_mode:
- app.ged.register_event_handler('agent-info-received', ged.GUI1,
- self._nec_agent_info_received)
- app.ged.register_event_handler('agent-info-error-received', ged.GUI1,
- self._nec_agent_info_error_received)
-
- # Hide account combobox if there is only one account
- if len(accounts) == 1:
- self.account_combo.hide()
- self.account_label.hide()
-
- self.account_combo.set_active_id(self.account)
-
- if self.minimal_mode:
- if '@' in self.room_jid:
- (room, server) = self.room_jid.split('@')
- self.room_entry.set_text(room)
- if not muc_caps_cache.supports(
- self.room_jid, 'muc_passwordprotected'):
- self.password_entry.hide()
- self.password_label.hide()
- self.nick_entry.grab_focus()
- else:
- self.password_entry.grab_focus()
- else:
- server = self.room_jid
- self.room_entry.grab_focus()
-
- self.server_combo.insert_text(0, server)
- self.server_combo.set_active(0)
-
- if self.password is not None:
- self.password_entry.set_text(self.password)
-
- # Set bookmark switch sensitive if server supports bookmarks
- acc = self.account_combo.get_active_id()
- if not app.connections[acc].private_storage_supported:
- self.bookmark_switch.set_sensitive(False)
- self.autojoin_switch.set_sensitive(False)
-
- self.show_all()
-
- def set_room(self, room_jid):
- room, server = app.get_name_and_server_from_jid(room_jid)
- self.room_entry.set_text(room)
- self.server_combo.get_child().set_text(server)
-
- def _fill_recent_and_servers(self, account):
- recently_liststore = self.recently_treeview.get_model()
- recently_liststore.clear()
- self.server_combo.remove_all()
- recent = app.get_recent_groupchats(account)
- servers = []
- for groupchat in recent:
- label = '%s@%s' % (groupchat.room, groupchat.server)
-
- recently_liststore.append([groupchat.server,
- groupchat.room,
- groupchat.nickname,
- label])
- servers.append(groupchat.server)
-
- self.recently_button.set_sensitive(bool(recent))
-
- for server in set(servers):
- self.server_combo.append_text(server)
-
- # Add own Server to ComboBox
- muc_domain = app.get_muc_domain(account)
- if muc_domain is not None:
- self.server_combo.insert_text(0, muc_domain)
-
- def _on_recent_selected(self, treeview, *args):
- (model, iter_) = treeview.get_selection().get_selected()
- self.server_combo.get_child().set_text(model[iter_][0])
- self.room_entry.set_text(model[iter_][1])
- self.nick_entry.set_text(model[iter_][2])
- self.recently_popover.popdown()
-
- def _on_account_combo_changed(self, combo):
- account = combo.get_active_id()
- self.account = account
- self.nick_entry.set_text(app.nicks[account])
- self._fill_recent_and_servers(account)
-
- def _on_jid_detection_changed(self, widget):
- text = widget.get_text()
- if text.startswith('xmpp:'):
- text = text[5:]
- if '@' in text:
- room, server = text.split('@', 1)
- server = server.split('?')[0]
- widget.set_text('')
-
- if room:
- self.room_entry.set_text(room)
-
- if server:
- self.server_combo.get_child().set_text(server)
- else:
- self.server_combo.grab_focus()
-
- def _on_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.destroy()
- if event.keyval == Gdk.KEY_Return:
- self._on_join_clicked()
- return True
-
- def _on_join_clicked(self, *args):
- account = self.account_combo.get_active_id()
- nickname = self.nick_entry.get_text()
-
- invisible_show = app.SHOW_LIST.index('invisible')
- if app.connections[account].connected == invisible_show:
- app.interface.raise_dialog('join-while-invisible')
- return
-
- server = self.server_combo.get_active_text()
- room = self.room_entry.get_text()
-
- if room == '':
- ErrorDialog(_('Invalid Room'),
- _('Please choose a room'), transient_for=self)
- return
-
- self.room_jid = '%s@%s' % (room, server)
- self.room_jid = self.room_jid.lower()
-
- if app.in_groupchat(account, self.room_jid):
- # If we already in the groupchat, join_gc_room will bring
- # it to front
- app.interface.join_gc_room(account, self.room_jid, nickname, '')
- self.destroy()
- return
-
- if nickname == '':
- ErrorDialog(_('Invalid Nickname'),
- _('Please choose a nickname'), transient_for=self)
- return
-
- try:
- helpers.parse_resource(nickname)
- except helpers.InvalidFormat as error:
- ErrorDialog(_('Invalid Nickname'), str(error), transient_for=self)
- return
-
- try:
- helpers.parse_jid(self.room_jid)
- except helpers.InvalidFormat as error:
- ErrorDialog(_('Invalid JID'), str(error), transient_for=self)
- return
-
- if not app.account_is_connected(account):
- ErrorDialog(
- _('You are not connected to the server'),
- _('You can not join a group chat unless you are connected.'),
- transient_for=self)
- return
-
- password = self.password_entry.get_text()
- self._add_bookmark(account, nickname, password)
- app.add_recent_groupchat(account, self.room_jid, nickname)
-
- if self.automatic:
- app.automatic_rooms[self.account][self.room_jid] = self.automatic
-
- app.interface.join_gc_room(account, self.room_jid, nickname, password)
- self.destroy()
-
- def _on_cancel_clicked(self, *args):
- self.destroy()
-
- def _on_bookmark_activate(self, switch, param):
- bookmark_state = switch.get_active()
- self.autojoin_switch.set_sensitive(bookmark_state)
- if not bookmark_state:
- self.autojoin_switch.set_active(False)
-
- def _add_bookmark(self, account, nickname, password):
- con = app.connections[account]
- if not con.private_storage_supported:
- return
-
- add_bookmark = self.bookmark_switch.get_active()
- if not add_bookmark:
- return
-
- autojoin = int(self.autojoin_switch.get_active())
-
- # Add as bookmark, with autojoin and not minimized
- name = app.get_nick_from_jid(self.room_jid)
- con.get_module('Bookmarks').add_bookmark(
- name, self.room_jid, autojoin, 1, password, nickname)
-
- def _on_destroy(self, *args):
- if not self.minimal_mode:
- app.ged.remove_event_handler('agent-info-received', ged.GUI1,
- self._nec_agent_info_received)
- app.ged.remove_event_handler('agent-info-error-received', ged.GUI1,
- self._nec_agent_info_error_received)
-
- def _on_search_clicked(self, widget):
- server = self.server_combo.get_active_text().strip()
- self.requested_jid = server
- app.connections[self.account].discoverInfo(server)
-
- def _nec_agent_info_error_received(self, obj):
- if obj.conn.name != self.account:
- return
- if obj.jid != self.requested_jid:
- return
- self.requested_jid = None
- ErrorDialog(_('Wrong server'),
- _('%s is not a groupchat server') % obj.jid,
- transient_for=self)
-
- def _nec_agent_info_received(self, obj):
- if obj.conn.name != self.account:
- return
- if obj.jid != self.requested_jid:
- return
- self.requested_jid = None
- if nbxmpp.NS_MUC not in obj.features:
- ErrorDialog(_('Wrong server'),
- _('%s is not a groupchat server') % obj.jid,
- transient_for=self)
- return
- if obj.jid in app.interface.instances[self.account]['disco']:
- app.interface.instances[self.account]['disco'][obj.jid].window.\
- present()
- else:
- try:
- # Object will add itself to the window dict
- from gajim.disco import ServiceDiscoveryWindow
- ServiceDiscoveryWindow(
- self.account, obj.jid,
- initial_identities=[{'category': 'conference',
- 'type': 'text'}])
- except GajimGeneralException:
- pass
-
class SynchroniseSelectAccountDialog:
def __init__(self, account):
# 'account' can be None if we are about to create our first one
@@ -2752,321 +1061,6 @@ class SynchroniseSelectContactsDialog:
iter_ = model.iter_next(iter_)
self.dialog.destroy()
-class StartChatDialog(Gtk.ApplicationWindow):
- def __init__(self):
- Gtk.ApplicationWindow.__init__(self)
- self.set_name('StartChatDialog')
- self.set_application(app.app)
- self.set_position(Gtk.WindowPosition.CENTER)
- self.set_show_menubar(False)
- self.set_title(_('Start new Conversation'))
- self.set_default_size(-1, 400)
- self.ready_to_destroy = False
-
- self.builder = gtkgui_helpers.get_gtk_builder(
- 'start_chat_dialog.ui')
- self.listbox = self.builder.get_object('listbox')
- self.search_entry = self.builder.get_object('search_entry')
- self.box = self.builder.get_object('box')
-
- self.add(self.box)
-
- self.new_contact_row_visible = False
- self.new_contact_rows = {}
- self.new_groupchat_rows = {}
- self.accounts = app.connections.keys()
- self.add_contacts()
- self.add_groupchats()
-
- self.search_entry.connect('search-changed',
- self._on_search_changed)
- self.search_entry.connect('next-match',
- self._select_new_match, 'next')
- self.search_entry.connect('previous-match',
- self._select_new_match, 'prev')
- self.search_entry.connect('stop-search',
- lambda *args: self.search_entry.set_text(''))
-
- self.listbox.set_filter_func(self._filter_func, None)
- self.listbox.set_sort_func(self._sort_func, None)
- self.listbox.connect('row-activated', self._on_row_activated)
-
- self.connect('key-press-event', self._on_key_press)
- self.connect('destroy', self._destroy)
-
- self.select_first_row()
- self.show_all()
-
- def add_contacts(self):
- show_account = len(self.accounts) > 1
- for account in self.accounts:
- self.new_contact_rows[account] = None
- for jid in app.contacts.get_jid_list(account):
- contact = app.contacts.get_contact_with_highest_priority(
- account, jid)
- if contact.is_groupchat():
- continue
- row = ContactRow(account, contact, jid,
- contact.get_shown_name(), show_account)
- self.listbox.add(row)
-
- def add_groupchats(self):
- show_account = len(self.accounts) > 1
- for account in self.accounts:
- self.new_groupchat_rows[account] = None
- con = app.connections[account]
- bookmarks = con.get_module('Bookmarks').bookmarks
- groupchats = {}
- for jid, bookmark in bookmarks.items():
- groupchats[jid] = bookmark['name']
-
- for jid in app.contacts.get_gc_list(account):
- if jid in groupchats:
- continue
- groupchats[jid] = None
-
- for jid in groupchats:
- name = groupchats[jid]
- if not name:
- name = app.get_nick_from_jid(jid)
- row = ContactRow(account, None, jid, name,
- show_account, True)
- self.listbox.add(row)
-
- def _on_row_activated(self, listbox, row):
- row = row.get_child()
- self._start_new_chat(row)
-
- def _on_key_press(self, widget, event):
- if event.keyval in (Gdk.KEY_Down, Gdk.KEY_Tab):
- self.search_entry.emit('next-match')
- return True
- elif (event.state == Gdk.ModifierType.SHIFT_MASK and
- event.keyval == Gdk.KEY_ISO_Left_Tab):
- self.search_entry.emit('previous-match')
- return True
- elif event.keyval == Gdk.KEY_Up:
- self.search_entry.emit('previous-match')
- return True
- elif event.keyval == Gdk.KEY_Escape:
- if self.search_entry.get_text() != '':
- self.search_entry.emit('stop-search')
- else:
- self.destroy()
- return True
- elif event.keyval == Gdk.KEY_Return:
- row = self.listbox.get_selected_row()
- if row is not None:
- row.emit('activate')
- return True
- else:
- self.search_entry.grab_focus_without_selecting()
-
- def _start_new_chat(self, row):
- if row.new:
- if not app.account_is_connected(row.account):
- app.interface.raise_dialog('start-chat-not-connected')
- return
- try:
- helpers.parse_jid(row.jid)
- except helpers.InvalidFormat as e:
- app.interface.raise_dialog('invalid-jid-with-error', str(e))
- return
-
- if row.groupchat:
- app.interface.join_gc_minimal(row.account, row.jid)
- else:
- app.interface.new_chat_from_jid(row.account, row.jid)
-
- self.ready_to_destroy = True
-
- def _on_search_changed(self, entry):
- search_text = entry.get_text()
- if '@' in search_text:
- self._add_new_jid_row()
- self._update_new_jid_rows(search_text)
- else:
- self._remove_new_jid_row()
- self.listbox.invalidate_filter()
-
- def _add_new_jid_row(self):
- if self.new_contact_row_visible:
- return
- for account in self.new_contact_rows:
- show_account = len(self.accounts) > 1
- row = ContactRow(account, None, '', None, show_account)
- self.new_contact_rows[account] = row
- group_row = ContactRow(account, None, '', None, show_account, True)
- self.new_groupchat_rows[account] = group_row
- self.listbox.add(row)
- self.listbox.add(group_row)
- row.get_parent().show_all()
- self.new_contact_row_visible = True
-
- def _remove_new_jid_row(self):
- if not self.new_contact_row_visible:
- return
- for account in self.new_contact_rows:
- self.listbox.remove(self.new_contact_rows[account].get_parent())
- self.listbox.remove(self.new_groupchat_rows[account].get_parent())
- self.new_contact_row_visible = False
-
- def _update_new_jid_rows(self, search_text):
- for account in self.new_contact_rows:
- self.new_contact_rows[account].update_jid(search_text)
- self.new_groupchat_rows[account].update_jid(search_text)
-
- def _select_new_match(self, entry, direction):
- selected_row = self.listbox.get_selected_row()
- index = selected_row.get_index()
-
- if direction == 'next':
- index += 1
- else:
- index -= 1
-
- while True:
- new_selected_row = self.listbox.get_row_at_index(index)
- if new_selected_row is None:
- return
- if new_selected_row.get_child_visible():
- self.listbox.select_row(new_selected_row)
- new_selected_row.grab_focus()
- return
- if direction == 'next':
- index += 1
- else:
- index -= 1
-
- def select_first_row(self):
- first_row = self.listbox.get_row_at_y(0)
- self.listbox.select_row(first_row)
-
- def _filter_func(self, row, user_data):
- search_text = self.search_entry.get_text().lower()
- search_text_list = search_text.split()
- row_text = row.get_child().get_search_text().lower()
- for text in search_text_list:
- if text not in row_text:
- GLib.timeout_add(50, self.select_first_row)
- return
- GLib.timeout_add(50, self.select_first_row)
- return True
-
- @staticmethod
- def _sort_func(row1, row2, user_data):
- name1 = row1.get_child().get_search_text()
- name2 = row2.get_child().get_search_text()
- account1 = row1.get_child().account
- account2 = row2.get_child().account
- is_groupchat1 = row1.get_child().groupchat
- is_groupchat2 = row2.get_child().groupchat
- new1 = row1.get_child().new
- new2 = row2.get_child().new
-
- result = locale.strcoll(account1.lower(), account2.lower())
- if result != 0:
- return result
-
- if new1 != new2:
- return 1 if new1 else -1
-
- if is_groupchat1 != is_groupchat2:
- return 1 if is_groupchat1 else -1
-
- return locale.strcoll(name1.lower(), name2.lower())
-
- @staticmethod
- def _destroy(*args):
- del app.interface.instances['start_chat']
-
-class ContactRow(Gtk.Grid):
- def __init__(self, account, contact, jid, name, show_account,
- groupchat=False):
- Gtk.Grid.__init__(self)
- self.set_column_spacing(12)
- self.set_size_request(260, -1)
- self.account = account
- self.account_label = app.config.get_per(
- 'accounts', account, 'account_label') or account
- self.show_account = show_account
- self.jid = jid
- self.contact = contact
- self.name = name
- self.groupchat = groupchat
- self.new = jid == ''
-
- if self.groupchat:
- muc_icon = gtkgui_helpers.get_iconset_name_for(
- 'muc-inactive' if self.new else 'muc-active')
- image = Gtk.Image.new_from_icon_name(muc_icon, Gtk.IconSize.DND)
- else:
- scale = self.get_scale_factor()
- avatar = app.contacts.get_avatar(
- account, jid, AvatarSize.ROSTER, scale)
- if avatar is None:
- image = Gtk.Image.new_from_icon_name(
- 'avatar-default', Gtk.IconSize.DND)
- else:
- image = Gtk.Image.new_from_surface(avatar)
-
- image.set_size_request(AvatarSize.ROSTER, AvatarSize.ROSTER)
- self.add(image)
-
- middle_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
- middle_box.set_hexpand(True)
-
- if self.name is None:
- if self.groupchat:
- self.name = _('New Groupchat')
- else:
- self.name = _('New Contact')
-
- self.name_label = Gtk.Label(self.name)
- self.name_label.set_ellipsize(Pango.EllipsizeMode.END)
- self.name_label.set_xalign(0)
- self.name_label.set_width_chars(25)
- self.name_label.set_halign(Gtk.Align.START)
- self.name_label.get_style_context().add_class('bold16')
-
- status = contact.show if contact else 'offline'
- css_class = helpers.get_css_show_color(status)
- if css_class is not None:
- self.name_label.get_style_context().add_class(css_class)
- middle_box.add(self.name_label)
-
- self.jid_label = Gtk.Label(jid)
- self.jid_label.set_ellipsize(Pango.EllipsizeMode.END)
- self.jid_label.set_xalign(0)
- self.jid_label.set_width_chars(25)
- self.jid_label.set_halign(Gtk.Align.START)
- middle_box.add(self.jid_label)
-
- self.add(middle_box)
-
- if show_account:
- account_label = Gtk.Label(self.account_label)
- account_label.set_halign(Gtk.Align.START)
- account_label.set_valign(Gtk.Align.START)
-
- right_box = Gtk.Box()
- right_box.set_vexpand(True)
- right_box.add(account_label)
- self.add(right_box)
-
- self.show_all()
-
- def update_jid(self, jid):
- self.jid = jid
- self.jid_label.set_text(jid)
-
- def get_search_text(self):
- if self.contact is None and not self.groupchat:
- return self.jid
- if self.show_account:
- return '%s %s %s' % (self.name, self.jid, self.account_label)
- return '%s %s' % (self.name, self.jid)
-
class ChangePasswordDialog:
def __init__(self, account, on_response, transient_for=None):
# 'account' can be None if we are about to create our first one
@@ -3102,545 +1096,6 @@ class ChangePasswordDialog:
dialog.destroy()
self.on_response(password1)
-class SingleMessageWindow:
- """
- SingleMessageWindow can send or show a received singled message depending on
- action argument which can be 'send' or 'receive'
- """
- # Keep a reference on windows so garbage collector don't restroy them
- instances = []
- def __init__(self, account, to='', action='', from_whom='', subject='',
- message='', resource='', session=None, form_node=None):
- self.instances.append(self)
- self.account = account
- self.action = action
-
- self.subject = subject
- self.message = message
- self.to = to
- self.from_whom = from_whom
- self.resource = resource
- self.session = session
-
- self.xml = gtkgui_helpers.get_gtk_builder('single_message_window.ui')
- self.window = self.xml.get_object('single_message_window')
- self.count_chars_label = self.xml.get_object('count_chars_label')
- self.from_label = self.xml.get_object('from_label')
- self.from_entry = self.xml.get_object('from_entry')
- self.to_label = self.xml.get_object('to_label')
- self.to_entry = self.xml.get_object('to_entry')
- self.subject_entry = self.xml.get_object('subject_entry')
- self.message_scrolledwindow = self.xml.get_object(
- 'message_scrolledwindow')
- self.message_textview = self.xml.get_object('message_textview')
- self.message_tv_buffer = self.message_textview.get_buffer()
- self.conversation_scrolledwindow = self.xml.get_object(
- 'conversation_scrolledwindow')
- self.conversation_textview = conversation_textview.ConversationTextview(
- account, used_in_history_window=True)
- self.conversation_textview.tv.show()
- self.conversation_tv_buffer = self.conversation_textview.tv.get_buffer()
- self.xml.get_object('conversation_scrolledwindow').add(
- self.conversation_textview.tv)
-
- self.form_widget = None
- parent_box = self.xml.get_object('conversation_scrolledwindow').\
- get_parent()
- if form_node:
- dataform = dataforms.ExtendForm(node=form_node)
- dataform.type_ = 'submit'
- self.form_widget = dataforms_widget.DataFormWidget(dataform)
- self.form_widget.show_all()
- parent_box.add(self.form_widget)
- parent_box.child_set_property(self.form_widget, 'position',
- parent_box.child_get_property(self.xml.get_object(
- 'conversation_scrolledwindow'), 'position'))
- self.action = 'form'
-
- self.send_button = self.xml.get_object('send_button')
- self.reply_button = self.xml.get_object('reply_button')
- self.send_and_close_button = self.xml.get_object('send_and_close_button')
- self.cancel_button = self.xml.get_object('cancel_button')
- self.close_button = self.xml.get_object('close_button')
- self.message_tv_buffer.connect('changed', self.update_char_counter)
- if isinstance(to, list):
- jid = ', '.join( [i[0].get_full_jid() for i in to])
- self.to_entry.set_text(jid)
- self.to_entry.set_sensitive(False)
- else:
- self.to_entry.set_text(to)
-
- if app.config.get('use_speller') and app.is_installed('GSPELL') and action == 'send':
- lang = app.config.get('speller_language')
- gspell_lang = Gspell.language_lookup(lang)
- if gspell_lang is None:
- AspellDictError(lang)
- else:
- spell_buffer = Gspell.TextBuffer.get_from_gtk_text_buffer(
- self.message_textview.get_buffer())
- spell_buffer.set_spell_checker(Gspell.Checker.new(gspell_lang))
- spell_view = Gspell.TextView.get_from_gtk_text_view(
- self.message_textview)
- spell_view.set_inline_spell_checking(True)
- spell_view.set_enable_language_menu(True)
-
- self.prepare_widgets_for(self.action)
-
- # set_text(None) raises TypeError exception
- if self.subject is None:
- self.subject = ''
- self.subject_entry.set_text(self.subject)
-
-
- if to == '':
- liststore = gtkgui_helpers.get_completion_liststore(self.to_entry)
- self.completion_dict = helpers.get_contact_dict_for_account(account)
- keys = sorted(self.completion_dict.keys())
- for jid in keys:
- contact = self.completion_dict[jid]
- status_icon = gtkgui_helpers.get_iconset_name_for(contact.show)
- liststore.append((status_icon, jid))
- else:
- self.completion_dict = {}
- self.xml.connect_signals(self)
-
- # get window position and size from config
- gtkgui_helpers.resize_window(self.window,
- app.config.get('single-msg-width'),
- app.config.get('single-msg-height'))
- gtkgui_helpers.move_window(self.window,
- app.config.get('single-msg-x-position'),
- app.config.get('single-msg-y-position'))
-
- self.window.show_all()
-
- def on_single_message_window_destroy(self, widget):
- self.instances.remove(self)
- c = app.contacts.get_contact_with_highest_priority(self.account,
- self.from_whom)
- if not c:
- # Groupchat is maybe already destroyed
- return
- if c.is_groupchat() and not self.from_whom in \
- app.interface.minimized_controls[self.account] and self.action == \
- 'receive' and app.events.get_nb_roster_events(self.account,
- self.from_whom, types=['chat', 'normal']) == 0:
- app.interface.roster.remove_groupchat(self.from_whom, self.account)
-
- def set_cursor_to_end(self):
- end_iter = self.message_tv_buffer.get_end_iter()
- self.message_tv_buffer.place_cursor(end_iter)
-
- def save_pos(self):
- # save the window size and position
- x, y = self.window.get_position()
- app.config.set('single-msg-x-position', x)
- app.config.set('single-msg-y-position', y)
- width, height = self.window.get_size()
- app.config.set('single-msg-width', width)
- app.config.set('single-msg-height', height)
-
- def on_single_message_window_delete_event(self, window, ev):
- self.save_pos()
-
- def prepare_widgets_for(self, action):
- if len(app.connections) > 1:
- if action == 'send':
- title = _('Single Message using account %s') % self.account
- else:
- title = _('Single Message in account %s') % self.account
- else:
- title = _('Single Message')
-
- if action == 'send': # prepare UI for Sending
- title = _('Send %s') % title
- self.send_button.show()
- self.send_and_close_button.show()
- self.to_label.show()
- self.to_entry.show()
- self.reply_button.hide()
- self.from_label.hide()
- self.from_entry.hide()
- self.conversation_scrolledwindow.hide()
- self.message_scrolledwindow.show()
-
- if self.message: # we come from a reply?
- self.message_textview.grab_focus()
- self.cancel_button.hide()
- self.close_button.show()
- self.message_tv_buffer.set_text(self.message)
- GLib.idle_add(self.set_cursor_to_end)
- else: # we write a new message (not from reply)
- self.close_button.hide()
- if self.to: # do we already have jid?
- self.subject_entry.grab_focus()
-
- elif action == 'receive': # prepare UI for Receiving
- title = _('Received %s') % title
- self.reply_button.show()
- self.from_label.show()
- self.from_entry.show()
- self.send_button.hide()
- self.send_and_close_button.hide()
- self.to_label.hide()
- self.to_entry.hide()
- self.conversation_scrolledwindow.show()
- self.message_scrolledwindow.hide()
-
- if self.message:
- self.conversation_textview.print_real_text(self.message)
- fjid = self.from_whom
- if self.resource:
- fjid += '/' + self.resource # Full jid of sender (with resource)
- self.from_entry.set_text(fjid)
- self.from_entry.set_property('editable', False)
- self.subject_entry.set_property('editable', False)
- self.reply_button.grab_focus()
- self.cancel_button.hide()
- self.close_button.show()
- elif action == 'form': # prepare UI for Receiving
- title = _('Form %s') % title
- self.send_button.show()
- self.send_and_close_button.show()
- self.to_label.show()
- self.to_entry.show()
- self.reply_button.hide()
- self.from_label.hide()
- self.from_entry.hide()
- self.conversation_scrolledwindow.hide()
- self.message_scrolledwindow.hide()
-
- self.window.set_title(title)
-
- def on_cancel_button_clicked(self, widget):
- self.save_pos()
- self.window.destroy()
-
- def on_close_button_clicked(self, widget):
- self.save_pos()
- self.window.destroy()
-
- def update_char_counter(self, widget):
- characters_no = self.message_tv_buffer.get_char_count()
- self.count_chars_label.set_text(str(characters_no))
-
- def send_single_message(self):
- if app.connections[self.account].connected <= 1:
- # if offline or connecting
- ErrorDialog(_('Connection not available'),
- _('Please make sure you are connected with "%s".') % self.account)
- return True
- if isinstance(self.to, list):
- sender_list = []
- for i in self.to:
- if i[0].resource:
- sender_list.append(i[0].jid + '/' + i[0].resource)
- else:
- sender_list.append(i[0].jid)
- else:
- sender_list = [j.strip() for j in self.to_entry.get_text().split(
- ',')]
-
- subject = self.subject_entry.get_text()
- begin, end = self.message_tv_buffer.get_bounds()
- message = self.message_tv_buffer.get_text(begin, end, True)
-
- if self.form_widget:
- form_node = self.form_widget.data_form
- else:
- form_node = None
-
- recipient_list = []
-
- for to_whom_jid in sender_list:
- if to_whom_jid in self.completion_dict:
- to_whom_jid = self.completion_dict[to_whom_jid].jid
- try:
- to_whom_jid = helpers.parse_jid(to_whom_jid)
- except helpers.InvalidFormat:
- ErrorDialog(_('Invalid JID'),
- _('It is not possible to send a message to %s, this JID is not '
- 'valid.') % to_whom_jid)
- return True
-
- if '/announce/' in to_whom_jid:
- app.connections[self.account].send_motd(to_whom_jid, subject,
- message)
- continue
-
- recipient_list.append(to_whom_jid)
-
- app.nec.push_outgoing_event(MessageOutgoingEvent(None,
- account=self.account, jid=recipient_list, message=message,
- type_='normal', subject=subject, form_node=form_node))
-
- self.subject_entry.set_text('') # we sent ok, clear the subject
- self.message_tv_buffer.set_text('') # we sent ok, clear the textview
-
- def on_send_button_clicked(self, widget):
- self.send_single_message()
-
- def on_reply_button_clicked(self, widget):
- # we create a new blank window to send and we preset RE: and to jid
- self.subject = _('RE: %s') % self.subject
- self.message = _('%s wrote:\n') % self.from_whom + self.message
- # add > at the begining of each line
- self.message = self.message.replace('\n', '\n> ') + '\n\n'
- self.window.destroy()
- SingleMessageWindow(self.account, to=self.from_whom, action='send',
- from_whom=self.from_whom, subject=self.subject, message=self.message,
- session=self.session)
-
- def on_send_and_close_button_clicked(self, widget):
- if self.send_single_message():
- return
- self.save_pos()
- self.window.destroy()
-
- def on_single_message_window_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape: # ESCAPE
- self.save_pos()
- self.window.destroy()
-
-
-class XMLConsoleWindow(Gtk.Window):
- def __init__(self, account):
- Gtk.Window.__init__(self)
- self.account = account
- self.enabled = True
- self.presence = True
- self.message = True
- self.iq = True
- self.stream = True
- self.incoming = True
- self.outgoing = True
- self.filter_dialog = None
-
- glade_objects = ['textview', 'input', 'scrolled_input', 'headerbar',
- 'scrolled', 'actionbar', 'paned', 'box', 'menubutton']
- self.builder = gtkgui_helpers.get_gtk_builder('xml_console_window.ui')
- for obj in glade_objects:
- setattr(self, obj, self.builder.get_object(obj))
-
- self.set_titlebar(self.headerbar)
- jid = app.get_jid_from_account(account)
- self.headerbar.set_subtitle(jid)
- self.set_default_size(600, 600)
- self.add(self.box)
-
- self.paned.set_position(self.paned.get_property('max-position'))
-
- button = gtkgui_helpers.get_image_button(
- 'edit-clear-all-symbolic', _('Clear'))
- button.connect('clicked', self.on_clear)
- self.actionbar.pack_start(button)
-
- button = gtkgui_helpers.get_image_button(
- 'applications-system-symbolic', _('Filter'))
- button.connect('clicked', self.on_filter_options)
- self.actionbar.pack_start(button)
-
- button = gtkgui_helpers.get_image_button(
- 'document-edit-symbolic', _('XML Input'), toggle=True)
- button.connect('toggled', self.on_input)
- self.actionbar.pack_start(button)
-
- button = gtkgui_helpers.get_image_button('emblem-ok-symbolic', _('Send'))
- button.connect('clicked', self.on_send)
- self.actionbar.pack_end(button)
-
- self.actionbar.pack_start(self.menubutton)
-
- self.create_tags()
- self.show_all()
-
- self.scrolled_input.hide()
- self.menubutton.hide()
-
- self.connect("destroy", self.on_destroy)
- self.connect('key_press_event', self.on_key_press_event)
- self.builder.connect_signals(self)
-
- app.ged.register_event_handler('stanza-received', ged.GUI1,
- self._nec_stanza_received)
- app.ged.register_event_handler('stanza-sent', ged.GUI1,
- self._nec_stanza_sent)
-
- def create_tags(self):
- buffer_ = self.textview.get_buffer()
- in_color = app.config.get('inmsgcolor')
- out_color = app.config.get('outmsgcolor')
-
- tags = ['presence', 'message', 'stream', 'iq']
-
- tag = buffer_.create_tag('incoming')
- tag.set_property('foreground', in_color)
- tag = buffer_.create_tag('outgoing')
- tag.set_property('foreground', out_color)
-
- for tag_name in tags:
- buffer_.create_tag(tag_name)
-
- def on_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.destroy()
-
- def on_row_activated(self, listbox, row):
- text = row.get_child().get_text()
- input_text = None
- if text == 'Presence':
- input_text = (
- '\n'
- '\n'
- '\n'
- '\n'
- '')
- elif text == 'Message':
- input_text = (
- '\n'
- '\n'
- '')
- elif text == 'Iq':
- input_text = (
- '\n'
- '\n'
- '')
-
- if input_text is not None:
- buffer_ = self.input.get_buffer()
- buffer_.set_text(input_text)
- self.input.grab_focus()
-
- def on_send(self, *args):
- if app.connections[self.account].connected <= 1:
- # if offline or connecting
- ErrorDialog(_('Connection not available'),
- _('Please make sure you are connected with "%s".') % \
- self.account)
- return
- buffer_ = self.input.get_buffer()
- begin_iter, end_iter = buffer_.get_bounds()
- stanza = buffer_.get_text(begin_iter, end_iter, True)
- if stanza:
- try:
- node = nbxmpp.Protocol(node=stanza)
- if node.getNamespace() == 'http://www.gajim.org/xmlns/undeclared':
- node.setNamespace(nbxmpp.NS_CLIENT)
- except Exception as error:
- ErrorDialog(_('Invalid Node'), str(error))
- return
- app.connections[self.account].connection.send(node)
- buffer_.set_text('')
-
- def on_input(self, button, *args):
- if button.get_active():
- self.paned.get_child2().show()
- self.menubutton.show()
- self.input.grab_focus()
- else:
- self.paned.get_child2().hide()
- self.menubutton.hide()
-
- def on_filter_options(self, *args):
- if self.filter_dialog:
- self.filter_dialog.present()
- return
- options = [
- Option(OptionKind.SWITCH, 'Presence',
- OptionType.VALUE, self.presence,
- callback=self.on_option, data='presence'),
-
- Option(OptionKind.SWITCH, 'Message',
- OptionType.VALUE, self.message,
- callback=self.on_option, data='message'),
-
- Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq,
- callback=self.on_option, data='iq'),
-
- Option(OptionKind.SWITCH, 'Stream\nManagement',
- OptionType.VALUE, self.stream,
- callback=self.on_option, data='stream'),
-
- Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming,
- callback=self.on_option, data='incoming'),
-
- Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing,
- callback=self.on_option, data='outgoing'),
- ]
-
- self.filter_dialog = OptionsDialog(self, 'Filter',
- Gtk.DialogFlags.DESTROY_WITH_PARENT,
- options, self.account)
- self.filter_dialog.connect('destroy', self.on_filter_destroyed)
-
- def on_filter_destroyed(self, win):
- self.filter_dialog = None
-
- def on_clear(self, *args):
- buffer_ = self.textview.get_buffer().set_text('')
-
- def on_destroy(self, *args):
- del app.interface.instances[self.account]['xml_console']
- app.ged.remove_event_handler('stanza-received', ged.GUI1,
- self._nec_stanza_received)
- app.ged.remove_event_handler('stanza-sent', ged.GUI1,
- self._nec_stanza_sent)
-
- def on_enable(self, switch, param):
- self.enabled = switch.get_active()
-
- def on_option(self, value, data):
- setattr(self, data, value)
- value = not value
- table = self.textview.get_buffer().get_tag_table()
- tag = table.lookup(data)
- if data in ('incoming', 'outgoing'):
- if value:
- tag.set_priority(table.get_size() - 1)
- else:
- tag.set_priority(0)
- tag.set_property('invisible', value)
-
- def _nec_stanza_received(self, obj):
- if obj.conn.name != self.account:
- return
- self.print_stanza(obj.stanza_str, 'incoming')
-
- def _nec_stanza_sent(self, obj):
- if obj.conn.name != self.account:
- return
- self.print_stanza(obj.stanza_str, 'outgoing')
-
- def print_stanza(self, stanza, kind):
- # kind must be 'incoming' or 'outgoing'
- if not self.enabled:
- return
- if not stanza:
- return
-
- at_the_end = gtkgui_helpers.at_the_end(self.scrolled)
-
- buffer_ = self.textview.get_buffer()
- end_iter = buffer_.get_end_iter()
-
- type_ = kind
- if stanza.startswith('<', '>\n<'))
- buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)
-
- if at_the_end:
- GLib.idle_add(gtkgui_helpers.scroll_to_end, self.scrolled)
#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
@@ -3871,532 +1326,6 @@ class RosterItemExchangeWindow:
self.window.destroy()
-class PrivacyListWindow:
- """
- Window that is used for creating NEW or EDITING already there privacy lists
- """
-
- def __init__(self, account, privacy_list_name, action):
- '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
- or edit an already existing one'''
- self.account = account
- self.privacy_list_name = privacy_list_name
-
- # Dicts and Default Values
- self.active_rule = ''
- self.global_rules = {}
- self.list_of_groups = {}
-
- self.max_order = 0
-
- # Default Edit Values
- self.edit_rule_type = 'jid'
- self.allow_deny = 'allow'
-
- # Connect to gtk builder
- self.xml = gtkgui_helpers.get_gtk_builder('privacy_list_window.ui')
- self.window = self.xml.get_object('privacy_list_edit_window')
-
- # Add Widgets
-
- for widget_to_add in ('title_hbox', 'privacy_lists_title_label',
- 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
- 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
- 'list_of_rules_combobox', 'delete_open_buttons_hbox',
- 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
- 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
- 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
- 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
- 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
- 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
- 'edit_view_status_checkbutton', 'edit_all_checkbutton',
- 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
- 'privacy_list_refresh_button', 'privacy_list_close_button',
- 'edit_send_status_checkbutton', 'add_edit_vbox',
- 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
- self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
-
- self.privacy_lists_title_label.set_label(
- _('Privacy List %s') % \
- GLib.markup_escape_text(self.privacy_list_name))
-
- if len(app.connections) > 1:
- title = _('Privacy List for %s') % self.account
- else:
- title = _('Privacy List')
-
- self.delete_rule_button.set_sensitive(False)
- self.open_rule_button.set_sensitive(False)
- self.privacy_list_active_checkbutton.set_sensitive(False)
- self.privacy_list_default_checkbutton.set_sensitive(False)
- self.list_of_rules_combobox.set_sensitive(False)
-
- # set jabber id completion
- jids_list_store = Gtk.ListStore(GObject.TYPE_STRING)
- for jid in app.contacts.get_jid_list(self.account):
- jids_list_store.append([jid])
- jid_entry_completion = Gtk.EntryCompletion()
- jid_entry_completion.set_text_column(0)
- jid_entry_completion.set_model(jids_list_store)
- jid_entry_completion.set_popup_completion(True)
- self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
- if action == 'EDIT':
- self.refresh_rules()
-
- model = self.edit_type_group_combobox.get_model()
- count = 0
- for group in app.groups[self.account]:
- self.list_of_groups[group] = count
- count += 1
- model.append([group])
- self.edit_type_group_combobox.set_active(0)
-
- self.window.set_title(title)
-
- app.ged.register_event_handler('privacy-list-received', ged.GUI1,
- self._nec_privacy_list_received)
- app.ged.register_event_handler('privacy-lists-received', ged.GUI1,
- self._nec_privacy_lists_received)
-
- self.window.show_all()
- self.add_edit_vbox.hide()
-
- self.xml.connect_signals(self)
-
- def on_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.window.destroy()
-
- def on_privacy_list_edit_window_destroy(self, widget):
- key_name = 'privacy_list_%s' % self.privacy_list_name
- if key_name in app.interface.instances[self.account]:
- del app.interface.instances[self.account][key_name]
- app.ged.remove_event_handler('privacy-list-received', ged.GUI1,
- self._nec_privacy_list_received)
- app.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
- self._nec_privacy_lists_received)
-
- def _nec_privacy_lists_received(self, obj):
- if obj.conn.name != self.account:
- return
- if obj.active_list == self.privacy_list_name:
- self.privacy_list_active_checkbutton.set_active(True)
- else:
- self.privacy_list_active_checkbutton.set_active(False)
- if obj.default_list == self.privacy_list_name:
- self.privacy_list_default_checkbutton.set_active(True)
- else:
- self.privacy_list_default_checkbutton.set_active(False)
-
- def privacy_list_received(self, rules):
- model = self.list_of_rules_combobox.get_model()
- model.clear()
- self.global_rules = {}
- for rule in rules:
- if 'type' in rule:
- text_item = _('Order: %(order)s, action: %(action)s, type: %(type)s'
- ', value: %(value)s') % {'order': rule['order'],
- 'action': rule['action'], 'type': rule['type'],
- 'value': rule['value']}
- else:
- text_item = _('Order: %(order)s, action: %(action)s') % \
- {'order': rule['order'], 'action': rule['action']}
- if int(rule['order']) > self.max_order:
- self.max_order = int(rule['order'])
- self.global_rules[text_item] = rule
- model.append([text_item])
- if len(rules) == 0:
- self.title_hbox.set_sensitive(False)
- self.list_of_rules_combobox.set_sensitive(False)
- self.delete_rule_button.set_sensitive(False)
- self.open_rule_button.set_sensitive(False)
- self.privacy_list_active_checkbutton.set_sensitive(False)
- self.privacy_list_default_checkbutton.set_sensitive(False)
- else:
- self.list_of_rules_combobox.set_active(0)
- self.title_hbox.set_sensitive(True)
- self.list_of_rules_combobox.set_sensitive(True)
- self.delete_rule_button.set_sensitive(True)
- self.open_rule_button.set_sensitive(True)
- self.privacy_list_active_checkbutton.set_sensitive(True)
- self.privacy_list_default_checkbutton.set_sensitive(True)
- self.reset_fields()
- con = app.connections[self.account]
- con.get_module('PrivacyLists').get_privacy_lists()
-
- def _nec_privacy_list_received(self, obj):
- if obj.conn.name != self.account:
- return
- if obj.list_name != self.privacy_list_name:
- return
- self.privacy_list_received(obj.rules)
-
- def refresh_rules(self):
- con = app.connections[self.account]
- con.get_module('PrivacyLists').get_privacy_list(self.privacy_list_name)
-
- def on_delete_rule_button_clicked(self, widget):
- model = self.list_of_rules_combobox.get_model()
- iter_ = self.list_of_rules_combobox.get_active_iter()
- _rule = model[iter_][0]
- tags = []
- for rule in self.global_rules:
- if rule != _rule:
- tags.append(self.global_rules[rule])
- con = app.connections[self.account]
- con.get_module('PrivacyLists').set_privacy_list(
- self.privacy_list_name, tags)
- self.privacy_list_received(tags)
- self.add_edit_vbox.hide()
- if not tags: # we removed latest rule
- if 'privacy_lists' in app.interface.instances[self.account]:
- win = app.interface.instances[self.account]['privacy_lists']
- win.remove_privacy_list_from_combobox(self.privacy_list_name)
- win.draw_widgets()
-
- def on_open_rule_button_clicked(self, widget):
- self.add_edit_rule_label.set_label(
- _('Edit a rule'))
- active_num = self.list_of_rules_combobox.get_active()
- if active_num == -1:
- self.active_rule = ''
- else:
- model = self.list_of_rules_combobox.get_model()
- iter_ = self.list_of_rules_combobox.get_active_iter()
- self.active_rule = model[iter_][0]
- if self.active_rule != '':
- rule_info = self.global_rules[self.active_rule]
- self.edit_order_spinbutton.set_value(int(rule_info['order']))
- if 'type' in rule_info:
- if rule_info['type'] == 'jid':
- self.edit_type_jabberid_radiobutton.set_active(True)
- self.edit_type_jabberid_entry.set_text(rule_info['value'])
- elif rule_info['type'] == 'group':
- self.edit_type_group_radiobutton.set_active(True)
- if rule_info['value'] in self.list_of_groups:
- self.edit_type_group_combobox.set_active(
- self.list_of_groups[rule_info['value']])
- else:
- self.edit_type_group_combobox.set_active(0)
- elif rule_info['type'] == 'subscription':
- self.edit_type_subscription_radiobutton.set_active(True)
- sub_value = rule_info['value']
- if sub_value == 'none':
- self.edit_type_subscription_combobox.set_active(0)
- elif sub_value == 'both':
- self.edit_type_subscription_combobox.set_active(1)
- elif sub_value == 'from':
- self.edit_type_subscription_combobox.set_active(2)
- elif sub_value == 'to':
- self.edit_type_subscription_combobox.set_active(3)
- else:
- self.edit_type_select_all_radiobutton.set_active(True)
- else:
- self.edit_type_select_all_radiobutton.set_active(True)
- self.edit_send_messages_checkbutton.set_active(False)
- self.edit_queries_send_checkbutton.set_active(False)
- self.edit_view_status_checkbutton.set_active(False)
- self.edit_send_status_checkbutton.set_active(False)
- self.edit_all_checkbutton.set_active(False)
- if not rule_info['child']:
- self.edit_all_checkbutton.set_active(True)
- else:
- if 'presence-out' in rule_info['child']:
- self.edit_send_status_checkbutton.set_active(True)
- if 'presence-in' in rule_info['child']:
- self.edit_view_status_checkbutton.set_active(True)
- if 'iq' in rule_info['child']:
- self.edit_queries_send_checkbutton.set_active(True)
- if 'message' in rule_info['child']:
- self.edit_send_messages_checkbutton.set_active(True)
-
- if rule_info['action'] == 'allow':
- self.edit_allow_radiobutton.set_active(True)
- else:
- self.edit_deny_radiobutton.set_active(True)
- self.add_edit_vbox.show()
-
- def on_edit_all_checkbutton_toggled(self, widget):
- if widget.get_active():
- self.edit_send_messages_checkbutton.set_active(True)
- self.edit_queries_send_checkbutton.set_active(True)
- self.edit_view_status_checkbutton.set_active(True)
- self.edit_send_status_checkbutton.set_active(True)
- self.edit_send_messages_checkbutton.set_sensitive(False)
- self.edit_queries_send_checkbutton.set_sensitive(False)
- self.edit_view_status_checkbutton.set_sensitive(False)
- self.edit_send_status_checkbutton.set_sensitive(False)
- else:
- self.edit_send_messages_checkbutton.set_active(False)
- self.edit_queries_send_checkbutton.set_active(False)
- self.edit_view_status_checkbutton.set_active(False)
- self.edit_send_status_checkbutton.set_active(False)
- self.edit_send_messages_checkbutton.set_sensitive(True)
- self.edit_queries_send_checkbutton.set_sensitive(True)
- self.edit_view_status_checkbutton.set_sensitive(True)
- self.edit_send_status_checkbutton.set_sensitive(True)
-
- def on_privacy_list_active_checkbutton_toggled(self, widget):
- name = None
- if widget.get_active():
- name = self.privacy_list_name
-
- con = app.connections[self.account]
- con.get_module('PrivacyLists').set_active_list(name)
-
- def on_privacy_list_default_checkbutton_toggled(self, widget):
- name = None
- if widget.get_active():
- name = self.privacy_list_name
-
- con = app.connections[self.account]
- con.get_module('PrivacyLists').set_default_list(name)
-
- def on_new_rule_button_clicked(self, widget):
- self.reset_fields()
- self.add_edit_vbox.show()
-
- def reset_fields(self):
- self.edit_type_jabberid_entry.set_text('')
- self.edit_allow_radiobutton.set_active(True)
- self.edit_type_jabberid_radiobutton.set_active(True)
- self.active_rule = ''
- self.edit_send_messages_checkbutton.set_active(False)
- self.edit_queries_send_checkbutton.set_active(False)
- self.edit_view_status_checkbutton.set_active(False)
- self.edit_send_status_checkbutton.set_active(False)
- self.edit_all_checkbutton.set_active(False)
- self.edit_order_spinbutton.set_value(self.max_order + 1)
- self.edit_type_group_combobox.set_active(0)
- self.edit_type_subscription_combobox.set_active(0)
- self.add_edit_rule_label.set_label(
- _('Add a rule'))
-
- def get_current_tags(self):
- if self.edit_type_jabberid_radiobutton.get_active():
- edit_type = 'jid'
- edit_value = self.edit_type_jabberid_entry.get_text()
- elif self.edit_type_group_radiobutton.get_active():
- edit_type = 'group'
- model = self.edit_type_group_combobox.get_model()
- iter_ = self.edit_type_group_combobox.get_active_iter()
- edit_value = model[iter_][0]
- elif self.edit_type_subscription_radiobutton.get_active():
- edit_type = 'subscription'
- subs = ['none', 'both', 'from', 'to']
- edit_value = subs[self.edit_type_subscription_combobox.get_active()]
- elif self.edit_type_select_all_radiobutton.get_active():
- edit_type = ''
- edit_value = ''
- edit_order = str(self.edit_order_spinbutton.get_value_as_int())
- if self.edit_allow_radiobutton.get_active():
- edit_deny = 'allow'
- else:
- edit_deny = 'deny'
- child = []
- if not self.edit_all_checkbutton.get_active():
- if self.edit_send_messages_checkbutton.get_active():
- child.append('message')
- if self.edit_queries_send_checkbutton.get_active():
- child.append('iq')
- if self.edit_send_status_checkbutton.get_active():
- child.append('presence-out')
- if self.edit_view_status_checkbutton.get_active():
- child.append('presence-in')
- if edit_type != '':
- return {'order': edit_order, 'action': edit_deny,
- 'type': edit_type, 'value': edit_value, 'child': child}
- return {'order': edit_order, 'action': edit_deny, 'child': child}
-
- def on_save_rule_button_clicked(self, widget):
- tags=[]
- current_tags = self.get_current_tags()
- if int(current_tags['order']) > self.max_order:
- self.max_order = int(current_tags['order'])
- if self.active_rule == '':
- tags.append(current_tags)
-
- for rule in self.global_rules:
- if rule != self.active_rule:
- tags.append(self.global_rules[rule])
- else:
- tags.append(current_tags)
-
- con = app.connections[self.account]
- con.get_module('PrivacyLists').set_privacy_list(
- self.privacy_list_name, tags)
- self.refresh_rules()
- self.add_edit_vbox.hide()
- if 'privacy_lists' in app.interface.instances[self.account]:
- win = app.interface.instances[self.account]['privacy_lists']
- win.add_privacy_list_to_combobox(self.privacy_list_name)
- win.draw_widgets()
-
- def on_list_of_rules_combobox_changed(self, widget):
- self.add_edit_vbox.hide()
-
- def on_edit_type_radiobutton_changed(self, widget, radiobutton):
- active_bool = widget.get_active()
- if active_bool:
- self.edit_rule_type = radiobutton
-
- def on_edit_allow_radiobutton_changed(self, widget, radiobutton):
- active_bool = widget.get_active()
- if active_bool:
- self.allow_deny = radiobutton
-
- def on_close_button_clicked(self, widget):
- self.window.destroy()
-
-class PrivacyListsWindow:
- """
- Window that is the main window for Privacy Lists; we can list there the
- privacy lists and ask to create a new one or edit an already there one
- """
- def __init__(self, account):
- self.account = account
- self.privacy_lists_save = []
-
- self.xml = gtkgui_helpers.get_gtk_builder('privacy_lists_window.ui')
-
- self.window = self.xml.get_object('privacy_lists_first_window')
- for widget_to_add in ('list_of_privacy_lists_combobox',
- 'delete_privacy_list_button', 'open_privacy_list_button',
- 'new_privacy_list_button', 'new_privacy_list_entry',
- 'privacy_lists_refresh_button', 'close_privacy_lists_window_button'):
- self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
-
- self.draw_privacy_lists_in_combobox([])
- self.privacy_lists_refresh()
-
- self.enabled = True
-
- if len(app.connections) > 1:
- title = _('Privacy Lists for %s') % self.account
- else:
- title = _('Privacy Lists')
-
- self.window.set_title(title)
-
- app.ged.register_event_handler('privacy-lists-received', ged.GUI1,
- self._nec_privacy_lists_received)
- app.ged.register_event_handler('privacy-list-removed', ged.GUI1,
- self._nec_privacy_lists_removed)
-
- self.window.show_all()
-
- self.xml.connect_signals(self)
-
- def on_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.window.destroy()
-
- def on_privacy_lists_first_window_destroy(self, widget):
- if 'privacy_lists' in app.interface.instances[self.account]:
- del app.interface.instances[self.account]['privacy_lists']
- app.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
- self._nec_privacy_lists_received)
- app.ged.remove_event_handler('privacy-list-removed', ged.GUI1,
- self._nec_privacy_lists_removed)
-
- def remove_privacy_list_from_combobox(self, privacy_list):
- if privacy_list not in self.privacy_lists_save:
- return
-
- model = self.list_of_privacy_lists_combobox.get_model()
- for entry in model:
- if entry[0] == privacy_list:
- model.remove(entry.iter)
-
- self.privacy_lists_save.remove(privacy_list)
-
- def add_privacy_list_to_combobox(self, privacy_list):
- if privacy_list in self.privacy_lists_save:
- return
- model = self.list_of_privacy_lists_combobox.get_model()
- model.append([privacy_list])
- self.privacy_lists_save.append(privacy_list)
-
- def draw_privacy_lists_in_combobox(self, privacy_lists):
- self.list_of_privacy_lists_combobox.set_active(-1)
- self.list_of_privacy_lists_combobox.get_model().clear()
- self.privacy_lists_save = []
- for add_item in privacy_lists:
- self.add_privacy_list_to_combobox(add_item)
- self.draw_widgets()
-
- def draw_widgets(self):
- if len(self.privacy_lists_save) == 0:
- self.list_of_privacy_lists_combobox.set_sensitive(False)
- self.open_privacy_list_button.set_sensitive(False)
- self.delete_privacy_list_button.set_sensitive(False)
- else:
- self.list_of_privacy_lists_combobox.set_sensitive(True)
- self.list_of_privacy_lists_combobox.set_active(0)
- self.open_privacy_list_button.set_sensitive(True)
- self.delete_privacy_list_button.set_sensitive(True)
-
- def on_close_button_clicked(self, widget):
- self.window.destroy()
-
- def on_delete_privacy_list_button_clicked(self, widget):
- active_list = self.privacy_lists_save[
- self.list_of_privacy_lists_combobox.get_active()]
- con = app.connections[self.account]
- con.get_module('PrivacyLists').del_privacy_list(active_list)
-
- def privacy_list_removed(self, active_list):
- self.privacy_lists_save.remove(active_list)
- self.privacy_lists_received(self.privacy_lists_save)
-
- def _nec_privacy_lists_removed(self, obj):
- if obj.conn.name != self.account:
- return
- self.privacy_list_removed(obj.list_name)
-
- def privacy_lists_received(self, lists):
- privacy_lists = []
- for privacy_list in lists:
- privacy_lists.append(privacy_list)
- self.draw_privacy_lists_in_combobox(privacy_lists)
-
- def _nec_privacy_lists_received(self, obj):
- if obj.conn.name != self.account:
- return
- self.privacy_lists_received(obj.lists)
-
- def privacy_lists_refresh(self):
- con = app.connections[self.account]
- con.get_module('PrivacyLists').get_privacy_lists()
-
- def on_new_privacy_list_button_clicked(self, widget):
- name = self.new_privacy_list_entry.get_text()
- if not name:
- ErrorDialog(_('Invalid List Name'),
- _('You must enter a name to create a privacy list.'),
- transient_for=self.window)
- return
- key_name = 'privacy_list_%s' % name
- if key_name in app.interface.instances[self.account]:
- app.interface.instances[self.account][key_name].window.present()
- else:
- app.interface.instances[self.account][key_name] = \
- PrivacyListWindow(self.account, name, 'NEW')
- self.new_privacy_list_entry.set_text('')
-
- def on_privacy_lists_refresh_button_clicked(self, widget):
- self.privacy_lists_refresh()
-
- def on_open_privacy_list_button_clicked(self, widget):
- name = self.privacy_lists_save[
- self.list_of_privacy_lists_combobox.get_active()]
- key_name = 'privacy_list_%s' % name
- if key_name in app.interface.instances[self.account]:
- app.interface.instances[self.account][key_name].window.present()
- else:
- app.interface.instances[self.account][key_name] = \
- PrivacyListWindow(self.account, name, 'EDIT')
-
class InvitationReceivedDialog:
def __init__(self, account, room_jid, contact_fjid, password=None,
comment=None, is_continued=False):
@@ -4614,6 +1543,51 @@ class TransformChatToMUC:
str(randrange(9999999))
self._nec_unique_room_id_supported(obj)
+class Dialog(Gtk.Dialog):
+ def __init__(self, parent, title, buttons, default=None,
+ on_response_ok=None, on_response_cancel=None):
+ Gtk.Dialog.__init__(self, title, parent,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT)
+
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+ self.set_border_width(6)
+ self.get_content_area().set_spacing(12)
+ self.set_resizable(False)
+
+ for stock, response in buttons:
+ b = self.add_button(stock, response)
+
+ if default is not None:
+ self.set_default_response(default)
+ else:
+ self.set_default_response(buttons[-1][1])
+
+ self.connect('response', self.on_response)
+
+ def on_response(self, widget, response_id):
+ if response_id == Gtk.ResponseType.OK:
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](*self.user_response_ok[1:])
+ else:
+ self.user_response_ok()
+ self.destroy()
+ elif response_id == Gtk.ResponseType.CANCEL:
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](*self.user_response_ok[1:])
+ else:
+ self.user_response_cancel()
+ self.destroy()
+
+ def just_destroy(self, widget):
+ self.destroy()
+
+ def get_button(self, index):
+ buttons = self.get_action_area().get_children()
+ return index < len(buttons) and buttons[index] or None
+
class DataFormWindow(Dialog):
def __init__(self, form, on_response_ok):
self.df_response_ok = on_response_ok
@@ -4792,66 +1766,6 @@ class VoIPCallReceivedDialog(object):
dialog.destroy()
-class CertificatDialog(InformationDialog):
- def __init__(self, parent, account, cert):
- issuer = cert.get_issuer()
- subject = cert.get_subject()
- InformationDialog.__init__(self,
- _('Certificate for account %s') % account, _('''Issued to:
-Common Name (CN): %(scn)s
-Organization (O): %(sorg)s
-Organizationl Unit (OU): %(sou)s
-Serial Number: %(sn)s
-
-Issued by:
-Common Name (CN): %(icn)s
-Organization (O): %(iorg)s
-Organizationl Unit (OU): %(iou)s
-
-Validity:
-Issued on: %(io)s
-Expires on: %(eo)s
-
-Fingerprint
-SHA-1 Fingerprint: %(sha1)s
-
-SHA-256 Fingerprint: %(sha256)s
-''') % {
- 'scn': subject.commonName, 'sorg': subject.organizationName,
- 'sou': subject.organizationalUnitName,
- 'sn': cert.get_serial_number(), 'icn': issuer.commonName,
- 'iorg': issuer.organizationName,
- 'iou': issuer.organizationalUnitName,
- 'io': cert.get_notBefore().decode('utf-8'),
- 'eo': cert.get_notAfter().decode('utf-8'),
- 'sha1': cert.digest('sha1').decode('utf-8'),
- 'sha256': cert.digest('sha256').decode('utf-8')})
- pix = gtkgui_helpers.get_icon_pixmap('application-certificate', size=32,
- quiet=True)
- if pix:
- img = Gtk.Image.new_from_pixbuf(pix)
- img.show_all()
- self.set_image(img)
- self.set_transient_for(parent)
- self.set_title(_('Certificate for account %s') % account)
-
-class SSLErrorDialog(ConfirmationDialogDoubleCheck):
- def __init__(self, account, certificate, pritext, sectext, checktext1,
- checktext2, on_response_ok=None, on_response_cancel=None):
- self.account = account
- self.cert = certificate
- ConfirmationDialogDoubleCheck.__init__(self, pritext, sectext,
- checktext1, checktext2, on_response_ok=on_response_ok,
- on_response_cancel=on_response_cancel, is_modal=False)
- b = Gtk.Button(_('View cert…'))
- b.connect('clicked', self.on_cert_clicked)
- b.show_all()
- area = self.get_action_area()
- area.pack_start(b, True, True, 0)
-
- def on_cert_clicked(self, button):
- d = CertificatDialog(self, self.account, self.cert)
-
class ProgressWindow(Gtk.ApplicationWindow):
def __init__(self, file):
Gtk.ApplicationWindow.__init__(self)
diff --git a/gajim/disco.py b/gajim/disco.py
index 9c7fb3062..d3fd47295 100644
--- a/gajim/disco.py
+++ b/gajim/disco.py
@@ -52,7 +52,8 @@ from gi.repository import Gdk
from gi.repository import GdkPixbuf
from gi.repository import Pango
-from gajim import dialogs
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InformationDialog
from gajim import gtkgui_helpers
from gajim import groups
from gajim import adhoc_commands
@@ -526,7 +527,7 @@ class ServiceDiscoveryWindow(object):
# Check connection
if app.connections[account].connected < 2:
- dialogs.ErrorDialog(_('You are not connected to the server'),
+ ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not browse available services'))
raise RuntimeError('You must be connected to browse services')
@@ -722,14 +723,14 @@ _('Without a connection, you can not browse available services'))
if not self.address_comboboxtext:
# We can't travel anywhere else.
self.destroy()
- dialogs.ErrorDialog(_('The service could not be found'),
+ ErrorDialog(_('The service could not be found'),
_('There is no service at the address you entered, or it is '
'not responding. Check the address and try again.'),
transient_for=self.window)
return
klass = self.cache.get_browser(identities, features)
if not klass:
- dialogs.ErrorDialog(_('The service is not browsable'),
+ ErrorDialog(_('The service is not browsable'),
_('This type of service does not contain any items to browse.'),
transient_for=self.window)
return
@@ -772,7 +773,7 @@ _('Without a connection, you can not browse available services'))
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat as s:
pritext = _('Invalid Server Name')
- dialogs.ErrorDialog(pritext, str(s))
+ ErrorDialog(pritext, str(s))
return
self.travel(jid, '')
@@ -782,7 +783,7 @@ _('Without a connection, you can not browse available services'))
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat as s:
pritext = _('Invalid Server Name')
- dialogs.ErrorDialog(pritext, str(s),
+ ErrorDialog(pritext, str(s),
transient_for=self.window)
return
if jid == self.jid: # jid has not changed
@@ -1081,7 +1082,7 @@ class AgentBrowser:
if not self.window.address_comboboxtext:
# We can't travel anywhere else.
self.window.destroy()
- dialogs.ErrorDialog(_('The service is not browsable'),
+ ErrorDialog(_('The service is not browsable'),
_('This service does not contain any items to browse.'),
transient_for=self.window.window)
return
@@ -1772,7 +1773,7 @@ class MucBrowser(AgentBrowser):
}
if room_jid in con.get_module('Bookmarks').bookmarks:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Bookmark already set'),
_('Group Chat "%s" is already in your bookmarks.') % room_jid,
transient_for=self.window.window)
@@ -1783,7 +1784,7 @@ class MucBrowser(AgentBrowser):
gui_menu_builder.build_bookmark_menu(self.account)
- dialogs.InformationDialog(
+ InformationDialog(
_('Bookmark has been added successfully'),
_('You can manage your bookmarks via Actions menu in your roster.'),
transient_for=self.window.window)
diff --git a/gajim/filetransfers_window.py b/gajim/filetransfers_window.py
index 530971430..3329cc88c 100644
--- a/gajim/filetransfers_window.py
+++ b/gajim/filetransfers_window.py
@@ -36,7 +36,12 @@ from datetime import datetime
from gajim import gtkgui_helpers
from gajim import tooltips
-from gajim import dialogs
+from gajim.gtk import HigDialog
+from gajim.gtk import InformationDialog
+from gajim.gtk import YesNoDialog
+from gajim.gtk import ErrorDialog
+from gajim.gtk import FTOverwriteConfirmationDialog
+from gajim.gtk import NonModalConfirmationDialog
from gajim.common import app
from gajim.common import helpers
@@ -247,7 +252,7 @@ class FileTransfersWindow:
sectext += recipient
if file_props.type_ == 'r':
sectext += '\n\t' + _('Saved in: %s') % file_path
- dialog = dialogs.HigDialog(app.interface.roster.window, Gtk.MessageType.INFO,
+ dialog = HigDialog(app.interface.roster.window, Gtk.MessageType.INFO,
Gtk.ButtonsType.NONE, _('File transfer completed'), sectext)
if file_props.type_ == 'r':
button = Gtk.Button.new_with_mnemonic(_('Open _Containing Folder'))
@@ -263,14 +268,14 @@ class FileTransfersWindow:
"""
Show error dialog to the recipient saying that transfer has been canceled
"""
- dialogs.InformationDialog(_('File transfer cancelled'), _('Connection with peer cannot be established.'))
+ InformationDialog(_('File transfer cancelled'), _('Connection with peer cannot be established.'))
self.tree.get_selection().unselect_all()
def show_send_error(self, file_props):
"""
Show error dialog to the sender saying that transfer has been canceled
"""
- dialogs.InformationDialog(_('File transfer cancelled'),
+ InformationDialog(_('File transfer cancelled'),
_('Connection with peer cannot be established.'))
self.tree.get_selection().unselect_all()
@@ -283,7 +288,7 @@ class FileTransfersWindow:
sectext += '\n\t' + _('Recipient: %s') % jid
if error_msg:
sectext += '\n\t' + _('Error message: %s') % error_msg
- dialogs.ErrorDialog(_('File transfer stopped'), sectext)
+ ErrorDialog(_('File transfer stopped'), sectext)
self.tree.get_selection().unselect_all()
def show_hash_error(self, jid, file_props, account):
@@ -318,7 +323,7 @@ class FileTransfersWindow:
file_name = os.path.basename(file_props.file_name)
else:
file_name = file_props.name
- dialogs.YesNoDialog(('File transfer error'),
+ YesNoDialog(('File transfer error'),
_('The file %(file)s has been received, but it seems to have '
'been damaged along the way.\nDo you want to download it again?') % \
{'file': file_name}, on_response_yes=(on_yes, jid, file_props,
@@ -335,7 +340,7 @@ class FileTransfersWindow:
if gtkgui_helpers.file_is_locked(file_path):
pritext = _('Gajim can not read this file')
sextext = _('Another process is using this file.')
- dialogs.ErrorDialog(pritext, sextext)
+ ErrorDialog(pritext, sextext)
return
if isinstance(contact, str):
@@ -378,7 +383,7 @@ class FileTransfersWindow:
if not os.access(file_path, os.W_OK):
file_name = GLib.markup_escape_text(os.path.basename(
file_path))
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Cannot overwrite existing file "%s"' % file_name),
_('A file with this name already exists and you do not '
'have permission to overwrite it.'))
@@ -395,7 +400,7 @@ class FileTransfersWindow:
file_props.offset = dl_size
self._start_receive(file_path, account, contact, file_props)
- dialog = dialogs.FTOverwriteConfirmationDialog(
+ dialog = FTOverwriteConfirmationDialog(
_('This file already exists'), _('What do you want to do?'),
propose_resume=not dl_finished, on_response=on_response)
dialog.set_destroy_with_parent(True)
@@ -406,7 +411,7 @@ class FileTransfersWindow:
# read-only bit is used to mark special folder under
# windows, not to mark that a folder is read-only.
# See ticket #3587
- dialogs.ErrorDialog(_('Directory "%s" is not writable') % \
+ ErrorDialog(_('Directory "%s" is not writable') % \
dirname, _('You do not have permission to create files '
'in this directory.'))
return
@@ -445,7 +450,7 @@ class FileTransfersWindow:
def on_response_cancel(account, file_props):
app.connections[account].send_file_rejection(file_props)
- dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
+ dialog = NonModalConfirmationDialog(prim_text, sec_text,
on_response_ok=(on_response_ok, account, contact, file_props),
on_response_cancel=(on_response_cancel, account, file_props))
dialog.connect('delete-event', lambda widget, event:
@@ -674,10 +679,10 @@ class FileTransfersWindow:
if os.path.isfile(file_path):
stat = os.stat(file_path)
else:
- dialogs.ErrorDialog(_('Invalid File'), _('File: ') + file_path)
+ ErrorDialog(_('Invalid File'), _('File: ') + file_path)
return None
if stat[6] == 0:
- dialogs.ErrorDialog(_('Invalid File'),
+ ErrorDialog(_('Invalid File'),
_('It is not possible to send empty files'))
return None
file_props = FilesProp.getNewFileProp(account,
diff --git a/gajim/gajim_themes_window.py b/gajim/gajim_themes_window.py
index 4dfe3909b..2cc22ad0c 100644
--- a/gajim/gajim_themes_window.py
+++ b/gajim/gajim_themes_window.py
@@ -25,15 +25,17 @@
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
-from gajim import dialogs
-from gajim import gtkgui_helpers
from gajim.common import app
+from gajim.gtk import ErrorDialog
+from gajim.gtk.util import get_builder
+
+
class GajimThemesWindow:
def __init__(self):
- self.xml = gtkgui_helpers.get_gtk_builder('gajim_themes_window.ui')
+ self.xml = get_builder('gajim_themes_window.ui')
self.window = self.xml.get_object('gajim_themes_window')
self.window.set_transient_for(app.interface.instances[
'preferences'].window)
@@ -94,7 +96,7 @@ class GajimThemesWindow:
if old_name == new_name:
return
if old_name == 'default':
- dialogs.ErrorDialog(
+ ErrorDialog(
_('You cannot make changes to the default theme'),
_('Please create a new clean theme.'))
return
@@ -183,7 +185,7 @@ class GajimThemesWindow:
if not iter_:
return
if self.current_theme == app.config.get('roster_theme'):
- dialogs.ErrorDialog(
+ ErrorDialog(
_('You cannot delete your current theme'),
_('Pick another theme to use first.'))
return
diff --git a/gajim/groupchat_control.py b/gajim/groupchat_control.py
index 61df955c7..e866fb85d 100644
--- a/gajim/groupchat_control.py
+++ b/gajim/groupchat_control.py
@@ -41,11 +41,11 @@ from gajim import gtkgui_helpers
from gajim import gui_menu_builder
from gajim import message_control
from gajim import tooltips
-from gajim import dialogs
from gajim import config
from gajim import vcard
from gajim import dataforms_widget
from gajim import adhoc_commands
+from gajim.gtk import AddNewContactWindow
from gajim.common.const import AvatarSize
from gajim.common.caps_cache import muc_caps_cache
import nbxmpp
@@ -63,6 +63,12 @@ from gajim.common import contacts
from gajim.chat_control import ChatControl
from gajim.chat_control_base import ChatControlBase
from gajim.filechoosers import AvatarChooserDialog
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InputTextDialog
+from gajim.gtk import ConfirmationDialogCheck
+from gajim.gtk import DoubleInputDialog
+from gajim.gtk import InputDialog
+from gajim.gtk import ChangeNickDialog
from gajim.command_system.implementation.hosts import PrivateChatCommands
from gajim.command_system.implementation.hosts import GroupChatCommands
@@ -239,7 +245,7 @@ class PrivateChatControl(ChatControl):
room, nick = app.get_room_and_nick_from_fjid(self.contact.jid)
gc_contact = app.contacts.get_gc_contact(self.account, room, nick)
if not gc_contact:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Sending private message failed'),
#in second %s code replaces with nickname
_('You are no longer in group chat "%(room)s" or '
@@ -646,7 +652,7 @@ class GroupchatControl(ChatControlBase):
app.connections[self.account].send_gc_subject(
self.room_jid, subject)
- dialogs.InputTextDialog(_('Changing Subject'),
+ InputTextDialog(_('Changing Subject'),
_('Please specify the new subject:'), input_str=self.subject,
ok_handler=on_ok, transient_for=self.parent_win.window)
@@ -657,7 +663,7 @@ class GroupchatControl(ChatControlBase):
title = _('Changing Nickname')
prompt = _('Please specify the new nickname you want to use:')
app.interface.instances['change_nick_dialog'] = \
- dialogs.ChangeNickDialog(self.account, self.room_jid, title,
+ ChangeNickDialog(self.account, self.room_jid, title,
prompt, change_nick=True, transient_for=self.parent_win.window)
def _on_disconnect(self, action, param):
@@ -672,7 +678,7 @@ class GroupchatControl(ChatControlBase):
try:
jid = helpers.parse_jid(jid)
except Exception:
- dialogs.ErrorDialog(_('Invalid group chat JID'),
+ ErrorDialog(_('Invalid group chat JID'),
_('The group chat JID has not allowed characters.'))
return
app.connections[self.account].destroy_gc_room(
@@ -683,7 +689,7 @@ class GroupchatControl(ChatControlBase):
self.force_non_minimizable = False
# Ask for a reason
- dialogs.DoubleInputDialog(_('Destroying %s') % '\u200E' + \
+ DoubleInputDialog(_('Destroying %s') % '\u200E' + \
self.room_jid, _('You are going to remove this room permanently.'
'\nYou may specify a reason below:'),
_('You may also enter an alternate venue:'), ok_handler=on_ok,
@@ -751,7 +757,7 @@ class GroupchatControl(ChatControlBase):
def _on_accept(filename):
sha = app.interface.save_avatar(filename, publish=True)
if sha is None:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Could not load image'),
transient_for=self.parent_win.window)
return
@@ -2343,7 +2349,7 @@ class GroupchatControl(ChatControlBase):
sectext = _('If you close this window, you will be disconnected '
'from this group chat.')
- dialogs.ConfirmationDialogCheck(pritext, sectext,
+ ConfirmationDialogCheck(pritext, sectext,
_('_Do not ask me again'), on_response_ok=on_ok,
on_response_cancel=on_cancel,
transient_for=self.parent_win.window)
@@ -2379,7 +2385,7 @@ class GroupchatControl(ChatControlBase):
app.connections[self.account].send_gc_subject(self.room_jid,
subject)
- dialogs.InputTextDialog(_('Changing Subject'),
+ InputTextDialog(_('Changing Subject'),
_('Please specify the new subject:'), input_str=self.subject,
ok_handler=on_ok, transient_for=self.parent_win.window)
@@ -2560,7 +2566,7 @@ class GroupchatControl(ChatControlBase):
'none', reason)
# ask for reason
- dialogs.InputDialog(_('Kicking %s') % nick,
+ InputDialog(_('Kicking %s') % nick,
_('You may specify a reason below:'), ok_handler=on_ok,
transient_for=self.parent_win.window)
@@ -2857,7 +2863,7 @@ class GroupchatControl(ChatControlBase):
# to ban we know the real jid. so jid is not fakejid
nick = app.get_nick_from_jid(jid)
# ask for reason
- dialogs.InputDialog(_('Banning %s') % nick,
+ InputDialog(_('Banning %s') % nick,
_('You may specify a reason below:'), ok_handler=on_ok,
transient_for=self.parent_win.window)
@@ -2922,7 +2928,7 @@ class GroupchatControl(ChatControlBase):
self._on_history_menuitem_activate(widget=widget, jid=jid)
def on_add_to_roster(self, widget, jid):
- dialogs.AddNewContactWindow(self.account, jid)
+ AddNewContactWindow(self.account, jid)
def on_block(self, widget, nick):
fjid = self.room_jid + '/' + nick
diff --git a/gajim/gtk/__init__.py b/gajim/gtk/__init__.py
index e69de29bb..9ddae92c9 100644
--- a/gajim/gtk/__init__.py
+++ b/gajim/gtk/__init__.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2003-2005 Vincent Hanquez
+# Copyright (C) 2003-2014 Yann Leboulanger
+# Copyright (C) 2005 Alex Mauer
+# Copyright (C) 2005-2006 Dimitur Kirov
+# Travis Shirk
+# Copyright (C) 2005-2008 Nikos Kouremenos
+# Copyright (C) 2006-2008 Jean-Marie Traissard
+# Copyright (C) 2007 Lukas Petrovicky
+# Copyright (C) 2007-2008 Brendan Taylor
+# Julien Pivotto
+# Stephan Erb
+# Copyright (C) 2008 Jonathan Schleifer
+# Copyright (C) 2018 Philipp Hörist
+#
+# 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 .
+
+from gajim.gtk.dialogs import ErrorDialog
+from gajim.gtk.dialogs import InformationDialog
+from gajim.gtk.dialogs import ChangeNickDialog
+from gajim.gtk.dialogs import FTOverwriteConfirmationDialog
+from gajim.gtk.dialogs import InputDialog
+from gajim.gtk.dialogs import ConfirmationDialogDoubleRadio
+from gajim.gtk.dialogs import InputDialogCheck
+from gajim.gtk.dialogs import DoubleInputDialog
+from gajim.gtk.dialogs import InputTextDialog
+from gajim.gtk.dialogs import PlainConnectionDialog
+from gajim.gtk.dialogs import ConfirmationDialogDoubleCheck
+from gajim.gtk.dialogs import ConfirmationDialogCheck
+from gajim.gtk.dialogs import YesNoDialog
+from gajim.gtk.dialogs import WarningDialog
+from gajim.gtk.dialogs import NonModalConfirmationDialog
+from gajim.gtk.dialogs import ConfirmationDialog
+from gajim.gtk.dialogs import AspellDictError
+from gajim.gtk.dialogs import HigDialog
+from gajim.gtk.dialogs import SSLErrorDialog
+
+from gajim.gtk.about import AboutDialog
+from gajim.gtk.join_groupchat import JoinGroupchatWindow
+from gajim.gtk.add_contact import AddNewContactWindow
+from gajim.gtk.start_chat import StartChatDialog
+from gajim.gtk.xml_console import XMLConsoleWindow
+from gajim.gtk.privacy_list import PrivacyListsWindow
+from gajim.gtk.single_message import SingleMessageWindow
+from gajim.gtk.server_info import ServerInfoDialog
diff --git a/gajim/gtk/about.py b/gajim/gtk/about.py
new file mode 100644
index 000000000..5f12a6afd
--- /dev/null
+++ b/gajim/gtk/about.py
@@ -0,0 +1,66 @@
+# 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 .
+
+import nbxmpp
+
+from gi.repository import Gtk
+from gi.repository import GObject
+
+from gajim.common import app
+from gajim.common.const import DEVS_CURRENT
+from gajim.common.const import DEVS_PAST
+from gajim.common.const import ARTISTS
+from gajim.common.const import THANKS
+
+
+class AboutDialog(Gtk.AboutDialog):
+ def __init__(self):
+ Gtk.AboutDialog.__init__(self)
+ self.set_transient_for(app.interface.roster.window)
+ self.set_name('Gajim')
+ self.set_version(app.version)
+ self.set_copyright('Copyright © 2003-2018 Gajim Team')
+ self.set_license_type(Gtk.License.GPL_3_0_ONLY)
+ self.set_website('https://gajim.org/')
+
+ gtk_ver = '%i.%i.%i' % (
+ Gtk.get_major_version(),
+ Gtk.get_minor_version(),
+ Gtk.get_micro_version())
+ gobject_ver = '.'.join(map(str, GObject.pygobject_version))
+
+ comments = []
+ comments.append(_('A GTK+ XMPP client'))
+ comments.append(_('GTK+ Version: %s' % gtk_ver))
+ comments.append(_('PyGObject Version: %s') % gobject_ver)
+ comments.append(_('python-nbxmpp Version: %s') % nbxmpp.__version__)
+ self.set_comments("\n".join(comments))
+
+ self.add_credit_section(_('Current Developers'), DEVS_CURRENT)
+ self.add_credit_section(_('Past Developers'), DEVS_PAST)
+ self.add_credit_section(_('Artists'), ARTISTS)
+
+ thanks = list(THANKS)
+ thanks.append('')
+ thanks.append(_('Last but not least'))
+ thanks.append(_('we would like to thank all the package maintainers.'))
+ self.add_credit_section(_('Thanks'), thanks)
+
+ self.set_translator_credits(_('translator-credits'))
+ self.set_logo_icon_name('org.gajim.Gajim')
+
+ self.connect(
+ 'response', lambda dialog, *args: Gtk.AboutDialog.do_close(dialog))
+
+ self.show()
diff --git a/gajim/gtk/add_contact.py b/gajim/gtk/add_contact.py
new file mode 100644
index 000000000..f094f07e7
--- /dev/null
+++ b/gajim/gtk/add_contact.py
@@ -0,0 +1,483 @@
+# 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 .
+
+from gi.repository import Gdk
+from gi.repository import Gtk
+
+from gajim.common import app
+from gajim.common import ged
+from gajim.common import helpers
+from gajim.gtk import ErrorDialog
+from gajim.gtk.util import get_builder
+
+
+class AddNewContactWindow(Gtk.ApplicationWindow):
+
+ uid_labels = {'jabber': _('Jabber ID'),
+ 'gadu-gadu': _('GG Number'),
+ 'icq': _('ICQ Number')}
+
+ def __init__(self, account=None, jid=None, user_nick=None, group=None):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_application(app.app)
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.set_show_menubar(False)
+ self.set_resizable(False)
+ self.set_title(_('Add Contact'))
+
+ self.connect('destroy', self._on_destroy)
+ self.connect('key-press-event', self._on_key_press)
+
+ self.account = account
+ self.adding_jid = False
+
+ # fill accounts with active accounts
+ accounts = app.get_enabled_accounts_with_labels()
+
+ if not accounts:
+ return
+
+ if not account:
+ self.account = accounts[0][0]
+
+ self.xml = get_builder('add_new_contact_window.ui')
+ self.add(self.xml.get_object('add_contact_box'))
+ self.xml.connect_signals(self)
+
+ for w in ('account_combobox', 'account_label', 'prompt_label',
+ 'uid_label', 'uid_entry', 'protocol_combobox',
+ 'protocol_jid_combobox', 'protocol_label', 'nickname_entry',
+ 'message_scrolledwindow', 'save_message_checkbutton',
+ 'register_hbox', 'add_button', 'message_textview',
+ 'connected_label', 'group_comboboxentry',
+ 'auto_authorize_checkbutton', 'save_message_revealer',
+ 'nickname_label', 'group_label'):
+ self.__dict__[w] = self.xml.get_object(w)
+
+ self.subscription_table = [self.uid_label, self.uid_entry,
+ self.nickname_label, self.nickname_entry,
+ self.group_label, self.group_comboboxentry]
+
+ self.add_button.grab_default()
+
+ self.agents = {'jabber': []}
+ self.gateway_prompt = {}
+ # types to which we are not subscribed but account has an agent for it
+ self.available_types = []
+ for acct in accounts:
+ for j in app.contacts.get_jid_list(acct[0]):
+ if app.jid_is_transport(j):
+ type_ = app.get_transport_name_from_jid(j, False)
+ if not type_:
+ continue
+ if type_ in self.agents:
+ self.agents[type_].append(j)
+ else:
+ self.agents[type_] = [j]
+ self.gateway_prompt[j] = {'desc': None, 'prompt': None}
+ # Now add the one to which we can register
+ for acct in accounts:
+ for type_ in app.connections[acct[0]].available_transports:
+ if type_ in self.agents:
+ continue
+ self.agents[type_] = []
+ for jid_ in app.connections[acct[0]].available_transports[type_]:
+ if jid_ not in self.agents[type_]:
+ self.agents[type_].append(jid_)
+ self.gateway_prompt[jid_] = {'desc': None,
+ 'prompt': None}
+ self.available_types.append(type_)
+
+ uf_type = {'jabber': 'XMPP', 'gadu-gadu': 'Gadu Gadu', 'icq': 'ICQ'}
+ # Jabber as first
+ liststore = self.protocol_combobox.get_model()
+ liststore.append(['XMPP', 'xmpp', 'jabber'])
+ for type_ in self.agents:
+ if type_ == 'jabber':
+ continue
+ if type_ in uf_type:
+ liststore.append([uf_type[type_], type_ + '-online', type_])
+ else:
+ liststore.append([type_, type_ + '-online', type_])
+
+ if account:
+ for service in self.agents[type_]:
+ app.connections[account].request_gateway_prompt(service)
+ self.protocol_combobox.set_active(0)
+ self.auto_authorize_checkbutton.show()
+
+ if jid:
+ self.jid_escaped = True
+ type_ = app.get_transport_name_from_jid(jid)
+ if not type_:
+ type_ = 'jabber'
+ if type_ == 'jabber':
+ self.uid_entry.set_text(jid)
+ else:
+ uid, transport = app.get_name_and_server_from_jid(jid)
+ self.uid_entry.set_text(uid.replace('%', '@', 1))
+ # set protocol_combobox
+ model = self.protocol_combobox.get_model()
+ iter_ = model.get_iter_first()
+ i = 0
+ while iter_:
+ if model[iter_][2] == type_:
+ self.protocol_combobox.set_active(i)
+ break
+ iter_ = model.iter_next(iter_)
+ i += 1
+
+ # set protocol_jid_combobox
+ self.protocol_jid_combobox.set_active(0)
+ model = self.protocol_jid_combobox.get_model()
+ iter_ = model.get_iter_first()
+ i = 0
+ while iter_:
+ if model[iter_][0] == transport:
+ self.protocol_jid_combobox.set_active(i)
+ break
+ iter_ = model.iter_next(iter_)
+ i += 1
+ if user_nick:
+ self.nickname_entry.set_text(user_nick)
+ self.nickname_entry.grab_focus()
+ else:
+ self.jid_escaped = False
+ self.uid_entry.grab_focus()
+ group_names = []
+ for acct in accounts:
+ for g in app.groups[acct[0]].keys():
+ if g not in helpers.special_groups and g not in group_names:
+ group_names.append(g)
+ group_names.sort()
+ i = 0
+ for g in group_names:
+ self.group_comboboxentry.append_text(g)
+ if group == g:
+ self.group_comboboxentry.set_active(i)
+ i += 1
+
+ self.show_all()
+
+ self.prompt_label.hide()
+ self.save_message_revealer.hide()
+
+ if len(accounts) > 1:
+ liststore = self.account_combobox.get_model()
+ for acc in accounts:
+ liststore.append(acc)
+
+ self.account_combobox.set_active_id(self.account)
+ else:
+ self.account_label.hide()
+ self.account_combobox.hide()
+
+ if len(self.agents) == 1:
+ self.protocol_label.hide()
+ self.protocol_combobox.hide()
+ self.protocol_jid_combobox.hide()
+
+ if self.account:
+ message_buffer = self.message_textview.get_buffer()
+ msg = helpers.from_one_line(helpers.get_subscription_request_msg(
+ self.account))
+ message_buffer.set_text(msg)
+
+ app.ged.register_event_handler('gateway-prompt-received', ged.GUI1,
+ self._nec_gateway_prompt_received)
+ app.ged.register_event_handler('presence-received', ged.GUI1,
+ self._nec_presence_received)
+
+ def _on_destroy(self, widget):
+ app.ged.remove_event_handler('presence-received', ged.GUI1,
+ self._nec_presence_received)
+ app.ged.remove_event_handler('gateway-prompt-received', ged.GUI1,
+ self._nec_gateway_prompt_received)
+
+ def on_register_button_clicked(self, widget):
+ model = self.protocol_jid_combobox.get_model()
+ row = self.protocol_jid_combobox.get_active()
+ jid = model[row][0]
+ app.connections[self.account].request_register_agent_info(jid)
+
+ def _on_key_press(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.destroy()
+
+ def on_cancel_button_clicked(self, widget):
+ """
+ When Cancel button is clicked
+ """
+ self.destroy()
+
+ def on_message_textbuffer_changed(self, widget):
+ self.save_message_revealer.show()
+ self.save_message_revealer.set_reveal_child(True)
+
+ def on_add_button_clicked(self, widget):
+ """
+ When Subscribe button is clicked
+ """
+ jid = self.uid_entry.get_text().strip()
+ if not jid:
+ ErrorDialog(
+ _('%s Missing') % self.uid_label.get_text(),
+ _('You must supply the %s of the new contact.' %
+ self.uid_label.get_text())
+ )
+ return
+
+ model = self.protocol_combobox.get_model()
+ row = self.protocol_combobox.get_active_iter()
+ type_ = model[row][2]
+ if type_ != 'jabber':
+ model = self.protocol_jid_combobox.get_model()
+ row = self.protocol_jid_combobox.get_active()
+ transport = model[row][0]
+ if self.account and not self.jid_escaped:
+ self.adding_jid = (jid, transport, type_)
+ app.connections[self.account].request_gateway_prompt(
+ transport, jid)
+ else:
+ jid = jid.replace('@', '%') + '@' + transport
+ self._add_jid(jid, type_)
+ else:
+ self._add_jid(jid, type_)
+
+ def _add_jid(self, jid, type_):
+ # check if jid is conform to RFC and stringprep it
+ try:
+ jid = helpers.parse_jid(jid)
+ except helpers.InvalidFormat as s:
+ pritext = _('Invalid User ID')
+ ErrorDialog(pritext, str(s))
+ return
+
+ # No resource in jid
+ if jid.find('/') >= 0:
+ pritext = _('Invalid User ID')
+ ErrorDialog(pritext, _('The user ID must not contain a resource.'))
+ return
+
+ if jid == app.get_jid_from_account(self.account):
+ pritext = _('Invalid User ID')
+ ErrorDialog(pritext, _('You cannot add yourself to your roster.'))
+ return
+
+ if not app.account_is_connected(self.account):
+ ErrorDialog(
+ _('Account Offline'),
+ _('Your account must be online to add new contacts.')
+ )
+ return
+
+ nickname = self.nickname_entry.get_text() or ''
+ # get value of account combobox, if account was not specified
+ if not self.account:
+ model = self.account_combobox.get_model()
+ index = self.account_combobox.get_active()
+ self.account = model[index][1]
+
+ # Check if jid is already in roster
+ if jid in app.contacts.get_jid_list(self.account):
+ c = app.contacts.get_first_contact_from_jid(self.account, jid)
+ if _('Not in Roster') not in c.groups and c.sub in ('both', 'to'):
+ ErrorDialog(
+ _('Contact already in roster'),
+ _('This contact is already listed in your roster.'))
+ return
+
+ if type_ == 'jabber':
+ message_buffer = self.message_textview.get_buffer()
+ start_iter = message_buffer.get_start_iter()
+ end_iter = message_buffer.get_end_iter()
+ message = message_buffer.get_text(start_iter, end_iter, True)
+ if self.save_message_checkbutton.get_active():
+ msg = helpers.to_one_line(message)
+ app.config.set_per('accounts', self.account,
+ 'subscription_request_msg', msg)
+ else:
+ message = ''
+ group = self.group_comboboxentry.get_child().get_text()
+ groups = []
+ if group:
+ groups = [group]
+ auto_auth = self.auto_authorize_checkbutton.get_active()
+ app.interface.roster.req_sub(
+ self, jid, message, self.account,
+ groups=groups, nickname=nickname, auto_auth=auto_auth)
+ self.destroy()
+
+ def on_account_combobox_changed(self, widget):
+ account = widget.get_active_id()
+ message_buffer = self.message_textview.get_buffer()
+ message_buffer.set_text(helpers.get_subscription_request_msg(account))
+ self.account = account
+
+ def on_protocol_jid_combobox_changed(self, widget):
+ model = widget.get_model()
+ iter_ = widget.get_active_iter()
+ if not iter_:
+ return
+ jid_ = model[iter_][0]
+ model = self.protocol_combobox.get_model()
+ iter_ = self.protocol_combobox.get_active_iter()
+ type_ = model[iter_][2]
+
+ desc = None
+ if self.agents[type_] and jid_ in self.gateway_prompt:
+ desc = self.gateway_prompt[jid_]['desc']
+
+ if desc:
+ self.prompt_label.set_markup(desc)
+ self.prompt_label.show()
+ else:
+ self.prompt_label.hide()
+
+ prompt = None
+ if self.agents[type_] and jid_ in self.gateway_prompt:
+ prompt = self.gateway_prompt[jid_]['prompt']
+ if not prompt:
+ if type_ in self.uid_labels:
+ prompt = self.uid_labels[type_]
+ else:
+ prompt = _('User ID:')
+ self.uid_label.set_text(prompt)
+
+ def on_protocol_combobox_changed(self, widget):
+ model = widget.get_model()
+ iter_ = widget.get_active_iter()
+ type_ = model[iter_][2]
+ model = self.protocol_jid_combobox.get_model()
+ model.clear()
+ if len(self.agents[type_]):
+ for jid_ in self.agents[type_]:
+ model.append([jid_])
+ self.protocol_jid_combobox.set_active(0)
+ desc = None
+ if self.agents[type_]:
+ jid_ = self.agents[type_][0]
+ if jid_ in self.gateway_prompt:
+ desc = self.gateway_prompt[jid_]['desc']
+
+ if desc:
+ self.prompt_label.set_markup(desc)
+ self.prompt_label.show()
+ else:
+ self.prompt_label.hide()
+
+ if len(self.agents[type_]) > 1:
+ self.protocol_jid_combobox.show()
+ else:
+ self.protocol_jid_combobox.hide()
+ prompt = None
+ if self.agents[type_]:
+ jid_ = self.agents[type_][0]
+ if jid_ in self.gateway_prompt:
+ prompt = self.gateway_prompt[jid_]['prompt']
+ if not prompt:
+ if type_ in self.uid_labels:
+ prompt = self.uid_labels[type_]
+ else:
+ prompt = _('User ID:')
+ self.uid_label.set_text(prompt)
+
+ if type_ == 'jabber':
+ self.message_scrolledwindow.show()
+ self.save_message_checkbutton.show()
+ else:
+ self.message_scrolledwindow.hide()
+ self.save_message_checkbutton.hide()
+ if type_ in self.available_types:
+ self.register_hbox.show()
+ self.auto_authorize_checkbutton.hide()
+ self.connected_label.hide()
+ self._subscription_table_hide()
+ self.add_button.set_sensitive(False)
+ else:
+ self.register_hbox.hide()
+ if type_ != 'jabber':
+ model = self.protocol_jid_combobox.get_model()
+ row = self.protocol_jid_combobox.get_active()
+ jid = model[row][0]
+ contact = app.contacts.get_first_contact_from_jid(
+ self.account, jid)
+ if contact is None or contact.show in ('offline', 'error'):
+ self._subscription_table_hide()
+ self.connected_label.show()
+ self.add_button.set_sensitive(False)
+ self.auto_authorize_checkbutton.hide()
+ return
+ self._subscription_table_show()
+ self.auto_authorize_checkbutton.show()
+ self.connected_label.hide()
+ self.add_button.set_sensitive(True)
+
+ def transport_signed_in(self, jid):
+ model = self.protocol_jid_combobox.get_model()
+ row = self.protocol_jid_combobox.get_active()
+ _jid = model[row][0]
+ if _jid == jid:
+ self.register_hbox.hide()
+ self.connected_label.hide()
+ self._subscription_table_show()
+ self.auto_authorize_checkbutton.show()
+ self.add_button.set_sensitive(True)
+
+ def transport_signed_out(self, jid):
+ model = self.protocol_jid_combobox.get_model()
+ row = self.protocol_jid_combobox.get_active()
+ _jid = model[row][0]
+ if _jid == jid:
+ self._subscription_table_hide()
+ self.auto_authorize_checkbutton.hide()
+ self.connected_label.show()
+ self.add_button.set_sensitive(False)
+
+ def _nec_presence_received(self, obj):
+ if app.jid_is_transport(obj.jid):
+ if obj.old_show == 0 and obj.new_show > 1:
+ self.transport_signed_in(obj.jid)
+ elif obj.old_show > 1 and obj.new_show == 0:
+ self.transport_signed_out(obj.jid)
+
+ def _nec_gateway_prompt_received(self, obj):
+ if self.adding_jid:
+ jid, transport, type_ = self.adding_jid
+ if obj.stanza.getError():
+ ErrorDialog(
+ _('Error while adding transport contact'),
+ _('This error occured while adding a contact for transport '
+ '%(transport)s:\n\n%(error)s') % {
+ 'transport': transport,
+ 'error': obj.stanza.getErrorMsg()})
+ return
+ if obj.prompt_jid:
+ self._add_jid(obj.prompt_jid, type_)
+ else:
+ jid = jid.replace('@', '%') + '@' + transport
+ self._add_jid(jid, type_)
+ elif obj.jid in self.gateway_prompt:
+ if obj.desc:
+ self.gateway_prompt[obj.jid]['desc'] = obj.desc
+ if obj.prompt:
+ self.gateway_prompt[obj.jid]['prompt'] = obj.prompt
+
+ def _subscription_table_hide(self):
+ for widget in self.subscription_table:
+ widget.hide()
+
+ def _subscription_table_show(self):
+ for widget in self.subscription_table:
+ widget.show()
diff --git a/gajim/gtk/dialogs.py b/gajim/gtk/dialogs.py
new file mode 100644
index 000000000..731fb7b4c
--- /dev/null
+++ b/gajim/gtk/dialogs.py
@@ -0,0 +1,933 @@
+# 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 .
+
+from gi.repository import Gtk
+
+from gajim.common import app
+from gajim.common import helpers
+from gajim.gtk.util import get_builder
+from gajim.gtk.util import load_icon
+
+
+class HigDialog(Gtk.MessageDialog):
+ def __init__(self, parent, type_, buttons, pritext, sectext,
+ on_response_ok=None, on_response_cancel=None, on_response_yes=None,
+ on_response_no=None):
+ self.call_cancel_on_destroy = True
+ Gtk.MessageDialog.__init__(self, transient_for=parent,
+ modal=True, destroy_with_parent=True,
+ message_type=type_, buttons=buttons, text=pritext)
+
+ self.format_secondary_markup(sectext)
+
+ self.possible_responses = {Gtk.ResponseType.OK: on_response_ok,
+ Gtk.ResponseType.CANCEL: on_response_cancel,
+ Gtk.ResponseType.YES: on_response_yes,
+ Gtk.ResponseType.NO: on_response_no}
+
+ self.connect('response', self.on_response)
+ self.connect('destroy', self.on_dialog_destroy)
+
+ def on_response(self, dialog, response_id):
+ if response_id not in self.possible_responses:
+ return
+ if not self.possible_responses[response_id]:
+ self.destroy()
+ elif isinstance(self.possible_responses[response_id], tuple):
+ if len(self.possible_responses[response_id]) == 1:
+ self.possible_responses[response_id][0](dialog)
+ else:
+ self.possible_responses[response_id][0](dialog,
+ *self.possible_responses[response_id][1:])
+ else:
+ self.possible_responses[response_id](dialog)
+
+ def on_dialog_destroy(self, widget):
+ if not self.call_cancel_on_destroy:
+ return
+ cancel_handler = self.possible_responses[Gtk.ResponseType.CANCEL]
+ if not cancel_handler:
+ return False
+ if isinstance(cancel_handler, tuple):
+ cancel_handler[0](None, *cancel_handler[1:])
+ else:
+ cancel_handler(None)
+
+ def popup(self):
+ """
+ Show dialog
+ """
+ vb = self.get_children()[0].get_children()[0] # Give focus to top vbox
+# vb.set_flags(Gtk.CAN_FOCUS)
+ vb.grab_focus()
+ self.show_all()
+
+
+class AspellDictError:
+ def __init__(self, lang):
+ ErrorDialog(
+ _('Dictionary for language "%s" not available') % lang,
+ _('You have to install the dictionary "%s" to use spellchecking, '
+ 'or choose another language by setting the speller_language '
+ 'option.\n\n'
+ 'Highlighting misspelled words feature will not be used') % lang)
+
+
+class ConfirmationDialog(HigDialog):
+ """
+ HIG compliant confirmation dialog
+ """
+
+ def __init__(self, pritext, sectext='', on_response_ok=None,
+ on_response_cancel=None, transient_for=None):
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+ HigDialog.__init__(self, transient_for,
+ Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, pritext, sectext,
+ self.on_response_ok, self.on_response_cancel)
+ self.popup()
+
+ def on_response_ok(self, widget):
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](*self.user_response_ok[1:])
+ else:
+ self.user_response_ok()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_cancel(self, widget):
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](*self.user_response_ok[1:])
+ else:
+ self.user_response_cancel()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+
+class NonModalConfirmationDialog(HigDialog):
+ """
+ HIG compliant non modal confirmation dialog
+ """
+
+ def __init__(self, pritext, sectext='', on_response_ok=None,
+ on_response_cancel=None):
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+ if hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
+ self.on_response_cancel)
+ self.set_modal(False)
+
+ def on_response_ok(self, widget):
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](*self.user_response_ok[1:])
+ else:
+ self.user_response_ok()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_cancel(self, widget):
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](*self.user_response_cancel[1:])
+ else:
+ self.user_response_cancel()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+
+class WarningDialog(HigDialog):
+ """
+ HIG compliant warning dialog
+ """
+
+ def __init__(self, pritext, sectext='', transient_for=None):
+ if not transient_for and hasattr(app.interface, 'roster') and \
+ app.interface.roster:
+ transient_for = app.interface.roster.window
+ HigDialog.__init__(self, transient_for, Gtk.MessageType.WARNING,
+ Gtk.ButtonsType.OK, pritext, sectext)
+ self.set_modal(False)
+ self.popup()
+
+
+class InformationDialog(HigDialog):
+ """
+ HIG compliant info dialog
+ """
+
+ def __init__(self, pritext, sectext='', transient_for=None):
+ if transient_for:
+ parent = transient_for
+ elif hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.INFO, Gtk.ButtonsType.OK,
+ pritext, sectext)
+ self.set_modal(False)
+ self.popup()
+
+
+class ErrorDialog(HigDialog):
+ """
+ HIG compliant error dialog
+ """
+
+ def __init__(self, pritext, sectext='', on_response_ok=None,
+ on_response_cancel=None, transient_for=None):
+ if transient_for:
+ parent = transient_for
+ elif hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
+ pritext, sectext, on_response_ok=on_response_ok,
+ on_response_cancel=on_response_cancel)
+ self.popup()
+
+
+class YesNoDialog(HigDialog):
+ """
+ HIG compliant YesNo dialog
+ """
+
+ def __init__(self, pritext, sectext='', checktext='', text_label=None,
+ on_response_yes=None, on_response_no=None, type_=Gtk.MessageType.QUESTION,
+ transient_for=None):
+ self.user_response_yes = on_response_yes
+ self.user_response_no = on_response_no
+ if transient_for:
+ parent = transient_for
+ elif hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, type_, Gtk.ButtonsType.YES_NO, pritext,
+ sectext, on_response_yes=self.on_response_yes,
+ on_response_no=self.on_response_no)
+
+ vbox = self.get_content_area()
+ if checktext:
+ self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
+ vbox.pack_start(self.checkbutton, False, True, 0)
+ else:
+ self.checkbutton = None
+ if text_label:
+ label = Gtk.Label(label=text_label)
+ vbox.pack_start(label, False, True, 0)
+ buff = Gtk.TextBuffer()
+ self.textview = Gtk.TextView.new_with_buffer(buff)
+ frame = Gtk.Frame()
+ frame.set_shadow_type(Gtk.ShadowType.IN)
+ frame.add(self.textview)
+ vbox.pack_start(frame, False, True, 0)
+ else:
+ self.textview = None
+ self.set_modal(False)
+ self.popup()
+
+ def on_response_yes(self, widget):
+ if self.user_response_yes:
+ if self.textview:
+ buff = self.textview.get_buffer()
+ start, end = buff.get_bounds()
+ txt = self.textview.get_buffer().get_text(start, end, True)
+
+ if isinstance(self.user_response_yes, tuple):
+ if self.textview:
+ self.user_response_yes[0](self.is_checked(), txt,
+ *self.user_response_yes[1:])
+ else:
+ self.user_response_yes[0](self.is_checked(),
+ *self.user_response_yes[1:])
+ else:
+ if self.textview:
+ self.user_response_yes(self.is_checked(), txt)
+ else:
+ self.user_response_yes(self.is_checked())
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_no(self, widget):
+ if self.user_response_no:
+ if self.textview:
+ buff = self.textview.get_buffer()
+ start, end = buff.get_bounds()
+ txt = self.textview.get_buffer().get_text(start, end, True)
+
+ if isinstance(self.user_response_no, tuple):
+ if self.textview:
+ self.user_response_no[0](txt, *self.user_response_no[1:])
+ else:
+ self.user_response_no[0](*self.user_response_no[1:])
+ else:
+ if self.textview:
+ self.user_response_no(txt)
+ else:
+ self.user_response_no()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def is_checked(self):
+ """
+ Get active state of the checkbutton
+ """
+ if not self.checkbutton:
+ return False
+ return self.checkbutton.get_active()
+
+
+class ConfirmationDialogCheck(ConfirmationDialog):
+ """
+ HIG compliant confirmation dialog with checkbutton
+ """
+
+ def __init__(self, pritext, sectext='', checktext='', on_response_ok=None,
+ on_response_cancel=None, is_modal=True, transient_for=None):
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+
+ if transient_for:
+ parent = transient_for
+ elif hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
+ self.on_response_cancel)
+
+ self.set_default_response(Gtk.ResponseType.OK)
+
+ ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
+ ok_button.grab_focus()
+
+ self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
+ self.get_content_area().pack_start(self.checkbutton, False, True, 0)
+ self.set_modal(is_modal)
+ self.popup()
+
+ def on_response_ok(self, widget):
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](self.is_checked(),
+ *self.user_response_ok[1:])
+ else:
+ self.user_response_ok(self.is_checked())
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_cancel(self, widget):
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](self.is_checked(),
+ *self.user_response_cancel[1:])
+ else:
+ self.user_response_cancel(self.is_checked())
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def is_checked(self):
+ """
+ Get active state of the checkbutton
+ """
+ return self.checkbutton.get_active()
+
+
+class ConfirmationDialogDoubleCheck(ConfirmationDialog):
+ """
+ HIG compliant confirmation dialog with 2 checkbuttons
+ """
+
+ def __init__(self, pritext, sectext='', checktext1='', checktext2='',
+ tooltip1='', tooltip2='', on_response_ok=None, on_response_cancel=None,
+ is_modal=True):
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+
+ if hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
+ self.on_response_cancel)
+
+ self.set_default_response(Gtk.ResponseType.OK)
+
+ ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
+ ok_button.grab_focus()
+
+ vbox = self.get_content_area()
+ if checktext1:
+ self.checkbutton1 = Gtk.CheckButton.new_with_mnemonic(checktext1)
+ if tooltip1:
+ self.checkbutton1.set_tooltip_text(tooltip1)
+ vbox.pack_start(self.checkbutton1, False, True, 0)
+ else:
+ self.checkbutton1 = None
+ if checktext2:
+ self.checkbutton2 = Gtk.CheckButton.new_with_mnemonic(checktext2)
+ if tooltip2:
+ self.checkbutton2.set_tooltip_text(tooltip2)
+ vbox.pack_start(self.checkbutton2, False, True, 0)
+ else:
+ self.checkbutton2 = None
+
+ self.set_modal(is_modal)
+ self.popup()
+
+ def on_response_ok(self, widget):
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](self.is_checked(),
+ *self.user_response_ok[1:])
+ else:
+ self.user_response_ok(self.is_checked())
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_cancel(self, widget):
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](*self.user_response_cancel[1:])
+ else:
+ self.user_response_cancel()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def is_checked(self):
+ ''' Get active state of the checkbutton '''
+ if self.checkbutton1:
+ is_checked_1 = self.checkbutton1.get_active()
+ else:
+ is_checked_1 = False
+ if self.checkbutton2:
+ is_checked_2 = self.checkbutton2.get_active()
+ else:
+ is_checked_2 = False
+ return [is_checked_1, is_checked_2]
+
+
+class PlainConnectionDialog(ConfirmationDialogDoubleCheck):
+ """
+ Dialog that is shown when using an insecure connection
+ """
+ def __init__(self, account, on_ok, on_cancel):
+ pritext = _('Insecure connection')
+ sectext = _('You are about to connect to the account %(account)s '
+ '(%(server)s) insecurely. This means conversations will not be '
+ 'encrypted, and is strongly discouraged.\nAre you sure you want '
+ 'to do that?') % {'account': account,
+ 'server': app.get_hostname_from_account(account)}
+ checktext1 = _('Yes, I really want to connect insecurely')
+ tooltip1 = _('Gajim will NOT connect unless you check this box')
+ checktext2 = _('_Do not ask me again')
+ ConfirmationDialogDoubleCheck.__init__(self, pritext, sectext,
+ checktext1, checktext2, tooltip1=tooltip1, on_response_ok=on_ok,
+ on_response_cancel=on_cancel, is_modal=False)
+ self.ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
+ self.ok_button.set_sensitive(False)
+ self.checkbutton1.connect('clicked', self.on_checkbutton_clicked)
+ self.set_title(_('Insecure connection'))
+
+ def on_checkbutton_clicked(self, widget):
+ self.ok_button.set_sensitive(widget.get_active())
+
+class ConfirmationDialogDoubleRadio(ConfirmationDialog):
+ """
+ HIG compliant confirmation dialog with 2 radios
+ """
+ def __init__(self, pritext, sectext='', radiotext1='', radiotext2='',
+ on_response_ok=None, on_response_cancel=None, is_modal=True, transient_for=None):
+ self.user_response_ok = on_response_ok
+ self.user_response_cancel = on_response_cancel
+
+ if transient_for is None:
+ transient_for = app.app.get_active_window()
+ HigDialog.__init__(self, transient_for, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.OK_CANCEL, pritext, sectext, self.on_response_ok,
+ self.on_response_cancel)
+
+ self.set_default_response(Gtk.ResponseType.OK)
+
+ ok_button = self.get_widget_for_response(Gtk.ResponseType.OK)
+ ok_button.grab_focus()
+
+ vbox = self.get_content_area()
+ self.radiobutton1 = Gtk.RadioButton(label=radiotext1)
+ vbox.pack_start(self.radiobutton1, False, True, 0)
+
+ self.radiobutton2 = Gtk.RadioButton(group=self.radiobutton1,
+ label=radiotext2)
+ vbox.pack_start(self.radiobutton2, False, True, 0)
+
+ self.set_modal(is_modal)
+ self.popup()
+
+ def on_response_ok(self, widget):
+ if self.user_response_ok:
+ if isinstance(self.user_response_ok, tuple):
+ self.user_response_ok[0](self.is_checked(),
+ *self.user_response_ok[1:])
+ else:
+ self.user_response_ok(self.is_checked())
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def on_response_cancel(self, widget):
+ if self.user_response_cancel:
+ if isinstance(self.user_response_cancel, tuple):
+ self.user_response_cancel[0](*self.user_response_cancel[1:])
+ else:
+ self.user_response_cancel()
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+ def is_checked(self):
+ ''' Get active state of the checkbutton '''
+ if self.radiobutton1:
+ is_checked_1 = self.radiobutton1.get_active()
+ else:
+ is_checked_1 = False
+ if self.radiobutton2:
+ is_checked_2 = self.radiobutton2.get_active()
+ else:
+ is_checked_2 = False
+ return [is_checked_1, is_checked_2]
+
+class FTOverwriteConfirmationDialog(ConfirmationDialog):
+ """
+ HIG compliant confirmation dialog to overwrite or resume a file transfert
+ """
+ def __init__(self, pritext, sectext='', propose_resume=True,
+ on_response=None, transient_for=None):
+ if transient_for:
+ parent = transient_for
+ elif hasattr(app.interface, 'roster') and app.interface.roster:
+ parent = app.interface.roster.window
+ else:
+ parent = None
+ HigDialog.__init__(self, parent, Gtk.MessageType.QUESTION,
+ Gtk.ButtonsType.CANCEL, pritext, sectext)
+
+ self.on_response = on_response
+
+ if propose_resume:
+ b = Gtk.Button(label='', stock=Gtk.STOCK_REFRESH)
+ align = b.get_children()[0]
+ hbox = align.get_children()[0]
+ label = hbox.get_children()[1]
+ label.set_text(_('_Resume'))
+ label.set_use_underline(True)
+ self.add_action_widget(b, 100)
+
+ b = Gtk.Button(label='', stock=Gtk.STOCK_SAVE_AS)
+ align = b.get_children()[0]
+ hbox = align.get_children()[0]
+ label = hbox.get_children()[1]
+ label.set_text(_('Re_place'))
+ label.set_use_underline(True)
+ self.add_action_widget(b, 200)
+
+ self.connect('response', self.on_dialog_response)
+ self.show_all()
+
+ def on_dialog_response(self, dialog, response):
+ if self.on_response:
+ if isinstance(self.on_response, tuple):
+ self.on_response[0](response, *self.on_response[1:])
+ else:
+ self.on_response(response)
+ self.call_cancel_on_destroy = False
+ self.destroy()
+
+
+class CommonInputDialog:
+ """
+ Common Class for Input dialogs
+ """
+
+ def __init__(self, title, label_str, is_modal, ok_handler, cancel_handler,
+ transient_for=None):
+ self.dialog = self.xml.get_object('input_dialog')
+ label = self.xml.get_object('label')
+ self.dialog.set_title(title)
+ label.set_markup(label_str)
+ self.cancel_handler = cancel_handler
+ self.vbox = self.xml.get_object('vbox')
+ if transient_for:
+ self.dialog.set_transient_for(transient_for)
+ else:
+ self.dialog.set_transient_for(app.interface.roster.window)
+
+ self.ok_handler = ok_handler
+ okbutton = self.xml.get_object('okbutton')
+ okbutton.connect('clicked', self.on_okbutton_clicked)
+ cancelbutton = self.xml.get_object('cancelbutton')
+ cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
+ self.xml.connect_signals(self)
+ self.dialog.show_all()
+
+ def on_input_dialog_destroy(self, widget):
+ if self.cancel_handler:
+ self.cancel_handler()
+
+ def on_okbutton_clicked(self, widget):
+ user_input = self.get_text()
+ if user_input:
+ user_input = user_input
+ self.cancel_handler = None
+ self.dialog.destroy()
+ if isinstance(self.ok_handler, tuple):
+ self.ok_handler[0](user_input, *self.ok_handler[1:])
+ else:
+ self.ok_handler(user_input)
+
+ def on_cancelbutton_clicked(self, widget):
+ self.dialog.destroy()
+
+ def destroy(self):
+ self.dialog.destroy()
+
+
+class InputDialog(CommonInputDialog):
+ """
+ Class for Input dialog
+ """
+
+ def __init__(self, title, label_str, input_str=None, is_modal=True,
+ ok_handler=None, cancel_handler=None, transient_for=None):
+ self.xml = get_builder('input_dialog.ui')
+ CommonInputDialog.__init__(self, title, label_str, is_modal,
+ ok_handler, cancel_handler,
+ transient_for=transient_for)
+ self.input_entry = self.xml.get_object('input_entry')
+ if input_str:
+ self.set_entry(input_str)
+
+ def on_input_dialog_delete_event(self, widget, event):
+ '''
+ may be implemented by subclasses
+ '''
+ pass
+
+ def set_entry(self, value):
+ self.input_entry.set_text(value)
+ self.input_entry.select_region(0, -1) # select all
+
+ def get_text(self):
+ return self.input_entry.get_text()
+
+
+class InputDialogCheck(InputDialog):
+ """
+ Class for Input dialog
+ """
+
+ def __init__(self, title, label_str, checktext='', input_str=None,
+ is_modal=True, ok_handler=None, cancel_handler=None,
+ transient_for=None):
+ self.xml = get_builder('input_dialog.ui')
+ InputDialog.__init__(self, title, label_str, input_str=input_str,
+ is_modal=is_modal, ok_handler=ok_handler,
+ cancel_handler=cancel_handler,
+ transient_for=transient_for)
+ self.input_entry = self.xml.get_object('input_entry')
+ if input_str:
+ self.input_entry.set_text(input_str)
+ self.input_entry.select_region(0, -1) # select all
+
+ if checktext:
+ self.checkbutton = Gtk.CheckButton.new_with_mnemonic(checktext)
+ self.vbox.pack_start(self.checkbutton, False, True, 0)
+ self.checkbutton.show()
+
+ def on_okbutton_clicked(self, widget):
+ user_input = self.get_text()
+ if user_input:
+ user_input = user_input
+ self.cancel_handler = None
+ self.dialog.destroy()
+ if isinstance(self.ok_handler, tuple):
+ self.ok_handler[0](user_input, self.is_checked(), *self.ok_handler[1:])
+ else:
+ self.ok_handler(user_input, self.is_checked())
+
+ def get_text(self):
+ return self.input_entry.get_text()
+
+ def is_checked(self):
+ """
+ Get active state of the checkbutton
+ """
+ try:
+ return self.checkbutton.get_active()
+ except Exception:
+ # There is no checkbutton
+ return False
+
+
+class ChangeNickDialog(InputDialogCheck):
+ """
+ Class for changing room nickname in case of conflict
+ """
+
+ def __init__(self, account, room_jid, title, prompt, check_text=None,
+ change_nick=False, transient_for=None):
+ """
+ change_nick must be set to True when we are already occupant of the room
+ and we are changing our nick
+ """
+ InputDialogCheck.__init__(self, title, '', checktext=check_text,
+ input_str='', is_modal=True, ok_handler=None,
+ cancel_handler=None,
+ transient_for=transient_for)
+ self.room_queue = [(account, room_jid, prompt, change_nick)]
+ self.check_next()
+
+ def on_input_dialog_delete_event(self, widget, event):
+ self.on_cancelbutton_clicked(widget)
+ return True
+
+ def setup_dialog(self):
+ self.gc_control = app.interface.msg_win_mgr.get_gc_control(
+ self.room_jid, self.account)
+ if not self.gc_control and \
+ self.room_jid in app.interface.minimized_controls[self.account]:
+ self.gc_control = \
+ app.interface.minimized_controls[self.account][self.room_jid]
+ if not self.gc_control:
+ self.check_next()
+ return
+ label = self.xml.get_object('label')
+ label.set_markup(self.prompt)
+ self.set_entry(self.gc_control.nick + \
+ app.config.get('gc_proposed_nick_char'))
+
+ def check_next(self):
+ if len(self.room_queue) == 0:
+ self.cancel_handler = None
+ self.dialog.destroy()
+ if 'change_nick_dialog' in app.interface.instances:
+ del app.interface.instances['change_nick_dialog']
+ return
+ self.account, self.room_jid, self.prompt, self.change_nick = \
+ self.room_queue.pop(0)
+ self.setup_dialog()
+
+ if app.new_room_nick is not None and not app.gc_connected[
+ self.account][self.room_jid] and self.gc_control.nick != \
+ app.new_room_nick:
+ self.dialog.hide()
+ self.on_ok(app.new_room_nick, True)
+ else:
+ self.dialog.show()
+
+ def on_okbutton_clicked(self, widget):
+ nick = self.get_text()
+ if nick:
+ nick = nick
+ # send presence to room
+ try:
+ nick = helpers.parse_resource(nick)
+ except Exception:
+ # invalid char
+ ErrorDialog(_('Invalid nickname'),
+ _('The nickname contains invalid characters.'))
+ return
+ self.on_ok(nick, self.is_checked())
+
+ def on_ok(self, nick, is_checked):
+ if is_checked:
+ app.new_room_nick = nick
+ app.connections[self.account].join_gc(nick, self.room_jid, None,
+ change_nick=self.change_nick)
+ if app.gc_connected[self.account][self.room_jid]:
+ # We are changing nick, we will change self.nick when we receive
+ # presence that inform that it works
+ self.gc_control.new_nick = nick
+ else:
+ # We are connecting, we will not get a changed nick presence so
+ # change it NOW. We don't already have a nick so it's harmless
+ self.gc_control.nick = nick
+ self.check_next()
+
+ def on_cancelbutton_clicked(self, widget):
+ self.gc_control.new_nick = ''
+ self.check_next()
+
+ def add_room(self, account, room_jid, prompt, change_nick=False):
+ if (account, room_jid, prompt, change_nick) not in self.room_queue:
+ self.room_queue.append((account, room_jid, prompt, change_nick))
+
+
+class InputTextDialog(CommonInputDialog):
+ """
+ Class for multilines Input dialog (more place than InputDialog)
+ """
+
+ def __init__(self, title, label_str, input_str=None, is_modal=True,
+ ok_handler=None, cancel_handler=None, transient_for=None):
+ self.xml = get_builder('input_text_dialog.ui')
+ CommonInputDialog.__init__(self, title, label_str, is_modal,
+ ok_handler, cancel_handler,
+ transient_for=transient_for)
+ self.input_buffer = self.xml.get_object('input_textview').get_buffer()
+ if input_str:
+ self.input_buffer.set_text(input_str)
+ start_iter, end_iter = self.input_buffer.get_bounds()
+ self.input_buffer.select_range(start_iter, end_iter) # select all
+
+ def get_text(self):
+ start_iter, end_iter = self.input_buffer.get_bounds()
+ return self.input_buffer.get_text(start_iter, end_iter, True)
+
+
+class DoubleInputDialog:
+ """
+ Class for Double Input dialog
+ """
+
+ def __init__(self, title, label_str1, label_str2, input_str1=None,
+ input_str2=None, is_modal=True, ok_handler=None, cancel_handler=None,
+ transient_for=None):
+ self.xml = get_builder('dubbleinput_dialog.ui')
+ self.dialog = self.xml.get_object('dubbleinput_dialog')
+ label1 = self.xml.get_object('label1')
+ self.input_entry1 = self.xml.get_object('input_entry1')
+ label2 = self.xml.get_object('label2')
+ self.input_entry2 = self.xml.get_object('input_entry2')
+ self.dialog.set_title(title)
+ label1.set_markup(label_str1)
+ label2.set_markup(label_str2)
+ self.cancel_handler = cancel_handler
+ if input_str1:
+ self.input_entry1.set_text(input_str1)
+ self.input_entry1.select_region(0, -1) # select all
+ if input_str2:
+ self.input_entry2.set_text(input_str2)
+ self.input_entry2.select_region(0, -1) # select all
+ if transient_for:
+ self.dialog.set_transient_for(transient_for)
+
+ self.dialog.set_modal(is_modal)
+
+ self.ok_handler = ok_handler
+ okbutton = self.xml.get_object('okbutton')
+ okbutton.connect('clicked', self.on_okbutton_clicked)
+ cancelbutton = self.xml.get_object('cancelbutton')
+ cancelbutton.connect('clicked', self.on_cancelbutton_clicked)
+ self.xml.connect_signals(self)
+ self.dialog.show_all()
+
+ def on_dubbleinput_dialog_destroy(self, widget):
+ if not self.cancel_handler:
+ return False
+ if isinstance(self.cancel_handler, tuple):
+ self.cancel_handler[0](*self.cancel_handler[1:])
+ else:
+ self.cancel_handler()
+
+ def on_okbutton_clicked(self, widget):
+ user_input1 = self.input_entry1.get_text()
+ user_input2 = self.input_entry2.get_text()
+ self.cancel_handler = None
+ self.dialog.destroy()
+ if not self.ok_handler:
+ return
+ if isinstance(self.ok_handler, tuple):
+ self.ok_handler[0](user_input1, user_input2, *self.ok_handler[1:])
+ else:
+ self.ok_handler(user_input1, user_input2)
+
+ def on_cancelbutton_clicked(self, widget):
+ self.dialog.destroy()
+ if not self.cancel_handler:
+ return
+ if isinstance(self.cancel_handler, tuple):
+ self.cancel_handler[0](*self.cancel_handler[1:])
+ else:
+ self.cancel_handler()
+
+
+class CertificatDialog(InformationDialog):
+ def __init__(self, parent, account, cert):
+ issuer = cert.get_issuer()
+ subject = cert.get_subject()
+ InformationDialog.__init__(self,
+ _('Certificate for account %s') % account, _('''Issued to:
+Common Name (CN): %(scn)s
+Organization (O): %(sorg)s
+Organizationl Unit (OU): %(sou)s
+Serial Number: %(sn)s
+
+Issued by:
+Common Name (CN): %(icn)s
+Organization (O): %(iorg)s
+Organizationl Unit (OU): %(iou)s
+
+Validity:
+Issued on: %(io)s
+Expires on: %(eo)s
+
+Fingerprint
+SHA-1 Fingerprint: %(sha1)s
+
+SHA-256 Fingerprint: %(sha256)s
+''') % {
+ 'scn': subject.commonName, 'sorg': subject.organizationName,
+ 'sou': subject.organizationalUnitName,
+ 'sn': cert.get_serial_number(), 'icn': issuer.commonName,
+ 'iorg': issuer.organizationName,
+ 'iou': issuer.organizationalUnitName,
+ 'io': cert.get_notBefore().decode('utf-8'),
+ 'eo': cert.get_notAfter().decode('utf-8'),
+ 'sha1': cert.digest('sha1').decode('utf-8'),
+ 'sha256': cert.digest('sha256').decode('utf-8')})
+ surface = load_icon('application-certificate', self, size=32)
+ if surface is not None:
+ img = Gtk.Image.new_from_surface(surface)
+ img.show_all()
+ self.set_image(img)
+ self.set_transient_for(parent)
+ self.set_title(_('Certificate for account %s') % account)
+
+
+class SSLErrorDialog(ConfirmationDialogDoubleCheck):
+ def __init__(self, account, certificate, pritext, sectext, checktext1,
+ checktext2, on_response_ok=None, on_response_cancel=None):
+ self.account = account
+ self.cert = certificate
+ ConfirmationDialogDoubleCheck.__init__(
+ self, pritext, sectext,
+ checktext1, checktext2, on_response_ok=on_response_ok,
+ on_response_cancel=on_response_cancel, is_modal=False)
+ b = Gtk.Button(_('View cert…'))
+ b.connect('clicked', self.on_cert_clicked)
+ b.show_all()
+ area = self.get_action_area()
+ area.pack_start(b, True, True, 0)
+
+ def on_cert_clicked(self, button):
+ CertificatDialog(self, self.account, self.cert)
diff --git a/gajim/gtk/history_sync.py b/gajim/gtk/history_sync.py
index 09e0db981..918da6cad 100644
--- a/gajim/gtk/history_sync.py
+++ b/gajim/gtk/history_sync.py
@@ -1,5 +1,3 @@
-# Copyright (C) 2017 Philipp Hörist
-#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
@@ -25,7 +23,7 @@ from gajim.common import ged
from gajim.common.const import ArchiveState
from gajim.gtk.util import load_icon
-log = logging.getLogger('gajim.c.message_archiving')
+log = logging.getLogger('gajim.gtk.history_sync')
class Pages(IntEnum):
diff --git a/gajim/gtk/join_groupchat.py b/gajim/gtk/join_groupchat.py
new file mode 100644
index 000000000..bb4072a33
--- /dev/null
+++ b/gajim/gtk/join_groupchat.py
@@ -0,0 +1,325 @@
+# 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 .
+
+import nbxmpp
+from gi.repository import Gtk
+from gi.repository import Gdk
+
+from gajim.common import app
+from gajim.common import helpers
+from gajim.common import ged
+from gajim.common.caps_cache import muc_caps_cache
+from gajim.common.exceptions import GajimGeneralException
+from gajim.gtk import ErrorDialog
+from gajim.gtk.util import get_builder
+
+
+class JoinGroupchatWindow(Gtk.ApplicationWindow):
+ def __init__(self, account, room_jid, password=None, automatic=None,
+ transient_for=None):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_name('JoinGroupchat')
+ self.set_application(app.app)
+ self.set_show_menubar(False)
+ self.set_resizable(False)
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.set_title(_('Join Group Chat'))
+ if transient_for:
+ self.set_transient_for(transient_for)
+
+ self.automatic = automatic
+ self.password = password
+ self.requested_jid = None
+ self.room_jid = room_jid
+ self.account = account
+
+ if self.room_jid is None:
+ self.minimal_mode = False
+ else:
+ self.minimal_mode = True
+
+ glade_objects = ['main_box', 'account_label', 'account_combo',
+ 'server_label', 'server_combo', 'room_entry',
+ 'recently_button', 'recently_popover',
+ 'recently_treeview', 'search_button', 'password_label',
+ 'password_entry', 'nick_entry', 'bookmark_switch',
+ 'autojoin_switch']
+
+ self.builder = get_builder('join_groupchat_window.ui')
+ for obj in glade_objects:
+ setattr(self, obj, self.builder.get_object(obj))
+
+ self.add(self.main_box)
+
+ # Show widgets depending on the mode the window is in
+ if not self.minimal_mode:
+ self.recently_button.show()
+ self.search_button.show()
+
+ accounts = app.get_enabled_accounts_with_labels()
+ account_liststore = self.account_combo.get_model()
+ for acc in accounts:
+ account_liststore.append(acc)
+
+ if not accounts:
+ return
+
+ if not self.account:
+ self.account = accounts[0][0]
+
+ self.builder.connect_signals(self)
+ self.connect('key-press-event', self._on_key_press_event)
+ self.connect('destroy', self._on_destroy)
+
+ if not self.minimal_mode:
+ app.ged.register_event_handler('agent-info-received', ged.GUI1,
+ self._nec_agent_info_received)
+ app.ged.register_event_handler('agent-info-error-received', ged.GUI1,
+ self._nec_agent_info_error_received)
+
+ # Hide account combobox if there is only one account
+ if len(accounts) == 1:
+ self.account_combo.hide()
+ self.account_label.hide()
+
+ self.account_combo.set_active_id(self.account)
+
+ if self.minimal_mode:
+ if '@' in self.room_jid:
+ (room, server) = self.room_jid.split('@')
+ self.room_entry.set_text(room)
+ if not muc_caps_cache.supports(
+ self.room_jid, 'muc_passwordprotected'):
+ self.password_entry.hide()
+ self.password_label.hide()
+ self.nick_entry.grab_focus()
+ else:
+ self.password_entry.grab_focus()
+ else:
+ server = self.room_jid
+ self.room_entry.grab_focus()
+
+ self.server_combo.insert_text(0, server)
+ self.server_combo.set_active(0)
+
+ if self.password is not None:
+ self.password_entry.set_text(self.password)
+
+ # Set bookmark switch sensitive if server supports bookmarks
+ acc = self.account_combo.get_active_id()
+ if not app.connections[acc].private_storage_supported:
+ self.bookmark_switch.set_sensitive(False)
+ self.autojoin_switch.set_sensitive(False)
+
+ self.show_all()
+
+ def set_room(self, room_jid):
+ room, server = app.get_name_and_server_from_jid(room_jid)
+ self.room_entry.set_text(room)
+ self.server_combo.get_child().set_text(server)
+
+ def _fill_recent_and_servers(self, account):
+ recently_liststore = self.recently_treeview.get_model()
+ recently_liststore.clear()
+ self.server_combo.remove_all()
+ recent = app.get_recent_groupchats(account)
+ servers = []
+ for groupchat in recent:
+ label = '%s@%s' % (groupchat.room, groupchat.server)
+
+ recently_liststore.append([groupchat.server,
+ groupchat.room,
+ groupchat.nickname,
+ label])
+ servers.append(groupchat.server)
+
+ self.recently_button.set_sensitive(bool(recent))
+
+ for server in set(servers):
+ self.server_combo.append_text(server)
+
+ # Add own Server to ComboBox
+ muc_domain = app.get_muc_domain(account)
+ if muc_domain is not None:
+ self.server_combo.insert_text(0, muc_domain)
+
+ def _on_recent_selected(self, treeview, *args):
+ (model, iter_) = treeview.get_selection().get_selected()
+ self.server_combo.get_child().set_text(model[iter_][0])
+ self.room_entry.set_text(model[iter_][1])
+ self.nick_entry.set_text(model[iter_][2])
+ self.recently_popover.popdown()
+
+ def _on_account_combo_changed(self, combo):
+ account = combo.get_active_id()
+ self.account = account
+ self.nick_entry.set_text(app.nicks[account])
+ self._fill_recent_and_servers(account)
+
+ def _on_jid_detection_changed(self, widget):
+ text = widget.get_text()
+ if text.startswith('xmpp:'):
+ text = text[5:]
+ if '@' in text:
+ room, server = text.split('@', 1)
+ server = server.split('?')[0]
+ widget.set_text('')
+
+ if room:
+ self.room_entry.set_text(room)
+
+ if server:
+ self.server_combo.get_child().set_text(server)
+ else:
+ self.server_combo.grab_focus()
+
+ def _on_key_press_event(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.destroy()
+ if event.keyval == Gdk.KEY_Return:
+ self._on_join_clicked()
+ return True
+
+ def _on_join_clicked(self, *args):
+ account = self.account_combo.get_active_id()
+ nickname = self.nick_entry.get_text()
+
+ invisible_show = app.SHOW_LIST.index('invisible')
+ if app.connections[account].connected == invisible_show:
+ app.interface.raise_dialog('join-while-invisible')
+ return
+
+ server = self.server_combo.get_active_text()
+ room = self.room_entry.get_text()
+
+ if room == '':
+ ErrorDialog(_('Invalid Room'),
+ _('Please choose a room'), transient_for=self)
+ return
+
+ self.room_jid = '%s@%s' % (room, server)
+ self.room_jid = self.room_jid.lower()
+
+ if app.in_groupchat(account, self.room_jid):
+ # If we already in the groupchat, join_gc_room will bring
+ # it to front
+ app.interface.join_gc_room(account, self.room_jid, nickname, '')
+ self.destroy()
+ return
+
+ if nickname == '':
+ ErrorDialog(_('Invalid Nickname'),
+ _('Please choose a nickname'), transient_for=self)
+ return
+
+ try:
+ helpers.parse_resource(nickname)
+ except helpers.InvalidFormat as error:
+ ErrorDialog(_('Invalid Nickname'), str(error), transient_for=self)
+ return
+
+ try:
+ helpers.parse_jid(self.room_jid)
+ except helpers.InvalidFormat as error:
+ ErrorDialog(_('Invalid JID'), str(error), transient_for=self)
+ return
+
+ if not app.account_is_connected(account):
+ ErrorDialog(
+ _('You are not connected to the server'),
+ _('You can not join a group chat unless you are connected.'),
+ transient_for=self)
+ return
+
+ password = self.password_entry.get_text()
+ self._add_bookmark(account, nickname, password)
+ app.add_recent_groupchat(account, self.room_jid, nickname)
+
+ if self.automatic:
+ app.automatic_rooms[self.account][self.room_jid] = self.automatic
+
+ app.interface.join_gc_room(account, self.room_jid, nickname, password)
+ self.destroy()
+
+ def _on_cancel_clicked(self, *args):
+ self.destroy()
+
+ def _on_bookmark_activate(self, switch, param):
+ bookmark_state = switch.get_active()
+ self.autojoin_switch.set_sensitive(bookmark_state)
+ if not bookmark_state:
+ self.autojoin_switch.set_active(False)
+
+ def _add_bookmark(self, account, nickname, password):
+ con = app.connections[account]
+ if not con.private_storage_supported:
+ return
+
+ add_bookmark = self.bookmark_switch.get_active()
+ if not add_bookmark:
+ return
+
+ autojoin = int(self.autojoin_switch.get_active())
+
+ # Add as bookmark, with autojoin and not minimized
+ name = app.get_nick_from_jid(self.room_jid)
+ con.get_module('Bookmarks').add_bookmark(
+ name, self.room_jid, autojoin, 1, password, nickname)
+
+ def _on_destroy(self, *args):
+ if not self.minimal_mode:
+ app.ged.remove_event_handler('agent-info-received', ged.GUI1,
+ self._nec_agent_info_received)
+ app.ged.remove_event_handler('agent-info-error-received', ged.GUI1,
+ self._nec_agent_info_error_received)
+
+ def _on_search_clicked(self, widget):
+ server = self.server_combo.get_active_text().strip()
+ self.requested_jid = server
+ app.connections[self.account].discoverInfo(server)
+
+ def _nec_agent_info_error_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ if obj.jid != self.requested_jid:
+ return
+ self.requested_jid = None
+ ErrorDialog(_('Wrong server'),
+ _('%s is not a groupchat server') % obj.jid,
+ transient_for=self)
+
+ def _nec_agent_info_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ if obj.jid != self.requested_jid:
+ return
+ self.requested_jid = None
+ if nbxmpp.NS_MUC not in obj.features:
+ ErrorDialog(_('Wrong server'),
+ _('%s is not a groupchat server') % obj.jid,
+ transient_for=self)
+ return
+ if obj.jid in app.interface.instances[self.account]['disco']:
+ app.interface.instances[self.account]['disco'][obj.jid].window.\
+ present()
+ else:
+ try:
+ # Object will add itself to the window dict
+ from gajim.disco import ServiceDiscoveryWindow
+ ServiceDiscoveryWindow(
+ self.account, obj.jid,
+ initial_identities=[{'category': 'conference',
+ 'type': 'text'}])
+ except GajimGeneralException:
+ pass
diff --git a/gajim/gtk/mam_preferences.py b/gajim/gtk/mam_preferences.py
index b19f35c3a..cb461ca9f 100644
--- a/gajim/gtk/mam_preferences.py
+++ b/gajim/gtk/mam_preferences.py
@@ -21,7 +21,7 @@ from gajim.common import app
from gajim.common import ged
from gajim.gtk.util import get_builder
-from gajim.dialogs import HigDialog
+from gajim.gtk import HigDialog
log = logging.getLogger('gajim.gtk.mam_preferences')
diff --git a/gajim/gtk/privacy_list.py b/gajim/gtk/privacy_list.py
new file mode 100644
index 000000000..d76ea61d1
--- /dev/null
+++ b/gajim/gtk/privacy_list.py
@@ -0,0 +1,547 @@
+# 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 .
+
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import GLib
+from gi.repository import GObject
+
+from gajim.common import app
+from gajim.common import ged
+from gajim.gtk import ErrorDialog
+from gajim.gtk.util import get_builder
+
+
+class PrivacyListWindow:
+ def __init__(self, account, privacy_list_name, action):
+ '''action is 'EDIT' or 'NEW' depending on if we create a new priv list
+ or edit an already existing one'''
+ self.account = account
+ self.privacy_list_name = privacy_list_name
+
+ # Dicts and Default Values
+ self.active_rule = ''
+ self.global_rules = {}
+ self.list_of_groups = {}
+
+ self.max_order = 0
+
+ # Default Edit Values
+ self.edit_rule_type = 'jid'
+ self.allow_deny = 'allow'
+
+ # Connect to gtk builder
+ self.xml = get_builder('privacy_list_window.ui')
+ self.window = self.xml.get_object('privacy_list_edit_window')
+
+ # Add Widgets
+
+ for widget_to_add in ('title_hbox', 'privacy_lists_title_label',
+ 'list_of_rules_label', 'add_edit_rule_label', 'delete_open_buttons_hbox',
+ 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton',
+ 'list_of_rules_combobox', 'delete_open_buttons_hbox',
+ 'delete_rule_button', 'open_rule_button', 'edit_allow_radiobutton',
+ 'edit_deny_radiobutton', 'edit_type_jabberid_radiobutton',
+ 'edit_type_jabberid_entry', 'edit_type_group_radiobutton',
+ 'edit_type_group_combobox', 'edit_type_subscription_radiobutton',
+ 'edit_type_subscription_combobox', 'edit_type_select_all_radiobutton',
+ 'edit_queries_send_checkbutton', 'edit_send_messages_checkbutton',
+ 'edit_view_status_checkbutton', 'edit_all_checkbutton',
+ 'edit_order_spinbutton', 'new_rule_button', 'save_rule_button',
+ 'privacy_list_refresh_button', 'privacy_list_close_button',
+ 'edit_send_status_checkbutton', 'add_edit_vbox',
+ 'privacy_list_active_checkbutton', 'privacy_list_default_checkbutton'):
+ self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
+
+ self.privacy_lists_title_label.set_label(
+ _('Privacy List %s') %
+ GLib.markup_escape_text(self.privacy_list_name))
+
+ if len(app.connections) > 1:
+ title = _('Privacy List for %s') % self.account
+ else:
+ title = _('Privacy List')
+
+ self.delete_rule_button.set_sensitive(False)
+ self.open_rule_button.set_sensitive(False)
+ self.privacy_list_active_checkbutton.set_sensitive(False)
+ self.privacy_list_default_checkbutton.set_sensitive(False)
+ self.list_of_rules_combobox.set_sensitive(False)
+
+ # set jabber id completion
+ jids_list_store = Gtk.ListStore(GObject.TYPE_STRING)
+ for jid in app.contacts.get_jid_list(self.account):
+ jids_list_store.append([jid])
+ jid_entry_completion = Gtk.EntryCompletion()
+ jid_entry_completion.set_text_column(0)
+ jid_entry_completion.set_model(jids_list_store)
+ jid_entry_completion.set_popup_completion(True)
+ self.edit_type_jabberid_entry.set_completion(jid_entry_completion)
+ if action == 'EDIT':
+ self.refresh_rules()
+
+ model = self.edit_type_group_combobox.get_model()
+ count = 0
+ for group in app.groups[self.account]:
+ self.list_of_groups[group] = count
+ count += 1
+ model.append([group])
+ self.edit_type_group_combobox.set_active(0)
+
+ self.window.set_title(title)
+
+ app.ged.register_event_handler('privacy-list-received', ged.GUI1,
+ self._nec_privacy_list_received)
+ app.ged.register_event_handler('privacy-lists-received', ged.GUI1,
+ self._nec_privacy_lists_received)
+
+ self.window.show_all()
+ self.add_edit_vbox.hide()
+
+ self.xml.connect_signals(self)
+
+ def on_key_press_event(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.window.destroy()
+
+ def on_privacy_list_edit_window_destroy(self, widget):
+ key_name = 'privacy_list_%s' % self.privacy_list_name
+ if key_name in app.interface.instances[self.account]:
+ del app.interface.instances[self.account][key_name]
+ app.ged.remove_event_handler('privacy-list-received', ged.GUI1,
+ self._nec_privacy_list_received)
+ app.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
+ self._nec_privacy_lists_received)
+
+ def _nec_privacy_lists_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ if obj.active_list == self.privacy_list_name:
+ self.privacy_list_active_checkbutton.set_active(True)
+ else:
+ self.privacy_list_active_checkbutton.set_active(False)
+ if obj.default_list == self.privacy_list_name:
+ self.privacy_list_default_checkbutton.set_active(True)
+ else:
+ self.privacy_list_default_checkbutton.set_active(False)
+
+ def privacy_list_received(self, rules):
+ model = self.list_of_rules_combobox.get_model()
+ model.clear()
+ self.global_rules = {}
+ for rule in rules:
+ if 'type' in rule:
+ text_item = _('Order: %(order)s, action: %(action)s, type: %(type)s'
+ ', value: %(value)s') % {'order': rule['order'],
+ 'action': rule['action'], 'type': rule['type'],
+ 'value': rule['value']}
+ else:
+ text_item = _('Order: %(order)s, action: %(action)s') % \
+ {'order': rule['order'], 'action': rule['action']}
+ if int(rule['order']) > self.max_order:
+ self.max_order = int(rule['order'])
+ self.global_rules[text_item] = rule
+ model.append([text_item])
+ if len(rules) == 0:
+ self.title_hbox.set_sensitive(False)
+ self.list_of_rules_combobox.set_sensitive(False)
+ self.delete_rule_button.set_sensitive(False)
+ self.open_rule_button.set_sensitive(False)
+ self.privacy_list_active_checkbutton.set_sensitive(False)
+ self.privacy_list_default_checkbutton.set_sensitive(False)
+ else:
+ self.list_of_rules_combobox.set_active(0)
+ self.title_hbox.set_sensitive(True)
+ self.list_of_rules_combobox.set_sensitive(True)
+ self.delete_rule_button.set_sensitive(True)
+ self.open_rule_button.set_sensitive(True)
+ self.privacy_list_active_checkbutton.set_sensitive(True)
+ self.privacy_list_default_checkbutton.set_sensitive(True)
+ self.reset_fields()
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').get_privacy_lists()
+
+ def _nec_privacy_list_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ if obj.list_name != self.privacy_list_name:
+ return
+ self.privacy_list_received(obj.rules)
+
+ def refresh_rules(self):
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').get_privacy_list(self.privacy_list_name)
+
+ def on_delete_rule_button_clicked(self, widget):
+ model = self.list_of_rules_combobox.get_model()
+ iter_ = self.list_of_rules_combobox.get_active_iter()
+ _rule = model[iter_][0]
+ tags = []
+ for rule in self.global_rules:
+ if rule != _rule:
+ tags.append(self.global_rules[rule])
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').set_privacy_list(
+ self.privacy_list_name, tags)
+ self.privacy_list_received(tags)
+ self.add_edit_vbox.hide()
+ if not tags: # we removed latest rule
+ if 'privacy_lists' in app.interface.instances[self.account]:
+ win = app.interface.instances[self.account]['privacy_lists']
+ win.remove_privacy_list_from_combobox(self.privacy_list_name)
+ win.draw_widgets()
+
+ def on_open_rule_button_clicked(self, widget):
+ self.add_edit_rule_label.set_label(_('Edit a rule'))
+ active_num = self.list_of_rules_combobox.get_active()
+ if active_num == -1:
+ self.active_rule = ''
+ else:
+ model = self.list_of_rules_combobox.get_model()
+ iter_ = self.list_of_rules_combobox.get_active_iter()
+ self.active_rule = model[iter_][0]
+ if self.active_rule != '':
+ rule_info = self.global_rules[self.active_rule]
+ self.edit_order_spinbutton.set_value(int(rule_info['order']))
+ if 'type' in rule_info:
+ if rule_info['type'] == 'jid':
+ self.edit_type_jabberid_radiobutton.set_active(True)
+ self.edit_type_jabberid_entry.set_text(rule_info['value'])
+ elif rule_info['type'] == 'group':
+ self.edit_type_group_radiobutton.set_active(True)
+ if rule_info['value'] in self.list_of_groups:
+ self.edit_type_group_combobox.set_active(
+ self.list_of_groups[rule_info['value']])
+ else:
+ self.edit_type_group_combobox.set_active(0)
+ elif rule_info['type'] == 'subscription':
+ self.edit_type_subscription_radiobutton.set_active(True)
+ sub_value = rule_info['value']
+ if sub_value == 'none':
+ self.edit_type_subscription_combobox.set_active(0)
+ elif sub_value == 'both':
+ self.edit_type_subscription_combobox.set_active(1)
+ elif sub_value == 'from':
+ self.edit_type_subscription_combobox.set_active(2)
+ elif sub_value == 'to':
+ self.edit_type_subscription_combobox.set_active(3)
+ else:
+ self.edit_type_select_all_radiobutton.set_active(True)
+ else:
+ self.edit_type_select_all_radiobutton.set_active(True)
+ self.edit_send_messages_checkbutton.set_active(False)
+ self.edit_queries_send_checkbutton.set_active(False)
+ self.edit_view_status_checkbutton.set_active(False)
+ self.edit_send_status_checkbutton.set_active(False)
+ self.edit_all_checkbutton.set_active(False)
+ if not rule_info['child']:
+ self.edit_all_checkbutton.set_active(True)
+ else:
+ if 'presence-out' in rule_info['child']:
+ self.edit_send_status_checkbutton.set_active(True)
+ if 'presence-in' in rule_info['child']:
+ self.edit_view_status_checkbutton.set_active(True)
+ if 'iq' in rule_info['child']:
+ self.edit_queries_send_checkbutton.set_active(True)
+ if 'message' in rule_info['child']:
+ self.edit_send_messages_checkbutton.set_active(True)
+
+ if rule_info['action'] == 'allow':
+ self.edit_allow_radiobutton.set_active(True)
+ else:
+ self.edit_deny_radiobutton.set_active(True)
+ self.add_edit_vbox.show()
+
+ def on_edit_all_checkbutton_toggled(self, widget):
+ if widget.get_active():
+ self.edit_send_messages_checkbutton.set_active(True)
+ self.edit_queries_send_checkbutton.set_active(True)
+ self.edit_view_status_checkbutton.set_active(True)
+ self.edit_send_status_checkbutton.set_active(True)
+ self.edit_send_messages_checkbutton.set_sensitive(False)
+ self.edit_queries_send_checkbutton.set_sensitive(False)
+ self.edit_view_status_checkbutton.set_sensitive(False)
+ self.edit_send_status_checkbutton.set_sensitive(False)
+ else:
+ self.edit_send_messages_checkbutton.set_active(False)
+ self.edit_queries_send_checkbutton.set_active(False)
+ self.edit_view_status_checkbutton.set_active(False)
+ self.edit_send_status_checkbutton.set_active(False)
+ self.edit_send_messages_checkbutton.set_sensitive(True)
+ self.edit_queries_send_checkbutton.set_sensitive(True)
+ self.edit_view_status_checkbutton.set_sensitive(True)
+ self.edit_send_status_checkbutton.set_sensitive(True)
+
+ def on_privacy_list_active_checkbutton_toggled(self, widget):
+ name = None
+ if widget.get_active():
+ name = self.privacy_list_name
+
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').set_active_list(name)
+
+ def on_privacy_list_default_checkbutton_toggled(self, widget):
+ name = None
+ if widget.get_active():
+ name = self.privacy_list_name
+
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').set_default_list(name)
+
+ def on_new_rule_button_clicked(self, widget):
+ self.reset_fields()
+ self.add_edit_vbox.show()
+
+ def reset_fields(self):
+ self.edit_type_jabberid_entry.set_text('')
+ self.edit_allow_radiobutton.set_active(True)
+ self.edit_type_jabberid_radiobutton.set_active(True)
+ self.active_rule = ''
+ self.edit_send_messages_checkbutton.set_active(False)
+ self.edit_queries_send_checkbutton.set_active(False)
+ self.edit_view_status_checkbutton.set_active(False)
+ self.edit_send_status_checkbutton.set_active(False)
+ self.edit_all_checkbutton.set_active(False)
+ self.edit_order_spinbutton.set_value(self.max_order + 1)
+ self.edit_type_group_combobox.set_active(0)
+ self.edit_type_subscription_combobox.set_active(0)
+ self.add_edit_rule_label.set_label(_('Add a rule'))
+
+ def get_current_tags(self):
+ if self.edit_type_jabberid_radiobutton.get_active():
+ edit_type = 'jid'
+ edit_value = self.edit_type_jabberid_entry.get_text()
+ elif self.edit_type_group_radiobutton.get_active():
+ edit_type = 'group'
+ model = self.edit_type_group_combobox.get_model()
+ iter_ = self.edit_type_group_combobox.get_active_iter()
+ edit_value = model[iter_][0]
+ elif self.edit_type_subscription_radiobutton.get_active():
+ edit_type = 'subscription'
+ subs = ['none', 'both', 'from', 'to']
+ edit_value = subs[self.edit_type_subscription_combobox.get_active()]
+ elif self.edit_type_select_all_radiobutton.get_active():
+ edit_type = ''
+ edit_value = ''
+ edit_order = str(self.edit_order_spinbutton.get_value_as_int())
+ if self.edit_allow_radiobutton.get_active():
+ edit_deny = 'allow'
+ else:
+ edit_deny = 'deny'
+ child = []
+ if not self.edit_all_checkbutton.get_active():
+ if self.edit_send_messages_checkbutton.get_active():
+ child.append('message')
+ if self.edit_queries_send_checkbutton.get_active():
+ child.append('iq')
+ if self.edit_send_status_checkbutton.get_active():
+ child.append('presence-out')
+ if self.edit_view_status_checkbutton.get_active():
+ child.append('presence-in')
+ if edit_type != '':
+ return {'order': edit_order, 'action': edit_deny,
+ 'type': edit_type, 'value': edit_value, 'child': child}
+ return {'order': edit_order, 'action': edit_deny, 'child': child}
+
+ def on_save_rule_button_clicked(self, widget):
+ tags = []
+ current_tags = self.get_current_tags()
+ if int(current_tags['order']) > self.max_order:
+ self.max_order = int(current_tags['order'])
+ if self.active_rule == '':
+ tags.append(current_tags)
+
+ for rule in self.global_rules:
+ if rule != self.active_rule:
+ tags.append(self.global_rules[rule])
+ else:
+ tags.append(current_tags)
+
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').set_privacy_list(
+ self.privacy_list_name, tags)
+ self.refresh_rules()
+ self.add_edit_vbox.hide()
+ if 'privacy_lists' in app.interface.instances[self.account]:
+ win = app.interface.instances[self.account]['privacy_lists']
+ win.add_privacy_list_to_combobox(self.privacy_list_name)
+ win.draw_widgets()
+
+ def on_list_of_rules_combobox_changed(self, widget):
+ self.add_edit_vbox.hide()
+
+ def on_edit_type_radiobutton_changed(self, widget, radiobutton):
+ active_bool = widget.get_active()
+ if active_bool:
+ self.edit_rule_type = radiobutton
+
+ def on_edit_allow_radiobutton_changed(self, widget, radiobutton):
+ active_bool = widget.get_active()
+ if active_bool:
+ self.allow_deny = radiobutton
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
+
+
+class PrivacyListsWindow:
+ """
+ Window that is the main window for Privacy Lists; we can list there the
+ privacy lists and ask to create a new one or edit an already there one
+ """
+ def __init__(self, account):
+ self.account = account
+ self.privacy_lists_save = []
+
+ self.xml = get_builder('privacy_lists_window.ui')
+
+ self.window = self.xml.get_object('privacy_lists_first_window')
+ for widget_to_add in (
+ 'list_of_privacy_lists_combobox', 'delete_privacy_list_button',
+ 'open_privacy_list_button', 'new_privacy_list_button',
+ 'new_privacy_list_entry', 'privacy_lists_refresh_button',
+ 'close_privacy_lists_window_button'):
+ self.__dict__[widget_to_add] = self.xml.get_object(widget_to_add)
+
+ self.draw_privacy_lists_in_combobox([])
+ self.privacy_lists_refresh()
+
+ self.enabled = True
+
+ if len(app.connections) > 1:
+ title = _('Privacy Lists for %s') % self.account
+ else:
+ title = _('Privacy Lists')
+
+ self.window.set_title(title)
+
+ app.ged.register_event_handler('privacy-lists-received', ged.GUI1,
+ self._nec_privacy_lists_received)
+ app.ged.register_event_handler('privacy-list-removed', ged.GUI1,
+ self._nec_privacy_lists_removed)
+
+ self.window.show_all()
+
+ self.xml.connect_signals(self)
+
+ def on_key_press_event(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.window.destroy()
+
+ def on_privacy_lists_first_window_destroy(self, widget):
+ if 'privacy_lists' in app.interface.instances[self.account]:
+ del app.interface.instances[self.account]['privacy_lists']
+ app.ged.remove_event_handler('privacy-lists-received', ged.GUI1,
+ self._nec_privacy_lists_received)
+ app.ged.remove_event_handler('privacy-list-removed', ged.GUI1,
+ self._nec_privacy_lists_removed)
+
+ def remove_privacy_list_from_combobox(self, privacy_list):
+ if privacy_list not in self.privacy_lists_save:
+ return
+
+ model = self.list_of_privacy_lists_combobox.get_model()
+ for entry in model:
+ if entry[0] == privacy_list:
+ model.remove(entry.iter)
+
+ self.privacy_lists_save.remove(privacy_list)
+
+ def add_privacy_list_to_combobox(self, privacy_list):
+ if privacy_list in self.privacy_lists_save:
+ return
+ model = self.list_of_privacy_lists_combobox.get_model()
+ model.append([privacy_list])
+ self.privacy_lists_save.append(privacy_list)
+
+ def draw_privacy_lists_in_combobox(self, privacy_lists):
+ self.list_of_privacy_lists_combobox.set_active(-1)
+ self.list_of_privacy_lists_combobox.get_model().clear()
+ self.privacy_lists_save = []
+ for add_item in privacy_lists:
+ self.add_privacy_list_to_combobox(add_item)
+ self.draw_widgets()
+
+ def draw_widgets(self):
+ if len(self.privacy_lists_save) == 0:
+ self.list_of_privacy_lists_combobox.set_sensitive(False)
+ self.open_privacy_list_button.set_sensitive(False)
+ self.delete_privacy_list_button.set_sensitive(False)
+ else:
+ self.list_of_privacy_lists_combobox.set_sensitive(True)
+ self.list_of_privacy_lists_combobox.set_active(0)
+ self.open_privacy_list_button.set_sensitive(True)
+ self.delete_privacy_list_button.set_sensitive(True)
+
+ def on_close_button_clicked(self, widget):
+ self.window.destroy()
+
+ def on_delete_privacy_list_button_clicked(self, widget):
+ active_list = self.privacy_lists_save[
+ self.list_of_privacy_lists_combobox.get_active()]
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').del_privacy_list(active_list)
+
+ def privacy_list_removed(self, active_list):
+ self.privacy_lists_save.remove(active_list)
+ self.privacy_lists_received(self.privacy_lists_save)
+
+ def _nec_privacy_lists_removed(self, obj):
+ if obj.conn.name != self.account:
+ return
+ self.privacy_list_removed(obj.list_name)
+
+ def privacy_lists_received(self, lists):
+ privacy_lists = []
+ for privacy_list in lists:
+ privacy_lists.append(privacy_list)
+ self.draw_privacy_lists_in_combobox(privacy_lists)
+
+ def _nec_privacy_lists_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ self.privacy_lists_received(obj.lists)
+
+ def privacy_lists_refresh(self):
+ con = app.connections[self.account]
+ con.get_module('PrivacyLists').get_privacy_lists()
+
+ def on_new_privacy_list_button_clicked(self, widget):
+ name = self.new_privacy_list_entry.get_text()
+ if not name:
+ ErrorDialog(
+ _('Invalid List Name'),
+ _('You must enter a name to create a privacy list.'),
+ transient_for=self.window)
+ return
+ key_name = 'privacy_list_%s' % name
+ if key_name in app.interface.instances[self.account]:
+ app.interface.instances[self.account][key_name].window.present()
+ else:
+ app.interface.instances[self.account][key_name] = \
+ PrivacyListWindow(self.account, name, 'NEW')
+ self.new_privacy_list_entry.set_text('')
+
+ def on_privacy_lists_refresh_button_clicked(self, widget):
+ self.privacy_lists_refresh()
+
+ def on_open_privacy_list_button_clicked(self, widget):
+ name = self.privacy_lists_save[
+ self.list_of_privacy_lists_combobox.get_active()]
+ key_name = 'privacy_list_%s' % name
+ if key_name in app.interface.instances[self.account]:
+ app.interface.instances[self.account][key_name].window.present()
+ else:
+ app.interface.instances[self.account][key_name] = \
+ PrivacyListWindow(self.account, name, 'EDIT')
diff --git a/gajim/server_info.py b/gajim/gtk/server_info.py
similarity index 91%
rename from gajim/server_info.py
rename to gajim/gtk/server_info.py
index d88904acc..152c961be 100644
--- a/gajim/server_info.py
+++ b/gajim/gtk/server_info.py
@@ -1,34 +1,28 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2017 Philipp Hörist
-#
# 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, either version 3 of the License, or
-# (at your option) any later version.
+# 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
+# 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 .
+import logging
from collections import namedtuple
from datetime import timedelta
-import logging
-from gi.repository import Gtk
import nbxmpp
+from gi.repository import Gtk
from gajim.common import app
from gajim.common import ged
-from gajim.gtkgui_helpers import get_icon_pixmap, Color
-log = logging.getLogger('gajim.serverinfo')
+log = logging.getLogger('gajim.gtk.serverinfo')
class ServerInfoDialog(Gtk.Dialog):
@@ -221,16 +215,18 @@ class FeatureItem(Gtk.Grid):
def set_feature(self, available, enabled):
if not available:
- self.icon.set_from_pixbuf(
- get_icon_pixmap('window-close-symbolic', color=[Color.RED]))
+ self.icon.set_from_icon_name('window-close-symbolic',
+ Gtk.IconSize.MENU)
+ self.icon.get_style_context().add_class('error-color')
elif enabled is False:
- self.icon.set_from_pixbuf(
- get_icon_pixmap('dialog-warning-symbolic',
- color=[Color.ORANGE]))
+ self.icon.set_from_icon_name('dialog-warning-symbolic',
+ Gtk.IconSize.MENU)
self.tooltip += _('\nDisabled in config')
+ self.icon.get_style_context().add_class('warning-color')
else:
- self.icon.set_from_pixbuf(
- get_icon_pixmap('emblem-ok-symbolic', color=[Color.GREEN]))
+ self.icon.set_from_icon_name('emblem-ok-symbolic',
+ Gtk.IconSize.MENU)
+ self.icon.get_style_context().add_class('success-color')
def update(self, feature):
self.tooltip = feature.tooltip
diff --git a/gajim/gtk/single_message.py b/gajim/gtk/single_message.py
new file mode 100644
index 000000000..a10efcc3f
--- /dev/null
+++ b/gajim/gtk/single_message.py
@@ -0,0 +1,335 @@
+# 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 .
+
+from gi.repository import Gdk
+from gi.repository import GLib
+
+from gajim.common import app
+from gajim.common import dataforms
+from gajim.common import helpers
+from gajim.common.connection_handlers_events import MessageOutgoingEvent
+from gajim.gtk import ErrorDialog
+from gajim.gtk import AspellDictError
+from gajim.gtk.util import get_builder
+from gajim.gtk.util import get_iconset_name_for
+from gajim.gtk.util import get_completion_liststore
+from gajim.gtk.util import move_window
+from gajim.gtk.util import resize_window
+from gajim.dataforms_widget import DataFormWidget
+from gajim.conversation_textview import ConversationTextview
+
+
+if app.is_installed('GSPELL'):
+ from gi.repository import Gspell
+
+
+class SingleMessageWindow:
+ """
+ SingleMessageWindow can send or show a received singled message depending on
+ action argument which can be 'send' or 'receive'
+ """
+ # Keep a reference on windows so garbage collector don't restroy them
+ instances = []
+ def __init__(self, account, to='', action='', from_whom='', subject='',
+ message='', resource='', session=None, form_node=None):
+ self.instances.append(self)
+ self.account = account
+ self.action = action
+
+ self.subject = subject
+ self.message = message
+ self.to = to
+ self.from_whom = from_whom
+ self.resource = resource
+ self.session = session
+
+ self.xml = get_builder('single_message_window.ui')
+ self.window = self.xml.get_object('single_message_window')
+ self.count_chars_label = self.xml.get_object('count_chars_label')
+ self.from_label = self.xml.get_object('from_label')
+ self.from_entry = self.xml.get_object('from_entry')
+ self.to_label = self.xml.get_object('to_label')
+ self.to_entry = self.xml.get_object('to_entry')
+ self.subject_entry = self.xml.get_object('subject_entry')
+ self.message_scrolledwindow = self.xml.get_object(
+ 'message_scrolledwindow')
+ self.message_textview = self.xml.get_object('message_textview')
+ self.message_tv_buffer = self.message_textview.get_buffer()
+ self.conversation_scrolledwindow = self.xml.get_object(
+ 'conversation_scrolledwindow')
+ self.conversation_textview = ConversationTextview(
+ account, used_in_history_window=True)
+ self.conversation_textview.tv.show()
+ self.conversation_tv_buffer = self.conversation_textview.tv.get_buffer()
+ self.xml.get_object('conversation_scrolledwindow').add(
+ self.conversation_textview.tv)
+
+ self.form_widget = None
+ parent_box = self.xml.get_object('conversation_scrolledwindow').\
+ get_parent()
+ if form_node:
+ dataform = dataforms.ExtendForm(node=form_node)
+ dataform.type_ = 'submit'
+ self.form_widget = DataFormWidget(dataform)
+ self.form_widget.show_all()
+ parent_box.add(self.form_widget)
+ parent_box.child_set_property(self.form_widget, 'position',
+ parent_box.child_get_property(self.xml.get_object(
+ 'conversation_scrolledwindow'), 'position'))
+ self.action = 'form'
+
+ self.send_button = self.xml.get_object('send_button')
+ self.reply_button = self.xml.get_object('reply_button')
+ self.send_and_close_button = self.xml.get_object('send_and_close_button')
+ self.cancel_button = self.xml.get_object('cancel_button')
+ self.close_button = self.xml.get_object('close_button')
+ self.message_tv_buffer.connect('changed', self.update_char_counter)
+ if isinstance(to, list):
+ jid = ', '.join( [i[0].get_full_jid() for i in to])
+ self.to_entry.set_text(jid)
+ self.to_entry.set_sensitive(False)
+ else:
+ self.to_entry.set_text(to)
+
+ if app.config.get('use_speller') and app.is_installed('GSPELL') and action == 'send':
+ lang = app.config.get('speller_language')
+ gspell_lang = Gspell.language_lookup(lang)
+ if gspell_lang is None:
+ AspellDictError(lang)
+ else:
+ spell_buffer = Gspell.TextBuffer.get_from_gtk_text_buffer(
+ self.message_textview.get_buffer())
+ spell_buffer.set_spell_checker(Gspell.Checker.new(gspell_lang))
+ spell_view = Gspell.TextView.get_from_gtk_text_view(
+ self.message_textview)
+ spell_view.set_inline_spell_checking(True)
+ spell_view.set_enable_language_menu(True)
+
+ self.prepare_widgets_for(self.action)
+
+ # set_text(None) raises TypeError exception
+ if self.subject is None:
+ self.subject = ''
+ self.subject_entry.set_text(self.subject)
+
+
+ if to == '':
+ liststore = get_completion_liststore(self.to_entry)
+ self.completion_dict = helpers.get_contact_dict_for_account(account)
+ keys = sorted(self.completion_dict.keys())
+ for jid in keys:
+ contact = self.completion_dict[jid]
+ status_icon = get_iconset_name_for(contact.show)
+ liststore.append((status_icon, jid))
+ else:
+ self.completion_dict = {}
+ self.xml.connect_signals(self)
+
+ # get window position and size from config
+ resize_window(self.window,
+ app.config.get('single-msg-width'),
+ app.config.get('single-msg-height'))
+ move_window(self.window,
+ app.config.get('single-msg-x-position'),
+ app.config.get('single-msg-y-position'))
+
+ self.window.show_all()
+
+ def on_single_message_window_destroy(self, widget):
+ self.instances.remove(self)
+ c = app.contacts.get_contact_with_highest_priority(self.account,
+ self.from_whom)
+ if not c:
+ # Groupchat is maybe already destroyed
+ return
+ if c.is_groupchat() and self.from_whom not in \
+ app.interface.minimized_controls[self.account] and self.action == \
+ 'receive' and app.events.get_nb_roster_events(self.account,
+ self.from_whom, types=['chat', 'normal']) == 0:
+ app.interface.roster.remove_groupchat(self.from_whom, self.account)
+
+ def set_cursor_to_end(self):
+ end_iter = self.message_tv_buffer.get_end_iter()
+ self.message_tv_buffer.place_cursor(end_iter)
+
+ def save_pos(self):
+ # save the window size and position
+ x, y = self.window.get_position()
+ app.config.set('single-msg-x-position', x)
+ app.config.set('single-msg-y-position', y)
+ width, height = self.window.get_size()
+ app.config.set('single-msg-width', width)
+ app.config.set('single-msg-height', height)
+
+ def on_single_message_window_delete_event(self, window, ev):
+ self.save_pos()
+
+ def prepare_widgets_for(self, action):
+ if len(app.connections) > 1:
+ if action == 'send':
+ title = _('Single Message using account %s') % self.account
+ else:
+ title = _('Single Message in account %s') % self.account
+ else:
+ title = _('Single Message')
+
+ if action == 'send': # prepare UI for Sending
+ title = _('Send %s') % title
+ self.send_button.show()
+ self.send_and_close_button.show()
+ self.to_label.show()
+ self.to_entry.show()
+ self.reply_button.hide()
+ self.from_label.hide()
+ self.from_entry.hide()
+ self.conversation_scrolledwindow.hide()
+ self.message_scrolledwindow.show()
+
+ if self.message: # we come from a reply?
+ self.message_textview.grab_focus()
+ self.cancel_button.hide()
+ self.close_button.show()
+ self.message_tv_buffer.set_text(self.message)
+ GLib.idle_add(self.set_cursor_to_end)
+ else: # we write a new message (not from reply)
+ self.close_button.hide()
+ if self.to: # do we already have jid?
+ self.subject_entry.grab_focus()
+
+ elif action == 'receive': # prepare UI for Receiving
+ title = _('Received %s') % title
+ self.reply_button.show()
+ self.from_label.show()
+ self.from_entry.show()
+ self.send_button.hide()
+ self.send_and_close_button.hide()
+ self.to_label.hide()
+ self.to_entry.hide()
+ self.conversation_scrolledwindow.show()
+ self.message_scrolledwindow.hide()
+
+ if self.message:
+ self.conversation_textview.print_real_text(self.message)
+ fjid = self.from_whom
+ if self.resource:
+ fjid += '/' + self.resource # Full jid of sender (with resource)
+ self.from_entry.set_text(fjid)
+ self.from_entry.set_property('editable', False)
+ self.subject_entry.set_property('editable', False)
+ self.reply_button.grab_focus()
+ self.cancel_button.hide()
+ self.close_button.show()
+ elif action == 'form': # prepare UI for Receiving
+ title = _('Form %s') % title
+ self.send_button.show()
+ self.send_and_close_button.show()
+ self.to_label.show()
+ self.to_entry.show()
+ self.reply_button.hide()
+ self.from_label.hide()
+ self.from_entry.hide()
+ self.conversation_scrolledwindow.hide()
+ self.message_scrolledwindow.hide()
+
+ self.window.set_title(title)
+
+ def on_cancel_button_clicked(self, widget):
+ self.save_pos()
+ self.window.destroy()
+
+ def on_close_button_clicked(self, widget):
+ self.save_pos()
+ self.window.destroy()
+
+ def update_char_counter(self, widget):
+ characters_no = self.message_tv_buffer.get_char_count()
+ self.count_chars_label.set_text(str(characters_no))
+
+ def send_single_message(self):
+ if app.connections[self.account].connected <= 1:
+ # if offline or connecting
+ ErrorDialog(_('Connection not available'),
+ _('Please make sure you are connected with "%s".') % self.account)
+ return True
+ if isinstance(self.to, list):
+ sender_list = []
+ for i in self.to:
+ if i[0].resource:
+ sender_list.append(i[0].jid + '/' + i[0].resource)
+ else:
+ sender_list.append(i[0].jid)
+ else:
+ sender_list = [j.strip() for j in self.to_entry.get_text().split(
+ ',')]
+
+ subject = self.subject_entry.get_text()
+ begin, end = self.message_tv_buffer.get_bounds()
+ message = self.message_tv_buffer.get_text(begin, end, True)
+
+ if self.form_widget:
+ form_node = self.form_widget.data_form
+ else:
+ form_node = None
+
+ recipient_list = []
+
+ for to_whom_jid in sender_list:
+ if to_whom_jid in self.completion_dict:
+ to_whom_jid = self.completion_dict[to_whom_jid].jid
+ try:
+ to_whom_jid = helpers.parse_jid(to_whom_jid)
+ except helpers.InvalidFormat:
+ ErrorDialog(_('Invalid JID'),
+ _('It is not possible to send a message to %s, this JID is not '
+ 'valid.') % to_whom_jid)
+ return True
+
+ if '/announce/' in to_whom_jid:
+ app.connections[self.account].send_motd(to_whom_jid, subject,
+ message)
+ continue
+
+ recipient_list.append(to_whom_jid)
+
+ app.nec.push_outgoing_event(MessageOutgoingEvent(None,
+ account=self.account, jid=recipient_list, message=message,
+ type_='normal', subject=subject, form_node=form_node))
+
+ self.subject_entry.set_text('') # we sent ok, clear the subject
+ self.message_tv_buffer.set_text('') # we sent ok, clear the textview
+
+ def on_send_button_clicked(self, widget):
+ self.send_single_message()
+
+ def on_reply_button_clicked(self, widget):
+ # we create a new blank window to send and we preset RE: and to jid
+ self.subject = _('RE: %s') % self.subject
+ self.message = _('%s wrote:\n') % self.from_whom + self.message
+ # add > at the begining of each line
+ self.message = self.message.replace('\n', '\n> ') + '\n\n'
+ self.window.destroy()
+ SingleMessageWindow(self.account, to=self.from_whom, action='send',
+ from_whom=self.from_whom, subject=self.subject, message=self.message,
+ session=self.session)
+
+ def on_send_and_close_button_clicked(self, widget):
+ if self.send_single_message():
+ return
+ self.save_pos()
+ self.window.destroy()
+
+ def on_single_message_window_key_press_event(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape: # ESCAPE
+ self.save_pos()
+ self.window.destroy()
diff --git a/gajim/gtk/start_chat.py b/gajim/gtk/start_chat.py
new file mode 100644
index 000000000..e85ac26f8
--- /dev/null
+++ b/gajim/gtk/start_chat.py
@@ -0,0 +1,343 @@
+# 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 .
+
+import locale
+
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import GLib
+from gi.repository import Pango
+
+from gajim.common import app
+from gajim.common import helpers
+from gajim.common.const import AvatarSize
+
+from gajim.gtk.util import get_iconset_name_for
+from gajim.gtk.util import get_builder
+
+
+class StartChatDialog(Gtk.ApplicationWindow):
+ def __init__(self):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_name('StartChatDialog')
+ self.set_application(app.app)
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.set_show_menubar(False)
+ self.set_title(_('Start new Conversation'))
+ self.set_default_size(-1, 400)
+ self.ready_to_destroy = False
+
+ self.builder = get_builder('start_chat_dialog.ui')
+ self.listbox = self.builder.get_object('listbox')
+ self.search_entry = self.builder.get_object('search_entry')
+ self.box = self.builder.get_object('box')
+
+ self.add(self.box)
+
+ self.new_contact_row_visible = False
+ self.new_contact_rows = {}
+ self.new_groupchat_rows = {}
+ self.accounts = app.connections.keys()
+ self.add_contacts()
+ self.add_groupchats()
+
+ self.search_entry.connect('search-changed',
+ self._on_search_changed)
+ self.search_entry.connect('next-match',
+ self._select_new_match, 'next')
+ self.search_entry.connect('previous-match',
+ self._select_new_match, 'prev')
+ self.search_entry.connect('stop-search',
+ lambda *args: self.search_entry.set_text(''))
+
+ self.listbox.set_filter_func(self._filter_func, None)
+ self.listbox.set_sort_func(self._sort_func, None)
+ self.listbox.connect('row-activated', self._on_row_activated)
+
+ self.connect('key-press-event', self._on_key_press)
+ self.connect('destroy', self._destroy)
+
+ self.select_first_row()
+ self.show_all()
+
+ def add_contacts(self):
+ show_account = len(self.accounts) > 1
+ for account in self.accounts:
+ self.new_contact_rows[account] = None
+ for jid in app.contacts.get_jid_list(account):
+ contact = app.contacts.get_contact_with_highest_priority(
+ account, jid)
+ if contact.is_groupchat():
+ continue
+ row = ContactRow(account, contact, jid,
+ contact.get_shown_name(), show_account)
+ self.listbox.add(row)
+
+ def add_groupchats(self):
+ show_account = len(self.accounts) > 1
+ for account in self.accounts:
+ self.new_groupchat_rows[account] = None
+ con = app.connections[account]
+ bookmarks = con.get_module('Bookmarks').bookmarks
+ groupchats = {}
+ for jid, bookmark in bookmarks.items():
+ groupchats[jid] = bookmark['name']
+
+ for jid in app.contacts.get_gc_list(account):
+ if jid in groupchats:
+ continue
+ groupchats[jid] = None
+
+ for jid in groupchats:
+ name = groupchats[jid]
+ if not name:
+ name = app.get_nick_from_jid(jid)
+ row = ContactRow(account, None, jid, name,
+ show_account, True)
+ self.listbox.add(row)
+
+ def _on_row_activated(self, listbox, row):
+ row = row.get_child()
+ self._start_new_chat(row)
+
+ def _on_key_press(self, widget, event):
+ if event.keyval in (Gdk.KEY_Down, Gdk.KEY_Tab):
+ self.search_entry.emit('next-match')
+ return True
+ elif (event.state == Gdk.ModifierType.SHIFT_MASK and
+ event.keyval == Gdk.KEY_ISO_Left_Tab):
+ self.search_entry.emit('previous-match')
+ return True
+ elif event.keyval == Gdk.KEY_Up:
+ self.search_entry.emit('previous-match')
+ return True
+ elif event.keyval == Gdk.KEY_Escape:
+ if self.search_entry.get_text() != '':
+ self.search_entry.emit('stop-search')
+ else:
+ self.destroy()
+ return True
+ elif event.keyval == Gdk.KEY_Return:
+ row = self.listbox.get_selected_row()
+ if row is not None:
+ row.emit('activate')
+ return True
+ else:
+ self.search_entry.grab_focus_without_selecting()
+
+ def _start_new_chat(self, row):
+ if row.new:
+ if not app.account_is_connected(row.account):
+ app.interface.raise_dialog('start-chat-not-connected')
+ return
+ try:
+ helpers.parse_jid(row.jid)
+ except helpers.InvalidFormat as e:
+ app.interface.raise_dialog('invalid-jid-with-error', str(e))
+ return
+
+ if row.groupchat:
+ app.interface.join_gc_minimal(row.account, row.jid)
+ else:
+ app.interface.new_chat_from_jid(row.account, row.jid)
+
+ self.ready_to_destroy = True
+
+ def _on_search_changed(self, entry):
+ search_text = entry.get_text()
+ if '@' in search_text:
+ self._add_new_jid_row()
+ self._update_new_jid_rows(search_text)
+ else:
+ self._remove_new_jid_row()
+ self.listbox.invalidate_filter()
+
+ def _add_new_jid_row(self):
+ if self.new_contact_row_visible:
+ return
+ for account in self.new_contact_rows:
+ show_account = len(self.accounts) > 1
+ row = ContactRow(account, None, '', None, show_account)
+ self.new_contact_rows[account] = row
+ group_row = ContactRow(account, None, '', None, show_account, True)
+ self.new_groupchat_rows[account] = group_row
+ self.listbox.add(row)
+ self.listbox.add(group_row)
+ row.get_parent().show_all()
+ self.new_contact_row_visible = True
+
+ def _remove_new_jid_row(self):
+ if not self.new_contact_row_visible:
+ return
+ for account in self.new_contact_rows:
+ self.listbox.remove(self.new_contact_rows[account].get_parent())
+ self.listbox.remove(self.new_groupchat_rows[account].get_parent())
+ self.new_contact_row_visible = False
+
+ def _update_new_jid_rows(self, search_text):
+ for account in self.new_contact_rows:
+ self.new_contact_rows[account].update_jid(search_text)
+ self.new_groupchat_rows[account].update_jid(search_text)
+
+ def _select_new_match(self, entry, direction):
+ selected_row = self.listbox.get_selected_row()
+ index = selected_row.get_index()
+
+ if direction == 'next':
+ index += 1
+ else:
+ index -= 1
+
+ while True:
+ new_selected_row = self.listbox.get_row_at_index(index)
+ if new_selected_row is None:
+ return
+ if new_selected_row.get_child_visible():
+ self.listbox.select_row(new_selected_row)
+ new_selected_row.grab_focus()
+ return
+ if direction == 'next':
+ index += 1
+ else:
+ index -= 1
+
+ def select_first_row(self):
+ first_row = self.listbox.get_row_at_y(0)
+ self.listbox.select_row(first_row)
+
+ def _filter_func(self, row, user_data):
+ search_text = self.search_entry.get_text().lower()
+ search_text_list = search_text.split()
+ row_text = row.get_child().get_search_text().lower()
+ for text in search_text_list:
+ if text not in row_text:
+ GLib.timeout_add(50, self.select_first_row)
+ return
+ GLib.timeout_add(50, self.select_first_row)
+ return True
+
+ @staticmethod
+ def _sort_func(row1, row2, user_data):
+ name1 = row1.get_child().get_search_text()
+ name2 = row2.get_child().get_search_text()
+ account1 = row1.get_child().account
+ account2 = row2.get_child().account
+ is_groupchat1 = row1.get_child().groupchat
+ is_groupchat2 = row2.get_child().groupchat
+ new1 = row1.get_child().new
+ new2 = row2.get_child().new
+
+ result = locale.strcoll(account1.lower(), account2.lower())
+ if result != 0:
+ return result
+
+ if new1 != new2:
+ return 1 if new1 else -1
+
+ if is_groupchat1 != is_groupchat2:
+ return 1 if is_groupchat1 else -1
+
+ return locale.strcoll(name1.lower(), name2.lower())
+
+ @staticmethod
+ def _destroy(*args):
+ del app.interface.instances['start_chat']
+
+
+class ContactRow(Gtk.Grid):
+ def __init__(self, account, contact, jid, name, show_account,
+ groupchat=False):
+ Gtk.Grid.__init__(self)
+ self.set_column_spacing(12)
+ self.set_size_request(260, -1)
+ self.account = account
+ self.account_label = app.config.get_per(
+ 'accounts', account, 'account_label') or account
+ self.show_account = show_account
+ self.jid = jid
+ self.contact = contact
+ self.name = name
+ self.groupchat = groupchat
+ self.new = jid == ''
+
+ if self.groupchat:
+ muc_icon = get_iconset_name_for(
+ 'muc-inactive' if self.new else 'muc-active')
+ image = Gtk.Image.new_from_icon_name(muc_icon, Gtk.IconSize.DND)
+ else:
+ scale = self.get_scale_factor()
+ avatar = app.contacts.get_avatar(
+ account, jid, AvatarSize.ROSTER, scale)
+ if avatar is None:
+ image = Gtk.Image.new_from_icon_name(
+ 'avatar-default', Gtk.IconSize.DND)
+ else:
+ image = Gtk.Image.new_from_surface(avatar)
+
+ image.set_size_request(AvatarSize.ROSTER, AvatarSize.ROSTER)
+ self.add(image)
+
+ middle_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
+ middle_box.set_hexpand(True)
+
+ if self.name is None:
+ if self.groupchat:
+ self.name = _('New Groupchat')
+ else:
+ self.name = _('New Contact')
+
+ self.name_label = Gtk.Label(self.name)
+ self.name_label.set_ellipsize(Pango.EllipsizeMode.END)
+ self.name_label.set_xalign(0)
+ self.name_label.set_width_chars(25)
+ self.name_label.set_halign(Gtk.Align.START)
+ self.name_label.get_style_context().add_class('bold16')
+
+ status = contact.show if contact else 'offline'
+ css_class = helpers.get_css_show_color(status)
+ if css_class is not None:
+ self.name_label.get_style_context().add_class(css_class)
+ middle_box.add(self.name_label)
+
+ self.jid_label = Gtk.Label(jid)
+ self.jid_label.set_ellipsize(Pango.EllipsizeMode.END)
+ self.jid_label.set_xalign(0)
+ self.jid_label.set_width_chars(25)
+ self.jid_label.set_halign(Gtk.Align.START)
+ middle_box.add(self.jid_label)
+
+ self.add(middle_box)
+
+ if show_account:
+ account_label = Gtk.Label(self.account_label)
+ account_label.set_halign(Gtk.Align.START)
+ account_label.set_valign(Gtk.Align.START)
+
+ right_box = Gtk.Box()
+ right_box.set_vexpand(True)
+ right_box.add(account_label)
+ self.add(right_box)
+
+ self.show_all()
+
+ def update_jid(self, jid):
+ self.jid = jid
+ self.jid_label.set_text(jid)
+
+ def get_search_text(self):
+ if self.contact is None and not self.groupchat:
+ return self.jid
+ if self.show_account:
+ return '%s %s %s' % (self.name, self.jid, self.account_label)
+ return '%s %s' % (self.name, self.jid)
diff --git a/gajim/gtk/util.py b/gajim/gtk/util.py
index c736f0f24..e127598fc 100644
--- a/gajim/gtk/util.py
+++ b/gajim/gtk/util.py
@@ -15,11 +15,13 @@
import os
import sys
import logging
-
-from gi.repository import Gtk
-from gi.repository import GLib
import xml.etree.ElementTree as ET
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import GLib
+
+from gajim.common import app
from gajim.common import i18n
from gajim.common import configpaths
@@ -29,7 +31,7 @@ _icon_theme.append_search_path(configpaths.get('ICONS'))
log = logging.getLogger('gajim.gtk.util')
-def load_icon(icon_name, widget, size=16,
+def load_icon(icon_name, widget, size=16, pixbuf=False,
flags=Gtk.IconLookupFlags.FORCE_SIZE):
scale = widget.get_scale_factor()
@@ -40,6 +42,8 @@ def load_icon(icon_name, widget, size=16,
try:
iconinfo = _icon_theme.lookup_icon_for_scale(
icon_name, size, scale, flags)
+ if pixbuf:
+ return iconinfo.load_icon()
return iconinfo.load_surface(None)
except GLib.GError as e:
log.error('Unable to load icon %s: %s', icon_name, str(e))
@@ -75,3 +79,127 @@ def _translate(gui_file, widget):
builder.add_objects_from_file(gui_file, [widget])
return builder
return Gtk.Builder.new_from_file(gui_file)
+
+
+def get_iconset_name_for(name):
+ if name == 'not in roster':
+ name = 'notinroster'
+ iconset = app.config.get('iconset')
+ if not iconset:
+ iconset = app.config.DEFAULT_ICONSET
+ return '%s-%s' % (iconset, name)
+
+
+def get_total_screen_geometry():
+ screen = Gdk.Screen.get_default()
+ window = Gdk.Screen.get_root_window(screen)
+ w, h = window.get_width(), window.get_height()
+ log.debug('Get screen geometry: %s %s', w, h)
+ return w, h
+
+
+def resize_window(window, w, h):
+ """
+ Resize window, but also checks if huge window or negative values
+ """
+ screen_w, screen_h = get_total_screen_geometry()
+ if not w or not h:
+ return
+ if w > screen_w:
+ w = screen_w
+ if h > screen_h:
+ h = screen_h
+ window.resize(abs(w), abs(h))
+
+
+def move_window(window, x, y):
+ """
+ Move the window, but also check if out of screen
+ """
+ screen_w, screen_h = get_total_screen_geometry()
+ if x < 0:
+ x = 0
+ if y < 0:
+ y = 0
+ w, h = window.get_size()
+ if x + w > screen_w:
+ x = screen_w - w
+ if y + h > screen_h:
+ y = screen_h - h
+ window.move(x, y)
+
+
+def get_completion_liststore(entry):
+ """
+ Create a completion model for entry widget completion list consists of
+ (Pixbuf, Text) rows
+ """
+ completion = Gtk.EntryCompletion()
+ liststore = Gtk.ListStore(str, str)
+
+ render_pixbuf = Gtk.CellRendererPixbuf()
+ completion.pack_start(render_pixbuf, False)
+ completion.add_attribute(render_pixbuf, 'icon_name', 0)
+
+ render_text = Gtk.CellRendererText()
+ completion.pack_start(render_text, True)
+ completion.add_attribute(render_text, 'text', 1)
+ completion.set_property('text_column', 1)
+ completion.set_model(liststore)
+ entry.set_completion(completion)
+ return liststore
+
+
+def get_cursor(attr):
+ display = Gdk.Display.get_default()
+ cursor = getattr(Gdk.CursorType, attr)
+ return Gdk.Cursor.new_for_display(display, cursor)
+
+
+def scroll_to_end(widget):
+ """Scrolls to the end of a GtkScrolledWindow.
+
+ Args:
+ widget (GtkScrolledWindow)
+
+ Returns:
+ bool: The return value is False so it can be used with GLib.idle_add.
+ """
+ adj_v = widget.get_vadjustment()
+ if adj_v is None:
+ # This can happen when the Widget is already destroyed when called
+ # from GLib.idle_add
+ return False
+ max_scroll_pos = adj_v.get_upper() - adj_v.get_page_size()
+ adj_v.set_value(max_scroll_pos)
+
+ adj_h = widget.get_hadjustment()
+ adj_h.set_value(0)
+ return False
+
+
+def at_the_end(widget):
+ """Determines if a Scrollbar in a GtkScrolledWindow is at the end.
+
+ Args:
+ widget (GtkScrolledWindow)
+
+ Returns:
+ bool: The return value is True if at the end, False if not.
+ """
+ adj_v = widget.get_vadjustment()
+ max_scroll_pos = adj_v.get_upper() - adj_v.get_page_size()
+ at_the_end = (adj_v.get_value() == max_scroll_pos)
+ return at_the_end
+
+
+def get_image_button(icon_name, tooltip, toggle=False):
+ if toggle:
+ button = Gtk.ToggleButton()
+ image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.MENU)
+ button.set_image(image)
+ else:
+ button = Gtk.Button.new_from_icon_name(
+ icon_name, Gtk.IconSize.MENU)
+ button.set_tooltip_text(tooltip)
+ return button
diff --git a/gajim/gtk/xml_console.py b/gajim/gtk/xml_console.py
new file mode 100644
index 000000000..6f1700e01
--- /dev/null
+++ b/gajim/gtk/xml_console.py
@@ -0,0 +1,273 @@
+# 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 .
+
+import time
+
+import nbxmpp
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import GLib
+
+from gajim.common import app
+from gajim.common import ged
+from gajim.common.const import Option, OptionKind, OptionType
+from gajim.gtk import ErrorDialog
+from gajim.gtk import util
+from gajim.gtk.util import get_builder
+from gajim.gtk.util import get_image_button
+from gajim.options_dialog import OptionsDialog
+
+
+UNDECLARED = 'http://www.gajim.org/xmlns/undeclared'
+
+
+class XMLConsoleWindow(Gtk.Window):
+ def __init__(self, account):
+ Gtk.Window.__init__(self)
+ self.account = account
+ self.enabled = True
+ self.presence = True
+ self.message = True
+ self.iq = True
+ self.stream = True
+ self.incoming = True
+ self.outgoing = True
+ self.filter_dialog = None
+
+ glade_objects = ['textview', 'input', 'scrolled_input', 'headerbar',
+ 'scrolled', 'actionbar', 'paned', 'box', 'menubutton']
+ self.builder = get_builder('xml_console_window.ui')
+ for obj in glade_objects:
+ setattr(self, obj, self.builder.get_object(obj))
+
+ self.set_titlebar(self.headerbar)
+ jid = app.get_jid_from_account(account)
+ self.headerbar.set_subtitle(jid)
+ self.set_default_size(600, 600)
+ self.add(self.box)
+
+ self.paned.set_position(self.paned.get_property('max-position'))
+
+ button = get_image_button(
+ 'edit-clear-all-symbolic', _('Clear'))
+ button.connect('clicked', self.on_clear)
+ self.actionbar.pack_start(button)
+
+ button = get_image_button(
+ 'applications-system-symbolic', _('Filter'))
+ button.connect('clicked', self.on_filter_options)
+ self.actionbar.pack_start(button)
+
+ button = get_image_button(
+ 'document-edit-symbolic', _('XML Input'), toggle=True)
+ button.connect('toggled', self.on_input)
+ self.actionbar.pack_start(button)
+
+ button = get_image_button('emblem-ok-symbolic', _('Send'))
+ button.connect('clicked', self.on_send)
+ self.actionbar.pack_end(button)
+
+ self.actionbar.pack_start(self.menubutton)
+
+ self.create_tags()
+ self.show_all()
+
+ self.scrolled_input.hide()
+ self.menubutton.hide()
+
+ self.connect("destroy", self.on_destroy)
+ self.connect('key_press_event', self.on_key_press_event)
+ self.builder.connect_signals(self)
+
+ app.ged.register_event_handler(
+ 'stanza-received', ged.GUI1, self._nec_stanza_received)
+ app.ged.register_event_handler(
+ 'stanza-sent', ged.GUI1, self._nec_stanza_sent)
+
+ def create_tags(self):
+ buffer_ = self.textview.get_buffer()
+ in_color = app.config.get('inmsgcolor')
+ out_color = app.config.get('outmsgcolor')
+
+ tags = ['presence', 'message', 'stream', 'iq']
+
+ tag = buffer_.create_tag('incoming')
+ tag.set_property('foreground', in_color)
+ tag = buffer_.create_tag('outgoing')
+ tag.set_property('foreground', out_color)
+
+ for tag_name in tags:
+ buffer_.create_tag(tag_name)
+
+ def on_key_press_event(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.destroy()
+
+ def on_row_activated(self, listbox, row):
+ text = row.get_child().get_text()
+ input_text = None
+ if text == 'Presence':
+ input_text = (
+ '\n'
+ '\n'
+ '\n'
+ '\n'
+ '')
+ elif text == 'Message':
+ input_text = (
+ '\n'
+ '\n'
+ '')
+ elif text == 'Iq':
+ input_text = (
+ '\n'
+ '\n'
+ '')
+
+ if input_text is not None:
+ buffer_ = self.input.get_buffer()
+ buffer_.set_text(input_text)
+ self.input.grab_focus()
+
+ def on_send(self, *args):
+ if app.connections[self.account].connected <= 1:
+ # if offline or connecting
+ ErrorDialog(
+ _('Connection not available'),
+ _('Please make sure you are connected with "%s".') %
+ self.account)
+ return
+ buffer_ = self.input.get_buffer()
+ begin_iter, end_iter = buffer_.get_bounds()
+ stanza = buffer_.get_text(begin_iter, end_iter, True)
+ if stanza:
+ try:
+ node = nbxmpp.Protocol(node=stanza)
+ if node.getNamespace() == UNDECLARED:
+ node.setNamespace(nbxmpp.NS_CLIENT)
+ except Exception as error:
+ ErrorDialog(_('Invalid Node'), str(error))
+ return
+ app.connections[self.account].connection.send(node)
+ buffer_.set_text('')
+
+ def on_input(self, button, *args):
+ if button.get_active():
+ self.paned.get_child2().show()
+ self.menubutton.show()
+ self.input.grab_focus()
+ else:
+ self.paned.get_child2().hide()
+ self.menubutton.hide()
+
+ def on_filter_options(self, *args):
+ if self.filter_dialog:
+ self.filter_dialog.present()
+ return
+ options = [
+ Option(OptionKind.SWITCH, 'Presence',
+ OptionType.VALUE, self.presence,
+ callback=self.on_option, data='presence'),
+
+ Option(OptionKind.SWITCH, 'Message',
+ OptionType.VALUE, self.message,
+ callback=self.on_option, data='message'),
+
+ Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq,
+ callback=self.on_option, data='iq'),
+
+ Option(OptionKind.SWITCH, 'Stream\nManagement',
+ OptionType.VALUE, self.stream,
+ callback=self.on_option, data='stream'),
+
+ Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming,
+ callback=self.on_option, data='incoming'),
+
+ Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing,
+ callback=self.on_option, data='outgoing'),
+ ]
+
+ self.filter_dialog = OptionsDialog(self, 'Filter',
+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ options, self.account)
+ self.filter_dialog.connect('destroy', self.on_filter_destroyed)
+
+ def on_filter_destroyed(self, win):
+ self.filter_dialog = None
+
+ def on_clear(self, *args):
+ self.textview.get_buffer().set_text('')
+
+ def on_destroy(self, *args):
+ del app.interface.instances[self.account]['xml_console']
+ app.ged.remove_event_handler(
+ 'stanza-received', ged.GUI1, self._nec_stanza_received)
+ app.ged.remove_event_handler(
+ 'stanza-sent', ged.GUI1, self._nec_stanza_sent)
+
+ def on_enable(self, switch, param):
+ self.enabled = switch.get_active()
+
+ def on_option(self, value, data):
+ setattr(self, data, value)
+ value = not value
+ table = self.textview.get_buffer().get_tag_table()
+ tag = table.lookup(data)
+ if data in ('incoming', 'outgoing'):
+ if value:
+ tag.set_priority(table.get_size() - 1)
+ else:
+ tag.set_priority(0)
+ tag.set_property('invisible', value)
+
+ def _nec_stanza_received(self, obj):
+ if obj.conn.name != self.account:
+ return
+ self.print_stanza(obj.stanza_str, 'incoming')
+
+ def _nec_stanza_sent(self, obj):
+ if obj.conn.name != self.account:
+ return
+ self.print_stanza(obj.stanza_str, 'outgoing')
+
+ def print_stanza(self, stanza, kind):
+ # kind must be 'incoming' or 'outgoing'
+ if not self.enabled:
+ return
+ if not stanza:
+ return
+
+ at_the_end = util.at_the_end(self.scrolled)
+
+ buffer_ = self.textview.get_buffer()
+ end_iter = buffer_.get_end_iter()
+
+ type_ = kind
+ if stanza.startswith('<', '>\n<'))
+ buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)
+
+ if at_the_end:
+ GLib.idle_add(util.scroll_to_end, self.scrolled)
diff --git a/gajim/gtkgui_helpers.py b/gajim/gtkgui_helpers.py
index 108577f49..be73370c3 100644
--- a/gajim/gtkgui_helpers.py
+++ b/gajim/gtkgui_helpers.py
@@ -479,7 +479,9 @@ def scale_pixbuf_from_data(data, size):
return scale_pixbuf(pixbuf, size)
def on_avatar_save_as_menuitem_activate(widget, avatar, default_name=''):
- from gajim import dialogs
+ from gajim.gtk import ErrorDialog
+ from gajim.gtk import ConfirmationDialog
+ from gajim.gtk import FTOverwriteConfirmationDialog
def on_continue(response, file_path):
if response < 0:
return
@@ -509,7 +511,7 @@ def on_avatar_save_as_menuitem_activate(widget, avatar, default_name=''):
new_file_path = '.'.join(file_path.split('.')[:-1]) + '.png'
def on_ok(file_path, pixbuf):
pixbuf.savev(file_path, 'png', [], [])
- dialogs.ConfirmationDialog(_('Extension not supported'),
+ ConfirmationDialog(_('Extension not supported'),
_('Image cannot be saved in %(type)s format. Save as '
'%(new_filename)s?') % {'type': image_format,
'new_filename': new_file_path},
@@ -520,18 +522,18 @@ def on_avatar_save_as_menuitem_activate(widget, avatar, default_name=''):
# check if we have write permissions
if not os.access(file_path, os.W_OK):
file_name = os.path.basename(file_path)
- dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"') % \
+ ErrorDialog(_('Cannot overwrite existing file "%s"') % \
file_name, _('A file with this name already exists and you '
'do not have permission to overwrite it.'))
return
- dialog2 = dialogs.FTOverwriteConfirmationDialog(
+ dialog2 = FTOverwriteConfirmationDialog(
_('This file already exists'), _('What do you want to do?'),
propose_resume=False, on_response=(on_continue, file_path))
dialog2.set_destroy_with_parent(True)
else:
dirname = os.path.dirname(file_path)
if not os.access(dirname, os.W_OK):
- dialogs.ErrorDialog(_('Directory "%s" is not writable') % \
+ ErrorDialog(_('Directory "%s" is not writable') % \
dirname, _('You do not have permission to create files in '
'this directory.'))
return
diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py
index 7896307ed..2d37176db 100644
--- a/gajim/gui_interface.py
+++ b/gajim/gui_interface.py
@@ -69,6 +69,7 @@ from gajim import notify
from gajim import message_control
from gajim.dialog_messages import get_dialog
from gajim.dialogs import ProgressWindow
+
from gajim.filechoosers import FileChooserDialog
from gajim.chat_control_base import ChatControlBase
@@ -111,6 +112,18 @@ from threading import Thread
from gajim.common import ged
from gajim.common.caps_cache import muc_caps_cache
+from gajim.gtk import JoinGroupchatWindow
+from gajim.gtk import ErrorDialog
+from gajim.gtk import WarningDialog
+from gajim.gtk import InformationDialog
+from gajim.gtk import InputDialog
+from gajim.gtk import YesNoDialog
+from gajim.gtk import InputTextDialog
+from gajim.gtk import PlainConnectionDialog
+from gajim.gtk import SSLErrorDialog
+from gajim.gtk import ConfirmationDialogDoubleCheck
+from gajim.gtk import ChangeNickDialog
+
from gajim.common import configpaths
from gajim.common import optparser
@@ -129,7 +142,7 @@ class Interface:
#('DB_ERROR', account, error)
if self.db_error_dialog:
return
- self.db_error_dialog = dialogs.ErrorDialog(_('Database Error'), error)
+ self.db_error_dialog = ErrorDialog(_('Database Error'), error)
def destroyed(win):
self.db_error_dialog = None
self.db_error_dialog.connect('destroy', destroyed)
@@ -144,11 +157,11 @@ class Interface:
return
if obj.level == 'error':
- cls = dialogs.ErrorDialog
+ cls = ErrorDialog
elif obj.level == 'warn':
- cls = dialogs.WarningDialog
+ cls = WarningDialog
elif obj.level == 'info':
- cls = dialogs.InformationDialog
+ cls = InformationDialog
else:
return
@@ -169,7 +182,7 @@ class Interface:
self.instances['change_nick_dialog'].add_room(account, room_jid,
prompt)
else:
- self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
+ self.instances['change_nick_dialog'] = ChangeNickDialog(
account, room_jid, title, prompt, transient_for=parent_win)
@staticmethod
@@ -188,7 +201,7 @@ class Interface:
sec_msg = _('Do you accept this request on account %s?') % account
if obj.msg:
sec_msg = obj.msg + '\n' + sec_msg
- dialog = dialogs.YesNoDialog(_('HTTP (%(method)s) Authorization for '
+ dialog = YesNoDialog(_('HTTP (%(method)s) Authorization for '
'%(url)s (ID: %(id)s)') % {'method': obj.method, 'url': obj.url,
'id': obj.iq_id}, sec_msg, on_response_yes=(on_yes, obj),
on_response_no=(response, obj, 'no'))
@@ -285,14 +298,14 @@ class Interface:
def on_close(dummy):
gc_control.error_dialog.destroy()
gc_control.error_dialog = None
- gc_control.error_dialog = dialogs.ErrorDialog(pritext, sectext,
+ gc_control.error_dialog = ErrorDialog(pritext, sectext,
on_response_ok=on_close, on_response_cancel=on_close)
gc_control.error_dialog.set_modal(False)
if gc_control.parent_win:
gc_control.error_dialog.set_transient_for(
gc_control.parent_win.window)
else:
- d = dialogs.ErrorDialog(pritext, sectext)
+ d = ErrorDialog(pritext, sectext)
if gc_control and gc_control.parent_win:
d.set_transient_for(gc_control.parent_win.window)
d.set_modal(False)
@@ -318,7 +331,7 @@ class Interface:
if gc_control.error_dialog:
gc_control.error_dialog.destroy()
- gc_control.error_dialog = dialogs.InputDialog(_('Password Required'),
+ gc_control.error_dialog = InputDialog(_('Password Required'),
_('A Password is required to join the room %s. Please type it.') % \
room_jid, is_modal=False, ok_handler=on_ok,
cancel_handler=on_cancel)
@@ -530,7 +543,7 @@ class Interface:
status='online', ask='to', resource=obj.resource, keyID=keyID)
app.contacts.add_contact(account, contact1)
self.roster.add_contact(obj.jid, account)
- dialogs.InformationDialog(_('Authorization accepted'),
+ InformationDialog(_('Authorization accepted'),
_('The contact "%s" has authorized you to see their status.')
% obj.jid)
@@ -538,7 +551,7 @@ class Interface:
def on_yes(is_checked, list_):
self.roster.on_req_usub(None, list_)
list_ = [(contact, account)]
- dialogs.YesNoDialog(
+ YesNoDialog(
_('Contact "%s" removed subscription from you') % contact.jid,
_('You will always see them as offline.\nDo you want to '
'remove them from your contact list?'),
@@ -575,7 +588,7 @@ class Interface:
config.ServiceRegistrationWindow(obj.agent, obj.config,
obj.conn.name, obj.is_form)
else:
- dialogs.ErrorDialog(_('Contact with "%s" cannot be established') % \
+ ErrorDialog(_('Contact with "%s" cannot be established') % \
obj.agent, _('Check your connection or try again later.'))
def handle_event_gc_config(self, obj):
@@ -673,7 +686,7 @@ class Interface:
'\n')
sectext += _('You are currently connected without your OpenPGP '
'key.')
- dialogs.WarningDialog(_('Wrong passphrase'), sectext)
+ WarningDialog(_('Wrong passphrase'), sectext)
else:
account = obj.conn.name
app.notification.popup(
@@ -716,7 +729,7 @@ class Interface:
def on_no():
obj.callback(False)
- dialogs.YesNoDialog(_('Untrusted OpenPGP key'), _('The OpenPGP key '
+ YesNoDialog(_('Untrusted OpenPGP key'), _('The OpenPGP key '
'used to encrypt this chat is not trusted. Do you really want to '
'encrypt this message?'), checktext=_('_Do not ask me again'),
on_response_yes=on_yes, on_response_no=on_no)
@@ -764,7 +777,7 @@ class Interface:
instruction = _('Please copy / paste the refresh token from the website'
' that has just been opened.')
- self.pass_dialog[account] = dialogs.InputTextDialog(
+ self.pass_dialog[account] = InputTextDialog(
_('Oauth2 Credentials'), instruction, is_modal=False,
ok_handler=on_ok, cancel_handler=on_cancel)
@@ -917,7 +930,7 @@ class Interface:
@staticmethod
def handle_event_file_error(title, message):
- dialogs.ErrorDialog(title, message)
+ ErrorDialog(title, message)
def handle_event_file_progress(self, account, file_props):
if time.time() - self.last_ftwindow_update > 0.5:
@@ -1185,7 +1198,7 @@ class Interface:
def on_cancel():
obj.conn.change_status('offline', '')
- dlg = dialogs.InputDialog(_('Username Conflict'),
+ dlg = InputDialog(_('Username Conflict'),
_('Please type a new username for your local account'),
input_str=obj.alt_name, is_modal=True, ok_handler=on_ok,
cancel_handler=on_cancel, transient_for=self.roster.window)
@@ -1337,7 +1350,7 @@ class Interface:
with open(my_ca_certs, encoding='utf-8') as f:
certs = f.read()
if pem in certs:
- dialogs.ErrorDialog(_('Certificate Already in File'),
+ ErrorDialog(_('Certificate Already in File'),
_('This certificate is already in file %s, so it\'s '
'not added again.') % my_ca_certs)
else:
@@ -1384,7 +1397,7 @@ class Interface:
if 'ssl_error' in self.instances[account]['online_dialog']:
self.instances[account]['online_dialog']['ssl_error'].destroy()
self.instances[account]['online_dialog']['ssl_error'] = \
- dialogs.SSLErrorDialog(obj.conn.name, obj.cert, pritext,
+ SSLErrorDialog(obj.conn.name, obj.cert, pritext,
sectext, checktext1, checktext2, on_response_ok=on_ok,
on_response_cancel=on_cancel)
self.instances[account]['online_dialog']['ssl_error'].set_title(
@@ -1393,7 +1406,7 @@ class Interface:
def handle_event_non_anonymous_server(self, obj):
account = obj.conn.name
server = app.config.get_per('accounts', account, 'hostname')
- dialogs.ErrorDialog(_('Non Anonymous Server'), sectext='Server "%s"'
+ ErrorDialog(_('Non Anonymous Server'), sectext='Server "%s"'
'does not support anonymous connection' % server,
transient_for=self.roster.window)
@@ -1426,7 +1439,7 @@ class Interface:
self.instances[obj.conn.name]['online_dialog']['plain_connection'].\
destroy()
self.instances[obj.conn.name]['online_dialog']['plain_connection'] = \
- dialogs.PlainConnectionDialog(obj.conn.name, on_ok, on_cancel)
+ PlainConnectionDialog(obj.conn.name, on_ok, on_cancel)
def handle_event_insecure_ssl_connection(self, obj):
# ('INSECURE_SSL_CONNECTION', account, (connection, connection_type))
@@ -1464,7 +1477,7 @@ class Interface:
self.instances[obj.conn.name]['online_dialog']['insecure_ssl'].\
destroy()
self.instances[obj.conn.name]['online_dialog']['insecure_ssl'] = \
- dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel,
is_modal=False)
@@ -1506,7 +1519,7 @@ class Interface:
self.instances[obj.conn.name]['online_dialog']\
['insecure_password'].destroy()
self.instances[obj.conn.name]['online_dialog']['insecure_password'] = \
- dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel,
is_modal=False)
@@ -1749,7 +1762,7 @@ class Interface:
try:
room_jid = helpers.parse_jid(room_jid)
except helpers.InvalidFormat:
- dialogs.ErrorDialog('Invalid JID',
+ ErrorDialog('Invalid JID',
transient_for=app.app.get_active_window())
return
@@ -1757,7 +1770,7 @@ class Interface:
if account is not None and account not in connected_accounts:
connected_accounts = None
if not connected_accounts:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('You are not connected to the server'),
_('You can not join a group chat unless you are connected.'),
transient_for=app.app.get_active_window())
@@ -1765,10 +1778,10 @@ class Interface:
def _on_discover_result():
if not muc_caps_cache.is_cached(room_jid):
- dialogs.ErrorDialog(_('JID is not a Groupchat'),
+ ErrorDialog(_('JID is not a Groupchat'),
transient_for=app.app.get_active_window())
return
- dialogs.JoinGroupchatWindow(account, room_jid, password=password,
+ JoinGroupchatWindow(account, room_jid, password=password,
transient_for=transient_for)
disco_account = connected_accounts[0] if account is None else account
@@ -1928,7 +1941,7 @@ class Interface:
path = helpers.get_emoticon_theme_path(emot_theme)
if not emoticons.load(path, ascii_emoticons):
- dialogs.WarningDialog(
+ WarningDialog(
_('Emoticons disabled'),
_('Your configured emoticons theme could not be loaded.'
' See the log for more details.'),
@@ -1948,7 +1961,7 @@ class Interface:
if app.contacts.get_contact(account, room_jid) and \
not app.contacts.get_contact(account, room_jid).is_groupchat():
- dialogs.ErrorDialog(_('This is not a group chat'),
+ ErrorDialog(_('This is not a group chat'),
_('%(room_jid)s is already in your roster. Please check '
'if %(room_jid)s is a correct group chat name. If it is, '
'delete it from your roster and try joining the group chat '
@@ -1973,7 +1986,7 @@ class Interface:
invisible_show = app.SHOW_LIST.index('invisible')
if app.connections[account].connected == invisible_show:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('You cannot join a group chat while you are invisible'))
return
@@ -2331,7 +2344,7 @@ class Interface:
print(err_str, file=sys.stderr)
# it is good to notify the user
# in case he or she cannot see the output of the console
- error_dialog = dialogs.ErrorDialog(_('Could not save your settings and '
+ error_dialog = ErrorDialog(_('Could not save your settings and '
'preferences'), err_str)
error_dialog.run()
sys.exit()
@@ -2923,7 +2936,7 @@ class PassphraseRequest:
self.complete(passphrase)
return
elif result == 'expired':
- dialogs.ErrorDialog(_('OpenPGP key expired'),
+ ErrorDialog(_('OpenPGP key expired'),
_('Your OpenPGP key has expired, you will be connected to '
'%s without OpenPGP.') % account)
# Don't try to connect with GPG
diff --git a/gajim/history_manager.py b/gajim/history_manager.py
index b603a68c9..76ca8dad9 100644
--- a/gajim/history_manager.py
+++ b/gajim/history_manager.py
@@ -85,7 +85,9 @@ if is_standalone():
from gajim.common import app
from gajim.common.const import JIDConstant, KindConstant
from gajim.common import helpers
-from gajim import dialogs
+from gajim.gtk import YesNoDialog
+from gajim.gtk import ErrorDialog
+from gajim.gtk import ConfirmationDialog
from gajim.filechoosers import FileSaveDialog
from gajim import gtkgui_helpers
@@ -111,8 +113,8 @@ class HistoryManager:
log_db_path = configpaths.get('LOG_DB')
if not os.path.exists(log_db_path):
- dialogs.ErrorDialog(_('Cannot find history logs database'),
- '%s does not exist.' % log_db_path)
+ ErrorDialog(_('Cannot find history logs database'),
+ '%s does not exist.' % log_db_path)
sys.exit()
xml = gtkgui_helpers.get_gtk_builder('history_manager.ui')
@@ -250,7 +252,7 @@ class HistoryManager:
if is_standalone():
Gtk.main_quit()
- dialog = dialogs.YesNoDialog(
+ dialog = YesNoDialog(
_('Do you want to clean up the database? '
'(STRONGLY NOT RECOMMENDED IF GAJIM IS RUNNING)'),
_('Normally allocated database size will not be freed, '
@@ -581,7 +583,7 @@ class HistoryManager:
else:
pri_text = _(
'Do you wish to delete all correspondence with the selected contacts?')
- dialog = dialogs.ConfirmationDialog('',
+ dialog = ConfirmationDialog('',
_('This can not be undone.'), on_response_ok=(on_ok,
liststore, list_of_paths))
dialog.set_title(_('Deletion Confirmation'))
@@ -620,7 +622,7 @@ class HistoryManager:
pri_text = i18n.ngettext(
'Do you really want to delete the selected message?',
'Do you really want to delete the selected messages?', paths_len)
- dialog = dialogs.ConfirmationDialog(pri_text,
+ dialog = ConfirmationDialog(pri_text,
_('This is an irreversible operation.'), on_response_ok=(on_ok,
liststore, list_of_paths))
dialog.set_title(_('Deletion Confirmation'))
diff --git a/gajim/history_window.py b/gajim/history_window.py
index 3e68bd389..173870cf8 100644
--- a/gajim/history_window.py
+++ b/gajim/history_window.py
@@ -35,7 +35,7 @@ from enum import IntEnum, unique
from gajim import gtkgui_helpers
from gajim import conversation_textview
-from gajim import dialogs
+from gajim.gtk import ErrorDialog
from gajim.common import app
from gajim.common import helpers
@@ -386,7 +386,7 @@ class HistoryWindow:
log_days = app.logger.get_days_with_logs(
self.account, self.jid, year, month)
except exceptions.PysqliteOperationalError as e:
- dialogs.ErrorDialog(_('Disk Error'), str(e))
+ ErrorDialog(_('Disk Error'), str(e))
return
for date in log_days:
@@ -439,7 +439,7 @@ class HistoryWindow:
logs = app.logger.get_date_has_logs(
self.account, self.jid, _date)
except exceptions.PysqliteOperationalError as e:
- dialogs.ErrorDialog(_('Disk Error'), str(e))
+ ErrorDialog(_('Disk Error'), str(e))
return
gtk_month = gtkgui_helpers.make_python_month_gtk_month(_date.month)
diff --git a/gajim/htmltextview.py b/gajim/htmltextview.py
index 3332f7b13..1958a01e0 100644
--- a/gajim/htmltextview.py
+++ b/gajim/htmltextview.py
@@ -51,10 +51,12 @@ if __name__ == '__main__':
from gajim.common import configpaths
configpaths.init()
from gajim.common import app
-from gajim import gtkgui_helpers
-from gajim.gtkgui_helpers import get_icon_pixmap
+from gajim.gtk.util import load_icon
+from gajim.gtk.util import get_cursor
+from gajim.gtk.util import get_builder
from gajim.common import helpers
-from gajim import dialogs
+from gajim.gtk import JoinGroupchatWindow
+from gajim.gtk import AddNewContactWindow
import logging
log = logging.getLogger('gajim.htmlview')
@@ -554,7 +556,7 @@ class HtmlHandler(xml.sax.handler.ContentHandler):
if alt:
alt += '\n'
alt += _('Loading')
- pixbuf = get_icon_pixmap('gtk-no')
+ pixbuf = load_icon('image-missing', self.textview, pixbuf=True)
if mem:
# Caveat: GdkPixbuf is known not to be safe to load
# images from network... this program is now potentially
@@ -889,11 +891,11 @@ class HtmlTextView(Gtk.TextView):
text = text[:47] + '…'
tooltip.set_text(text)
if not self._changed_cursor:
- window.set_cursor(gtkgui_helpers.get_cursor('HAND2'))
+ window.set_cursor(get_cursor('HAND2'))
self._changed_cursor = True
return True
if self._changed_cursor:
- window.set_cursor(gtkgui_helpers.get_cursor('XTERM'))
+ window.set_cursor(get_cursor('XTERM'))
self._changed_cursor = False
return False
@@ -908,14 +910,13 @@ class HtmlTextView(Gtk.TextView):
# app.interface.new_chat_from_jid(self.account, jid)
def on_join_group_chat_menuitem_activate(self, widget, room_jid):
- dialogs.JoinGroupchatWindow(None, room_jid)
+ JoinGroupchatWindow(None, room_jid)
def on_add_to_roster_activate(self, widget, jid):
- dialogs.AddNewContactWindow(self.account, jid)
+ AddNewContactWindow(self.account, jid)
def make_link_menu(self, event, kind, text):
- from gajim.gtkgui_helpers import get_gtk_builder
- xml = get_gtk_builder('chat_context_menu.ui')
+ xml = get_builder('chat_context_menu.ui')
menu = xml.get_object('chat_context_menu')
childs = menu.get_children()
if kind == 'url':
@@ -1114,13 +1115,13 @@ if __name__ == '__main__':
y = pointer[2]
tags = htmlview.tv.get_iter_at_location(x, y)[1].get_tags()
if change_cursor:
- w.set_cursor(gtkgui_helpers.get_cursor('XTERM'))
+ w.set_cursor(get_cursor('XTERM'))
change_cursor = None
tag_table = htmlview.tv.get_buffer().get_tag_table()
for tag in tags:
try:
if tag.is_anchor:
- w.set_cursor(gtkgui_helpers.get_cursor('HAND2'))
+ w.set_cursor(get_cursor('HAND2'))
change_cursor = tag
elif tag == tag_table.lookup('focus-out-line'):
over_line = True
diff --git a/gajim/message_window.py b/gajim/message_window.py
index e91882f75..cc417ce31 100644
--- a/gajim/message_window.py
+++ b/gajim/message_window.py
@@ -37,7 +37,7 @@ from gi.repository import GLib
from gajim import common
from gajim import gtkgui_helpers
from gajim import message_control
-from gajim import dialogs
+from gajim.gtk import YesNoDialog
from gajim.chat_control_base import ChatControlBase
from gajim.chat_control import ChatControl
from gajim.common import app
@@ -240,7 +240,7 @@ class MessageWindow(object):
ctrl.minimize()
# destroy window
return False
- dialogs.YesNoDialog(
+ YesNoDialog(
_('You are going to close several tabs'),
_('Do you really want to close them all?'),
checktext=_('_Do not ask me again'), on_response_yes=on_yes1,
diff --git a/gajim/options_dialog.py b/gajim/options_dialog.py
index a7124bec1..a5e66ab5b 100644
--- a/gajim/options_dialog.py
+++ b/gajim/options_dialog.py
@@ -5,7 +5,7 @@ from gajim import gtkgui_helpers
from gajim.common.const import OptionKind, OptionType
from gajim.common.exceptions import GajimGeneralException
from gajim import dialogs
-
+from gajim.gtk import ErrorDialog
class OptionsDialog(Gtk.ApplicationWindow):
def __init__(self, parent, title, flags, options, account,
@@ -558,7 +558,7 @@ class GPGOption(DialogOption):
secret_keys[_('None')] = _('None')
if not secret_keys:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Failed to get secret keys'),
_('There is no OpenPGP secret key available.'),
transient_for=parent)
diff --git a/gajim/plugins/gui.py b/gajim/plugins/gui.py
index 5df3b3b38..70a0b290d 100644
--- a/gajim/plugins/gui.py
+++ b/gajim/plugins/gui.py
@@ -26,16 +26,16 @@ GUI classes related to plug-in management.
__all__ = ['PluginsWindow']
-from gi.repository import Pango
from gi.repository import Gtk
from gi.repository import GdkPixbuf
-from gi.repository import GLib, Gdk
+from gi.repository import Gdk
import os
from enum import IntEnum, unique
from gajim import gtkgui_helpers
-from gajim.dialogs import WarningDialog, YesNoDialog
+from gajim.gtk import WarningDialog
+from gajim.gtk import YesNoDialog
from gajim.filechoosers import ArchiveChooserDialog
from gajim.common import app
from gajim.common import configpaths
@@ -44,6 +44,7 @@ from gajim.plugins.helpers import GajimPluginActivateException
from gajim.plugins.plugins_i18n import _
from gajim.common.exceptions import PluginsystemError
+
@unique
class Column(IntEnum):
PLUGIN = 0
diff --git a/gajim/profile_window.py b/gajim/profile_window.py
index ca6a595a8..088914d3c 100644
--- a/gajim/profile_window.py
+++ b/gajim/profile_window.py
@@ -30,7 +30,8 @@ from gi.repository import Gdk
from gi.repository import GLib
from gajim import gtkgui_helpers
-from gajim import dialogs
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InformationDialog
from gajim.filechoosers import AvatarChooserDialog
from gajim.common.const import AvatarSize
from gajim.common import app
@@ -127,7 +128,7 @@ class ProfileWindow(Gtk.ApplicationWindow):
def on_ok(path_to_file):
sha = app.interface.save_avatar(path_to_file, publish=True)
if sha is None:
- dialogs.ErrorDialog(
+ ErrorDialog(
_('Could not load image'), transient_for=self)
return
@@ -181,7 +182,7 @@ class ProfileWindow(Gtk.ApplicationWindow):
except ValueError:
if not widget.is_focus():
pritext = _('Wrong date format')
- dialogs.ErrorDialog(pritext, _('Format of the date must be '
+ ErrorDialog(pritext, _('Format of the date must be '
'YYYY-MM-DD'), transient_for=self)
GLib.idle_add(lambda: widget.grab_focus())
return True
@@ -321,7 +322,7 @@ class ProfileWindow(Gtk.ApplicationWindow):
# Operation in progress
return
if app.connections[self.account].connected < 2:
- dialogs.ErrorDialog(_('You are not connected to the server'),
+ ErrorDialog(_('You are not connected to the server'),
_('Without a connection, you can not publish your contact '
'information.'), transient_for=self)
return
@@ -363,7 +364,7 @@ class ProfileWindow(Gtk.ApplicationWindow):
GLib.source_remove(self.update_progressbar_timeout_id)
self.progressbar.set_fraction(0)
self.update_progressbar_timeout_id = None
- dialogs.InformationDialog(_('vCard publication failed'),
+ InformationDialog(_('vCard publication failed'),
_('There was an error while publishing your personal information, '
'try again later.'), transient_for=self)
diff --git a/gajim/remote_control.py b/gajim/remote_control.py
index 7fb8bead1..9582f4c62 100644
--- a/gajim/remote_control.py
+++ b/gajim/remote_control.py
@@ -33,7 +33,7 @@ import mimetypes
from gajim.common import app
from gajim.common import helpers
-from gajim.dialogs import AddNewContactWindow
+from gajim.gtk import AddNewContactWindow
from gajim.common import ged
from gajim.common.connection_handlers_events import MessageOutgoingEvent
from gajim.common.connection_handlers_events import GcMessageOutgoingEvent
diff --git a/gajim/roster_window.py b/gajim/roster_window.py
index 58c0aa6f7..e6779481a 100644
--- a/gajim/roster_window.py
+++ b/gajim/roster_window.py
@@ -58,6 +58,17 @@ from gajim import cell_renderer_image
from gajim import tooltips
from gajim import message_control
from gajim import adhoc_commands
+from gajim.gtk import JoinGroupchatWindow
+from gajim.gtk import ConfirmationDialogCheck
+from gajim.gtk import ConfirmationDialog
+from gajim.gtk import ErrorDialog
+from gajim.gtk import InputDialog
+from gajim.gtk import WarningDialog
+from gajim.gtk import InformationDialog
+from gajim.gtk import NonModalConfirmationDialog
+from gajim.gtk import SingleMessageWindow
+from gajim.gtk import AddNewContactWindow
+
from gajim.common.const import AvatarSize
from gajim.common import app
@@ -1952,7 +1963,7 @@ class RosterWindow:
ft = app.interface.instances['file_transfers']
event = app.events.get_first_event(account, jid, event.type_)
if event.type_ == 'normal':
- dialogs.SingleMessageWindow(account, jid,
+ SingleMessageWindow(account, jid,
action='receive', from_whom=jid, subject=event.subject,
message=event.message, resource=event.resource,
session=event.session, form_node=event.form_node)
@@ -2025,7 +2036,7 @@ class RosterWindow:
Authorize a contact (by re-sending auth menuitem)
"""
app.connections[account].send_authorization(jid)
- dialogs.InformationDialog(_('Authorization sent'),
+ InformationDialog(_('Authorization sent'),
_('"%s" will now see your status.') %jid)
def req_sub(self, widget, jid, txt, account, groups=None, nickname=None,
@@ -2049,7 +2060,7 @@ class RosterWindow:
app.contacts.add_contact(account, contact)
else:
if not _('Not in Roster') in contact.get_shown_groups():
- dialogs.InformationDialog(_('Subscription request has been '
+ InformationDialog(_('Subscription request has been '
'sent'), _('If "%s" accepts this request you will know his '
'or her status.') % jid)
return
@@ -2064,7 +2075,7 @@ class RosterWindow:
Revoke a contact's authorization
"""
app.connections[account].refuse_authorization(jid)
- dialogs.InformationDialog(_('Authorization removed'),
+ InformationDialog(_('Authorization removed'),
_('Now "%s" will always see you as offline.') %jid)
def set_state(self, account, state):
@@ -2089,7 +2100,7 @@ class RosterWindow:
keyid = app.config.get_per('accounts', account, 'keyid')
if keyid and not app.connections[account].gpg:
- dialogs.WarningDialog(_('OpenPGP is not usable'),
+ WarningDialog(_('OpenPGP is not usable'),
_('Gajim needs python-gnupg >= 0.3.8\n'
'Beware there is an incompatible Python package called gnupg.\n'
'You will be connected to %s without OpenPGP.') % account)
@@ -2298,7 +2309,7 @@ class RosterWindow:
self.get_status_message(status, on_response)
if status == 'invisible' and self.connected_rooms(account):
- dialogs.ConfirmationDialog(
+ ConfirmationDialog(
_('You are participating in one or more group chats'),
_('Changing your status to invisible will result in '
'disconnection from those group chats. Are you sure you want '
@@ -2398,7 +2409,7 @@ class RosterWindow:
if checked:
app.config.set('quit_on_roster_x_button', True)
self.on_quit_request()
- dialogs.ConfirmationDialogCheck(_('Really quit Gajim?'),
+ ConfirmationDialogCheck(_('Really quit Gajim?'),
_('Are you sure you want to quit Gajim?'),
_('Always close Gajim'), on_response_ok=on_ok)
return True # do NOT destroy the window
@@ -2488,7 +2499,7 @@ class RosterWindow:
break
if transfer_active:
- dialogs.ConfirmationDialog(_('You have running file transfers'),
+ ConfirmationDialog(_('You have running file transfers'),
_('If you quit now, the file(s) being transferred will '
'be stopped. Do you still want to quit?'),
on_response_ok=(on_continue3, message, pep_dict))
@@ -2520,7 +2531,7 @@ class RosterWindow:
break
if unread or recent:
- dialogs.ConfirmationDialog(_('You have unread messages'),
+ ConfirmationDialog(_('You have unread messages'),
_('Messages will only be available for reading them later '
'if you have history enabled and contact is in your '
'roster.'), on_response_ok=(on_continue2,
@@ -2694,7 +2705,7 @@ class RosterWindow:
return
if obj.mtype == 'normal' and obj.popup:
# it's single message to be autopopuped
- dialogs.SingleMessageWindow(obj.conn.name, obj.jid,
+ SingleMessageWindow(obj.conn.name, obj.jid,
action='receive', from_whom=obj.jid, subject=obj.subject,
message=obj.msgtxt, resource=obj.resource, session=obj.session,
form_node=obj.form_node)
@@ -2798,7 +2809,7 @@ class RosterWindow:
has_unread_events = True
break
if has_unread_events:
- dialogs.ErrorDialog(_('You have unread messages'),
+ ErrorDialog(_('You have unread messages'),
_('You must read them before removing this transport.'))
return
if len(list_) == 1:
@@ -2813,7 +2824,7 @@ class RosterWindow:
jids = jids[:-1] + '.'
sectext = _('You will no longer be able to send and receive '
'messages to contacts from these transports: %s') % jids
- dialogs.ConfirmationDialog(pritext, sectext,
+ ConfirmationDialog(pritext, sectext,
on_response_ok = (remove, list_), transient_for=self.window)
def _nec_blocking(self, obj):
@@ -2864,7 +2875,7 @@ class RosterWindow:
' to continue?')
sectext = _('This contact will see you offline and you will not '
'receive messages it sends you.')
- dialogs.ConfirmationDialogCheck(pritext, sectext,
+ ConfirmationDialogCheck(pritext, sectext,
_('_Do not ask me again'), on_response_ok=_block_it)
def on_unblock(self, widget, list_, group=None):
@@ -2943,7 +2954,7 @@ class RosterWindow:
if 'rename' in app.interface.instances:
del app.interface.instances['rename']
- app.interface.instances['rename'] = dialogs.InputDialog(title,
+ app.interface.instances['rename'] = InputDialog(title,
message, old_text, False, (on_renamed, account, row_type, jid,
old_text), on_canceled, transient_for=self.window)
@@ -2958,7 +2969,7 @@ class RosterWindow:
app.connections[account].unsubscribe(contact.jid)
self.remove_contact(contact.jid, account, backend=True)
- dialogs.ConfirmationDialogCheck(_('Remove Group'),
+ ConfirmationDialogCheck(_('Remove Group'),
_('Do you want to remove group %s from the roster?') % group,
_('Also remove all contacts in this group from your roster'),
on_response_ok=on_ok)
@@ -3039,14 +3050,14 @@ class RosterWindow:
def on_send_single_message_menuitem_activate(self, widget, account,
contact=None):
if contact is None:
- dialogs.SingleMessageWindow(account, action='send')
+ SingleMessageWindow(account, action='send')
elif isinstance(contact, list):
- dialogs.SingleMessageWindow(account, contact, 'send')
+ SingleMessageWindow(account, contact, 'send')
else:
jid = contact.jid
if contact.jid == app.get_jid_from_account(account):
jid += '/' + contact.resource
- dialogs.SingleMessageWindow(account, jid, 'send')
+ SingleMessageWindow(account, jid, 'send')
def on_send_file_menuitem_activate(self, widget, contact, account,
resource=None):
@@ -3077,7 +3088,7 @@ class RosterWindow:
app.interface.instances[account]['join_gc'].destroy()
else:
app.interface.instances[account]['join_gc'] = \
- dialogs.JoinGroupchatWindow(
+ JoinGroupchatWindow(
account, None, automatic={'invities': jid_list})
break
@@ -3147,7 +3158,7 @@ class RosterWindow:
dialogs.ChangeStatusMessageDialog(on_response, show)
def on_add_to_roster(self, widget, contact, account):
- dialogs.AddNewContactWindow(account, contact.jid, contact.name)
+ AddNewContactWindow(account, contact.jid, contact.name)
def on_roster_treeview_key_press_event(self, widget, event):
"""
@@ -3410,17 +3421,17 @@ class RosterWindow:
'your roster.\n') % {'name': contact.get_shown_name(),
'jid': contact.jid}
if contact.sub == 'to':
- dialogs.ConfirmationDialog(pritext, sectext + \
+ ConfirmationDialog(pritext, sectext + \
_('By removing this contact you also remove authorization '
'resulting in them always seeing you as offline.'),
on_response_ok=(on_ok2, list_))
elif _('Not in Roster') in contact.get_shown_groups():
# Contact is not in roster
- dialogs.ConfirmationDialog(pritext, sectext + \
+ ConfirmationDialog(pritext, sectext + \
_('Do you want to continue?'), on_response_ok=(on_ok2,
list_))
else:
- dialogs.ConfirmationDialogCheck(pritext, sectext + \
+ ConfirmationDialogCheck(pritext, sectext + \
_('By removing this contact you also by default remove '
'authorization resulting in them always seeing you as'
' offline.'),
@@ -3436,7 +3447,7 @@ class RosterWindow:
sectext = _('By removing these contacts:%s\nyou also remove '
'authorization resulting in them always seeing you as '
'offline.') % jids
- dialogs.ConfirmationDialog(pritext, sectext,
+ ConfirmationDialog(pritext, sectext,
on_response_ok=(on_ok2, list_))
def on_send_custom_status(self, widget, contact_list, show, group=None):
@@ -3497,7 +3508,7 @@ class RosterWindow:
sectext = _('This contact will temporarily see you as %(status)s, '
'but only until you change your status. Then they will see '
'your global status.') % {'status': show}
- dialogs.ConfirmationDialogCheck(pritext, sectext,
+ ConfirmationDialogCheck(pritext, sectext,
_('_Do not ask me again'), on_response_ok=send_it)
def on_status_combobox_changed(self, widget):
@@ -3513,7 +3524,7 @@ class RosterWindow:
return
accounts = list(app.connections.keys())
if len(accounts) == 0:
- dialogs.ErrorDialog(_('No account available'),
+ ErrorDialog(_('No account available'),
_('You must create an account before you can chat with other '
'contacts.'))
self.update_status_combobox()
@@ -3593,7 +3604,7 @@ class RosterWindow:
def on_cancel():
self.update_status_combobox()
- dialogs.ConfirmationDialog(
+ ConfirmationDialog(
_('You are participating in one or more group chats'),
_('Changing your status to invisible will result in '
'disconnection from those group chats. Are you sure you '
@@ -3637,7 +3648,7 @@ class RosterWindow:
config.ManagePEPServicesWindow(account)
def on_add_new_contact(self, widget, account):
- dialogs.AddNewContactWindow(account)
+ AddNewContactWindow(account)
def on_join_gc_activate(self, widget, account):
"""
@@ -4140,7 +4151,7 @@ class RosterWindow:
if not app.connections[account_source].private_storage_supported or \
not app.connections[account_dest].private_storage_supported:
- dialogs.WarningDialog(_('Metacontacts storage not supported by '
+ WarningDialog(_('Metacontacts storage not supported by '
'your server'),
_('Your server does not support storing metacontacts '
'information. So this information will not be saved on next '
@@ -4243,7 +4254,7 @@ class RosterWindow:
sectext = _('Metacontacts are a way to regroup several contacts in one '
'line. Generally it is used when the same person has several '
'XMPP- or transport -accounts.')
- dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
+ dlg = ConfirmationDialogCheck(pritext, sectext,
_('_Do not ask me again'), on_response_ok=merge_contacts)
if not confirm_metacontacts: # First time we see this window
dlg.checkbutton.set_active(True)
@@ -4362,7 +4373,7 @@ class RosterWindow:
if not os.path.isfile(path):
bad_uris.append(a_uri)
if len(bad_uris):
- dialogs.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
+ ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
return
def _on_send_files(account, jid, uris):
c = app.contacts.get_contact_with_highest_priority(account,
@@ -4380,7 +4391,7 @@ class RosterWindow:
for uri in uri_splitted:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
sec_text += '\n' + os.path.basename(path)
- dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
+ dialog = NonModalConfirmationDialog(prim_text, sec_text,
on_response_ok=(_on_send_files, account_dest, jid_dest,
uri_splitted))
dialog.popup()
@@ -4475,7 +4486,7 @@ class RosterWindow:
if (type_dest == 'account' or not self.regroup) and \
account_source != account_dest:
# add to account in specified group
- dialogs.AddNewContactWindow(account=account_dest, jid=jid_source,
+ AddNewContactWindow(account=account_dest, jid=jid_source,
user_nick=c_source.name, group=grp_dest)
return
diff --git a/gajim/search_window.py b/gajim/search_window.py
index 7837e3867..092d48d54 100644
--- a/gajim/search_window.py
+++ b/gajim/search_window.py
@@ -28,10 +28,11 @@ from gajim.common import dataforms
from gajim.common import ged
from gajim import gtkgui_helpers
-from gajim import dialogs
from gajim import vcard
from gajim import config
from gajim import dataforms_widget
+from gajim.gtk import AddNewContactWindow
+
class SearchWindow:
def __init__(self, account, jid):
@@ -116,7 +117,7 @@ class SearchWindow:
if not iter_:
return
jid = model[iter_][self.jid_column]
- dialogs.AddNewContactWindow(self.account, jid)
+ AddNewContactWindow(self.account, jid)
def on_information_button_clicked(self, widget):
(model, iter_) = self.result_treeview.get_selection().get_selected()
diff --git a/gajim/session.py b/gajim/session.py
index c5897f907..10770e939 100644
--- a/gajim/session.py
+++ b/gajim/session.py
@@ -35,7 +35,7 @@ from gajim.common.connection_handlers_events import ChatstateReceivedEvent, \
from gajim.common.const import KindConstant
from gajim import message_control
from gajim import notify
-from gajim import dialogs
+from gajim.gtk import SingleMessageWindow
class ChatControlSession(object):
@@ -358,7 +358,7 @@ class ChatControlSession(object):
popup = helpers.allow_popup_window(self.conn.name)
if msg_type == 'normal' and popup: # it's single message to be autopopuped
- dialogs.SingleMessageWindow(self.conn.name, contact.jid,
+ SingleMessageWindow(self.conn.name, contact.jid,
action='receive', from_whom=jid, subject=subject, message=msg,
resource=resource, session=self, form_node=form_node)
return
diff --git a/gajim/statusicon.py b/gajim/statusicon.py
index ea1764975..747e79d0f 100644
--- a/gajim/statusicon.py
+++ b/gajim/statusicon.py
@@ -34,6 +34,8 @@ from gajim import tooltips
from gajim import gtkgui_helpers
from gajim.common import app
from gajim.common import helpers
+from gajim.gtk import SingleMessageWindow
+
class StatusIcon:
"""
@@ -163,7 +165,7 @@ class StatusIcon:
jid, account)
def on_single_message_menuitem_activate(self, widget, account):
- dialogs.SingleMessageWindow(account, action='send')
+ SingleMessageWindow(account, action='send')
def on_new_chat(self, widget, account):
app.app.activate_action('start-chat')