Move some dialogs into gtk module

This commit is contained in:
Philipp Hörist 2018-07-16 23:22:33 +02:00
parent bff5f14b92
commit 3a3b5aff20
42 changed files with 3836 additions and 3369 deletions

View File

@ -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?'),

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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})

View File

@ -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_)

View File

@ -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; }

View File

@ -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'])

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,56 @@
# Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
# Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
# Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
# Travis Shirk <travis AT pobox.com>
# Copyright (C) 2005-2008 Nikos Kouremenos <kourem AT gmail.com>
# Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
# Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
# Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
# Julien Pivotto <roidelapluie AT gmail.com>
# Stephan Erb <steve-e AT h3c.de>
# Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from 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

66
gajim/gtk/about.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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()

483
gajim/gtk/add_contact.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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()

933
gajim/gtk/dialogs.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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, _('''<b>Issued to:</b>
Common Name (CN): %(scn)s
Organization (O): %(sorg)s
Organizationl Unit (OU): %(sou)s
Serial Number: %(sn)s
<b>Issued by:</b>
Common Name (CN): %(icn)s
Organization (O): %(iorg)s
Organizationl Unit (OU): %(iou)s
<b>Validity:</b>
Issued on: %(io)s
Expires on: %(eo)s
<b>Fingerprint</b>
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)

View File

@ -1,5 +1,3 @@
# Copyright (C) 2017 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
@ -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):

325
gajim/gtk/join_groupchat.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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

View File

@ -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')

547
gajim/gtk/privacy_list.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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 <b><i>%s</i></b>') %
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(_('<b>Edit a rule</b>'))
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(_('<b>Add a rule</b>'))
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')

View File

@ -1,34 +1,28 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, 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 <http://www.gnu.org/licenses/>.
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

335
gajim/gtk/single_message.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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()

343
gajim/gtk/start_chat.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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)

View File

@ -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

273
gajim/gtk/xml_console.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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 = (
'<presence>\n'
'<show></show>\n'
'<status></status>\n'
'<priority></priority>\n'
'</presence>')
elif text == 'Message':
input_text = (
'<message to="" type="">\n'
'<body></body>\n'
'</message>')
elif text == 'Iq':
input_text = (
'<iq to="" type="">\n'
'<query xmlns=""></query>\n'
'</iq>')
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('<presence'):
type_ = 'presence'
elif stanza.startswith('<message'):
type_ = 'message'
elif stanza.startswith('<iq'):
type_ = 'iq'
elif stanza.startswith('<r') or stanza.startswith('<a'):
type_ = 'stream'
stanza = '<!-- {kind} {time} -->\n{stanza}\n\n'.format(
kind=kind.capitalize(),
time=time.strftime('%c'),
stanza=stanza.replace('><', '>\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)

View File

@ -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

View File

@ -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

View File

@ -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'))

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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')