Merge branch 'newdesign' into 'master'
New Style for Chat Windows See merge request gajim/gajim!144
This commit is contained in:
		
						commit
						347f0a8aad
					
				
					 26 changed files with 1102 additions and 1367 deletions
				
			
		| 
						 | 
				
			
			@ -30,7 +30,7 @@
 | 
			
		|||
import os
 | 
			
		||||
import time
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
from gi.repository import Gdk
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
from gi.repository import GdkPixbuf
 | 
			
		||||
from gi.repository import Pango
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
| 
						 | 
				
			
			@ -58,13 +58,6 @@ from gajim.common.exceptions import GajimGeneralException
 | 
			
		|||
from gajim.common.const import AvatarSize
 | 
			
		||||
 | 
			
		||||
from gajim.command_system.implementation.hosts import ChatCommands
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from gajim import gtkspell
 | 
			
		||||
    HAS_GTK_SPELL = True
 | 
			
		||||
except (ImportError, ValueError):
 | 
			
		||||
    HAS_GTK_SPELL = False
 | 
			
		||||
 | 
			
		||||
from gajim.chat_control_base import ChatControlBase
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
| 
						 | 
				
			
			@ -95,60 +88,10 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        self.last_recv_message_marks = None
 | 
			
		||||
        self.last_message_timestamp = None
 | 
			
		||||
 | 
			
		||||
        # for muc use:
 | 
			
		||||
        # widget = self.xml.get_object('muc_window_actions_button')
 | 
			
		||||
        self.actions_button = self.xml.get_object('message_window_actions_button')
 | 
			
		||||
        id_ = self.actions_button.connect('clicked',
 | 
			
		||||
            self.on_actions_button_clicked)
 | 
			
		||||
        self.handlers[id_] = self.actions_button
 | 
			
		||||
 | 
			
		||||
        self._formattings_button = self.xml.get_object('formattings_button')
 | 
			
		||||
        self.emoticons_button = self.xml.get_object('emoticons_button')
 | 
			
		||||
        self.toggle_emoticons()
 | 
			
		||||
 | 
			
		||||
        self._add_to_roster_button = self.xml.get_object(
 | 
			
		||||
            'add_to_roster_button')
 | 
			
		||||
        id_ = self._add_to_roster_button.connect('clicked',
 | 
			
		||||
            self._on_add_to_roster_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = self._add_to_roster_button
 | 
			
		||||
 | 
			
		||||
        self._audio_button = self.xml.get_object('audio_togglebutton')
 | 
			
		||||
        id_ = self._audio_button.connect('toggled', self.on_audio_button_toggled)
 | 
			
		||||
        self.handlers[id_] = self._audio_button
 | 
			
		||||
        # add a special img
 | 
			
		||||
        gtkgui_helpers.add_image_to_button(self._audio_button,
 | 
			
		||||
            'gajim-mic_inactive')
 | 
			
		||||
 | 
			
		||||
        self._video_button = self.xml.get_object('video_togglebutton')
 | 
			
		||||
        id_ = self._video_button.connect('toggled', self.on_video_button_toggled)
 | 
			
		||||
        self.handlers[id_] = self._video_button
 | 
			
		||||
        # add a special img
 | 
			
		||||
        gtkgui_helpers.add_image_to_button(self._video_button,
 | 
			
		||||
            'gajim-cam_inactive')
 | 
			
		||||
 | 
			
		||||
        self._send_file_button = self.xml.get_object('send_file_button')
 | 
			
		||||
        # add a special img for send file button
 | 
			
		||||
        pixbuf = gtkgui_helpers.get_icon_pixmap('document-send', quiet=True)
 | 
			
		||||
        img = Gtk.Image.new_from_pixbuf(pixbuf)
 | 
			
		||||
        self._send_file_button.set_image(img)
 | 
			
		||||
        id_ = self._send_file_button.connect('clicked',
 | 
			
		||||
            self._on_send_file_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = self._send_file_button
 | 
			
		||||
 | 
			
		||||
        self._convert_to_gc_button = self.xml.get_object(
 | 
			
		||||
            'convert_to_gc_button')
 | 
			
		||||
        id_ = self._convert_to_gc_button.connect('clicked',
 | 
			
		||||
            self._on_convert_to_gc_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = self._convert_to_gc_button
 | 
			
		||||
 | 
			
		||||
        self._contact_information_button = self.xml.get_object(
 | 
			
		||||
            'contact_information_button')
 | 
			
		||||
        id_ = self._contact_information_button.connect('clicked',
 | 
			
		||||
            self._on_contact_information_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = self._contact_information_button
 | 
			
		||||
 | 
			
		||||
        compact_view = app.config.get('compact_view')
 | 
			
		||||
        self.chat_buttons_set_visible(compact_view)
 | 
			
		||||
        self.widget_set_visible(self.xml.get_object('banner_eventbox'),
 | 
			
		||||
            app.config.get('hide_chat_banner'))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -161,10 +104,9 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        # Add lock image to show chat encryption
 | 
			
		||||
        self.lock_image = self.xml.get_object('lock_image')
 | 
			
		||||
 | 
			
		||||
        # Convert to GC icon
 | 
			
		||||
        img = self.xml.get_object('convert_to_gc_button_image')
 | 
			
		||||
        img.set_from_pixbuf(gtkgui_helpers.load_icon(
 | 
			
		||||
                'muc_active').get_pixbuf())
 | 
			
		||||
        # Menu for the HeaderBar
 | 
			
		||||
        self.control_menu = gui_menu_builder.get_singlechat_menu(
 | 
			
		||||
            self.control_id)
 | 
			
		||||
 | 
			
		||||
        self._audio_banner_image = self.xml.get_object('audio_banner_image')
 | 
			
		||||
        self._video_banner_image = self.xml.get_object('video_banner_image')
 | 
			
		||||
| 
						 | 
				
			
			@ -270,7 +212,7 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
 | 
			
		||||
        # Enable encryption if needed
 | 
			
		||||
        self.no_autonegotiation = False
 | 
			
		||||
 | 
			
		||||
        self.add_actions()
 | 
			
		||||
        self.update_ui()
 | 
			
		||||
        self.set_lock_image()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -298,6 +240,107 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        # PluginSystem: adding GUI extension point for this ChatControl
 | 
			
		||||
        # instance object
 | 
			
		||||
        app.plugin_manager.gui_extension_point('chat_control', self)
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def add_actions(self):
 | 
			
		||||
        actions = [
 | 
			
		||||
            ('send-file-', self._on_send_file),
 | 
			
		||||
            ('invite-contacts-', self._on_invite_contacts),
 | 
			
		||||
            ('add-to-roster-', self._on_add_to_roster),
 | 
			
		||||
            ('information-', self._on_information),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        for action in actions:
 | 
			
		||||
            action_name, func = action
 | 
			
		||||
            act = Gio.SimpleAction.new(action_name + self.control_id, None)
 | 
			
		||||
            act.connect("activate", func)
 | 
			
		||||
            self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
        act = Gio.SimpleAction.new_stateful(
 | 
			
		||||
            'toggle-audio-' + self.control_id, None,
 | 
			
		||||
            GLib.Variant.new_boolean(False))
 | 
			
		||||
        act.connect('change-state', self._on_audio)
 | 
			
		||||
        self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
        act = Gio.SimpleAction.new_stateful(
 | 
			
		||||
            'toggle-video-' + self.control_id,
 | 
			
		||||
            None, GLib.Variant.new_boolean(False))
 | 
			
		||||
        act.connect('change-state', self._on_video)
 | 
			
		||||
        self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
    def update_actions(self):
 | 
			
		||||
        win = self.parent_win.window
 | 
			
		||||
        online = app.account_is_connected(self.account)
 | 
			
		||||
 | 
			
		||||
        # Add to roster
 | 
			
		||||
        if not isinstance(self.contact, GC_Contact) \
 | 
			
		||||
        and _('Not in Roster') in self.contact.groups and \
 | 
			
		||||
        app.connections[self.account].roster_supported and online:
 | 
			
		||||
            win.lookup_action(
 | 
			
		||||
                'add-to-roster-' + self.control_id).set_enabled(True)
 | 
			
		||||
        else:
 | 
			
		||||
            win.lookup_action(
 | 
			
		||||
                'add-to-roster-' + self.control_id).set_enabled(False)
 | 
			
		||||
 | 
			
		||||
        # Audio
 | 
			
		||||
        win.lookup_action('toggle-audio-' + self.control_id).set_enabled(
 | 
			
		||||
            online and self.audio_available)
 | 
			
		||||
 | 
			
		||||
        # Video
 | 
			
		||||
        win.lookup_action('toggle-video-' + self.control_id).set_enabled(
 | 
			
		||||
            online and self.video_available)
 | 
			
		||||
 | 
			
		||||
        # Send file
 | 
			
		||||
        if ((self.contact.supports(NS_FILE) or \
 | 
			
		||||
        self.contact.supports(NS_JINGLE_FILE_TRANSFER_5)) and \
 | 
			
		||||
        (self.type_id == 'chat' or self.gc_contact.resource)) and \
 | 
			
		||||
        self.contact.show != 'offline' and online:
 | 
			
		||||
            win.lookup_action('send-file-' + self.control_id).set_enabled(
 | 
			
		||||
                True)
 | 
			
		||||
        else:
 | 
			
		||||
            win.lookup_action('send-file-' + self.control_id).set_enabled(
 | 
			
		||||
                False)
 | 
			
		||||
 | 
			
		||||
        # Convert to GC
 | 
			
		||||
        if app.config.get_per('accounts', self.account, 'is_zeroconf'):
 | 
			
		||||
            win.lookup_action(
 | 
			
		||||
                'invite-contacts-' + self.control_id).set_enabled(False)
 | 
			
		||||
        else:
 | 
			
		||||
            if self.contact.supports(NS_MUC) and online:
 | 
			
		||||
                win.lookup_action(
 | 
			
		||||
                    'invite-contacts-' + self.control_id).set_enabled(True)
 | 
			
		||||
            else:
 | 
			
		||||
                win.lookup_action(
 | 
			
		||||
                    'invite-contacts-' + self.control_id).set_enabled(False)
 | 
			
		||||
 | 
			
		||||
        # Information
 | 
			
		||||
        win.lookup_action(
 | 
			
		||||
            'information-' + self.control_id).set_enabled(online)
 | 
			
		||||
 | 
			
		||||
    def _on_send_file(self, action, param):
 | 
			
		||||
        super()._on_send_file()
 | 
			
		||||
 | 
			
		||||
    def _on_add_to_roster(self, action, param):
 | 
			
		||||
        dialogs.AddNewContactWindow(self.account, self.contact.jid)
 | 
			
		||||
 | 
			
		||||
    def _on_information(self, action, param):
 | 
			
		||||
        app.interface.roster.on_info(None, self.contact, self.account)
 | 
			
		||||
 | 
			
		||||
    def _on_invite_contacts(self, action, param):
 | 
			
		||||
        """
 | 
			
		||||
        User wants to invite some friends to chat
 | 
			
		||||
        """
 | 
			
		||||
        dialogs.TransformChatToMUC(self.account, [self.contact.jid])
 | 
			
		||||
 | 
			
		||||
    def _on_audio(self, action, param):
 | 
			
		||||
        action.set_state(param)
 | 
			
		||||
        state = param.get_boolean()
 | 
			
		||||
        self.on_jingle_button_toggled(state, 'audio')
 | 
			
		||||
 | 
			
		||||
    def _on_video(self, action, param):
 | 
			
		||||
        action.set_state(param)
 | 
			
		||||
        state = param.get_boolean()
 | 
			
		||||
        self.on_jingle_button_toggled(state, 'video')
 | 
			
		||||
 | 
			
		||||
    def subscribe_events(self):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -329,14 +372,6 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
                self._formattings_button.set_tooltip_text(_('This contact does '
 | 
			
		||||
                    'not support HTML'))
 | 
			
		||||
 | 
			
		||||
        # Add to roster
 | 
			
		||||
        if not isinstance(self.contact, GC_Contact) \
 | 
			
		||||
        and _('Not in Roster') in self.contact.groups and \
 | 
			
		||||
        app.connections[self.account].roster_supported:
 | 
			
		||||
            self._add_to_roster_button.show()
 | 
			
		||||
        else:
 | 
			
		||||
            self._add_to_roster_button.hide()
 | 
			
		||||
 | 
			
		||||
        # Jingle detection
 | 
			
		||||
        if self.contact.supports(NS_JINGLE_ICE_UDP) and \
 | 
			
		||||
        app.HAVE_FARSTREAM and self.contact.resource:
 | 
			
		||||
| 
						 | 
				
			
			@ -348,63 +383,6 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
            self.video_available = False
 | 
			
		||||
            self.audio_available = False
 | 
			
		||||
 | 
			
		||||
        # Audio buttons
 | 
			
		||||
        self._audio_button.set_sensitive(self.audio_available)
 | 
			
		||||
 | 
			
		||||
        # Video buttons
 | 
			
		||||
        self._video_button.set_sensitive(self.video_available)
 | 
			
		||||
 | 
			
		||||
        # change tooltip text for audio and video buttons if farstream is
 | 
			
		||||
        # not installed
 | 
			
		||||
        audio_tooltip_text = _('Toggle audio session') + '\n'
 | 
			
		||||
        video_tooltip_text = _('Toggle video session') + '\n'
 | 
			
		||||
        if not app.HAVE_FARSTREAM:
 | 
			
		||||
            ext_text = _('Feature not available, see Help->Features')
 | 
			
		||||
            self._audio_button.set_tooltip_text(audio_tooltip_text + ext_text)
 | 
			
		||||
            self._video_button.set_tooltip_text(video_tooltip_text + ext_text)
 | 
			
		||||
        elif not self.audio_available :
 | 
			
		||||
            ext_text =_('Feature not supported by remote client')
 | 
			
		||||
            self._audio_button.set_tooltip_text(audio_tooltip_text + ext_text)
 | 
			
		||||
            self._video_button.set_tooltip_text(video_tooltip_text + ext_text)
 | 
			
		||||
        else:
 | 
			
		||||
            self._audio_button.set_tooltip_text(audio_tooltip_text[:-1])
 | 
			
		||||
            self._video_button.set_tooltip_text(video_tooltip_text[:-1])
 | 
			
		||||
 | 
			
		||||
        # Send file
 | 
			
		||||
        if ((self.contact.supports(NS_FILE) or \
 | 
			
		||||
        self.contact.supports(NS_JINGLE_FILE_TRANSFER_5)) and \
 | 
			
		||||
        (self.type_id == 'chat' or self.gc_contact.resource)) and \
 | 
			
		||||
        self.contact.show != 'offline':
 | 
			
		||||
            self._send_file_button.set_sensitive(True)
 | 
			
		||||
            self._send_file_button.set_tooltip_text(_('Send files'))
 | 
			
		||||
        else:
 | 
			
		||||
            self._send_file_button.set_sensitive(False)
 | 
			
		||||
            if not (self.contact.supports(NS_FILE) or self.contact.supports(
 | 
			
		||||
            NS_JINGLE_FILE_TRANSFER_5)):
 | 
			
		||||
                self._send_file_button.set_tooltip_text(_(
 | 
			
		||||
                    "This contact does not support file transfer."))
 | 
			
		||||
            else:
 | 
			
		||||
                self._send_file_button.set_tooltip_text(
 | 
			
		||||
                    _("You need to know the real JID of the contact to send "
 | 
			
		||||
                    "them a file."))
 | 
			
		||||
 | 
			
		||||
        # Convert to GC
 | 
			
		||||
        if app.config.get_per('accounts', self.account, 'is_zeroconf'):
 | 
			
		||||
            self._convert_to_gc_button.set_no_show_all(True)
 | 
			
		||||
            self._convert_to_gc_button.hide()
 | 
			
		||||
        else:
 | 
			
		||||
            if self.contact.supports(NS_MUC):
 | 
			
		||||
                self._convert_to_gc_button.set_sensitive(True)
 | 
			
		||||
            else:
 | 
			
		||||
                self._convert_to_gc_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
        # Information
 | 
			
		||||
        if app.account_is_disconnected(self.account):
 | 
			
		||||
            self._contact_information_button.set_sensitive(False)
 | 
			
		||||
        else:
 | 
			
		||||
            self._contact_information_button.set_sensitive(True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def update_all_pep_types(self):
 | 
			
		||||
        for pep_type in self._pep_images:
 | 
			
		||||
            self.update_pep(pep_type)
 | 
			
		||||
| 
						 | 
				
			
			@ -751,12 +729,12 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        getattr(self, '_' + jingle_type + '_button').set_active(False)
 | 
			
		||||
        getattr(self, 'update_' + jingle_type)()
 | 
			
		||||
 | 
			
		||||
    def on_jingle_button_toggled(self, widget, jingle_type):
 | 
			
		||||
    def on_jingle_button_toggled(self, state, jingle_type):
 | 
			
		||||
        img_name = 'gajim-%s_%s' % ({'audio': 'mic', 'video': 'cam'}[jingle_type],
 | 
			
		||||
                        {True: 'active', False: 'inactive'}[widget.get_active()])
 | 
			
		||||
                        {True: 'active', False: 'inactive'}[state])
 | 
			
		||||
        path_to_img = gtkgui_helpers.get_icon_path(img_name)
 | 
			
		||||
 | 
			
		||||
        if widget.get_active():
 | 
			
		||||
        if state:
 | 
			
		||||
            if getattr(self, jingle_type + '_state') == \
 | 
			
		||||
            self.JINGLE_STATE_NULL:
 | 
			
		||||
                if jingle_type == 'video':
 | 
			
		||||
| 
						 | 
				
			
			@ -795,12 +773,6 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        img = getattr(self, '_' + jingle_type + '_button').get_property('image')
 | 
			
		||||
        img.set_from_file(path_to_img)
 | 
			
		||||
 | 
			
		||||
    def on_audio_button_toggled(self, widget):
 | 
			
		||||
        self.on_jingle_button_toggled(widget, 'audio')
 | 
			
		||||
 | 
			
		||||
    def on_video_button_toggled(self, widget):
 | 
			
		||||
        self.on_jingle_button_toggled(widget, 'video')
 | 
			
		||||
 | 
			
		||||
    def set_lock_image(self):
 | 
			
		||||
        loggable = self.session and self.session.is_loggable()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -832,10 +804,6 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        self.authentication_button.set_tooltip_text(tooltip)
 | 
			
		||||
        self.widget_set_visible(self.authentication_button, not visible)
 | 
			
		||||
        context = self.msg_scrolledwindow.get_style_context()
 | 
			
		||||
        if visible:
 | 
			
		||||
            context.add_class('authentication')
 | 
			
		||||
        else:
 | 
			
		||||
            context.remove_class('authentication')
 | 
			
		||||
        self.lock_image.set_sensitive(visible)
 | 
			
		||||
 | 
			
		||||
    def _on_authentication_button_clicked(self, widget):
 | 
			
		||||
| 
						 | 
				
			
			@ -1205,10 +1173,7 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
                self.handlers[i].disconnect(i)
 | 
			
		||||
            del self.handlers[i]
 | 
			
		||||
        self.conv_textview.del_handlers()
 | 
			
		||||
        if app.config.get('use_speller') and HAS_GTK_SPELL:
 | 
			
		||||
            spell_obj = gtkspell.get_from_text_view(self.msg_textview)
 | 
			
		||||
            if spell_obj:
 | 
			
		||||
                spell_obj.detach()
 | 
			
		||||
        self.remove_speller()
 | 
			
		||||
        self.msg_textview.destroy()
 | 
			
		||||
        # PluginSystem: calling shutdown of super class (ChatControlBase) to let
 | 
			
		||||
        # it remove it's GUI extension points
 | 
			
		||||
| 
						 | 
				
			
			@ -1272,10 +1237,18 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        if not app.config.get('show_avatar_in_chat'):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        pixbuf = app.contacts.get_avatar(
 | 
			
		||||
            self.account, self.contact.jid, AvatarSize.CHAT)
 | 
			
		||||
        if self.TYPE_ID == message_control.TYPE_CHAT:
 | 
			
		||||
            pixbuf = app.contacts.get_avatar(
 | 
			
		||||
                self.account, self.contact.jid, AvatarSize.CHAT)
 | 
			
		||||
        else:
 | 
			
		||||
            pixbuf = app.interface.get_avatar(
 | 
			
		||||
                self.gc_contact.avatar_sha, AvatarSize.CHAT)
 | 
			
		||||
 | 
			
		||||
        image = self.xml.get_object('avatar_image')
 | 
			
		||||
        image.set_from_pixbuf(pixbuf)
 | 
			
		||||
        if pixbuf is None:
 | 
			
		||||
            image.set_from_icon_name('avatar-default', Gtk.IconSize.DIALOG)
 | 
			
		||||
        else:
 | 
			
		||||
            image.set_from_pixbuf(pixbuf)
 | 
			
		||||
 | 
			
		||||
    def _nec_update_avatar(self, obj):
 | 
			
		||||
        if obj.account != self.account:
 | 
			
		||||
| 
						 | 
				
			
			@ -1446,15 +1419,6 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
            elif typ == 'pm':
 | 
			
		||||
                control.remove_contact(nick)
 | 
			
		||||
 | 
			
		||||
    def _on_send_file_menuitem_activate(self, widget):
 | 
			
		||||
        self._on_send_file()
 | 
			
		||||
 | 
			
		||||
    def _on_add_to_roster_menuitem_activate(self, widget):
 | 
			
		||||
        dialogs.AddNewContactWindow(self.account, self.contact.jid)
 | 
			
		||||
 | 
			
		||||
    def _on_contact_information_menuitem_activate(self, widget):
 | 
			
		||||
        app.interface.roster.on_info(widget, self.contact, self.account)
 | 
			
		||||
 | 
			
		||||
    def _on_convert_to_gc_menuitem_activate(self, widget):
 | 
			
		||||
        """
 | 
			
		||||
        User wants to invite some friends to chat
 | 
			
		||||
| 
						 | 
				
			
			@ -1522,21 +1486,11 @@ class ChatControl(ChatControlBase):
 | 
			
		|||
        if contact:
 | 
			
		||||
            self.contact = contact
 | 
			
		||||
        self.draw_banner()
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def got_disconnected(self):
 | 
			
		||||
        # Add to roster
 | 
			
		||||
        self._add_to_roster_button.hide()
 | 
			
		||||
        # Audio button
 | 
			
		||||
        self._audio_button.set_sensitive(False)
 | 
			
		||||
        # Video button
 | 
			
		||||
        self._video_button.set_sensitive(False)
 | 
			
		||||
        # Send file button
 | 
			
		||||
        self._send_file_button.set_tooltip_text('')
 | 
			
		||||
        self._send_file_button.set_sensitive(False)
 | 
			
		||||
        # Convert to GC button
 | 
			
		||||
        self._convert_to_gc_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
        ChatControlBase.got_disconnected(self)
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def update_status_display(self, name, uf_show, status):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,16 +35,17 @@ from gi.repository import Pango
 | 
			
		|||
from gi.repository import GObject
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
 | 
			
		||||
from gajim import gtkgui_helpers
 | 
			
		||||
from gajim.gtkgui_helpers import Color
 | 
			
		||||
from gajim import message_control
 | 
			
		||||
from gajim import dialogs
 | 
			
		||||
from gajim import history_window
 | 
			
		||||
from gajim import notify
 | 
			
		||||
from gajim import gtkspell
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
from gajim import emoticons
 | 
			
		||||
from gajim.scrolled_window import ScrolledWindow
 | 
			
		||||
from gajim.common import events
 | 
			
		||||
from gajim.common import app
 | 
			
		||||
from gajim.common import helpers
 | 
			
		||||
| 
						 | 
				
			
			@ -65,11 +66,6 @@ from gajim.command_system.implementation.middleware import CommandTools
 | 
			
		|||
from gajim.command_system.implementation import standard
 | 
			
		||||
from gajim.command_system.implementation import execute
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from gajim import gtkspell
 | 
			
		||||
    HAS_GTK_SPELL = True
 | 
			
		||||
except (ImportError, ValueError):
 | 
			
		||||
    HAS_GTK_SPELL = False
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		||||
| 
						 | 
				
			
			@ -256,28 +252,21 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        MessageControl.__init__(self, type_id, parent_win, widget_name,
 | 
			
		||||
            contact, acct, resource=resource)
 | 
			
		||||
 | 
			
		||||
        widget = self.xml.get_object('history_button')
 | 
			
		||||
        # set document-open-recent icon for history button
 | 
			
		||||
        if gtkgui_helpers.gtk_icon_theme.has_icon('document-open-recent'):
 | 
			
		||||
            img = self.xml.get_object('history_image')
 | 
			
		||||
            img.set_from_icon_name('document-open-recent', Gtk.IconSize.MENU)
 | 
			
		||||
 | 
			
		||||
        id_ = widget.connect('clicked', self._on_history_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = widget
 | 
			
		||||
 | 
			
		||||
        # Create banner and connect signals
 | 
			
		||||
        widget = self.xml.get_object('banner_eventbox')
 | 
			
		||||
        id_ = widget.connect('button-press-event',
 | 
			
		||||
            self._on_banner_eventbox_button_press_event)
 | 
			
		||||
        self.handlers[id_] = widget
 | 
			
		||||
        if self.TYPE_ID != message_control.TYPE_GC:
 | 
			
		||||
            # Create banner and connect signals
 | 
			
		||||
            widget = self.xml.get_object('banner_eventbox')
 | 
			
		||||
            id_ = widget.connect('button-press-event',
 | 
			
		||||
                self._on_banner_eventbox_button_press_event)
 | 
			
		||||
            self.handlers[id_] = widget
 | 
			
		||||
 | 
			
		||||
        self.urlfinder = re.compile(
 | 
			
		||||
            r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]")
 | 
			
		||||
 | 
			
		||||
        self.banner_status_label = self.xml.get_object('banner_label')
 | 
			
		||||
        id_ = self.banner_status_label.connect('populate_popup',
 | 
			
		||||
            self.on_banner_label_populate_popup)
 | 
			
		||||
        self.handlers[id_] = self.banner_status_label
 | 
			
		||||
        if self.banner_status_label is not None:
 | 
			
		||||
            id_ = self.banner_status_label.connect('populate_popup',
 | 
			
		||||
                self.on_banner_label_populate_popup)
 | 
			
		||||
            self.handlers[id_] = self.banner_status_label
 | 
			
		||||
 | 
			
		||||
        # Init DND
 | 
			
		||||
        self.TARGET_TYPE_URI_LIST = 80
 | 
			
		||||
| 
						 | 
				
			
			@ -332,9 +321,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
 | 
			
		||||
        self.msg_scrolledwindow = ScrolledWindow()
 | 
			
		||||
        self.msg_scrolledwindow.set_max_content_height(100)
 | 
			
		||||
        self.msg_scrolledwindow.set_min_content_height(23)
 | 
			
		||||
        self.msg_scrolledwindow.set_propagate_natural_height(True)
 | 
			
		||||
        self.msg_scrolledwindow.get_style_context().add_class('scrolledtextview')
 | 
			
		||||
 | 
			
		||||
        self.msg_scrolledwindow.set_property('shadow_type', Gtk.ShadowType.IN)
 | 
			
		||||
        self.msg_scrolledwindow.add(self.msg_textview)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -364,8 +352,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        self.set_emoticon_popover()
 | 
			
		||||
 | 
			
		||||
        # Attach speller
 | 
			
		||||
        if app.config.get('use_speller') and HAS_GTK_SPELL:
 | 
			
		||||
            self.set_speller()
 | 
			
		||||
        self.spell = None
 | 
			
		||||
        self.spell_handlers = []
 | 
			
		||||
        self.set_speller()
 | 
			
		||||
        self.conv_textview.tv.show()
 | 
			
		||||
 | 
			
		||||
        # For XEP-0172
 | 
			
		||||
| 
						 | 
				
			
			@ -417,6 +406,28 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        action.connect("change-state", self.change_encryption)
 | 
			
		||||
        self.parent_win.window.add_action(action)
 | 
			
		||||
 | 
			
		||||
        action = Gio.SimpleAction.new(
 | 
			
		||||
            'browse-history-%s' % self.control_id, GLib.VariantType.new('s'))
 | 
			
		||||
        action.connect('activate', self._on_history)
 | 
			
		||||
        self.parent_win.window.add_action(action)
 | 
			
		||||
 | 
			
		||||
    # Actions
 | 
			
		||||
 | 
			
		||||
    def _on_history(self, action, param):
 | 
			
		||||
        """
 | 
			
		||||
        When history menuitem is pressed: call history window
 | 
			
		||||
        """
 | 
			
		||||
        jid = param.get_string()
 | 
			
		||||
        if jid == 'none':
 | 
			
		||||
            jid = self.contact.jid
 | 
			
		||||
 | 
			
		||||
        if 'logs' in app.interface.instances:
 | 
			
		||||
            app.interface.instances['logs'].window.present()
 | 
			
		||||
            app.interface.instances['logs'].open_history(jid, self.account)
 | 
			
		||||
        else:
 | 
			
		||||
            app.interface.instances['logs'] = \
 | 
			
		||||
                    history_window.HistoryWindow(jid, self.account)
 | 
			
		||||
 | 
			
		||||
    def change_encryption(self, action, param):
 | 
			
		||||
        encryption = param.get_string()
 | 
			
		||||
        if encryption == 'disabled':
 | 
			
		||||
| 
						 | 
				
			
			@ -467,24 +478,57 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        image.set_from_pixbuf(icon)
 | 
			
		||||
 | 
			
		||||
    def set_speller(self):
 | 
			
		||||
        # now set the one the user selected
 | 
			
		||||
        if not gtkspell.HAS_GTK_SPELL or not app.config.get('use_speller'):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        def _on_focus_in(*args):
 | 
			
		||||
            if self.spell is None:
 | 
			
		||||
                return
 | 
			
		||||
            self.spell.attach(self.msg_textview)
 | 
			
		||||
 | 
			
		||||
        def _on_focus_out(*args):
 | 
			
		||||
            if self.spell is None:
 | 
			
		||||
                return
 | 
			
		||||
            if not self.msg_textview.has_text():
 | 
			
		||||
                self.spell.detach()
 | 
			
		||||
 | 
			
		||||
        lang = self.get_speller_language()
 | 
			
		||||
        if not lang:
 | 
			
		||||
            return
 | 
			
		||||
        try:
 | 
			
		||||
            self.spell = gtkspell.Spell(self.msg_textview, lang)
 | 
			
		||||
            self.spell.connect('language_changed', self.on_language_changed)
 | 
			
		||||
            handler_id = self.msg_textview.connect('focus-in-event',
 | 
			
		||||
                                                   _on_focus_in)
 | 
			
		||||
            self.spell_handlers.append(handler_id)
 | 
			
		||||
            handler_id = self.msg_textview.connect('focus-out-event',
 | 
			
		||||
                                                   _on_focus_out)
 | 
			
		||||
            self.spell_handlers.append(handler_id)
 | 
			
		||||
        except OSError:
 | 
			
		||||
            dialogs.AspellDictError(lang)
 | 
			
		||||
            app.config.set('use_speller', False)
 | 
			
		||||
 | 
			
		||||
    def remove_speller(self):
 | 
			
		||||
        if self.spell is None:
 | 
			
		||||
            return
 | 
			
		||||
        self.spell.detach()
 | 
			
		||||
        for id_ in self.spell_handlers:
 | 
			
		||||
            self.msg_textview.disconnect(id_)
 | 
			
		||||
            self.spell_handlers.remove(id_)
 | 
			
		||||
        self.spell = None
 | 
			
		||||
 | 
			
		||||
    def get_speller_language(self):
 | 
			
		||||
        per_type = 'contacts'
 | 
			
		||||
        if self.type_id == message_control.TYPE_GC:
 | 
			
		||||
        if self.type_id == 'gc':
 | 
			
		||||
            per_type = 'rooms'
 | 
			
		||||
        lang = app.config.get_per(per_type, self.contact.jid,
 | 
			
		||||
                'speller_language')
 | 
			
		||||
        lang = app.config.get_per(
 | 
			
		||||
            per_type, self.contact.jid, 'speller_language')
 | 
			
		||||
        if not lang:
 | 
			
		||||
            # use the default one
 | 
			
		||||
            lang = app.config.get('speller_language')
 | 
			
		||||
            if not lang:
 | 
			
		||||
                lang = app.LANG
 | 
			
		||||
        if lang:
 | 
			
		||||
            try:
 | 
			
		||||
                self.spell = gtkspell.Spell(self.msg_textview, lang)
 | 
			
		||||
                self.msg_textview.lang = lang
 | 
			
		||||
                self.spell.connect('language_changed', self.on_language_changed)
 | 
			
		||||
            except (GObject.GError, RuntimeError, TypeError, OSError):
 | 
			
		||||
                dialogs.AspellDictError(lang)
 | 
			
		||||
        return lang or None
 | 
			
		||||
 | 
			
		||||
    def on_language_changed(self, spell, lang):
 | 
			
		||||
        per_type = 'contacts'
 | 
			
		||||
| 
						 | 
				
			
			@ -492,9 +536,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
            per_type = 'rooms'
 | 
			
		||||
        if not app.config.get_per(per_type, self.contact.jid):
 | 
			
		||||
            app.config.add_per(per_type, self.contact.jid)
 | 
			
		||||
        app.config.set_per(per_type, self.contact.jid, 'speller_language',
 | 
			
		||||
            lang)
 | 
			
		||||
        self.msg_textview.lang = lang
 | 
			
		||||
        app.config.set_per(
 | 
			
		||||
            per_type, self.contact.jid, 'speller_language', lang)
 | 
			
		||||
 | 
			
		||||
    def on_banner_label_populate_popup(self, label, menu):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			@ -549,6 +592,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        menu.show_all()
 | 
			
		||||
 | 
			
		||||
    def on_quote(self, widget, text):
 | 
			
		||||
        self.msg_textview.remove_placeholder()
 | 
			
		||||
        text = '>' + text.replace('\n', '\n>') + '\n'
 | 
			
		||||
        message_buffer = self.msg_textview.get_buffer()
 | 
			
		||||
        message_buffer.insert_at_cursor(text)
 | 
			
		||||
| 
						 | 
				
			
			@ -1283,13 +1327,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
        else:
 | 
			
		||||
            widget.show_all()
 | 
			
		||||
 | 
			
		||||
    def chat_buttons_set_visible(self, state):
 | 
			
		||||
        """
 | 
			
		||||
        Toggle chat buttons
 | 
			
		||||
        """
 | 
			
		||||
        MessageControl.chat_buttons_set_visible(self, state)
 | 
			
		||||
        self.widget_set_visible(self.xml.get_object('actions_hbox'), state)
 | 
			
		||||
 | 
			
		||||
    def got_connected(self):
 | 
			
		||||
        self.msg_textview.set_sensitive(True)
 | 
			
		||||
        self.msg_textview.set_editable(True)
 | 
			
		||||
| 
						 | 
				
			
			@ -1302,3 +1339,19 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
 | 
			
		|||
 | 
			
		||||
        self.no_autonegotiation = False
 | 
			
		||||
        self.update_toolbar()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScrolledWindow(Gtk.ScrolledWindow):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def do_get_preferred_height(self):
 | 
			
		||||
        min_height, natural_height = Gtk.ScrolledWindow.do_get_preferred_height(self)
 | 
			
		||||
        child = self.get_child()
 | 
			
		||||
        if natural_height and self.get_max_content_height() > -1 and child:
 | 
			
		||||
            _, child_nat_height = child.get_preferred_height()
 | 
			
		||||
            if natural_height > child_nat_height:
 | 
			
		||||
                if child_nat_height < 26:
 | 
			
		||||
                    return 26, 26
 | 
			
		||||
 | 
			
		||||
        return min_height, natural_height
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,12 +41,6 @@ class StandardCommonCommands(CommandContainer):
 | 
			
		|||
    AUTOMATIC = True
 | 
			
		||||
    HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
 | 
			
		||||
 | 
			
		||||
    @command
 | 
			
		||||
    @doc(_("Hide the chat buttons"))
 | 
			
		||||
    def compact(self):
 | 
			
		||||
        new_status = not self.hide_chat_buttons
 | 
			
		||||
        self.chat_buttons_set_visible(new_status)
 | 
			
		||||
 | 
			
		||||
    @command(overlap=True)
 | 
			
		||||
    @doc(_("Show help on a given command or a list of available commands if -a is given"))
 | 
			
		||||
    def help(self, command=None, all=False):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -264,7 +264,6 @@ class Config:
 | 
			
		|||
            'show_roster_on_startup':[opt_str, 'always', _('Show roster on startup.\n\'always\' - Always show roster.\n\'never\' - Never show roster.\n\'last_state\' - Restore the last state roster.')],
 | 
			
		||||
            'show_avatar_in_chat': [opt_bool, True, _('If False, you will no longer see the avatar in the chat window.')],
 | 
			
		||||
            'escape_key_closes': [opt_bool, True, _('If True, pressing the escape key closes a tab/window.')],
 | 
			
		||||
            'compact_view': [opt_bool, False, _('Hides the buttons in chat windows.')],
 | 
			
		||||
            'hide_groupchat_banner': [opt_bool, False, _('Hides the banner in a group chat window')],
 | 
			
		||||
            'hide_chat_banner': [opt_bool, False, _('Hides the banner in two persons chat window')],
 | 
			
		||||
            'hide_groupchat_occupants_list': [opt_bool, False, _('Hides the group chat occupants list in group chat window.')],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2342,6 +2342,13 @@ class Connection(CommonConnection, ConnectionHandlers):
 | 
			
		|||
                if rule['type'] == 'group':
 | 
			
		||||
                    roster.draw_group(rule['value'], self.name)
 | 
			
		||||
 | 
			
		||||
    def bookmarks_available(self):
 | 
			
		||||
        if self.private_storage_supported:
 | 
			
		||||
            return True
 | 
			
		||||
        if self.pubsub_publish_options_supported:
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def _request_bookmarks_xml(self):
 | 
			
		||||
        if not app.account_is_connected(self.name):
 | 
			
		||||
            return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,8 @@ class OptionType(IntEnum):
 | 
			
		|||
 | 
			
		||||
class AvatarSize(IntEnum):
 | 
			
		||||
    ROSTER = 32
 | 
			
		||||
    CHAT = 48
 | 
			
		||||
    NOTIFICATION = 48
 | 
			
		||||
    CHAT = 52
 | 
			
		||||
    PROFILE = 64
 | 
			
		||||
    TOOLTIP = 125
 | 
			
		||||
    VCARD = 200
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,12 +50,7 @@ from gajim import message_control
 | 
			
		|||
from gajim.chat_control_base import ChatControlBase
 | 
			
		||||
from gajim import dataforms_widget
 | 
			
		||||
from gajim import gui_menu_builder
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from gajim import gtkspell
 | 
			
		||||
    HAS_GTK_SPELL = True
 | 
			
		||||
except (ImportError, ValueError):
 | 
			
		||||
    HAS_GTK_SPELL = False
 | 
			
		||||
from gajim import gtkspell
 | 
			
		||||
 | 
			
		||||
from gajim.common import helpers
 | 
			
		||||
from gajim.common import app
 | 
			
		||||
| 
						 | 
				
			
			@ -187,16 +182,12 @@ class PreferencesWindow:
 | 
			
		|||
        else:
 | 
			
		||||
            show_roster_combobox.set_active(0)
 | 
			
		||||
 | 
			
		||||
        # Compact View
 | 
			
		||||
        st = app.config.get('compact_view')
 | 
			
		||||
        self.xml.get_object('compact_view_checkbutton').set_active(st)
 | 
			
		||||
 | 
			
		||||
        # Ignore XHTML
 | 
			
		||||
        st = app.config.get('ignore_incoming_xhtml')
 | 
			
		||||
        self.xml.get_object('xhtml_checkbutton').set_active(st)
 | 
			
		||||
 | 
			
		||||
        # use speller
 | 
			
		||||
        if HAS_GTK_SPELL:
 | 
			
		||||
        if gtkspell.HAS_GTK_SPELL:
 | 
			
		||||
            st = app.config.get('use_speller')
 | 
			
		||||
            self.xml.get_object('speller_checkbutton').set_active(st)
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -657,12 +648,6 @@ class PreferencesWindow:
 | 
			
		|||
        config_type = c_config.opt_show_roster_on_startup[active]
 | 
			
		||||
        app.config.set('show_roster_on_startup', config_type)
 | 
			
		||||
 | 
			
		||||
    def on_compact_view_checkbutton_toggled(self, widget):
 | 
			
		||||
        active = widget.get_active()
 | 
			
		||||
        for ctrl in self._get_all_controls():
 | 
			
		||||
            ctrl.chat_buttons_set_visible(active)
 | 
			
		||||
        app.config.set('compact_view', active)
 | 
			
		||||
 | 
			
		||||
    def on_xhtml_checkbutton_toggled(self, widget):
 | 
			
		||||
        self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml')
 | 
			
		||||
        helpers.update_optional_features()
 | 
			
		||||
| 
						 | 
				
			
			@ -670,23 +655,13 @@ class PreferencesWindow:
 | 
			
		|||
    def apply_speller(self):
 | 
			
		||||
        for ctrl in self._get_all_controls():
 | 
			
		||||
            if isinstance(ctrl, ChatControlBase):
 | 
			
		||||
                try:
 | 
			
		||||
                    spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
 | 
			
		||||
                except (TypeError, RuntimeError, OSError):
 | 
			
		||||
                    spell_obj = None
 | 
			
		||||
 | 
			
		||||
                if not spell_obj:
 | 
			
		||||
                    ctrl.set_speller()
 | 
			
		||||
                ctrl.set_speller()
 | 
			
		||||
 | 
			
		||||
    def remove_speller(self):
 | 
			
		||||
        for ctrl in self._get_all_controls():
 | 
			
		||||
            if isinstance(ctrl, ChatControlBase):
 | 
			
		||||
                try:
 | 
			
		||||
                    spell_obj = gtkspell.get_from_text_view(ctrl.msg_textview)
 | 
			
		||||
                except (TypeError, RuntimeError):
 | 
			
		||||
                    spell_obj = None
 | 
			
		||||
                if spell_obj:
 | 
			
		||||
                    spell_obj.detach()
 | 
			
		||||
                if ctrl.spell is not None:
 | 
			
		||||
                    ctrl.remove_speller()
 | 
			
		||||
 | 
			
		||||
    def on_speller_checkbutton_toggled(self, widget):
 | 
			
		||||
        active = widget.get_active()
 | 
			
		||||
| 
						 | 
				
			
			@ -695,15 +670,10 @@ class PreferencesWindow:
 | 
			
		|||
            lang = app.config.get('speller_language')
 | 
			
		||||
            if not lang:
 | 
			
		||||
                lang = app.LANG
 | 
			
		||||
            tv = Gtk.TextView()
 | 
			
		||||
            try:
 | 
			
		||||
                gtkspell.Spell(tv, lang)
 | 
			
		||||
            except (TypeError, RuntimeError, OSError):
 | 
			
		||||
                dialogs.ErrorDialog(
 | 
			
		||||
                        _('Dictionary for lang %s not available') % lang,
 | 
			
		||||
                        _('You have to install %s dictionary to use spellchecking, or '
 | 
			
		||||
                        'choose another language by setting the speller_language option.'
 | 
			
		||||
                        ) % lang)
 | 
			
		||||
 | 
			
		||||
            available = gtkspell.test_language(lang)
 | 
			
		||||
            if not available:
 | 
			
		||||
                dialogs.AspellDictError(lang)
 | 
			
		||||
                app.config.set('use_speller', False)
 | 
			
		||||
                widget.set_active(False)
 | 
			
		||||
            else:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -374,7 +374,6 @@
 | 
			
		|||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can_focus">False</property>
 | 
			
		||||
            <property name="orientation">vertical</property>
 | 
			
		||||
            <property name="spacing">5</property>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkEventBox" id="banner_eventbox">
 | 
			
		||||
                <property name="name">ChatControl-BannerEventBox</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -385,16 +384,23 @@
 | 
			
		|||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="banner_status_image">
 | 
			
		||||
                      <object class="GtkEventBox" id="avatar_eventbox">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="ypad">5</property>
 | 
			
		||||
                        <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                        <property name="margin_top">5</property>
 | 
			
		||||
                        <property name="margin_bottom">5</property>
 | 
			
		||||
                        <property name="visible_window">False</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="avatar_image">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="padding">5</property>
 | 
			
		||||
                        <property name="position">0</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
| 
						 | 
				
			
			@ -545,21 +551,15 @@
 | 
			
		|||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkEventBox" id="avatar_eventbox">
 | 
			
		||||
                      <object class="GtkImage" id="banner_status_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="visible_window">False</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="avatar_image">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="padding">5</property>
 | 
			
		||||
                        <property name="position">3</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
| 
						 | 
				
			
			@ -573,94 +573,16 @@
 | 
			
		|||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkBox" id="vbox106">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="can_focus">False</property>
 | 
			
		||||
                <property name="orientation">vertical</property>
 | 
			
		||||
                <property name="spacing">6</property>
 | 
			
		||||
              <object class="GtkScrolledWindow" id="conversation_scrolledwindow">
 | 
			
		||||
                <property name="height_request">60</property>
 | 
			
		||||
                <property name="can_focus">True</property>
 | 
			
		||||
                <property name="shadow_type">in</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkScrolledWindow" id="conversation_scrolledwindow">
 | 
			
		||||
                    <property name="height_request">60</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="shadow_type">in</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <placeholder/>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">True</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkBox" id="hbox">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkMenuButton" id="emoticons_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="icon_name">face-smile</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="msgtextview-button"/>
 | 
			
		||||
                          <class name="left"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">0</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <placeholder/>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="authentication_button">
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="focus_on_click">False</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="no_show_all">True</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="lock_image">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-dialog-authentication</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="msgtextview-button"/>
 | 
			
		||||
                          <class name="right"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="pack_type">end</property>
 | 
			
		||||
                        <property name="position">2</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="position">1</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <style>
 | 
			
		||||
                  <class name="scrolled-no-border"/>
 | 
			
		||||
                </style>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">True</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -669,9 +591,48 @@
 | 
			
		|||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkBox" id="actions_hbox">
 | 
			
		||||
              <object class="GtkSeparator">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="can_focus">False</property>
 | 
			
		||||
                <style>
 | 
			
		||||
                  <class name="chatcontrol-separator"/>
 | 
			
		||||
                </style>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">True</property>
 | 
			
		||||
                <property name="position">2</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkBox" id="hbox">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="can_focus">False</property>
 | 
			
		||||
                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkMenuButton" id="emoticons_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Show a list of emoticons (Alt+M)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="icon_name">face-smile-symbolic</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkMenuButton" id="formattings_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -686,23 +647,13 @@
 | 
			
		|||
                      <object class="GtkImage" id="image10">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="stock">gtk-bold</property>
 | 
			
		||||
                        <property name="icon_name">format-text-bold-symbolic</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkSeparator" id="vseparator1">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="orientation">vertical</property>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -711,235 +662,15 @@
 | 
			
		|||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="add_to_roster_button">
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="no_show_all">True</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="tooltip_markup" translatable="yes">Add this contact to roster (Ctrl+D)</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Add this contact to roster (Ctrl+D)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="image9">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="stock">gtk-add</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="position">2</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="send_file_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="image3">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">3</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkToggleButton" id="audio_togglebutton">
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="audio_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">4</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkToggleButton" id="video_togglebutton">
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="video_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">5</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="convert_to_gc_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="tooltip_markup" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Invite contacts to the conversation (Ctrl+G)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="convert_to_gc_button_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">6</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="contact_information_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="tooltip_markup" translatable="yes">Show the contact&apos;s profile (Ctrl+I)</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Show the contact's profile (Ctrl+I)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="image2">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="stock">gtk-info</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">7</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="history_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="tooltip_markup" translatable="yes">Browse the chat history (Ctrl+H)</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Browse the chat history (Ctrl+H)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="history_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="stock">gtk-justify-fill</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">8</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkSeparator" id="vseparator3">
 | 
			
		||||
                  <object class="GtkComboBox" id="label_selector">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="orientation">vertical</property>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="expand">True</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">9</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkButton" id="message_window_actions_button">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="has_tooltip">True</property>
 | 
			
		||||
                    <property name="tooltip_markup" translatable="yes">Show advanced functions (Alt+D)</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Show advanced functions (Alt+D)</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="image1">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="stock">gtk-execute</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">10</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkMenuButton" id="encryption_menu">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Choose an encryption</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="icon_name">channel-secure-symbolic</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">11</property>
 | 
			
		||||
                    <property name="pack_type">end</property>
 | 
			
		||||
                    <property name="position">2</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -1042,26 +773,70 @@ audio-mic-volume-low</property>
 | 
			
		|||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">12</property>
 | 
			
		||||
                    <property name="pack_type">end</property>
 | 
			
		||||
                    <property name="position">3</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkComboBox" id="label_selector">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                  <object class="GtkButton" id="authentication_button">
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="focus_on_click">False</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="no_show_all">True</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage" id="lock_image">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="stock">gtk-dialog-authentication</property>
 | 
			
		||||
                        <property name="icon_size">1</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">True</property>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="pack_type">end</property>
 | 
			
		||||
                    <property name="position">4</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <placeholder/>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkMenuButton" id="encryption_menu">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">True</property>
 | 
			
		||||
                    <property name="receives_default">True</property>
 | 
			
		||||
                    <property name="tooltip_text" translatable="yes">Choose an encryption</property>
 | 
			
		||||
                    <property name="relief">none</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkImage">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="icon_name">channel-secure-symbolic</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">13</property>
 | 
			
		||||
                    <property name="pack_type">end</property>
 | 
			
		||||
                    <property name="position">6</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="expand">False</property>
 | 
			
		||||
                <property name="fill">True</property>
 | 
			
		||||
                <property name="pack_type">end</property>
 | 
			
		||||
                <property name="position">2</property>
 | 
			
		||||
                <property name="fill">False</property>
 | 
			
		||||
                <property name="position">3</property>
 | 
			
		||||
              </packing>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,82 @@
 | 
			
		|||
<!-- Generated with glade 3.20.0 -->
 | 
			
		||||
<interface>
 | 
			
		||||
  <requires lib="gtk+" version="3.20"/>
 | 
			
		||||
  <object class="GtkMenu" id="formattings_menu">
 | 
			
		||||
    <property name="visible">True</property>
 | 
			
		||||
    <property name="can_focus">False</property>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkCheckMenuItem" id="bold">
 | 
			
		||||
        <property name="name">bold</property>
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Bold</property>
 | 
			
		||||
        <signal name="activate" handler="on_formatting_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkCheckMenuItem" id="italic">
 | 
			
		||||
        <property name="name">italic</property>
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Italic</property>
 | 
			
		||||
        <signal name="activate" handler="on_formatting_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkCheckMenuItem" id="underline">
 | 
			
		||||
        <property name="name">underline</property>
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Underline</property>
 | 
			
		||||
        <signal name="activate" handler="on_formatting_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkCheckMenuItem" id="strike">
 | 
			
		||||
        <property name="name">strike</property>
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Strike</property>
 | 
			
		||||
        <signal name="activate" handler="on_formatting_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkSeparatorMenuItem">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkMenuItem" id="color">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Color</property>
 | 
			
		||||
        <signal name="activate" handler="on_color_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkMenuItem" id="font">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Font</property>
 | 
			
		||||
        <signal name="activate" handler="on_font_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkSeparatorMenuItem">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkMenuItem" id="clear_formatting">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">False</property>
 | 
			
		||||
        <property name="label" translatable="yes">Clear formatting</property>
 | 
			
		||||
        <signal name="activate" handler="on_clear_formatting_menuitem_activate" swapped="no"/>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object class="GtkBox" id="groupchat_control_hbox">
 | 
			
		||||
    <property name="can_focus">True</property>
 | 
			
		||||
    <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -13,83 +89,6 @@
 | 
			
		|||
        <property name="margin_bottom">7</property>
 | 
			
		||||
        <property name="orientation">vertical</property>
 | 
			
		||||
        <property name="spacing">5</property>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkEventBox" id="banner_eventbox">
 | 
			
		||||
            <property name="name">GroupChatControl-BannerEventBox</property>
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can_focus">False</property>
 | 
			
		||||
            <child>
 | 
			
		||||
              <object class="GtkBox" id="hbox3024">
 | 
			
		||||
                <property name="visible">True</property>
 | 
			
		||||
                <property name="can_focus">False</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkImage" id="gc_banner_status_image">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="padding">5</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkBox" id="banner_vbox">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                    <property name="border_width">5</property>
 | 
			
		||||
                    <property name="orientation">vertical</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkLabel" id="banner_name_label">
 | 
			
		||||
                        <property name="name">GroupChatControl-BannerNameLabel</property>
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="label"><span weight="heavy" size="large">room jid</span></property>
 | 
			
		||||
                        <property name="use_markup">True</property>
 | 
			
		||||
                        <property name="selectable">True</property>
 | 
			
		||||
                        <property name="xalign">0</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">True</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">0</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkLabel" id="banner_label">
 | 
			
		||||
                        <property name="name">GroupChatControl-BannerLabel</property>
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="label">label</property>
 | 
			
		||||
                        <property name="use_markup">True</property>
 | 
			
		||||
                        <property name="selectable">True</property>
 | 
			
		||||
                        <property name="xalign">0</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">True</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">1</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">True</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">1</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
            </child>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="expand">False</property>
 | 
			
		||||
            <property name="fill">True</property>
 | 
			
		||||
            <property name="position">0</property>
 | 
			
		||||
          </packing>
 | 
			
		||||
        </child>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkPaned" id="hpaned">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +103,68 @@
 | 
			
		|||
                <property name="can_focus">False</property>
 | 
			
		||||
                <property name="margin_right">4</property>
 | 
			
		||||
                <property name="orientation">vertical</property>
 | 
			
		||||
                <property name="spacing">6</property>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkEventBox" id="banner_eventbox">
 | 
			
		||||
                    <property name="name">GroupChatControl-BannerEventBox</property>
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkBox" id="hbox3024">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="gc_banner_status_image">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-missing-image</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="padding">5</property>
 | 
			
		||||
                            <property name="position">0</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkLabel" id="banner_name_label">
 | 
			
		||||
                            <property name="name">GroupChatControl-BannerNameLabel</property>
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="label"><span weight="heavy" size="large">room jid</span></property>
 | 
			
		||||
                            <property name="use_markup">True</property>
 | 
			
		||||
                            <property name="selectable">True</property>
 | 
			
		||||
                            <property name="xalign">0</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">True</property>
 | 
			
		||||
                            <property name="position">1</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkActionBar" id="banner_actionbar">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <style>
 | 
			
		||||
                              <class name="actionbar-no-border"/>
 | 
			
		||||
                            </style>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">True</property>
 | 
			
		||||
                            <property name="fill">True</property>
 | 
			
		||||
                            <property name="pack_type">end</property>
 | 
			
		||||
                            <property name="position">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkScrolledWindow" id="conversation_scrolledwindow">
 | 
			
		||||
                    <property name="width_request">200</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -115,11 +175,28 @@
 | 
			
		|||
                    <child>
 | 
			
		||||
                      <placeholder/>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="scrolled-no-border"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">True</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">0</property>
 | 
			
		||||
                    <property name="position">1</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkSeparator">
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <style>
 | 
			
		||||
                      <class name="chatcontrol-separator"/>
 | 
			
		||||
                    </style>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">2</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -138,12 +215,11 @@
 | 
			
		|||
                          <object class="GtkImage">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="icon_name">face-smile</property>
 | 
			
		||||
                            <property name="icon_name">face-smile-symbolic</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="msgtextview-button"/>
 | 
			
		||||
                          <class name="left"/>
 | 
			
		||||
                          <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +229,32 @@
 | 
			
		|||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <placeholder/>
 | 
			
		||||
                      <object class="GtkMenuButton" id="formattings_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="focus_on_click">False</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <property name="popup">formattings_menu</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image10">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="icon_name">format-text-bold-symbolic</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">1</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="authentication_button">
 | 
			
		||||
| 
						 | 
				
			
			@ -172,8 +273,7 @@
 | 
			
		|||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="msgtextview-button"/>
 | 
			
		||||
                          <class name="right"/>
 | 
			
		||||
                          <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
| 
						 | 
				
			
			@ -183,190 +283,19 @@
 | 
			
		|||
                        <property name="position">2</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="position">1</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
                <child>
 | 
			
		||||
                  <object class="GtkBox" id="actions_hbox">
 | 
			
		||||
                    <property name="height_request">34</property>
 | 
			
		||||
                    <property name="visible">True</property>
 | 
			
		||||
                    <property name="can_focus">False</property>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="formattings_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="focus_on_click">False</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Show a list of formattings</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image11">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-bold</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">0</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="change_nick_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Change your nickname (Ctrl+N)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image4">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                            <property name="stock">gtk-edit</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="position">1</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="change_subject_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Change the room's subject (Alt+T)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image6">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                            <property name="stock">gtk-properties</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="position">2</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="bookmark_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="no_show_all">True</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Bookmark this room (Ctrl+B)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image7">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                            <property name="stock">gtk-add</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="position">3</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="history_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Browse the chat history (Ctrl+H)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="history_image">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                            <property name="stock">gtk-justify-fill</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="position">4</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkSeparator" id="vseparator4">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">5</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkButton" id="muc_window_actions_button">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">True</property>
 | 
			
		||||
                        <property name="focus_on_click">False</property>
 | 
			
		||||
                        <property name="receives_default">True</property>
 | 
			
		||||
                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
 | 
			
		||||
                        <property name="has_tooltip">True</property>
 | 
			
		||||
                        <property name="tooltip_text" translatable="yes">Show advanced functions (Alt+D)</property>
 | 
			
		||||
                        <property name="relief">none</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkImage" id="image1344">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="stock">gtk-execute</property>
 | 
			
		||||
                            <property name="icon_size">1</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">False</property>
 | 
			
		||||
                        <property name="position">6</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                      <placeholder/>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
                      <object class="GtkComboBox" id="label_selector">
 | 
			
		||||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="can_focus">False</property>
 | 
			
		||||
                        <property name="no_show_all">True</property>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">True</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">7</property>
 | 
			
		||||
                        <property name="position">4</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                    <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -383,18 +312,22 @@
 | 
			
		|||
                            <property name="icon_name">channel-secure-symbolic</property>
 | 
			
		||||
                          </object>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <style>
 | 
			
		||||
                          <class name="chatcontrol-actionbar-button"/>
 | 
			
		||||
                        </style>
 | 
			
		||||
                      </object>
 | 
			
		||||
                      <packing>
 | 
			
		||||
                        <property name="expand">False</property>
 | 
			
		||||
                        <property name="fill">True</property>
 | 
			
		||||
                        <property name="position">8</property>
 | 
			
		||||
                        <property name="pack_type">end</property>
 | 
			
		||||
                        <property name="position">5</property>
 | 
			
		||||
                      </packing>
 | 
			
		||||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                  <packing>
 | 
			
		||||
                    <property name="expand">False</property>
 | 
			
		||||
                    <property name="fill">True</property>
 | 
			
		||||
                    <property name="position">2</property>
 | 
			
		||||
                    <property name="fill">False</property>
 | 
			
		||||
                    <property name="position">3</property>
 | 
			
		||||
                  </packing>
 | 
			
		||||
                </child>
 | 
			
		||||
              </object>
 | 
			
		||||
| 
						 | 
				
			
			@ -422,6 +355,9 @@
 | 
			
		|||
                    </child>
 | 
			
		||||
                  </object>
 | 
			
		||||
                </child>
 | 
			
		||||
                <style>
 | 
			
		||||
                  <class name="scrolled-no-border"/>
 | 
			
		||||
                </style>
 | 
			
		||||
              </object>
 | 
			
		||||
              <packing>
 | 
			
		||||
                <property name="resize">False</property>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<!-- Generated with glade 3.18.3 -->
 | 
			
		||||
<!-- Generated with glade 3.20.0 -->
 | 
			
		||||
<interface>
 | 
			
		||||
  <requires lib="gtk+" version="3.12"/>
 | 
			
		||||
  <object class="GtkEventBox" id="chat_tab_ebox">
 | 
			
		||||
| 
						 | 
				
			
			@ -28,10 +28,10 @@
 | 
			
		|||
            <property name="width_request">70</property>
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can_focus">False</property>
 | 
			
		||||
            <property name="xalign">0</property>
 | 
			
		||||
            <property name="use_markup">True</property>
 | 
			
		||||
            <property name="ellipsize">end</property>
 | 
			
		||||
            <property name="max_width_chars">9</property>
 | 
			
		||||
            <property name="xalign">0</property>
 | 
			
		||||
          </object>
 | 
			
		||||
          <packing>
 | 
			
		||||
            <property name="expand">False</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -64,15 +64,35 @@
 | 
			
		|||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object class="GtkHeaderBar" id="headerbar">
 | 
			
		||||
    <property name="visible">True</property>
 | 
			
		||||
    <property name="can_focus">False</property>
 | 
			
		||||
    <property name="show_close_button">True</property>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkMenuButton" id="header_menu">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">True</property>
 | 
			
		||||
        <property name="receives_default">True</property>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkImage">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can_focus">False</property>
 | 
			
		||||
            <property name="icon_name">open-menu-symbolic</property>
 | 
			
		||||
          </object>
 | 
			
		||||
        </child>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object class="GtkApplicationWindow" id="message_window">
 | 
			
		||||
    <property name="name">MessageWindow</property>
 | 
			
		||||
    <property name="can_focus">False</property>
 | 
			
		||||
    <property name="default_width">480</property>
 | 
			
		||||
    <property name="default_height">440</property>
 | 
			
		||||
    <property name="show_menubar">False</property>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkNotebook" id="notebook">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">True</property>
 | 
			
		||||
        <property name="margin_top">2</property>
 | 
			
		||||
        <property name="scrollable">True</property>
 | 
			
		||||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -426,23 +426,6 @@
 | 
			
		|||
                            <property name="top_attach">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkCheckButton" id="compact_view_checkbutton">
 | 
			
		||||
                            <property name="label" translatable="yes">Ma_ke message windows compact</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="receives_default">False</property>
 | 
			
		||||
                            <property name="tooltip_text" translatable="yes">Hide all buttons in chat windows</property>
 | 
			
		||||
                            <property name="use_underline">True</property>
 | 
			
		||||
                            <property name="xalign">0</property>
 | 
			
		||||
                            <property name="draw_indicator">True</property>
 | 
			
		||||
                            <signal name="toggled" handler="on_compact_view_checkbutton_toggled" swapped="no"/>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="left_attach">0</property>
 | 
			
		||||
                            <property name="top_attach">3</property>
 | 
			
		||||
                            <property name="width">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkCheckButton" id="xhtml_checkbutton">
 | 
			
		||||
                            <property name="label" translatable="yes">_Ignore rich content in incoming messages</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -457,7 +440,7 @@
 | 
			
		|||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="left_attach">0</property>
 | 
			
		||||
                            <property name="top_attach">4</property>
 | 
			
		||||
                            <property name="top_attach">3</property>
 | 
			
		||||
                            <property name="width">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
| 
						 | 
				
			
			@ -474,7 +457,7 @@
 | 
			
		|||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="left_attach">0</property>
 | 
			
		||||
                            <property name="top_attach">5</property>
 | 
			
		||||
                            <property name="top_attach">4</property>
 | 
			
		||||
                            <property name="width">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
| 
						 | 
				
			
			@ -490,7 +473,7 @@
 | 
			
		|||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="left_attach">0</property>
 | 
			
		||||
                            <property name="top_attach">6</property>
 | 
			
		||||
                            <property name="top_attach">5</property>
 | 
			
		||||
                            <property name="width">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +538,7 @@
 | 
			
		|||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="left_attach">0</property>
 | 
			
		||||
                            <property name="top_attach">7</property>
 | 
			
		||||
                            <property name="top_attach">6</property>
 | 
			
		||||
                            <property name="width">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
  <requires lib="gtk+" version="3.12"/>
 | 
			
		||||
  <object class="GtkAccelGroup" id="accelgroup1"/>
 | 
			
		||||
  <object class="GtkApplicationWindow" id="roster_window">
 | 
			
		||||
    <property name="name">RosterWindow</property>
 | 
			
		||||
    <property name="width_request">85</property>
 | 
			
		||||
    <property name="height_request">200</property>
 | 
			
		||||
    <property name="can_focus">False</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -115,4 +116,28 @@
 | 
			
		|||
      </object>
 | 
			
		||||
    </child>
 | 
			
		||||
  </object>
 | 
			
		||||
  <object class="GtkHeaderBar" id="headerbar">
 | 
			
		||||
    <property name="visible">True</property>
 | 
			
		||||
    <property name="can_focus">False</property>
 | 
			
		||||
    <property name="title">Gajim</property>
 | 
			
		||||
    <property name="show_close_button">True</property>
 | 
			
		||||
    <child>
 | 
			
		||||
      <object class="GtkMenuButton" id="header_menu">
 | 
			
		||||
        <property name="visible">True</property>
 | 
			
		||||
        <property name="can_focus">True</property>
 | 
			
		||||
        <property name="receives_default">True</property>
 | 
			
		||||
        <property name="no_show_all">True</property>
 | 
			
		||||
        <child>
 | 
			
		||||
          <object class="GtkImage">
 | 
			
		||||
            <property name="visible">True</property>
 | 
			
		||||
            <property name="can_focus">False</property>
 | 
			
		||||
            <property name="icon_name">open-menu-symbolic</property>
 | 
			
		||||
          </object>
 | 
			
		||||
        </child>
 | 
			
		||||
      </object>
 | 
			
		||||
      <packing>
 | 
			
		||||
        <property name="pack_type">end</property>
 | 
			
		||||
      </packing>
 | 
			
		||||
    </child>
 | 
			
		||||
  </object>
 | 
			
		||||
</interface>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,28 +1,31 @@
 | 
			
		|||
/* Gajim Application CSS File */
 | 
			
		||||
 | 
			
		||||
.msgtextview-button { 
 | 
			
		||||
 | 
			
		||||
.chatcontrol-actionbar-button { 
 | 
			
		||||
	padding: 0px 5px 0px 5px;
 | 
			
		||||
	background-color: @theme_base_color; 
 | 
			
		||||
	border: 1px solid;
 | 
			
		||||
	border-radius: 0px; 
 | 
			
		||||
	border-color: @borders;
 | 
			
		||||
	border: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
.msgtextview-button:hover, .msgtextview-button:checked { 
 | 
			
		||||
	color: @theme_base_color;
 | 
			
		||||
	border-color: @borders;
 | 
			
		||||
	text-shadow: none;
 | 
			
		||||
	-gtk-icon-shadow: none;
 | 
			
		||||
	box-shadow: none;
 | 
			
		||||
.scrolled-no-border {border: none}
 | 
			
		||||
.scrolled-no-border undershoot.top, undershoot.bottom { background-image: none; }
 | 
			
		||||
 | 
			
		||||
.actionbar-no-border box {border: none}
 | 
			
		||||
 | 
			
		||||
.actionbar-no-border button { 
 | 
			
		||||
	padding: 0px;
 | 
			
		||||
	background-color: @theme_base_color; 
 | 
			
		||||
	border: none;
 | 
			
		||||
	background-image: none;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#MessageWindow, #RosterWindow paned { background-color:  @theme_base_color; }
 | 
			
		||||
 | 
			
		||||
.msgtextview-button.left { border-right: none; }
 | 
			
		||||
.msgtextview-button.right { border-left: none; }
 | 
			
		||||
.scrolledtextview { border:none; }
 | 
			
		||||
 | 
			
		||||
.scrolledtextview { border-left:none; }
 | 
			
		||||
.scrolledtextview.authentication { border-right:none; }
 | 
			
		||||
.chatcontrol-separator {margin-bottom: 6px;}
 | 
			
		||||
 | 
			
		||||
#SubjectPopover box { padding: 10px; }
 | 
			
		||||
 | 
			
		||||
/* VCardWindow */
 | 
			
		||||
.VCard-GtkLinkButton { padding-left: 5px; border-left: none; }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ from gajim import gtkgui_helpers
 | 
			
		|||
from gajim import vcard
 | 
			
		||||
from gajim import conversation_textview
 | 
			
		||||
from gajim import dataforms_widget
 | 
			
		||||
from gajim import gtkspell
 | 
			
		||||
 | 
			
		||||
from random import randrange
 | 
			
		||||
from gajim.common import pep
 | 
			
		||||
| 
						 | 
				
			
			@ -50,12 +51,6 @@ from gajim.common import const
 | 
			
		|||
from gajim.options_dialog import OptionsDialog
 | 
			
		||||
from gajim.common.const import Option, OptionKind, OptionType
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    from gajim import gtkspell
 | 
			
		||||
    HAS_GTK_SPELL = True
 | 
			
		||||
except (ImportError, ValueError):
 | 
			
		||||
    HAS_GTK_SPELL = False
 | 
			
		||||
 | 
			
		||||
# those imports are not used in this file, but in files that 'import dialogs'
 | 
			
		||||
# so they can do dialog.GajimThemesWindow() for example
 | 
			
		||||
from gajim.filetransfers_window import FileTransfersWindow
 | 
			
		||||
| 
						 | 
				
			
			@ -1486,11 +1481,11 @@ class FileChooserDialog(Gtk.FileChooserDialog):
 | 
			
		|||
class AspellDictError:
 | 
			
		||||
    def __init__(self, lang):
 | 
			
		||||
        ErrorDialog(
 | 
			
		||||
            _('Dictionary for lang %s not available') % lang,
 | 
			
		||||
            _('You have to install %s dictionary to use spellchecking, or '
 | 
			
		||||
            'choose another language by setting the speller_language option.'
 | 
			
		||||
            '\n\nHighlighting misspelled words feature will not be used') % lang)
 | 
			
		||||
        app.config.set('use_speller', False)
 | 
			
		||||
            _('Dictionary for lang "%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):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			@ -3082,14 +3077,14 @@ class SingleMessageWindow:
 | 
			
		|||
        else:
 | 
			
		||||
            self.to_entry.set_text(to)
 | 
			
		||||
 | 
			
		||||
        if app.config.get('use_speller') and HAS_GTK_SPELL and action == 'send':
 | 
			
		||||
        if app.config.get('use_speller') and gtkspell.HAS_GTK_SPELL and action == 'send':
 | 
			
		||||
            try:
 | 
			
		||||
                lang = app.config.get('speller_language')
 | 
			
		||||
                if not lang:
 | 
			
		||||
                    lang = app.LANG
 | 
			
		||||
                gtkspell.Spell(self.conversation_textview.tv, lang)
 | 
			
		||||
                gtkspell.Spell(self.message_textview, lang)
 | 
			
		||||
            except (GObject.GError, TypeError, RuntimeError, OSError):
 | 
			
		||||
                self.spell = gtkspell.Spell(self.message_textview, lang)
 | 
			
		||||
                self.spell.attach(self.message_textview)
 | 
			
		||||
            except OSError:
 | 
			
		||||
                AspellDictError(lang)
 | 
			
		||||
 | 
			
		||||
        self.prepare_widgets_for(self.action)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -289,6 +289,7 @@ class EmoticonPopover(Gtk.Popover):
 | 
			
		|||
            self.append_emoticon(child.get_child().get_text())
 | 
			
		||||
 | 
			
		||||
    def append_emoticon(self, pix):
 | 
			
		||||
        self.text_widget.remove_placeholder()
 | 
			
		||||
        buffer_ = self.text_widget.get_buffer()
 | 
			
		||||
        if buffer_.get_char_count():
 | 
			
		||||
            buffer_.insert_at_cursor(' ')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -212,8 +212,15 @@ class GajimApplication(Gtk.Application):
 | 
			
		|||
        builder = Gtk.Builder()
 | 
			
		||||
        builder.set_translation_domain(i18n.APP)
 | 
			
		||||
        builder.add_from_file(path)
 | 
			
		||||
        self.set_menubar(builder.get_object("menubar"))
 | 
			
		||||
        self.set_app_menu(builder.get_object("appmenu"))
 | 
			
		||||
        menubar = builder.get_object("menubar")
 | 
			
		||||
        appmenu = builder.get_object("appmenu")
 | 
			
		||||
        if os.name != 'nt':
 | 
			
		||||
            self.set_app_menu(appmenu)
 | 
			
		||||
        else:
 | 
			
		||||
            # Dont set Application Menu for Windows
 | 
			
		||||
            # Add it to the menubar instead
 | 
			
		||||
            menubar.prepend_submenu('Gajim', appmenu)
 | 
			
		||||
        self.set_menubar(menubar)
 | 
			
		||||
 | 
			
		||||
    def do_activate(self):
 | 
			
		||||
        Gtk.Application.do_activate(self)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@ from gi.repository import Gdk
 | 
			
		|||
from gi.repository import GdkPixbuf
 | 
			
		||||
from gi.repository import Pango
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from gi.repository import Gio
 | 
			
		||||
from gajim import gtkgui_helpers
 | 
			
		||||
from gajim import gui_menu_builder
 | 
			
		||||
from gajim import message_control
 | 
			
		||||
| 
						 | 
				
			
			@ -251,15 +252,6 @@ class PrivateChatControl(ChatControl):
 | 
			
		|||
            return
 | 
			
		||||
        self.show_avatar()
 | 
			
		||||
 | 
			
		||||
    def show_avatar(self):
 | 
			
		||||
        if not app.config.get('show_avatar_in_chat'):
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        pixbuf = app.interface.get_avatar(
 | 
			
		||||
            self.gc_contact.avatar_sha, AvatarSize.CHAT)
 | 
			
		||||
        image = self.xml.get_object('avatar_image')
 | 
			
		||||
        image.set_from_pixbuf(pixbuf)
 | 
			
		||||
 | 
			
		||||
    def update_contact(self):
 | 
			
		||||
        self.contact = self.gc_contact.as_contact()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -274,20 +266,6 @@ class PrivateChatControl(ChatControl):
 | 
			
		|||
 | 
			
		||||
        self.session.negotiate_e2e(False)
 | 
			
		||||
 | 
			
		||||
    def prepare_context_menu(self, hide_buttonbar_items=False):
 | 
			
		||||
        """
 | 
			
		||||
        Set compact view menuitem active state sets active and sensitivity state
 | 
			
		||||
        for history_menuitem (False for tranasports) and file_transfer_menuitem 
 | 
			
		||||
        and hide()/show() for add_to_roster_menuitem
 | 
			
		||||
        """
 | 
			
		||||
        menu = gui_menu_builder.get_contact_menu(self.contact, self.account,
 | 
			
		||||
            use_multiple_contacts=False, show_start_chat=False,
 | 
			
		||||
            show_encryption=True, control=self,
 | 
			
		||||
            show_buttonbar_items=not hide_buttonbar_items,
 | 
			
		||||
            gc_contact=self.gc_contact,
 | 
			
		||||
            is_anonymous=self.room_ctrl.is_anonymous)
 | 
			
		||||
        return menu
 | 
			
		||||
 | 
			
		||||
    def got_disconnected(self):
 | 
			
		||||
        ChatControl.got_disconnected(self)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -317,56 +295,18 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        # Keep error dialog instance to be sure to have only once at a time
 | 
			
		||||
        self.error_dialog = None
 | 
			
		||||
 | 
			
		||||
        self.actions_button = self.xml.get_object('muc_window_actions_button')
 | 
			
		||||
        id_ = self.actions_button.connect('clicked',
 | 
			
		||||
            self.on_actions_button_clicked)
 | 
			
		||||
        self.handlers[id_] = self.actions_button
 | 
			
		||||
 | 
			
		||||
        self.emoticons_button = self.xml.get_object('emoticons_button')
 | 
			
		||||
        self.toggle_emoticons()
 | 
			
		||||
 | 
			
		||||
        widget = self.xml.get_object('change_nick_button')
 | 
			
		||||
        widget.set_sensitive(False)
 | 
			
		||||
        id_ = widget.connect('clicked', self._on_change_nick_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = widget
 | 
			
		||||
 | 
			
		||||
        widget = self.xml.get_object('change_subject_button')
 | 
			
		||||
        widget.set_sensitive(False)
 | 
			
		||||
        id_ = widget.connect('clicked',
 | 
			
		||||
            self._on_change_subject_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = widget
 | 
			
		||||
 | 
			
		||||
        formattings_button = self.xml.get_object('formattings_button')
 | 
			
		||||
        formattings_button.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
        widget = self.xml.get_object('bookmark_button')
 | 
			
		||||
        for bm in app.connections[self.account].bookmarks:
 | 
			
		||||
            if bm['jid'] == self.contact.jid:
 | 
			
		||||
                widget.hide()
 | 
			
		||||
                break
 | 
			
		||||
        else:
 | 
			
		||||
            id_ = widget.connect('clicked',
 | 
			
		||||
                self._on_bookmark_room_menuitem_activate)
 | 
			
		||||
            self.handlers[id_] = widget
 | 
			
		||||
 | 
			
		||||
            if gtkgui_helpers.gtk_icon_theme.has_icon('bookmark-new'):
 | 
			
		||||
                img = self.xml.get_object('image7')
 | 
			
		||||
                img.set_from_icon_name('bookmark-new', Gtk.IconSize.MENU)
 | 
			
		||||
            widget.set_sensitive(
 | 
			
		||||
                app.connections[self.account].private_storage_supported or \
 | 
			
		||||
                (app.connections[self.account].pep_supported and \
 | 
			
		||||
                app.connections[self.account].pubsub_publish_options_supported))
 | 
			
		||||
            widget.show()
 | 
			
		||||
 | 
			
		||||
        if gtkgui_helpers.gtk_icon_theme.has_icon('document-open-recent'):
 | 
			
		||||
            img = self.xml.get_object('history_image')
 | 
			
		||||
            img.set_from_icon_name('document-open-recent', Gtk.IconSize.MENU)
 | 
			
		||||
 | 
			
		||||
        self.current_tooltip = None
 | 
			
		||||
        if parent_win is not None:
 | 
			
		||||
            # On AutoJoin with minimize Groupchats are created without parent
 | 
			
		||||
            # Tooltip Window has to be created with parent
 | 
			
		||||
            # Tooltip Window and Actions have to be created with parent
 | 
			
		||||
            self.set_tooltip()
 | 
			
		||||
            self.add_actions()
 | 
			
		||||
 | 
			
		||||
        widget = self.xml.get_object('list_treeview')
 | 
			
		||||
        id_ = widget.connect('row_expanded', self.on_list_treeview_row_expanded)
 | 
			
		||||
| 
						 | 
				
			
			@ -399,8 +339,6 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        if not self.name:
 | 
			
		||||
            self.name = self.room_jid.split('@')[0]
 | 
			
		||||
 | 
			
		||||
        compact_view = app.config.get('compact_view')
 | 
			
		||||
        self.chat_buttons_set_visible(compact_view)
 | 
			
		||||
        self.widget_set_visible(self.xml.get_object('banner_eventbox'),
 | 
			
		||||
            app.config.get('hide_groupchat_banner'))
 | 
			
		||||
        self.widget_set_visible(self.xml.get_object('list_scrolledwindow'),
 | 
			
		||||
| 
						 | 
				
			
			@ -444,6 +382,10 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        id_ = self.hpaned.connect('notify', self.on_hpaned_notify)
 | 
			
		||||
        self.handlers[id_] = self.hpaned
 | 
			
		||||
 | 
			
		||||
        # Hide the Roster per default
 | 
			
		||||
        self.hpaned.get_child2().set_no_show_all(True)
 | 
			
		||||
        self.hpaned.get_child2().hide()
 | 
			
		||||
 | 
			
		||||
        # set the position of the current hpaned
 | 
			
		||||
        hpaned_position = app.config.get('gc-hpaned-position')
 | 
			
		||||
        self.hpaned.set_position(hpaned_position)
 | 
			
		||||
| 
						 | 
				
			
			@ -516,6 +458,22 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
            gui_menu_builder.get_encryption_menu(self.control_id, self.type_id))
 | 
			
		||||
        self.set_encryption_menu_icon()
 | 
			
		||||
 | 
			
		||||
        # Banner
 | 
			
		||||
        self.banner_actionbar = self.xml.get_object('banner_actionbar')
 | 
			
		||||
        self.hide_roster_button = Gtk.Button.new_from_icon_name(
 | 
			
		||||
            'go-previous-symbolic', Gtk.IconSize.MENU)
 | 
			
		||||
        self.hide_roster_button.connect('clicked',
 | 
			
		||||
                                        lambda *args: self.show_roster())
 | 
			
		||||
        self.subject_button = Gtk.MenuButton()
 | 
			
		||||
        self.subject_button.set_image(Gtk.Image.new_from_icon_name(
 | 
			
		||||
            'go-down-symbolic', Gtk.IconSize.MENU))
 | 
			
		||||
        self.subject_button.set_popover(SubjectPopover())
 | 
			
		||||
        self.subject_button.set_no_show_all(True)
 | 
			
		||||
        self.banner_actionbar.pack_end(self.hide_roster_button)
 | 
			
		||||
        self.banner_actionbar.pack_start(self.subject_button)
 | 
			
		||||
 | 
			
		||||
        self.control_menu = gui_menu_builder.get_groupchat_menu(self.control_id)
 | 
			
		||||
 | 
			
		||||
        app.ged.register_event_handler('gc-presence-received', ged.GUI1,
 | 
			
		||||
            self._nec_gc_presence_received)
 | 
			
		||||
        app.ged.register_event_handler('gc-message-received', ged.GUI1,
 | 
			
		||||
| 
						 | 
				
			
			@ -538,14 +496,199 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
 | 
			
		||||
        self.update_ui()
 | 
			
		||||
        self.widget.show_all()
 | 
			
		||||
 | 
			
		||||
        # PluginSystem: adding GUI extension point for this GroupchatControl
 | 
			
		||||
        # instance object
 | 
			
		||||
        app.plugin_manager.gui_extension_point('groupchat_control', self)
 | 
			
		||||
 | 
			
		||||
    def add_actions(self):
 | 
			
		||||
        actions = [
 | 
			
		||||
            ('change-subject-', self._on_change_subject),
 | 
			
		||||
            ('change-nick-', self._on_change_nick),
 | 
			
		||||
            ('disconnect-', self._on_disconnect),
 | 
			
		||||
            ('destroy-', self._on_destroy_room),
 | 
			
		||||
            ('configure-', self._on_configure_room),
 | 
			
		||||
            ('bookmark-', self._on_bookmark_room),
 | 
			
		||||
            ('request-voice-', self._on_request_voice),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        for action in actions:
 | 
			
		||||
            action_name, func = action
 | 
			
		||||
            act = Gio.SimpleAction.new(action_name + self.control_id, None)
 | 
			
		||||
            act.connect("activate", func)
 | 
			
		||||
            self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
        non_minimized_gc = app.config.get_per(
 | 
			
		||||
            'accounts', self.account, 'non_minimized_gc').split()
 | 
			
		||||
        value = self.contact.jid not in non_minimized_gc
 | 
			
		||||
 | 
			
		||||
        act = Gio.SimpleAction.new_stateful(
 | 
			
		||||
            'minimize-' + self.control_id, None,
 | 
			
		||||
            GLib.Variant.new_boolean(value))
 | 
			
		||||
        act.connect('change-state', self._on_minimize)
 | 
			
		||||
        self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
        value = app.config.get_per(
 | 
			
		||||
            'rooms', self.contact.jid, 'notify_on_all_messages')
 | 
			
		||||
 | 
			
		||||
        act = Gio.SimpleAction.new_stateful(
 | 
			
		||||
            'notify-on-message-' + self.control_id,
 | 
			
		||||
            None, GLib.Variant.new_boolean(value))
 | 
			
		||||
        act.connect('change-state', self._on_notify_on_all_messages)
 | 
			
		||||
        self.parent_win.window.add_action(act)
 | 
			
		||||
 | 
			
		||||
    def update_actions(self):
 | 
			
		||||
        if self.parent_win is None:
 | 
			
		||||
            return
 | 
			
		||||
        win = self.parent_win.window
 | 
			
		||||
        contact = app.contacts.get_gc_contact(
 | 
			
		||||
            self.account, self.room_jid, self.nick)
 | 
			
		||||
        online = app.gc_connected[self.account][self.room_jid]
 | 
			
		||||
 | 
			
		||||
        # Destroy Room
 | 
			
		||||
        win.lookup_action('destroy-' + self.control_id).set_enabled(
 | 
			
		||||
            online and contact.affiliation == 'owner')
 | 
			
		||||
 | 
			
		||||
        # Configure Room
 | 
			
		||||
        win.lookup_action('configure-' + self.control_id).set_enabled(
 | 
			
		||||
            online and contact.affiliation in ('admin', 'owner'))
 | 
			
		||||
 | 
			
		||||
        # Bookmarks
 | 
			
		||||
        con = app.connections[self.account]
 | 
			
		||||
        bookmark_support = con.bookmarks_available()
 | 
			
		||||
        not_bookmarked = True
 | 
			
		||||
        for bm in con.bookmarks:
 | 
			
		||||
            if bm['jid'] == self.room_jid:
 | 
			
		||||
                not_bookmarked = False
 | 
			
		||||
                break
 | 
			
		||||
        win.lookup_action('bookmark-' + self.control_id).set_enabled(
 | 
			
		||||
            online and bookmark_support and not_bookmarked)
 | 
			
		||||
 | 
			
		||||
        # Request Voice
 | 
			
		||||
        role = self.get_role(self.nick)
 | 
			
		||||
        win.lookup_action('request-voice-' + self.control_id).set_enabled(
 | 
			
		||||
            online and role == 'visitor')
 | 
			
		||||
 | 
			
		||||
        # Change Subject
 | 
			
		||||
        # Get this from Room Disco
 | 
			
		||||
        win.lookup_action('change-subject-' + self.control_id).set_enabled(
 | 
			
		||||
            online)
 | 
			
		||||
 | 
			
		||||
        # Change Nick
 | 
			
		||||
        win.lookup_action('change-nick-' + self.control_id).set_enabled(
 | 
			
		||||
            online)
 | 
			
		||||
 | 
			
		||||
    # Actions
 | 
			
		||||
 | 
			
		||||
    def _on_change_subject(self, action, param):
 | 
			
		||||
        def on_ok(subject):
 | 
			
		||||
            # Note, we don't update self.subject since we don't know whether it
 | 
			
		||||
            # will work yet
 | 
			
		||||
            app.connections[self.account].send_gc_subject(
 | 
			
		||||
                self.room_jid, subject)
 | 
			
		||||
 | 
			
		||||
        dialogs.InputTextDialog(_('Changing Subject'),
 | 
			
		||||
            _('Please specify the new subject:'), input_str=self.subject,
 | 
			
		||||
            ok_handler=on_ok, transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_change_nick(self, action, param):
 | 
			
		||||
        if 'change_nick_dialog' in app.interface.instances:
 | 
			
		||||
            app.interface.instances['change_nick_dialog'].dialog.present()
 | 
			
		||||
        else:
 | 
			
		||||
            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,
 | 
			
		||||
                prompt, change_nick=True, transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_disconnect(self, action, param):
 | 
			
		||||
        self.force_non_minimizable = True
 | 
			
		||||
        self.parent_win.remove_tab(self, self.parent_win.CLOSE_COMMAND)
 | 
			
		||||
        self.force_non_minimizable = False
 | 
			
		||||
 | 
			
		||||
    def _on_destroy_room(self, action, param):
 | 
			
		||||
        def on_ok(reason, jid):
 | 
			
		||||
            if jid:
 | 
			
		||||
                # Test jid
 | 
			
		||||
                try:
 | 
			
		||||
                    jid = helpers.parse_jid(jid)
 | 
			
		||||
                except Exception:
 | 
			
		||||
                    dialogs.ErrorDialog(_('Invalid group chat JID'),
 | 
			
		||||
                    _('The group chat JID has not allowed characters.'))
 | 
			
		||||
                    return
 | 
			
		||||
            app.connections[self.account].destroy_gc_room(
 | 
			
		||||
                self.room_jid, reason, jid)
 | 
			
		||||
 | 
			
		||||
        # Ask for a reason
 | 
			
		||||
        dialogs.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,
 | 
			
		||||
            transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_configure_room(self, action, param):
 | 
			
		||||
        c = app.contacts.get_gc_contact(
 | 
			
		||||
            self.account, self.room_jid, self.nick)
 | 
			
		||||
        if c.affiliation == 'owner':
 | 
			
		||||
            app.connections[self.account].request_gc_config(self.room_jid)
 | 
			
		||||
        elif c.affiliation == 'admin':
 | 
			
		||||
            if self.room_jid not in app.interface.instances[self.account][
 | 
			
		||||
            'gc_config']:
 | 
			
		||||
                app.interface.instances[self.account]['gc_config'][
 | 
			
		||||
                    self.room_jid] = config.GroupchatConfigWindow(self.account,
 | 
			
		||||
                    self.room_jid)
 | 
			
		||||
 | 
			
		||||
    def _on_bookmark_room(self, action, param):
 | 
			
		||||
        """
 | 
			
		||||
        Bookmark the room, without autojoin and not minimized
 | 
			
		||||
        """
 | 
			
		||||
        password = app.gc_passwords.get(self.room_jid, '')
 | 
			
		||||
        app.interface.add_gc_bookmark(
 | 
			
		||||
            self.account, self.name, self.room_jid,
 | 
			
		||||
            '0', '0', password, self.nick)
 | 
			
		||||
 | 
			
		||||
    def _on_request_voice(self, action, param):
 | 
			
		||||
        """
 | 
			
		||||
        Request voice in the current room
 | 
			
		||||
        """
 | 
			
		||||
        app.connections[self.account].request_voice(self.room_jid)
 | 
			
		||||
 | 
			
		||||
    def _on_minimize(self, action, param):
 | 
			
		||||
        """
 | 
			
		||||
        When a grouchat is minimized, unparent the tab, put it in roster etc
 | 
			
		||||
        """
 | 
			
		||||
        action.set_state(param)
 | 
			
		||||
        non_minimized_gc = app.config.get_per(
 | 
			
		||||
            'accounts', self.account, 'non_minimized_gc').split()
 | 
			
		||||
 | 
			
		||||
        minimize = param.get_boolean()
 | 
			
		||||
        if minimize:
 | 
			
		||||
            non_minimized_gc.remove(self.contact.jid)
 | 
			
		||||
        else:
 | 
			
		||||
            non_minimized_gc.append(self.contact.jid)
 | 
			
		||||
 | 
			
		||||
        app.config.set_per('accounts', self.account,
 | 
			
		||||
                           'non_minimized_gc', ' '.join(non_minimized_gc))
 | 
			
		||||
 | 
			
		||||
    def _on_notify_on_all_messages(self, action, param):
 | 
			
		||||
        action.set_state(param)
 | 
			
		||||
        app.config.set_per('rooms', self.contact.jid,
 | 
			
		||||
                           'notify_on_all_messages', param.get_boolean())
 | 
			
		||||
 | 
			
		||||
    def show_roster(self):
 | 
			
		||||
        new_state = not self.hpaned.get_child2().is_visible()
 | 
			
		||||
        image = self.hide_roster_button.get_image()
 | 
			
		||||
        if new_state:
 | 
			
		||||
            self.hpaned.get_child2().show()
 | 
			
		||||
            image.set_from_icon_name('go-next-symbolic', Gtk.IconSize.MENU)
 | 
			
		||||
        else:
 | 
			
		||||
            self.hpaned.get_child2().hide()
 | 
			
		||||
            image.set_from_icon_name('go-previous-symbolic', Gtk.IconSize.MENU)
 | 
			
		||||
 | 
			
		||||
    def on_groupchat_maximize(self):
 | 
			
		||||
        self.set_tooltip()
 | 
			
		||||
        self.add_window_actions()
 | 
			
		||||
        self.add_actions()
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
        self.set_lock_image()
 | 
			
		||||
        self._schedule_activity_timers()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -823,10 +966,6 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        self.authentication_button.set_tooltip_text(tooltip)
 | 
			
		||||
        self.widget_set_visible(self.authentication_button, not visible)
 | 
			
		||||
        context = self.msg_scrolledwindow.get_style_context()
 | 
			
		||||
        if visible:
 | 
			
		||||
            context.add_class('authentication')
 | 
			
		||||
        else:
 | 
			
		||||
            context.remove_class('authentication')
 | 
			
		||||
        self.lock_image.set_sensitive(visible)
 | 
			
		||||
 | 
			
		||||
    def _on_authentication_button_clicked(self, widget):
 | 
			
		||||
| 
						 | 
				
			
			@ -886,7 +1025,6 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        room jid, subject
 | 
			
		||||
        """
 | 
			
		||||
        self.name_label.set_ellipsize(Pango.EllipsizeMode.END)
 | 
			
		||||
        self.banner_status_label.set_ellipsize(Pango.EllipsizeMode.END)
 | 
			
		||||
        font_attrs, font_attrs_small = self.get_font_attrs()
 | 
			
		||||
        if self.is_continued:
 | 
			
		||||
            name = self.get_continued_conversation_name()
 | 
			
		||||
| 
						 | 
				
			
			@ -896,169 +1034,10 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        self.name_label.set_markup(text)
 | 
			
		||||
 | 
			
		||||
        if self.subject:
 | 
			
		||||
            subject = helpers.reduce_chars_newlines(self.subject, max_lines=2)
 | 
			
		||||
            subject = GLib.markup_escape_text(subject)
 | 
			
		||||
            subject = GLib.markup_escape_text(self.subject)
 | 
			
		||||
            subject_text = self.urlfinder.sub(self.make_href, subject)
 | 
			
		||||
            subject_text = '<span %s>%s</span>' % (font_attrs_small,
 | 
			
		||||
                subject_text)
 | 
			
		||||
 | 
			
		||||
            # tooltip must always hold ALL the subject
 | 
			
		||||
            self.event_box.set_tooltip_text(self.subject)
 | 
			
		||||
            self.banner_status_label.set_no_show_all(False)
 | 
			
		||||
            self.banner_status_label.show()
 | 
			
		||||
        else:
 | 
			
		||||
            subject_text = ''
 | 
			
		||||
            self.event_box.set_has_tooltip(False)
 | 
			
		||||
            self.banner_status_label.hide()
 | 
			
		||||
            self.banner_status_label.set_no_show_all(True)
 | 
			
		||||
 | 
			
		||||
        self.banner_status_label.set_markup(subject_text)
 | 
			
		||||
 | 
			
		||||
    def prepare_context_menu(self, hide_buttonbar_items=False):
 | 
			
		||||
        """
 | 
			
		||||
        Set sensitivity state for configure_room
 | 
			
		||||
        """
 | 
			
		||||
        xml = gtkgui_helpers.get_gtk_builder('gc_control_popup_menu.ui')
 | 
			
		||||
        menu = xml.get_object('gc_control_popup_menu')
 | 
			
		||||
 | 
			
		||||
        bookmark_room_menuitem = xml.get_object('bookmark_room_menuitem')
 | 
			
		||||
        change_nick_menuitem = xml.get_object('change_nick_menuitem')
 | 
			
		||||
        configure_room_menuitem = xml.get_object('configure_room_menuitem')
 | 
			
		||||
        destroy_room_menuitem = xml.get_object('destroy_room_menuitem')
 | 
			
		||||
        change_subject_menuitem = xml.get_object('change_subject_menuitem')
 | 
			
		||||
        history_menuitem = xml.get_object('history_menuitem')
 | 
			
		||||
        disconnect_menuitem = xml.get_object('disconnect_menuitem')
 | 
			
		||||
        minimize_menuitem = xml.get_object('minimize_menuitem')
 | 
			
		||||
        notify_menuitem = xml.get_object('notify_menuitem')
 | 
			
		||||
        request_voice_menuitem = xml.get_object('request_voice_menuitem')
 | 
			
		||||
        bookmark_separator = xml.get_object('bookmark_separator')
 | 
			
		||||
        separatormenuitem2 = xml.get_object('separatormenuitem2')
 | 
			
		||||
        request_voice_separator = xml.get_object('request_voice_separator')
 | 
			
		||||
 | 
			
		||||
        if hide_buttonbar_items:
 | 
			
		||||
            change_nick_menuitem.hide()
 | 
			
		||||
            change_subject_menuitem.hide()
 | 
			
		||||
            bookmark_room_menuitem.hide()
 | 
			
		||||
            history_menuitem.hide()
 | 
			
		||||
            bookmark_separator.hide()
 | 
			
		||||
            separatormenuitem2.hide()
 | 
			
		||||
        else:
 | 
			
		||||
            change_nick_menuitem.show()
 | 
			
		||||
            change_subject_menuitem.show()
 | 
			
		||||
            bookmark_room_menuitem.show()
 | 
			
		||||
            history_menuitem.show()
 | 
			
		||||
            bookmark_separator.show()
 | 
			
		||||
            separatormenuitem2.show()
 | 
			
		||||
            for bm in app.connections[self.account].bookmarks:
 | 
			
		||||
                if bm['jid'] == self.room_jid:
 | 
			
		||||
                    bookmark_room_menuitem.hide()
 | 
			
		||||
                    bookmark_separator.hide()
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
        ag = Gtk.accel_groups_from_object(self.parent_win.window)[0]
 | 
			
		||||
        change_nick_menuitem.add_accelerator('activate', ag, Gdk.KEY_n,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE)
 | 
			
		||||
        change_subject_menuitem.add_accelerator('activate', ag,
 | 
			
		||||
            Gdk.KEY_t, Gdk.ModifierType.MOD1_MASK, Gtk.AccelFlags.VISIBLE)
 | 
			
		||||
        bookmark_room_menuitem.add_accelerator('activate', ag, Gdk.KEY_b,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE)
 | 
			
		||||
        history_menuitem.add_accelerator('activate', ag, Gdk.KEY_h,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK, Gtk.AccelFlags.VISIBLE)
 | 
			
		||||
 | 
			
		||||
        if self.contact.jid not in app.config.get_per('accounts', self.account,
 | 
			
		||||
        'non_minimized_gc').split(' '):
 | 
			
		||||
            minimize_menuitem.set_active(True)
 | 
			
		||||
        notify_menuitem.set_active(app.config.get_per('rooms', self.contact.jid,
 | 
			
		||||
            'notify_on_all_messages'))
 | 
			
		||||
        conn = app.connections[self.account]
 | 
			
		||||
        if not conn.private_storage_supported and (not conn.pep_supported or \
 | 
			
		||||
        not conn.pubsub_publish_options_supported):
 | 
			
		||||
            bookmark_room_menuitem.set_sensitive(False)
 | 
			
		||||
        if app.gc_connected[self.account][self.room_jid]:
 | 
			
		||||
            c = app.contacts.get_gc_contact(self.account, self.room_jid,
 | 
			
		||||
                self.nick)
 | 
			
		||||
            if c.affiliation not in ('owner', 'admin'):
 | 
			
		||||
                configure_room_menuitem.set_sensitive(False)
 | 
			
		||||
            else:
 | 
			
		||||
                configure_room_menuitem.set_sensitive(True)
 | 
			
		||||
            if c.affiliation != 'owner':
 | 
			
		||||
                destroy_room_menuitem.set_sensitive(False)
 | 
			
		||||
            else:
 | 
			
		||||
                destroy_room_menuitem.set_sensitive(True)
 | 
			
		||||
            change_subject_menuitem.set_sensitive(True)
 | 
			
		||||
            change_nick_menuitem.set_sensitive(True)
 | 
			
		||||
            if c.role == 'visitor':
 | 
			
		||||
                request_voice_menuitem.set_sensitive(True)
 | 
			
		||||
            else:
 | 
			
		||||
                request_voice_menuitem.set_sensitive(False)
 | 
			
		||||
        else:
 | 
			
		||||
            # We are not connected to this groupchat, disable unusable menuitems
 | 
			
		||||
            configure_room_menuitem.set_sensitive(False)
 | 
			
		||||
            destroy_room_menuitem.set_sensitive(False)
 | 
			
		||||
            change_subject_menuitem.set_sensitive(False)
 | 
			
		||||
            change_nick_menuitem.set_sensitive(False)
 | 
			
		||||
            request_voice_menuitem.set_sensitive(False)
 | 
			
		||||
 | 
			
		||||
        # connect the menuitems to their respective functions
 | 
			
		||||
        id_ = bookmark_room_menuitem.connect('activate',
 | 
			
		||||
            self._on_bookmark_room_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = bookmark_room_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = change_nick_menuitem.connect('activate',
 | 
			
		||||
            self._on_change_nick_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = change_nick_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = configure_room_menuitem.connect('activate',
 | 
			
		||||
            self._on_configure_room_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = configure_room_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = destroy_room_menuitem.connect('activate',
 | 
			
		||||
            self._on_destroy_room_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = destroy_room_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = change_subject_menuitem.connect('activate',
 | 
			
		||||
            self._on_change_subject_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = change_subject_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = history_menuitem.connect('activate',
 | 
			
		||||
            self._on_history_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = history_menuitem
 | 
			
		||||
        
 | 
			
		||||
        id_ = disconnect_menuitem.connect('activate',
 | 
			
		||||
            self._on_disconnect_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = disconnect_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = request_voice_menuitem.connect('activate',
 | 
			
		||||
            self._on_request_voice_menuitem_activate)
 | 
			
		||||
        self.handlers[id_] = request_voice_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = minimize_menuitem.connect('toggled',
 | 
			
		||||
            self.on_minimize_menuitem_toggled)
 | 
			
		||||
        self.handlers[id_] = minimize_menuitem
 | 
			
		||||
 | 
			
		||||
        id_ = notify_menuitem.connect('toggled',
 | 
			
		||||
            self.on_notify_menuitem_toggled)
 | 
			
		||||
        self.handlers[id_] = notify_menuitem
 | 
			
		||||
 | 
			
		||||
        menu.connect('selection-done', self.destroy_menu,
 | 
			
		||||
            change_nick_menuitem, change_subject_menuitem,
 | 
			
		||||
            bookmark_room_menuitem, history_menuitem)
 | 
			
		||||
        return menu
 | 
			
		||||
 | 
			
		||||
    def destroy_menu(self, menu, change_nick_menuitem, change_subject_menuitem,
 | 
			
		||||
    bookmark_room_menuitem, history_menuitem):
 | 
			
		||||
        # destroy accelerators
 | 
			
		||||
        ag = Gtk.accel_groups_from_object(self.parent_win.window)[0]
 | 
			
		||||
        change_nick_menuitem.remove_accelerator(ag, Gdk.KEY_n,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.SHIFT_MASK)
 | 
			
		||||
        change_subject_menuitem.remove_accelerator(ag, Gdk.KEY_t,
 | 
			
		||||
            Gdk.ModifierType.MOD1_MASK)
 | 
			
		||||
        bookmark_room_menuitem.remove_accelerator(ag, Gdk.KEY_b,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK)
 | 
			
		||||
        history_menuitem.remove_accelerator(ag, Gdk.KEY_h,
 | 
			
		||||
            Gdk.ModifierType.CONTROL_MASK)
 | 
			
		||||
        # destroy menu
 | 
			
		||||
        menu.destroy()
 | 
			
		||||
            subject_text = '<span>%s</span>' % subject_text
 | 
			
		||||
            self.subject_button.get_popover().set_text(subject_text)
 | 
			
		||||
 | 
			
		||||
    def _nec_vcard_published(self, obj):
 | 
			
		||||
        if obj.conn.name != self.account:
 | 
			
		||||
| 
						 | 
				
			
			@ -1379,6 +1358,11 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
        else:
 | 
			
		||||
            self.print_conversation(text)
 | 
			
		||||
 | 
			
		||||
        if obj.subject == '':
 | 
			
		||||
            self.subject_button.hide()
 | 
			
		||||
        else:
 | 
			
		||||
            self.subject_button.show()
 | 
			
		||||
 | 
			
		||||
    def _nec_gc_config_changed_received(self, obj):
 | 
			
		||||
        # statuscode is a list
 | 
			
		||||
        # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
 | 
			
		||||
| 
						 | 
				
			
			@ -1467,18 +1451,12 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
 | 
			
		||||
        formattings_button = self.xml.get_object('formattings_button')
 | 
			
		||||
        formattings_button.set_sensitive(True)
 | 
			
		||||
        change_nick_button = self.xml.get_object('change_nick_button')
 | 
			
		||||
        change_nick_button.set_sensitive(True)
 | 
			
		||||
        change_subject_button = self.xml.get_object('change_subject_button')
 | 
			
		||||
        change_subject_button.set_sensitive(True)
 | 
			
		||||
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def got_disconnected(self):
 | 
			
		||||
        formattings_button = self.xml.get_object('formattings_button')
 | 
			
		||||
        formattings_button.set_sensitive(False)
 | 
			
		||||
        change_nick_button = self.xml.get_object('change_nick_button')
 | 
			
		||||
        change_nick_button.set_sensitive(False)
 | 
			
		||||
        change_subject_button = self.xml.get_object('change_subject_button')
 | 
			
		||||
        change_subject_button.set_sensitive(False)
 | 
			
		||||
        self.list_treeview.set_model(None)
 | 
			
		||||
        self.model.clear()
 | 
			
		||||
        nick_list = app.contacts.get_nick_list(self.account, self.room_jid)
 | 
			
		||||
| 
						 | 
				
			
			@ -1512,6 +1490,8 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
            if ar_to:
 | 
			
		||||
                self.autorejoin = GLib.timeout_add_seconds(ar_to, self.rejoin)
 | 
			
		||||
 | 
			
		||||
        self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def rejoin(self):
 | 
			
		||||
        if not self.autorejoin:
 | 
			
		||||
            return False
 | 
			
		||||
| 
						 | 
				
			
			@ -1904,6 +1884,11 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
                    st += ' (' + obj.status + ')'
 | 
			
		||||
                self.print_conversation(st, graphics=False)
 | 
			
		||||
 | 
			
		||||
        # Update Actions
 | 
			
		||||
        if obj.status_code:
 | 
			
		||||
            if '110' in obj.status_code:
 | 
			
		||||
                self.update_actions()
 | 
			
		||||
 | 
			
		||||
    def add_contact_to_roster(self, nick, show, role, affiliation, status,
 | 
			
		||||
    jid='', avatar_sha=None):
 | 
			
		||||
        role_name = helpers.get_uf_role(role, plural=True)
 | 
			
		||||
| 
						 | 
				
			
			@ -2262,67 +2247,6 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
            _('Please specify the new subject:'), input_str=self.subject,
 | 
			
		||||
            ok_handler=on_ok, transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_disconnect_menuitem_activate(self, widget):
 | 
			
		||||
        self.force_non_minimizable = True
 | 
			
		||||
        self.parent_win.remove_tab(self, self.parent_win.CLOSE_COMMAND)
 | 
			
		||||
        self.force_non_minimizable = False
 | 
			
		||||
    
 | 
			
		||||
    def _on_change_nick_menuitem_activate(self, widget):
 | 
			
		||||
        if 'change_nick_dialog' in app.interface.instances:
 | 
			
		||||
            app.interface.instances['change_nick_dialog'].dialog.present()
 | 
			
		||||
        else:
 | 
			
		||||
            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,
 | 
			
		||||
                prompt, change_nick=True, transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_configure_room_menuitem_activate(self, widget):
 | 
			
		||||
        c = app.contacts.get_gc_contact(self.account, self.room_jid,
 | 
			
		||||
            self.nick)
 | 
			
		||||
        if c.affiliation == 'owner':
 | 
			
		||||
            app.connections[self.account].request_gc_config(self.room_jid)
 | 
			
		||||
        elif c.affiliation == 'admin':
 | 
			
		||||
            if self.room_jid not in app.interface.instances[self.account][
 | 
			
		||||
            'gc_config']:
 | 
			
		||||
                app.interface.instances[self.account]['gc_config'][
 | 
			
		||||
                    self.room_jid] = config.GroupchatConfigWindow(self.account,
 | 
			
		||||
                    self.room_jid)
 | 
			
		||||
 | 
			
		||||
    def _on_destroy_room_menuitem_activate(self, widget):
 | 
			
		||||
        def on_ok(reason, jid):
 | 
			
		||||
            if jid:
 | 
			
		||||
                # Test jid
 | 
			
		||||
                try:
 | 
			
		||||
                    jid = helpers.parse_jid(jid)
 | 
			
		||||
                except Exception:
 | 
			
		||||
                    dialogs.ErrorDialog(_('Invalid group chat JID'),
 | 
			
		||||
                    _('The group chat JID has not allowed characters.'))
 | 
			
		||||
                    return
 | 
			
		||||
            app.connections[self.account].destroy_gc_room(self.room_jid,
 | 
			
		||||
                reason, jid)
 | 
			
		||||
 | 
			
		||||
        # Ask for a reason
 | 
			
		||||
        dialogs.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,
 | 
			
		||||
            transient_for=self.parent_win.window)
 | 
			
		||||
 | 
			
		||||
    def _on_bookmark_room_menuitem_activate(self, widget):
 | 
			
		||||
        """
 | 
			
		||||
        Bookmark the room, without autojoin and not minimized
 | 
			
		||||
        """
 | 
			
		||||
        password = app.gc_passwords.get(self.room_jid, '')
 | 
			
		||||
        app.interface.add_gc_bookmark(self.account, self.name, self.room_jid,\
 | 
			
		||||
            '0', '0', password, self.nick)
 | 
			
		||||
 | 
			
		||||
    def _on_request_voice_menuitem_activate(self, widget):
 | 
			
		||||
        """
 | 
			
		||||
        Request voice in the current room
 | 
			
		||||
        """
 | 
			
		||||
        app.connections[self.account].request_voice(self.room_jid)
 | 
			
		||||
 | 
			
		||||
    def _on_drag_data_received(self, widget, context, x, y, selection,
 | 
			
		||||
    target_type, timestamp):
 | 
			
		||||
        # Invite contact to groupchat
 | 
			
		||||
| 
						 | 
				
			
			@ -2913,3 +2837,40 @@ class GroupchatControl(ChatControlBase):
 | 
			
		|||
            self.grant_owner(widget, jid)
 | 
			
		||||
        else:
 | 
			
		||||
            self.revoke_owner(widget, jid)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SubjectPopover(Gtk.Popover):
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        Gtk.Popover.__init__(self)
 | 
			
		||||
        self.set_name('SubjectPopover')
 | 
			
		||||
 | 
			
		||||
        scrolledwindow = Gtk.ScrolledWindow()
 | 
			
		||||
        scrolledwindow.set_max_content_height(250)
 | 
			
		||||
        scrolledwindow.set_propagate_natural_height(True)
 | 
			
		||||
        scrolledwindow.set_propagate_natural_width(True)
 | 
			
		||||
        scrolledwindow.set_policy(Gtk.PolicyType.NEVER,
 | 
			
		||||
                                  Gtk.PolicyType.AUTOMATIC)
 | 
			
		||||
 | 
			
		||||
        self.label = Gtk.Label()
 | 
			
		||||
        self.label.set_line_wrap(True)
 | 
			
		||||
        self.label.set_line_wrap_mode(Pango.WrapMode.WORD_CHAR)
 | 
			
		||||
        self.label.set_max_width_chars(80)
 | 
			
		||||
 | 
			
		||||
        scrolledwindow.add(self.label)
 | 
			
		||||
 | 
			
		||||
        box = Gtk.Box()
 | 
			
		||||
        box.add(scrolledwindow)
 | 
			
		||||
        box.show_all()
 | 
			
		||||
        self.add(box)
 | 
			
		||||
 | 
			
		||||
        self.connect_after('show', self._after_show)
 | 
			
		||||
 | 
			
		||||
    def set_text(self, text):
 | 
			
		||||
        self.label.set_markup(text)
 | 
			
		||||
 | 
			
		||||
    def _after_show(self, *args):
 | 
			
		||||
        # Gtk Bug: If we set selectable True, on show
 | 
			
		||||
        # everything inside the Label is selected.
 | 
			
		||||
        # So we switch after show to False and again to True
 | 
			
		||||
        self.label.set_selectable(False)
 | 
			
		||||
        self.label.set_selectable(True)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,7 @@ class Color:
 | 
			
		|||
    BLACK = Gdk.RGBA(red=0, green=0, blue=0, alpha=1)
 | 
			
		||||
    GREEN = Gdk.RGBA(red=115/255, green=210/255, blue=22/255, alpha=1)
 | 
			
		||||
    RED = Gdk.RGBA(red=204/255, green=0, blue=0, alpha=1)
 | 
			
		||||
    GREY = Gdk.RGBA(red=195/255, green=195/255, blue=192/255, alpha=1)
 | 
			
		||||
 | 
			
		||||
def get_icon_pixmap(icon_name, size=16, color=None, quiet=False):
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,9 +19,17 @@
 | 
			
		|||
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
import gi
 | 
			
		||||
gi.require_version('GtkSpell', '3.0')
 | 
			
		||||
from gi.repository import GtkSpell
 | 
			
		||||
try:
 | 
			
		||||
    from gi.repository import GtkSpell
 | 
			
		||||
    HAS_GTK_SPELL = True
 | 
			
		||||
except ImportError:
 | 
			
		||||
    HAS_GTK_SPELL = False
 | 
			
		||||
 | 
			
		||||
from gajim.common import app
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_attached(func):
 | 
			
		||||
    def f(self, *args, **kwargs):
 | 
			
		||||
| 
						 | 
				
			
			@ -47,12 +55,13 @@ class Spell(GObject.GObject):
 | 
			
		|||
            if spell:
 | 
			
		||||
                raise RuntimeError("Textview has already a Spell obj attached")
 | 
			
		||||
            self.spell = GtkSpell.Checker.new()
 | 
			
		||||
            if not self.spell:
 | 
			
		||||
                raise OSError("Unable to create spell object.")
 | 
			
		||||
            if not self.spell.attach(textview):
 | 
			
		||||
                raise OSError("Unable to attach spell object.")
 | 
			
		||||
            if not self.spell.set_language(language):
 | 
			
		||||
                raise OSError("Unable to set language: '%s'" % language)
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                self.spell.set_language(language)
 | 
			
		||||
            except GLib.GError as error:
 | 
			
		||||
                if error.domain == 'gtkspell-error-quark':
 | 
			
		||||
                    raise OSError("Unable to set language: '%s'" % language)
 | 
			
		||||
 | 
			
		||||
            self.spell.connect('language-changed', self.on_language_changed)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -73,12 +82,25 @@ class Spell(GObject.GObject):
 | 
			
		|||
    def recheck_all(self):
 | 
			
		||||
        self.spell.recheck_all()
 | 
			
		||||
 | 
			
		||||
    @ensure_attached
 | 
			
		||||
    def detach(self):
 | 
			
		||||
        self.spell.detach()
 | 
			
		||||
        self.spell = None
 | 
			
		||||
        if self.spell is not None:
 | 
			
		||||
            self.spell.detach()
 | 
			
		||||
 | 
			
		||||
    def attach(self, textview):
 | 
			
		||||
        spell = GtkSpell.Checker.get_from_text_view(textview)
 | 
			
		||||
        if spell is None:
 | 
			
		||||
            print('attached')
 | 
			
		||||
            self.spell.attach(textview)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GObject.type_register(Spell)
 | 
			
		||||
 | 
			
		||||
def get_from_text_view(textview):
 | 
			
		||||
    return Spell(textview, create=False)
 | 
			
		||||
 | 
			
		||||
def test_language(lang):
 | 
			
		||||
    spell = GtkSpell.Checker.new()
 | 
			
		||||
    try:
 | 
			
		||||
        spell.set_language(lang)
 | 
			
		||||
    except GLib.GError as error:
 | 
			
		||||
        if error.domain == 'gtkspell-error-quark':
 | 
			
		||||
            return False
 | 
			
		||||
    return True
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2858,18 +2858,6 @@ class Interface:
 | 
			
		|||
        # get transports type from DB
 | 
			
		||||
        app.transport_type = app.logger.get_transports_type()
 | 
			
		||||
 | 
			
		||||
        # test is dictionnary is present for speller
 | 
			
		||||
        if app.config.get('use_speller'):
 | 
			
		||||
            lang = app.config.get('speller_language')
 | 
			
		||||
            if not lang:
 | 
			
		||||
                lang = app.LANG
 | 
			
		||||
            tv = Gtk.TextView()
 | 
			
		||||
            try:
 | 
			
		||||
                from gajim import gtkspell
 | 
			
		||||
                spell = gtkspell.Spell(tv, lang)
 | 
			
		||||
            except (ImportError, TypeError, RuntimeError, OSError, ValueError):
 | 
			
		||||
                dialogs.AspellDictError(lang)
 | 
			
		||||
 | 
			
		||||
        if app.config.get('soundplayer') == '':
 | 
			
		||||
            # only on first time Gajim starts
 | 
			
		||||
            commands = ('paplay', 'aplay', 'play', 'ossplay')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -606,6 +606,65 @@ Build dynamic Application Menus
 | 
			
		|||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_singlechat_menu(control_id):
 | 
			
		||||
    singlechat_menu = [
 | 
			
		||||
        ('win.send-file-', _('Send File...')),
 | 
			
		||||
        ('win.invite-contacts-', _('Invite Contacts')),
 | 
			
		||||
        ('win.add-to-roster-', _('Add to Roster')),
 | 
			
		||||
        ('win.toggle-audio-', _('Audio Session')),
 | 
			
		||||
        ('win.toggle-video-', _('Video Session')),
 | 
			
		||||
        ('win.information-', _('Information')),
 | 
			
		||||
        ('win.browse-history-', _('History')),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def build_menu(preset):
 | 
			
		||||
        menu = Gio.Menu()
 | 
			
		||||
        for item in preset:
 | 
			
		||||
            action_name, label = item
 | 
			
		||||
            if action_name == 'win.browse-history-':
 | 
			
		||||
                menu.append(label, action_name + control_id + '::none')
 | 
			
		||||
            else:
 | 
			
		||||
                menu.append(label, action_name + control_id)
 | 
			
		||||
        return menu
 | 
			
		||||
 | 
			
		||||
    return build_menu(singlechat_menu)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_groupchat_menu(control_id):
 | 
			
		||||
    groupchat_menu = [
 | 
			
		||||
        (_('Manage Room'), [
 | 
			
		||||
            ('win.change-subject-', _('Change Subject')),
 | 
			
		||||
            ('win.configure-', _('Configure Room')),
 | 
			
		||||
            ('win.destroy-', _('Destroy Room')),
 | 
			
		||||
            ]),
 | 
			
		||||
        ('win.change-nick-', _('Change Nick')),
 | 
			
		||||
        ('win.bookmark-', _('Bookmark Room')),
 | 
			
		||||
        ('win.request-voice-', _('Request Voice')),
 | 
			
		||||
        ('win.notify-on-message-', _('Notify on all messages')),
 | 
			
		||||
        ('win.minimize-', _('Minimize on close')),
 | 
			
		||||
        ('win.browse-history-', _('History')),
 | 
			
		||||
        ('win.disconnect-', _('Disconnect')),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def build_menu(preset):
 | 
			
		||||
        menu = Gio.Menu()
 | 
			
		||||
        for item in preset:
 | 
			
		||||
            if isinstance(item[1], str):
 | 
			
		||||
                action_name, label = item
 | 
			
		||||
                if action_name == 'win.browse-history-':
 | 
			
		||||
                    menu.append(label, action_name + control_id + '::none')
 | 
			
		||||
                else:
 | 
			
		||||
                    menu.append(label, action_name + control_id)
 | 
			
		||||
            else:
 | 
			
		||||
                label, sub_menu = item
 | 
			
		||||
                # This is a submenu
 | 
			
		||||
                submenu = build_menu(sub_menu)
 | 
			
		||||
                menu.append_submenu(label, submenu)
 | 
			
		||||
        return menu
 | 
			
		||||
 | 
			
		||||
    return build_menu(groupchat_menu)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_bookmarks_menu(account, rebuild=False):
 | 
			
		||||
    if not app.connections[account].bookmarks:
 | 
			
		||||
        return None
 | 
			
		||||
| 
						 | 
				
			
			@ -708,7 +767,11 @@ def get_account_menu(account):
 | 
			
		|||
def build_accounts_menu():
 | 
			
		||||
    menubar = app.app.get_menubar()
 | 
			
		||||
    # Accounts Submenu
 | 
			
		||||
    acc_menu = menubar.get_item_link(0, 'submenu')
 | 
			
		||||
    menu_position = 0
 | 
			
		||||
    if os.name == 'nt':
 | 
			
		||||
        menu_position = 1
 | 
			
		||||
 | 
			
		||||
    acc_menu = menubar.get_item_link(menu_position, 'submenu')
 | 
			
		||||
    acc_menu.remove_all()
 | 
			
		||||
    accounts_list = sorted(app.contacts.get_accounts())
 | 
			
		||||
    if not accounts_list:
 | 
			
		||||
| 
						 | 
				
			
			@ -721,8 +784,8 @@ def build_accounts_menu():
 | 
			
		|||
                acc, get_account_menu(acc))
 | 
			
		||||
    else:
 | 
			
		||||
        acc_menu = get_account_menu(accounts_list[0])
 | 
			
		||||
        menubar.remove(0)
 | 
			
		||||
        menubar.insert_submenu(0, 'Accounts', acc_menu)
 | 
			
		||||
        menubar.remove(menu_position)
 | 
			
		||||
        menubar.insert_submenu(menu_position, 'Accounts', acc_menu)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_bookmark_menu(account):
 | 
			
		||||
| 
						 | 
				
			
			@ -731,8 +794,12 @@ def build_bookmark_menu(account):
 | 
			
		|||
    if not bookmark_menu:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    menu_position = 0
 | 
			
		||||
    if os.name == 'nt':
 | 
			
		||||
        menu_position = 1
 | 
			
		||||
 | 
			
		||||
    # Accounts Submenu
 | 
			
		||||
    acc_menu = menubar.get_item_link(0, 'submenu')
 | 
			
		||||
    acc_menu = menubar.get_item_link(menu_position, 'submenu')
 | 
			
		||||
 | 
			
		||||
    # We have more than one Account active
 | 
			
		||||
    if acc_menu.get_item_link(0, 'submenu'):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,6 @@ class MessageControl(object):
 | 
			
		|||
        self.widget_name = widget_name
 | 
			
		||||
        self.contact = contact
 | 
			
		||||
        self.account = account
 | 
			
		||||
        self.hide_chat_buttons = False
 | 
			
		||||
        self.resource = resource
 | 
			
		||||
        # control_id is a unique id for the control,
 | 
			
		||||
        # its used as action name for actions that belong to a control
 | 
			
		||||
| 
						 | 
				
			
			@ -175,12 +174,6 @@ class MessageControl(object):
 | 
			
		|||
        """
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    def chat_buttons_set_visible(self, state):
 | 
			
		||||
        """
 | 
			
		||||
        Derived classes MAY implement this
 | 
			
		||||
        """
 | 
			
		||||
        self.hide_chat_buttons = state
 | 
			
		||||
 | 
			
		||||
    def got_connected(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,7 +24,6 @@
 | 
			
		|||
import gc
 | 
			
		||||
 | 
			
		||||
from gi.repository import Gtk
 | 
			
		||||
from gi.repository import GObject
 | 
			
		||||
from gi.repository import GLib
 | 
			
		||||
from gi.repository import Pango
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +36,7 @@ class MessageTextView(Gtk.TextView):
 | 
			
		|||
    chat/groupchat windows
 | 
			
		||||
    """
 | 
			
		||||
    UNDO_LIMIT = 20
 | 
			
		||||
    PLACEHOLDER = _('Write a message..')
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        Gtk.TextView.__init__(self)
 | 
			
		||||
| 
						 | 
				
			
			@ -57,13 +57,16 @@ class MessageTextView(Gtk.TextView):
 | 
			
		|||
        self.undo_list = []
 | 
			
		||||
        # needed to know if we undid something
 | 
			
		||||
        self.undo_pressed = False
 | 
			
		||||
        self.lang = None # Lang used for spell checking
 | 
			
		||||
 | 
			
		||||
        _buffer = self.get_buffer()
 | 
			
		||||
        self.begin_tags = {}
 | 
			
		||||
        self.end_tags = {}
 | 
			
		||||
        self.color_tags = []
 | 
			
		||||
        self.fonts_tags = []
 | 
			
		||||
        self.other_tags = {}
 | 
			
		||||
        self.placeholder_tag = _buffer.create_tag('placeholder')
 | 
			
		||||
        self.placeholder_tag.set_property('foreground_rgba',
 | 
			
		||||
                                      gtkgui_helpers.Color.GREY)
 | 
			
		||||
        self.other_tags['bold'] = _buffer.create_tag('bold')
 | 
			
		||||
        self.other_tags['bold'].set_property('weight', Pango.Weight.BOLD)
 | 
			
		||||
        self.begin_tags['bold'] = '<strong>'
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +85,33 @@ class MessageTextView(Gtk.TextView):
 | 
			
		|||
        self.end_tags['strike'] = '</span>'
 | 
			
		||||
 | 
			
		||||
        self.connect_after('paste-clipboard', self.after_paste_clipboard)
 | 
			
		||||
        self.connect('focus-in-event', self._on_focus_in)
 | 
			
		||||
        self.connect('focus-out-event', self._on_focus_out)
 | 
			
		||||
 | 
			
		||||
        start, end = _buffer.get_bounds()
 | 
			
		||||
        _buffer.insert_with_tags(
 | 
			
		||||
            start, self.PLACEHOLDER, self.placeholder_tag)
 | 
			
		||||
 | 
			
		||||
    def has_text(self):
 | 
			
		||||
        buf = self.get_buffer()
 | 
			
		||||
        start, end = buf.get_bounds()
 | 
			
		||||
        text = buf.get_text(start, end, True)
 | 
			
		||||
        return text != self.PLACEHOLDER
 | 
			
		||||
 | 
			
		||||
    def _on_focus_in(self, *args):
 | 
			
		||||
        if not self.has_text():
 | 
			
		||||
            self.get_buffer().set_text('')
 | 
			
		||||
 | 
			
		||||
    def _on_focus_out(self, *args):
 | 
			
		||||
        buf = self.get_buffer()
 | 
			
		||||
        start, end = buf.get_bounds()
 | 
			
		||||
        text = buf.get_text(start, end, True)
 | 
			
		||||
        if text == '':
 | 
			
		||||
            buf.insert_with_tags(
 | 
			
		||||
                start, self.PLACEHOLDER, self.placeholder_tag)
 | 
			
		||||
 | 
			
		||||
    def remove_placeholder(self):
 | 
			
		||||
        self._on_focus_in()
 | 
			
		||||
 | 
			
		||||
    def after_paste_clipboard(self, textview):
 | 
			
		||||
        buffer_ = textview.get_buffer()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,6 @@ class MessageWindow(object):
 | 
			
		|||
        self.xml = gtkgui_helpers.get_gtk_builder('%s.ui' % self.widget_name)
 | 
			
		||||
        self.window = self.xml.get_object(self.widget_name)
 | 
			
		||||
        self.window.set_application(app.app)
 | 
			
		||||
        self.window.set_show_menubar(False)
 | 
			
		||||
        self.notebook = self.xml.get_object('notebook')
 | 
			
		||||
        self.parent_paned = None
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,17 +93,26 @@ class MessageWindow(object):
 | 
			
		|||
            if app.config.get('roster_on_the_right'):
 | 
			
		||||
                child1 = self.parent_paned.get_child1()
 | 
			
		||||
                self.parent_paned.remove(child1)
 | 
			
		||||
                self.parent_paned.add(self.notebook)
 | 
			
		||||
                self.parent_paned.pack1(self.notebook, resize=False,
 | 
			
		||||
                    shrink=True)
 | 
			
		||||
                self.parent_paned.pack2(child1, resize=True, shrink=True)
 | 
			
		||||
                self.parent_paned.pack1(self.notebook, resize=False)
 | 
			
		||||
                self.parent_paned.pack2(child1)
 | 
			
		||||
            else:
 | 
			
		||||
                self.parent_paned.add(self.notebook)
 | 
			
		||||
                self.parent_paned.pack2(self.notebook, resize=True, shrink=True)
 | 
			
		||||
                self.parent_paned.pack2(self.notebook)
 | 
			
		||||
            self.window.lookup_action('show-roster').set_enabled(True)
 | 
			
		||||
            orig_window.destroy()
 | 
			
		||||
            del orig_window
 | 
			
		||||
 | 
			
		||||
        # Set headermenu
 | 
			
		||||
        # single-window mode: show the header menu on the roster window
 | 
			
		||||
        # all other modes: add the headerbar to the new window
 | 
			
		||||
        # A headerbar has to be set before the window calls show()
 | 
			
		||||
        if parent_window:
 | 
			
		||||
            self.header_menu = app.interface.roster.header_menu
 | 
			
		||||
            self.header_menu.show()
 | 
			
		||||
        else:
 | 
			
		||||
            self.header_menu = self.xml.get_object('header_menu')
 | 
			
		||||
            headerbar = self.xml.get_object('headerbar')
 | 
			
		||||
            self.window.set_titlebar(headerbar)
 | 
			
		||||
 | 
			
		||||
        # NOTE: we use 'connect_after' here because in
 | 
			
		||||
        # MessageWindowMgr._new_window we register handler that saves window
 | 
			
		||||
        # state when closing it, and it should be called before
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +170,9 @@ class MessageWindow(object):
 | 
			
		|||
        self.notebook.set_show_border(app.config.get('tabs_border'))
 | 
			
		||||
        self.show_icon()
 | 
			
		||||
 | 
			
		||||
    def set_header_menu(self, menu):
 | 
			
		||||
        self.header_menu.set_menu_model(menu)
 | 
			
		||||
 | 
			
		||||
    def change_account_name(self, old_name, new_name):
 | 
			
		||||
        if old_name in self._controls:
 | 
			
		||||
            self._controls[new_name] = self._controls[old_name]
 | 
			
		||||
| 
						 | 
				
			
			@ -324,6 +335,7 @@ class MessageWindow(object):
 | 
			
		|||
            self.notebook.show_all()
 | 
			
		||||
        else:
 | 
			
		||||
            self.window.show_all()
 | 
			
		||||
 | 
			
		||||
        # NOTE: we do not call set_control_active(True) since we don't know
 | 
			
		||||
        # whether the tab is the active one.
 | 
			
		||||
        self.show_title()
 | 
			
		||||
| 
						 | 
				
			
			@ -436,9 +448,6 @@ class MessageWindow(object):
 | 
			
		|||
            elif chr(keyval) in st: # ALT + 1,2,3..
 | 
			
		||||
                self.notebook.set_current_page(st.index(chr(keyval)))
 | 
			
		||||
                return True
 | 
			
		||||
            elif keyval == Gdk.KEY_c: # ALT + C toggles chat buttons
 | 
			
		||||
                control.chat_buttons_set_visible(not control.hide_chat_buttons)
 | 
			
		||||
                return True
 | 
			
		||||
            elif keyval == Gdk.KEY_m: # ALT + M show emoticons menu
 | 
			
		||||
                control.emoticons_button.get_popover().show()
 | 
			
		||||
                return True
 | 
			
		||||
| 
						 | 
				
			
			@ -570,6 +579,7 @@ class MessageWindow(object):
 | 
			
		|||
        ask any confirmation
 | 
			
		||||
        """
 | 
			
		||||
        def close(ctrl):
 | 
			
		||||
            self.remove_headermenu(self.notebook, ctrl)
 | 
			
		||||
            if reason is not None: # We are leaving gc with a status message
 | 
			
		||||
                ctrl.shutdown(reason)
 | 
			
		||||
            else: # We are leaving gc without status message or it's a chat
 | 
			
		||||
| 
						 | 
				
			
			@ -607,6 +617,7 @@ class MessageWindow(object):
 | 
			
		|||
 | 
			
		||||
        def on_minimize(ctrl):
 | 
			
		||||
            if method != self.CLOSE_COMMAND:
 | 
			
		||||
                self.remove_headermenu(self.notebook, ctrl)
 | 
			
		||||
                ctrl.minimize()
 | 
			
		||||
                self.check_tabs()
 | 
			
		||||
                return
 | 
			
		||||
| 
						 | 
				
			
			@ -618,6 +629,13 @@ class MessageWindow(object):
 | 
			
		|||
        else:
 | 
			
		||||
            ctrl.allow_shutdown(method, on_yes, on_no, on_minimize)
 | 
			
		||||
 | 
			
		||||
    def remove_headermenu(self, notebook, ctrl):
 | 
			
		||||
        page_num = notebook.page_num(ctrl.widget)
 | 
			
		||||
        if page_num == notebook.get_current_page():
 | 
			
		||||
            self.set_header_menu(None)
 | 
			
		||||
        elif notebook.get_n_pages() == 1:
 | 
			
		||||
            self.set_header_menu(None)
 | 
			
		||||
 | 
			
		||||
    def check_tabs(self):
 | 
			
		||||
        if self.parent_paned:
 | 
			
		||||
            # Do nothing in single window mode
 | 
			
		||||
| 
						 | 
				
			
			@ -822,6 +840,8 @@ class MessageWindow(object):
 | 
			
		|||
 | 
			
		||||
    def popup_menu(self, event):
 | 
			
		||||
        menu = self.get_active_control().prepare_context_menu()
 | 
			
		||||
        if menu is None:
 | 
			
		||||
            return
 | 
			
		||||
        # show the menu
 | 
			
		||||
        menu.attach_to_widget(app.interface.roster.window, None)
 | 
			
		||||
        menu.show_all()
 | 
			
		||||
| 
						 | 
				
			
			@ -836,6 +856,7 @@ class MessageWindow(object):
 | 
			
		|||
        new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num))
 | 
			
		||||
        new_ctrl.set_control_active(True)
 | 
			
		||||
        self.show_title(control = new_ctrl)
 | 
			
		||||
        self.set_header_menu(new_ctrl.control_menu)
 | 
			
		||||
 | 
			
		||||
        control = self.get_active_control()
 | 
			
		||||
        if isinstance(control, ChatControlBase):
 | 
			
		||||
| 
						 | 
				
			
			@ -879,6 +900,7 @@ class MessageWindow(object):
 | 
			
		|||
 | 
			
		||||
        if isinstance(control, ChatControlBase):
 | 
			
		||||
            # we forwarded it to message textview
 | 
			
		||||
            control.msg_textview.remove_placeholder()
 | 
			
		||||
            control.msg_textview.event(event)
 | 
			
		||||
            control.msg_textview.grab_focus()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1289,6 +1311,7 @@ class MessageWindowMgr(GObject.GObject):
 | 
			
		|||
                gtkgui_helpers.resize_window(w.window,
 | 
			
		||||
                        app.config.get('roster_width'),
 | 
			
		||||
                        app.config.get('roster_height'))
 | 
			
		||||
                self.hide_header_bar(self.parent_win)
 | 
			
		||||
 | 
			
		||||
        self._windows = {}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1298,8 +1321,16 @@ class MessageWindowMgr(GObject.GObject):
 | 
			
		|||
                mw = self.create_window(ctrl.contact, ctrl.account,
 | 
			
		||||
                                        ctrl.type_id)
 | 
			
		||||
            ctrl.parent_win = mw
 | 
			
		||||
            ctrl.add_actions()
 | 
			
		||||
            ctrl.update_actions()
 | 
			
		||||
            mw.new_tab(ctrl)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def hide_header_bar(parent_win):
 | 
			
		||||
        header_bar = parent_win.get_titlebar()
 | 
			
		||||
        for child in header_bar.get_children():
 | 
			
		||||
            child.hide()
 | 
			
		||||
 | 
			
		||||
    def save_opened_controls(self):
 | 
			
		||||
        if not app.config.get('remember_opened_chat_controls'):
 | 
			
		||||
            return
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5696,9 +5696,18 @@ class RosterWindow:
 | 
			
		|||
        application.add_window(self.window)
 | 
			
		||||
        self.add_actions()
 | 
			
		||||
        self.hpaned = self.xml.get_object('roster_hpaned')
 | 
			
		||||
 | 
			
		||||
        app.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
 | 
			
		||||
        app.interface.msg_win_mgr.connect('window-delete',
 | 
			
		||||
            self.on_message_window_delete)
 | 
			
		||||
 | 
			
		||||
        # Set headermenu but hide it.
 | 
			
		||||
        # MessageWindow will show it if we are in single-window mode
 | 
			
		||||
        headerbar = self.xml.get_object('headerbar')
 | 
			
		||||
        self.window.set_titlebar(headerbar)
 | 
			
		||||
        self.header_menu = self.xml.get_object('header_menu')
 | 
			
		||||
        self.header_menu.hide()
 | 
			
		||||
 | 
			
		||||
        self.advanced_menus = [] # We keep them to destroy them
 | 
			
		||||
        if app.config.get('roster_window_skip_taskbar'):
 | 
			
		||||
            self.window.set_property('skip-taskbar-hint', True)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,89 +0,0 @@
 | 
			
		|||
# -*- coding:utf-8 -*-
 | 
			
		||||
# Copyright (C) 2015 Patrick Griffis <tingping@tingping.se>
 | 
			
		||||
# Copyright (C) 2014 Christian Hergert <christian@hergert.me>
 | 
			
		||||
#
 | 
			
		||||
# This program 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.
 | 
			
		||||
#
 | 
			
		||||
# This program is distributed in the hope that it will be useful, but
 | 
			
		||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
 | 
			
		||||
# MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
from gi.repository import GObject, Gtk
 | 
			
		||||
 | 
			
		||||
class ScrolledWindow(Gtk.ScrolledWindow):
 | 
			
		||||
    """
 | 
			
		||||
        ScrolledWindow that sets a max size for the child to grow into.
 | 
			
		||||
        Taken from the Gnome Builder project:
 | 
			
		||||
            https://git.gnome.org/browse/gnome-builder/tree/contrib/egg/egg-scrolled-window.c
 | 
			
		||||
    """
 | 
			
		||||
    __gtype_name__ = "EggScrolledWindow"
 | 
			
		||||
 | 
			
		||||
    max_content_height = GObject.Property(type=int, default=-1, nick="Max Content Height",
 | 
			
		||||
                                          blurb="The maximum height request that can be made")
 | 
			
		||||
    max_content_width = GObject.Property(type=int, default=-1, nick="Max Content Width",
 | 
			
		||||
                                         blurb="The maximum width request that can be made")
 | 
			
		||||
    min_content_height = GObject.Property(type=int, default=-1, nick="Min Content Height",
 | 
			
		||||
                                          blurb="The minimum height request that can be made")
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.connect_after("notify::max-content-height", lambda obj, param: self.queue_resize())
 | 
			
		||||
        self.connect_after("notify::max-content-width", lambda obj, param: self.queue_resize())
 | 
			
		||||
 | 
			
		||||
    def set_min_content_height(self, value):
 | 
			
		||||
        self.min_content_height = value
 | 
			
		||||
 | 
			
		||||
    def set_max_content_height(self, value):
 | 
			
		||||
        self.max_content_height = value
 | 
			
		||||
 | 
			
		||||
    def set_max_content_width(self, value):
 | 
			
		||||
        self.max_content_width = value
 | 
			
		||||
 | 
			
		||||
    def get_max_content_height(self):
 | 
			
		||||
        return self.max_content_height
 | 
			
		||||
 | 
			
		||||
    def get_max_content_width(self):
 | 
			
		||||
        return self.max_content_width
 | 
			
		||||
 | 
			
		||||
    def do_get_preferred_height(self):
 | 
			
		||||
        min_height, natural_height = Gtk.ScrolledWindow.do_get_preferred_height(self)
 | 
			
		||||
        child = self.get_child()
 | 
			
		||||
 | 
			
		||||
        if natural_height and self.max_content_height > -1 and child:
 | 
			
		||||
 | 
			
		||||
            style = self.get_style_context()
 | 
			
		||||
            border = style.get_border(style.get_state())
 | 
			
		||||
            additional = border.top + border.bottom
 | 
			
		||||
 | 
			
		||||
            child_min_height, child_nat_height = child.get_preferred_height()
 | 
			
		||||
            if child_nat_height > natural_height and self.max_content_height > natural_height:
 | 
			
		||||
                natural_height = min(self.max_content_height, child_nat_height) + additional
 | 
			
		||||
            elif natural_height > child_nat_height:
 | 
			
		||||
                if child_nat_height < self.min_content_height:
 | 
			
		||||
                    return self.min_content_height, self.min_content_height
 | 
			
		||||
                min_height, natural_height = child_min_height + additional, child_nat_height + additional
 | 
			
		||||
 | 
			
		||||
        return min_height, natural_height
 | 
			
		||||
 | 
			
		||||
    def do_get_preferred_width(self):
 | 
			
		||||
        min_width, natural_width = Gtk.ScrolledWindow.do_get_preferred_width(self)
 | 
			
		||||
        child = self.get_child()
 | 
			
		||||
 | 
			
		||||
        if natural_width and self.max_content_width > -1 and child:
 | 
			
		||||
 | 
			
		||||
            style = self.get_style_context()
 | 
			
		||||
            border = style.get_border(style.get_state())
 | 
			
		||||
            additional = border.left + border.right + 1
 | 
			
		||||
 | 
			
		||||
            child_min_width, child_nat_width = child.get_preferred_width()
 | 
			
		||||
            if child_nat_width > natural_width and self.max_content_width > natural_width:
 | 
			
		||||
                natural_width = min(self.max_content_width, child_nat_width) + additional
 | 
			
		||||
 | 
			
		||||
        return min_width, natural_width
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue