diff --git a/data/style/gajim.css b/data/style/gajim.css
index afa75bbf0..1b241ad6d 100644
--- a/data/style/gajim.css
+++ b/data/style/gajim.css
@@ -29,3 +29,14 @@ popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
#ServerInfoGrid > list > row { padding: 10px 20px 10px 10px; }
#ServerInfoGrid > list > label { padding:10px; color: @insensitive_fg_color; font-weight: bold; }
#ServerInfoGrid > list > row.activatable:active { box-shadow: none; }
+
+/* Generic Options Dialog */
+#OptionsDialog list > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
+#OptionsDialog list > row:last-child { border-bottom: 0px}
+#OptionsDialog list > row { padding: 10px; }
+#OptionsDialog list > row.activatable:active { box-shadow: none; }
+
+/* Generic Popover Menu with Buttons */
+.PopoverButtonListbox { margin-top: 10px; margin-bottom: 10px; }
+.PopoverButtonListbox > row { padding: 10px 20px 10px 20px; }
+.PopoverButtonListbox row.activatable:active { box-shadow: none; background-color: @theme_selected_bg_color }
diff --git a/gajim/app_actions.py b/gajim/app_actions.py
index f3b980f18..5e2c947c9 100644
--- a/gajim/app_actions.py
+++ b/gajim/app_actions.py
@@ -175,7 +175,7 @@ class AppActions():
def on_xml_console(self, action, param):
account = param.get_string()
if 'xml_console' in gajim.interface.instances[account]:
- gajim.interface.instances[account]['xml_console'].window.present()
+ gajim.interface.instances[account]['xml_console'].present()
else:
gajim.interface.instances[account]['xml_console'] = \
dialogs.XMLConsoleWindow(account)
diff --git a/gajim/dialogs.py b/gajim/dialogs.py
index aed265cb5..f3e1d5e2c 100644
--- a/gajim/dialogs.py
+++ b/gajim/dialogs.py
@@ -3347,139 +3347,256 @@ class SingleMessageWindow:
self.save_pos()
self.window.destroy()
-class XMLConsoleWindow:
+
+class XMLConsoleWindow(Gtk.Window):
def __init__(self, account):
+ Gtk.Window.__init__(self)
self.account = account
+ self.set_default_size(600, 600)
+ self.connect("destroy", self.on_destroy)
- self.xml = gtkgui_helpers.get_gtk_builder('xml_console_window.ui')
- self.window = self.xml.get_object('xml_console_window')
- self.input_textview = self.xml.get_object('input_textview')
- self.stanzas_log_textview = self.xml.get_object('stanzas_log_textview')
- self.input_tv_buffer = self.input_textview.get_buffer()
- self.parent = self.stanzas_log_textview.get_parent()
- buffer_ = self.stanzas_log_textview.get_buffer()
+ headerbar = Gtk.HeaderBar()
+ headerbar.set_title(_('XML Console'))
+ headerbar.set_show_close_button(True)
+ self.set_titlebar(headerbar)
- self.tagIn = buffer_.create_tag('incoming')
- color = gajim.config.get('inmsgcolor')
- self.tagIn.set_property('foreground', color)
- self.tagInPresence = buffer_.create_tag('incoming_presence')
- self.tagInPresence.set_property('foreground', color)
- self.tagInMessage = buffer_.create_tag('incoming_message')
- self.tagInMessage.set_property('foreground', color)
- self.tagInIq = buffer_.create_tag('incoming_iq')
- self.tagInIq.set_property('foreground', color)
+ switch = Gtk.Switch()
+ switch.set_active(True)
+ switch.connect("notify::active", self.on_enable)
+ headerbar.pack_start(switch)
- self.tagOut = buffer_.create_tag('outgoing')
- color = gajim.config.get('outmsgcolor')
- self.tagOut.set_property('foreground', color)
- self.tagOutPresence = buffer_.create_tag('outgoing_presence')
- self.tagOutPresence.set_property('foreground', color)
- self.tagOutMessage = buffer_.create_tag('outgoing_message')
- self.tagOutMessage.set_property('foreground', color)
- self.tagOutIq = buffer_.create_tag('outgoing_iq')
- self.tagOutIq.set_property('foreground', color)
- buffer_.create_tag('') # Default tag
+ headerbar.set_decoration_layout(':minimize,close')
self.enabled = True
- self.xml.get_object('enable_checkbutton').set_active(True)
+ self.presence = True
+ self.message = True
+ self.iq = True
+ self.stream = True
+ self.incoming = True
+ self.outgoing = True
- if len(gajim.connections) > 1:
- title = _('XML Console for %s') % self.account
- else:
- title = _('XML Console')
+ self.textview = Gtk.TextView()
+ self.textview.set_size_request(-1, 400)
+ self.textview.set_editable(False)
+ self.textview.set_cursor_visible(False)
+ self.textview.set_hexpand(True)
+
+ self.scrolled = Gtk.ScrolledWindow()
+ self.scrolled.add(self.textview)
+
+ self.input = Gtk.TextView()
+ self.input.show()
+ self.input.set_vexpand(True)
+
+ self.scrolled_input = Gtk.ScrolledWindow()
+ self.scrolled_input.set_size_request(-1, 150)
+ self.scrolled_input.add(self.input)
+ self.scrolled_input.set_no_show_all(True)
+
+ self.paned = Gtk.VPaned()
+ self.paned.set_vexpand(True)
+ self.paned.pack1(self.scrolled, True, False)
+ self.paned.pack2(self.scrolled_input, False, False)
+ self.paned.set_position(self.paned.get_property('max-position'))
+
+ self.actionbar = Gtk.ActionBar()
+
+ icon = gtkgui_helpers.get_icon_pixmap('edit-clear-all-symbolic')
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button = Gtk.Button()
+ button.set_tooltip_text(_('Clear'))
+ button.connect('clicked', self.on_clear)
+ button.set_image(image)
+ self.actionbar.pack_start(button)
+
+ icon = gtkgui_helpers.get_icon_pixmap('applications-system-symbolic')
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button = Gtk.Button()
+ button.set_tooltip_text(_('Filter'))
+ button.connect('clicked', self.on_filter_options)
+ button.set_image(image)
+ self.actionbar.pack_start(button)
+
+ icon = gtkgui_helpers.get_icon_pixmap('document-edit-symbolic')
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button = Gtk.ToggleButton()
+ button.set_tooltip_text(_('XML Input'))
+ button.set_active(False)
+ button.connect('toggled', self.on_input)
+ button.set_image(image)
+ self.actionbar.pack_start(button)
+
+ icon = gtkgui_helpers.get_icon_pixmap('emblem-ok-symbolic')
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button = Gtk.Button()
+ button.set_tooltip_text(_('Send'))
+ button.connect('clicked', self.on_send)
+ button.set_image(image)
+ self.actionbar.pack_end(button)
+
+ listbox = Gtk.ListBox()
+ context = listbox.get_style_context()
+ context.add_class('PopoverButtonListbox')
+
+ label = Gtk.Label(label='Message')
+ listbox.add(label)
+
+ label = Gtk.Label(label='Presence')
+ listbox.add(label)
+
+ label = Gtk.Label(label='Iq')
+ listbox.add(label)
+ listbox.show_all()
+ listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+ listbox.connect('row-activated', self.on_row_activated)
+
+ popover = Gtk.Popover()
+ popover.add(listbox)
+
+ self.menubutton = Gtk.MenuButton()
+ self.menubutton.set_direction(Gtk.ArrowType.UP)
+ self.menubutton.set_popover(popover)
+ self.menubutton.set_tooltip_text(_('Presets'))
+ self.menubutton.set_no_show_all(True)
+
+ self.actionbar.pack_start(self.menubutton)
+
+ box = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
+ box.add(self.paned)
+ box.add(self.actionbar)
+
+ self.add(box)
+
+ self.connect('key_press_event', self.on_key_press_event)
+
+ self.create_tags()
+ self.show_all()
- self.window.set_title(title)
- self.window.show_all()
gajim.ged.register_event_handler('stanza-received', ged.GUI1,
self._nec_stanza_received)
gajim.ged.register_event_handler('stanza-sent', ged.GUI1,
self._nec_stanza_sent)
- self.xml.connect_signals(self)
+ def create_tags(self):
+ buffer_ = self.textview.get_buffer()
+ in_color = gajim.config.get('inmsgcolor')
+ out_color = gajim.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.window.destroy()
+ self.destroy()
- def on_xml_console_window_destroy(self, widget):
+ 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 gajim.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:
+ gajim.connections[self.account].send_stanza(stanza)
+ 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):
+ options = [
+ SwitchOption('Presence', self.presence,
+ self.on_option,
+ 'presence'),
+ SwitchOption('Message', self.message,
+ self.on_option,
+ 'message'),
+ SwitchOption('Iq', self.iq,
+ self.on_option,
+ 'iq'),
+ SwitchOption('Stream\nManagement', self.stream,
+ self.on_option,
+ 'stream'),
+ SwitchOption('In', self.incoming,
+ self.on_option,
+ 'incoming'),
+ SwitchOption('Out', self.outgoing,
+ self.on_option,
+ 'outgoing')]
+
+ OptionsDialog(self, 'Filter', options)
+
+ def on_clear(self, *args):
+ buffer_ = self.textview.get_buffer().set_text('')
+
+ def on_destroy(self, *args):
del gajim.interface.instances[self.account]['xml_console']
gajim.ged.remove_event_handler('stanza-received', ged.GUI1,
self._nec_stanza_received)
gajim.ged.remove_event_handler('stanza-sent', ged.GUI1,
self._nec_stanza_sent)
- def on_clear_button_clicked(self, widget):
- buffer_ = self.stanzas_log_textview.get_buffer()
- buffer_.set_text('')
+ def on_enable(self, switch, param):
+ self.enabled = switch.get_active()
- def on_enable_checkbutton_toggled(self, widget):
- self.enabled = widget.get_active()
-
- def on_in_stanza_checkbutton_toggled(self, widget):
- active = widget.get_active()
- self.tagIn.set_property('invisible', active)
- self.tagInPresence.set_property('invisible', active)
- self.tagInMessage.set_property('invisible', active)
- self.tagInIq.set_property('invisible', active)
-
- def on_presence_stanza_checkbutton_toggled(self, widget):
- active = widget.get_active()
- self.tagInPresence.set_property('invisible', active)
- self.tagOutPresence.set_property('invisible', active)
-
- def on_out_stanza_checkbutton_toggled(self, widget):
- active = widget.get_active()
- self.tagOut.set_property('invisible', active)
- self.tagOutPresence.set_property('invisible', active)
- self.tagOutMessage.set_property('invisible', active)
- self.tagOutIq.set_property('invisible', active)
-
- def on_message_stanza_checkbutton_toggled(self, widget):
- active = widget.get_active()
- self.tagInMessage.set_property('invisible', active)
- self.tagOutMessage.set_property('invisible', active)
-
- def on_iq_stanza_checkbutton_toggled(self, widget):
- active = widget.get_active()
- self.tagInIq.set_property('invisible', active)
- self.tagOutIq.set_property('invisible', active)
-
- def print_stanza(self, stanza, kind):
- # kind must be 'incoming' or 'outgoing'
- if not self.enabled:
- return
- if not stanza:
- return
-
- at_the_end = gtkgui_helpers.at_the_end(self.parent)
-
- buffer = self.stanzas_log_textview.get_buffer()
- end_iter = buffer.get_end_iter()
-
- type_ = ''
- if stanza[1:9] == 'presence':
- type_ = 'presence'
- elif stanza[1:8] == 'message':
- type_ = 'message'
- elif stanza[1:3] == 'iq':
- type_ = 'iq'
-
- if type_:
- type_ = kind + '_' + type_
- else:
- type_ = kind # 'incoming' or 'outgoing'
-
- if kind == 'incoming':
- buffer.insert_with_tags_by_name(end_iter, '\n' % \
- time.strftime('%c'), type_)
- elif kind == 'outgoing':
- buffer.insert_with_tags_by_name(end_iter, '\n' % \
- time.strftime('%c'), type_)
- end_iter = buffer.get_end_iter()
- buffer.insert_with_tags_by_name(end_iter, stanza.replace('><', '>\n<') \
- + '\n\n', type_)
- if at_the_end:
- GLib.idle_add(gtkgui_helpers.scroll_to_end, self.parent)
+ def on_option(self, switch, param, *user_data):
+ kind = user_data[0]
+ setattr(self, kind, switch.get_active())
+ value = not switch.get_active()
+ table = self.textview.get_buffer().get_tag_table()
+ tag = table.lookup(kind)
+ if kind 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:
@@ -3491,36 +3608,87 @@ class XMLConsoleWindow:
return
self.print_stanza(obj.stanza_str, 'outgoing')
- def on_send_button_clicked(self, widget):
- if gajim.connections[self.account].connected <= 1:
- # if offline or connecting
- ErrorDialog(_('Connection not available'),
- _('Please make sure you are connected with "%s".') % \
- self.account)
+ def print_stanza(self, stanza, kind):
+ # kind must be 'incoming' or 'outgoing'
+ if not self.enabled:
+ return
+ if not stanza:
return
- begin_iter, end_iter = self.input_tv_buffer.get_bounds()
- stanza = self.input_tv_buffer.get_text(begin_iter, end_iter, True)
- if stanza:
- gajim.connections[self.account].send_stanza(stanza)
- self.input_tv_buffer.set_text('') # we sent ok, clear the textview
- def on_presence_button_clicked(self, widget):
- self.input_tv_buffer.set_text(
- ''
- '')
+ at_the_end = gtkgui_helpers.at_the_end(self.scrolled)
- def on_iq_button_clicked(self, widget):
- self.input_tv_buffer.set_text(
- '')
+ buffer_ = self.textview.get_buffer()
+ end_iter = buffer_.get_end_iter()
- def on_message_button_clicked(self, widget):
- self.input_tv_buffer.set_text(
- '')
+ type_ = kind
+ if stanza.startswith('<', '>\n<'))
+ buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)
+
+ if at_the_end:
+ GLib.idle_add(gtkgui_helpers.scroll_to_end, self.scrolled)
+
+
+class OptionsDialog(Gtk.Dialog):
+ def __init__(self, parent, title, options):
+ Gtk.Dialog.__init__(self, title, parent,
+ Gtk.DialogFlags.DESTROY_WITH_PARENT)
+ self.set_name('OptionsDialog')
+ self.set_resizable(False)
+ self.set_default_size(250, -1)
+
+ self.remove(self.get_content_area())
+
+ listbox = Gtk.ListBox()
+ listbox.set_hexpand(True)
+ listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ for option in options:
+ listbox.add(option)
+
+ self.add(listbox)
+
+ self.show_all()
+ listbox.connect('row-activated', self.on_row_activated)
+
+ def on_row_activated(self, listbox, row):
+ row.get_child().set_switch_state()
+
+
+class SwitchOption(Gtk.Grid):
+ def __init__(self, label, state, callback, *user_data):
+ Gtk.Grid.__init__(self)
+ self.set_column_spacing(6)
+
+ label = Gtk.Label(label=label)
+ label.set_hexpand(True)
+ label.set_halign(Gtk.Align.START)
+
+ self.switch = Gtk.Switch()
+ self.switch.set_active(state)
+ self.switch.connect("notify::active", callback, *user_data)
+ self.switch.set_hexpand(True)
+ self.switch.set_halign(Gtk.Align.END)
+
+ self.add(label)
+ self.add(self.switch)
+ self.show_all()
+
+ def set_switch_state(self):
+ state = self.switch.get_active()
+ self.switch.set_active(not state)
+
#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),