# This file is part of Gajim. # # Gajim is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published # by the Free Software Foundation; version 3 only. # # Gajim is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Gajim. If not, see . import time import nbxmpp from gi.repository import Gdk from gi.repository import Gtk from gi.repository import GLib from gajim.common import app from gajim.common import ged from gajim.common.i18n import _ from gajim.common.const import Option from gajim.common.const import OptionKind from gajim.common.const import OptionType from gajim.common.const import StyleAttr from gajim.gtk import util from gajim.gtk.util import get_builder from gajim.gtk.util import get_image_button from gajim.gtk.dialogs import ErrorDialog 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.css_config.get_value( '.gajim-incoming-nickname', StyleAttr.COLOR) out_color = app.css_config.get_value( '.gajim-outgoing-nickname', StyleAttr.COLOR) tags = ['presence', 'message', 'stream', 'iq'] tag = buffer_.create_tag('incoming') tag.set_property('foreground', in_color) tag = buffer_.create_tag('outgoing') tag.set_property('foreground', out_color) for tag_name in tags: buffer_.create_tag(tag_name) def on_key_press_event(self, widget, event): if event.keyval == Gdk.KEY_Escape: self.destroy() def on_row_activated(self, listbox, row): text = row.get_child().get_text() input_text = None if text == 'Presence': input_text = ( '\n' '\n' '\n' '\n' '') elif text == 'Message': input_text = ( '\n' '\n' '') elif text == 'Iq': input_text = ( '\n' '\n' '') if input_text is not None: buffer_ = self.input.get_buffer() buffer_.set_text(input_text) self.input.grab_focus() def on_send(self, *args): if app.connections[self.account].connected <= 1: # if offline or connecting ErrorDialog( _('Connection not available'), _('Please make sure you are connected with "%s".') % self.account) return buffer_ = self.input.get_buffer() begin_iter, end_iter = buffer_.get_bounds() stanza = buffer_.get_text(begin_iter, end_iter, True) if stanza: try: node = nbxmpp.Protocol(node=stanza) if node.getNamespace() == UNDECLARED: node.setNamespace(nbxmpp.NS_CLIENT) except Exception as error: ErrorDialog(_('Invalid Node'), str(error)) return app.connections[self.account].connection.send(node) buffer_.set_text('') def on_input(self, button, *args): if button.get_active(): self.paned.get_child2().show() self.menubutton.show() self.input.grab_focus() else: self.paned.get_child2().hide() self.menubutton.hide() def on_filter_options(self, *args): if self.filter_dialog: self.filter_dialog.present() return options = [ Option(OptionKind.SWITCH, 'Presence', OptionType.VALUE, self.presence, callback=self.on_option, data='presence'), Option(OptionKind.SWITCH, 'Message', OptionType.VALUE, self.message, callback=self.on_option, data='message'), Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq, callback=self.on_option, data='iq'), Option(OptionKind.SWITCH, 'Stream\nManagement', OptionType.VALUE, self.stream, callback=self.on_option, data='stream'), Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming, callback=self.on_option, data='incoming'), Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing, callback=self.on_option, data='outgoing'), ] self.filter_dialog = OptionsDialog(self, 'Filter', Gtk.DialogFlags.DESTROY_WITH_PARENT, options, self.account) self.filter_dialog.connect('destroy', self.on_filter_destroyed) def on_filter_destroyed(self, win): self.filter_dialog = None def on_clear(self, *args): self.textview.get_buffer().set_text('') def on_destroy(self, *args): del app.interface.instances[self.account]['xml_console'] app.ged.remove_event_handler( 'stanza-received', ged.GUI1, self._nec_stanza_received) app.ged.remove_event_handler( 'stanza-sent', ged.GUI1, self._nec_stanza_sent) def on_enable(self, switch, param): self.enabled = switch.get_active() def on_option(self, value, data): setattr(self, data, value) value = not value table = self.textview.get_buffer().get_tag_table() tag = table.lookup(data) if data in ('incoming', 'outgoing'): if value: tag.set_priority(table.get_size() - 1) else: tag.set_priority(0) tag.set_property('invisible', value) def _nec_stanza_received(self, obj): if obj.conn.name != self.account: return self.print_stanza(obj.stanza_str, 'incoming') def _nec_stanza_sent(self, obj): if obj.conn.name != self.account: return self.print_stanza(obj.stanza_str, 'outgoing') def print_stanza(self, stanza, kind): # kind must be 'incoming' or 'outgoing' if not self.enabled: return if not stanza: return at_the_end = util.at_the_end(self.scrolled) buffer_ = self.textview.get_buffer() end_iter = buffer_.get_end_iter() type_ = kind if stanza.startswith('<', '>\n<')) buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind) if at_the_end: GLib.idle_add(util.scroll_to_end, self.scrolled)