New style for ChatControl
- Move ActionBar into HeaderMenu - Make Design of ChatControl look cleaner - Hide the Roster in Groupchats per default - Add Button to hide/show Roster in Groupchats - Move Groupchat topic into popover - Display Avatars on the right side of the ChatControl and status on the left - Add a default Avatar for contacts that have none
This commit is contained in:
		
							parent
							
								
									398ad0eed8
								
							
						
					
					
						commit
						970d6f8c3f
					
				
					 24 changed files with 994 additions and 1266 deletions
				
			
		|  | @ -30,7 +30,7 @@ | ||||||
| import os | import os | ||||||
| import time | import time | ||||||
| from gi.repository import Gtk | from gi.repository import Gtk | ||||||
| from gi.repository import Gdk | from gi.repository import Gio | ||||||
| from gi.repository import GdkPixbuf | from gi.repository import GdkPixbuf | ||||||
| from gi.repository import Pango | from gi.repository import Pango | ||||||
| from gi.repository import GLib | from gi.repository import GLib | ||||||
|  | @ -95,60 +95,10 @@ class ChatControl(ChatControlBase): | ||||||
|         self.last_recv_message_marks = None |         self.last_recv_message_marks = None | ||||||
|         self.last_message_timestamp = 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._formattings_button = self.xml.get_object('formattings_button') | ||||||
|         self.emoticons_button = self.xml.get_object('emoticons_button') |         self.emoticons_button = self.xml.get_object('emoticons_button') | ||||||
|         self.toggle_emoticons() |         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'), |         self.widget_set_visible(self.xml.get_object('banner_eventbox'), | ||||||
|             app.config.get('hide_chat_banner')) |             app.config.get('hide_chat_banner')) | ||||||
| 
 | 
 | ||||||
|  | @ -161,10 +111,9 @@ class ChatControl(ChatControlBase): | ||||||
|         # Add lock image to show chat encryption |         # Add lock image to show chat encryption | ||||||
|         self.lock_image = self.xml.get_object('lock_image') |         self.lock_image = self.xml.get_object('lock_image') | ||||||
| 
 | 
 | ||||||
|         # Convert to GC icon |         # Menu for the HeaderBar | ||||||
|         img = self.xml.get_object('convert_to_gc_button_image') |         self.control_menu = gui_menu_builder.get_singlechat_menu( | ||||||
|         img.set_from_pixbuf(gtkgui_helpers.load_icon( |             self.control_id) | ||||||
|                 'muc_active').get_pixbuf()) |  | ||||||
| 
 | 
 | ||||||
|         self._audio_banner_image = self.xml.get_object('audio_banner_image') |         self._audio_banner_image = self.xml.get_object('audio_banner_image') | ||||||
|         self._video_banner_image = self.xml.get_object('video_banner_image') |         self._video_banner_image = self.xml.get_object('video_banner_image') | ||||||
|  | @ -270,7 +219,7 @@ class ChatControl(ChatControlBase): | ||||||
| 
 | 
 | ||||||
|         # Enable encryption if needed |         # Enable encryption if needed | ||||||
|         self.no_autonegotiation = False |         self.no_autonegotiation = False | ||||||
| 
 |         self.add_actions() | ||||||
|         self.update_ui() |         self.update_ui() | ||||||
|         self.set_lock_image() |         self.set_lock_image() | ||||||
| 
 | 
 | ||||||
|  | @ -298,6 +247,107 @@ class ChatControl(ChatControlBase): | ||||||
|         # PluginSystem: adding GUI extension point for this ChatControl |         # PluginSystem: adding GUI extension point for this ChatControl | ||||||
|         # instance object |         # instance object | ||||||
|         app.plugin_manager.gui_extension_point('chat_control', self) |         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): |     def subscribe_events(self): | ||||||
|         """ |         """ | ||||||
|  | @ -329,14 +379,6 @@ class ChatControl(ChatControlBase): | ||||||
|                 self._formattings_button.set_tooltip_text(_('This contact does ' |                 self._formattings_button.set_tooltip_text(_('This contact does ' | ||||||
|                     'not support HTML')) |                     '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 |         # Jingle detection | ||||||
|         if self.contact.supports(NS_JINGLE_ICE_UDP) and \ |         if self.contact.supports(NS_JINGLE_ICE_UDP) and \ | ||||||
|         app.HAVE_FARSTREAM and self.contact.resource: |         app.HAVE_FARSTREAM and self.contact.resource: | ||||||
|  | @ -348,63 +390,6 @@ class ChatControl(ChatControlBase): | ||||||
|             self.video_available = False |             self.video_available = False | ||||||
|             self.audio_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): |     def update_all_pep_types(self): | ||||||
|         for pep_type in self._pep_images: |         for pep_type in self._pep_images: | ||||||
|             self.update_pep(pep_type) |             self.update_pep(pep_type) | ||||||
|  | @ -751,12 +736,12 @@ class ChatControl(ChatControlBase): | ||||||
|         getattr(self, '_' + jingle_type + '_button').set_active(False) |         getattr(self, '_' + jingle_type + '_button').set_active(False) | ||||||
|         getattr(self, 'update_' + jingle_type)() |         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], |         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) |         path_to_img = gtkgui_helpers.get_icon_path(img_name) | ||||||
| 
 | 
 | ||||||
|         if widget.get_active(): |         if state: | ||||||
|             if getattr(self, jingle_type + '_state') == \ |             if getattr(self, jingle_type + '_state') == \ | ||||||
|             self.JINGLE_STATE_NULL: |             self.JINGLE_STATE_NULL: | ||||||
|                 if jingle_type == 'video': |                 if jingle_type == 'video': | ||||||
|  | @ -795,12 +780,6 @@ class ChatControl(ChatControlBase): | ||||||
|         img = getattr(self, '_' + jingle_type + '_button').get_property('image') |         img = getattr(self, '_' + jingle_type + '_button').get_property('image') | ||||||
|         img.set_from_file(path_to_img) |         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): |     def set_lock_image(self): | ||||||
|         loggable = self.session and self.session.is_loggable() |         loggable = self.session and self.session.is_loggable() | ||||||
| 
 | 
 | ||||||
|  | @ -832,10 +811,6 @@ class ChatControl(ChatControlBase): | ||||||
|         self.authentication_button.set_tooltip_text(tooltip) |         self.authentication_button.set_tooltip_text(tooltip) | ||||||
|         self.widget_set_visible(self.authentication_button, not visible) |         self.widget_set_visible(self.authentication_button, not visible) | ||||||
|         context = self.msg_scrolledwindow.get_style_context() |         context = self.msg_scrolledwindow.get_style_context() | ||||||
|         if visible: |  | ||||||
|             context.add_class('authentication') |  | ||||||
|         else: |  | ||||||
|             context.remove_class('authentication') |  | ||||||
|         self.lock_image.set_sensitive(visible) |         self.lock_image.set_sensitive(visible) | ||||||
| 
 | 
 | ||||||
|     def _on_authentication_button_clicked(self, widget): |     def _on_authentication_button_clicked(self, widget): | ||||||
|  | @ -1272,10 +1247,18 @@ class ChatControl(ChatControlBase): | ||||||
|         if not app.config.get('show_avatar_in_chat'): |         if not app.config.get('show_avatar_in_chat'): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         pixbuf = app.contacts.get_avatar( |         if self.TYPE_ID == message_control.TYPE_CHAT: | ||||||
|             self.account, self.contact.jid, AvatarSize.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 = 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): |     def _nec_update_avatar(self, obj): | ||||||
|         if obj.account != self.account: |         if obj.account != self.account: | ||||||
|  | @ -1446,15 +1429,6 @@ class ChatControl(ChatControlBase): | ||||||
|             elif typ == 'pm': |             elif typ == 'pm': | ||||||
|                 control.remove_contact(nick) |                 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): |     def _on_convert_to_gc_menuitem_activate(self, widget): | ||||||
|         """ |         """ | ||||||
|         User wants to invite some friends to chat |         User wants to invite some friends to chat | ||||||
|  | @ -1522,21 +1496,11 @@ class ChatControl(ChatControlBase): | ||||||
|         if contact: |         if contact: | ||||||
|             self.contact = contact |             self.contact = contact | ||||||
|         self.draw_banner() |         self.draw_banner() | ||||||
|  |         self.update_actions() | ||||||
| 
 | 
 | ||||||
|     def got_disconnected(self): |     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) |         ChatControlBase.got_disconnected(self) | ||||||
|  |         self.update_actions() | ||||||
| 
 | 
 | ||||||
|     def update_status_display(self, name, uf_show, status): |     def update_status_display(self, name, uf_show, status): | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  | @ -44,7 +44,6 @@ from gajim import notify | ||||||
| import re | import re | ||||||
| 
 | 
 | ||||||
| from gajim import emoticons | from gajim import emoticons | ||||||
| from gajim.scrolled_window import ScrolledWindow |  | ||||||
| from gajim.common import events | from gajim.common import events | ||||||
| from gajim.common import app | from gajim.common import app | ||||||
| from gajim.common import helpers | from gajim.common import helpers | ||||||
|  | @ -256,28 +255,21 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
|         MessageControl.__init__(self, type_id, parent_win, widget_name, |         MessageControl.__init__(self, type_id, parent_win, widget_name, | ||||||
|             contact, acct, resource=resource) |             contact, acct, resource=resource) | ||||||
| 
 | 
 | ||||||
|         widget = self.xml.get_object('history_button') |         if self.TYPE_ID != message_control.TYPE_GC: | ||||||
|         # set document-open-recent icon for history button |             # Create banner and connect signals | ||||||
|         if gtkgui_helpers.gtk_icon_theme.has_icon('document-open-recent'): |             widget = self.xml.get_object('banner_eventbox') | ||||||
|             img = self.xml.get_object('history_image') |             id_ = widget.connect('button-press-event', | ||||||
|             img.set_from_icon_name('document-open-recent', Gtk.IconSize.MENU) |                 self._on_banner_eventbox_button_press_event) | ||||||
| 
 |             self.handlers[id_] = widget | ||||||
|         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 |  | ||||||
| 
 | 
 | ||||||
|         self.urlfinder = re.compile( |         self.urlfinder = re.compile( | ||||||
|             r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]") |             r"(www\.(?!\.)|[a-z][a-z0-9+.-]*://)[^\s<>'\"]+[^!,\.\s<>\)'\"\]]") | ||||||
| 
 | 
 | ||||||
|         self.banner_status_label = self.xml.get_object('banner_label') |         self.banner_status_label = self.xml.get_object('banner_label') | ||||||
|         id_ = self.banner_status_label.connect('populate_popup', |         if self.banner_status_label is not None: | ||||||
|             self.on_banner_label_populate_popup) |             id_ = self.banner_status_label.connect('populate_popup', | ||||||
|         self.handlers[id_] = self.banner_status_label |                 self.on_banner_label_populate_popup) | ||||||
|  |             self.handlers[id_] = self.banner_status_label | ||||||
| 
 | 
 | ||||||
|         # Init DND |         # Init DND | ||||||
|         self.TARGET_TYPE_URI_LIST = 80 |         self.TARGET_TYPE_URI_LIST = 80 | ||||||
|  | @ -332,9 +324,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
| 
 | 
 | ||||||
|         self.msg_scrolledwindow = ScrolledWindow() |         self.msg_scrolledwindow = ScrolledWindow() | ||||||
|         self.msg_scrolledwindow.set_max_content_height(100) |         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.get_style_context().add_class('scrolledtextview') | ||||||
| 
 |  | ||||||
|         self.msg_scrolledwindow.set_property('shadow_type', Gtk.ShadowType.IN) |         self.msg_scrolledwindow.set_property('shadow_type', Gtk.ShadowType.IN) | ||||||
|         self.msg_scrolledwindow.add(self.msg_textview) |         self.msg_scrolledwindow.add(self.msg_textview) | ||||||
| 
 | 
 | ||||||
|  | @ -417,6 +408,28 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
|         action.connect("change-state", self.change_encryption) |         action.connect("change-state", self.change_encryption) | ||||||
|         self.parent_win.window.add_action(action) |         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): |     def change_encryption(self, action, param): | ||||||
|         encryption = param.get_string() |         encryption = param.get_string() | ||||||
|         if encryption == 'disabled': |         if encryption == 'disabled': | ||||||
|  | @ -549,6 +562,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
|         menu.show_all() |         menu.show_all() | ||||||
| 
 | 
 | ||||||
|     def on_quote(self, widget, text): |     def on_quote(self, widget, text): | ||||||
|  |         self.msg_textview.remove_placeholder() | ||||||
|         text = '>' + text.replace('\n', '\n>') + '\n' |         text = '>' + text.replace('\n', '\n>') + '\n' | ||||||
|         message_buffer = self.msg_textview.get_buffer() |         message_buffer = self.msg_textview.get_buffer() | ||||||
|         message_buffer.insert_at_cursor(text) |         message_buffer.insert_at_cursor(text) | ||||||
|  | @ -1283,13 +1297,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
|         else: |         else: | ||||||
|             widget.show_all() |             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): |     def got_connected(self): | ||||||
|         self.msg_textview.set_sensitive(True) |         self.msg_textview.set_sensitive(True) | ||||||
|         self.msg_textview.set_editable(True) |         self.msg_textview.set_editable(True) | ||||||
|  | @ -1302,3 +1309,19 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): | ||||||
| 
 | 
 | ||||||
|         self.no_autonegotiation = False |         self.no_autonegotiation = False | ||||||
|         self.update_toolbar() |         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 |     AUTOMATIC = True | ||||||
|     HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands |     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) |     @command(overlap=True) | ||||||
|     @doc(_("Show help on a given command or a list of available commands if -a is given")) |     @doc(_("Show help on a given command or a list of available commands if -a is given")) | ||||||
|     def help(self, command=None, all=False): |     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_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.')], |             '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.')], |             '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_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_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.')], |             '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': |                 if rule['type'] == 'group': | ||||||
|                     roster.draw_group(rule['value'], self.name) |                     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): |     def _request_bookmarks_xml(self): | ||||||
|         if not app.account_is_connected(self.name): |         if not app.account_is_connected(self.name): | ||||||
|             return |             return | ||||||
|  |  | ||||||
|  | @ -30,8 +30,8 @@ class OptionType(IntEnum): | ||||||
| 
 | 
 | ||||||
| class AvatarSize(IntEnum): | class AvatarSize(IntEnum): | ||||||
|     ROSTER = 32 |     ROSTER = 32 | ||||||
|  |     CHAT = 48 | ||||||
|     NOTIFICATION = 48 |     NOTIFICATION = 48 | ||||||
|     CHAT = 52 |  | ||||||
|     PROFILE = 64 |     PROFILE = 64 | ||||||
|     TOOLTIP = 125 |     TOOLTIP = 125 | ||||||
|     VCARD = 200 |     VCARD = 200 | ||||||
|  |  | ||||||
|  | @ -187,10 +187,6 @@ class PreferencesWindow: | ||||||
|         else: |         else: | ||||||
|             show_roster_combobox.set_active(0) |             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 |         # Ignore XHTML | ||||||
|         st = app.config.get('ignore_incoming_xhtml') |         st = app.config.get('ignore_incoming_xhtml') | ||||||
|         self.xml.get_object('xhtml_checkbutton').set_active(st) |         self.xml.get_object('xhtml_checkbutton').set_active(st) | ||||||
|  | @ -657,12 +653,6 @@ class PreferencesWindow: | ||||||
|         config_type = c_config.opt_show_roster_on_startup[active] |         config_type = c_config.opt_show_roster_on_startup[active] | ||||||
|         app.config.set('show_roster_on_startup', config_type) |         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): |     def on_xhtml_checkbutton_toggled(self, widget): | ||||||
|         self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml') |         self.on_checkbutton_toggled(widget, 'ignore_incoming_xhtml') | ||||||
|         helpers.update_optional_features() |         helpers.update_optional_features() | ||||||
|  |  | ||||||
|  | @ -374,7 +374,6 @@ | ||||||
|             <property name="visible">True</property> |             <property name="visible">True</property> | ||||||
|             <property name="can_focus">False</property> |             <property name="can_focus">False</property> | ||||||
|             <property name="orientation">vertical</property> |             <property name="orientation">vertical</property> | ||||||
|             <property name="spacing">5</property> |  | ||||||
|             <child> |             <child> | ||||||
|               <object class="GtkEventBox" id="banner_eventbox"> |               <object class="GtkEventBox" id="banner_eventbox"> | ||||||
|                 <property name="name">ChatControl-BannerEventBox</property> |                 <property name="name">ChatControl-BannerEventBox</property> | ||||||
|  | @ -385,16 +384,23 @@ | ||||||
|                     <property name="visible">True</property> |                     <property name="visible">True</property> | ||||||
|                     <property name="can_focus">False</property> |                     <property name="can_focus">False</property> | ||||||
|                     <child> |                     <child> | ||||||
|                       <object class="GtkImage" id="banner_status_image"> |                       <object class="GtkEventBox" id="avatar_eventbox"> | ||||||
|                         <property name="visible">True</property> |                         <property name="visible">True</property> | ||||||
|                         <property name="can_focus">False</property> |                         <property name="can_focus">False</property> | ||||||
|                         <property name="ypad">5</property> |                         <property name="margin_top">5</property> | ||||||
|                         <property name="stock">gtk-missing-image</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> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|                         <property name="expand">False</property> |                         <property name="expand">False</property> | ||||||
|                         <property name="fill">False</property> |                         <property name="fill">False</property> | ||||||
|                         <property name="padding">5</property> |  | ||||||
|                         <property name="position">0</property> |                         <property name="position">0</property> | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|  | @ -545,21 +551,15 @@ | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|                     <child> |                     <child> | ||||||
|                       <object class="GtkEventBox" id="avatar_eventbox"> |                       <object class="GtkImage" id="banner_status_image"> | ||||||
|                         <property name="visible">True</property> |                         <property name="visible">True</property> | ||||||
|                         <property name="can_focus">False</property> |                         <property name="can_focus">False</property> | ||||||
|                         <property name="visible_window">False</property> |                         <property name="stock">gtk-missing-image</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> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|                         <property name="expand">False</property> |                         <property name="expand">False</property> | ||||||
|                         <property name="fill">False</property> |                         <property name="fill">False</property> | ||||||
|  |                         <property name="padding">5</property> | ||||||
|                         <property name="position">3</property> |                         <property name="position">3</property> | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|  | @ -573,94 +573,16 @@ | ||||||
|               </packing> |               </packing> | ||||||
|             </child> |             </child> | ||||||
|             <child> |             <child> | ||||||
|               <object class="GtkBox" id="vbox106"> |               <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> | ||||||
|                 <property name="visible">True</property> |                 <property name="height_request">60</property> | ||||||
|                 <property name="can_focus">False</property> |                 <property name="can_focus">True</property> | ||||||
|                 <property name="orientation">vertical</property> |                 <property name="shadow_type">in</property> | ||||||
|                 <property name="spacing">6</property> |  | ||||||
|                 <child> |                 <child> | ||||||
|                   <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> |                   <placeholder/> | ||||||
|                     <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> |  | ||||||
|                 </child> |                 </child> | ||||||
|  |                 <style> | ||||||
|  |                   <class name="scrolled-no-border"/> | ||||||
|  |                 </style> | ||||||
|               </object> |               </object> | ||||||
|               <packing> |               <packing> | ||||||
|                 <property name="expand">True</property> |                 <property name="expand">True</property> | ||||||
|  | @ -669,9 +591,48 @@ | ||||||
|               </packing> |               </packing> | ||||||
|             </child> |             </child> | ||||||
|             <child> |             <child> | ||||||
|               <object class="GtkBox" id="actions_hbox"> |               <object class="GtkSeparator"> | ||||||
|                 <property name="visible">True</property> |                 <property name="visible">True</property> | ||||||
|                 <property name="can_focus">False</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> |                 <child> | ||||||
|                   <object class="GtkMenuButton" id="formattings_button"> |                   <object class="GtkMenuButton" id="formattings_button"> | ||||||
|                     <property name="visible">True</property> |                     <property name="visible">True</property> | ||||||
|  | @ -686,23 +647,13 @@ | ||||||
|                       <object class="GtkImage" id="image10"> |                       <object class="GtkImage" id="image10"> | ||||||
|                         <property name="visible">True</property> |                         <property name="visible">True</property> | ||||||
|                         <property name="can_focus">False</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> |                         <property name="icon_size">1</property> | ||||||
|                       </object> |                       </object> | ||||||
|                     </child> |                     </child> | ||||||
|                   </object> |                     <style> | ||||||
|                   <packing> |                       <class name="chatcontrol-actionbar-button"/> | ||||||
|                     <property name="expand">False</property> |                     </style> | ||||||
|                     <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> |  | ||||||
|                   </object> |                   </object> | ||||||
|                   <packing> |                   <packing> | ||||||
|                     <property name="expand">False</property> |                     <property name="expand">False</property> | ||||||
|  | @ -711,235 +662,15 @@ | ||||||
|                   </packing> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|                 <child> |                 <child> | ||||||
|                   <object class="GtkButton" id="add_to_roster_button"> |                   <object class="GtkComboBox" id="label_selector"> | ||||||
|                     <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"> |  | ||||||
|                     <property name="visible">True</property> |                     <property name="visible">True</property> | ||||||
|                     <property name="can_focus">False</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> |                   </object> | ||||||
|                   <packing> |                   <packing> | ||||||
|                     <property name="expand">False</property> |                     <property name="expand">True</property> | ||||||
|                     <property name="fill">True</property> |                     <property name="fill">True</property> | ||||||
|                     <property name="position">9</property> |                     <property name="pack_type">end</property> | ||||||
|                   </packing> |                     <property name="position">2</property> | ||||||
|                 </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> |  | ||||||
|                   </packing> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|                 <child> |                 <child> | ||||||
|  | @ -1042,26 +773,70 @@ audio-mic-volume-low</property> | ||||||
|                   <packing> |                   <packing> | ||||||
|                     <property name="expand">False</property> |                     <property name="expand">False</property> | ||||||
|                     <property name="fill">True</property> |                     <property name="fill">True</property> | ||||||
|                     <property name="position">12</property> |                     <property name="pack_type">end</property> | ||||||
|  |                     <property name="position">3</property> | ||||||
|                   </packing> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|                 <child> |                 <child> | ||||||
|                   <object class="GtkComboBox" id="label_selector"> |                   <object class="GtkButton" id="authentication_button"> | ||||||
|                     <property name="visible">True</property> |                     <property name="can_focus">True</property> | ||||||
|                     <property name="can_focus">False</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> |                   </object> | ||||||
|                   <packing> |                   <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="fill">True</property> | ||||||
|                     <property name="position">13</property> |                     <property name="pack_type">end</property> | ||||||
|  |                     <property name="position">6</property> | ||||||
|                   </packing> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|               </object> |               </object> | ||||||
|               <packing> |               <packing> | ||||||
|                 <property name="expand">False</property> |                 <property name="expand">False</property> | ||||||
|                 <property name="fill">True</property> |                 <property name="fill">False</property> | ||||||
|                 <property name="pack_type">end</property> |                 <property name="position">3</property> | ||||||
|                 <property name="position">2</property> |  | ||||||
|               </packing> |               </packing> | ||||||
|             </child> |             </child> | ||||||
|           </object> |           </object> | ||||||
|  |  | ||||||
|  | @ -2,6 +2,82 @@ | ||||||
| <!-- Generated with glade 3.20.0 --> | <!-- Generated with glade 3.20.0 --> | ||||||
| <interface> | <interface> | ||||||
|   <requires lib="gtk+" version="3.20"/> |   <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"> |   <object class="GtkBox" id="groupchat_control_hbox"> | ||||||
|     <property name="can_focus">True</property> |     <property name="can_focus">True</property> | ||||||
|     <child> |     <child> | ||||||
|  | @ -13,83 +89,6 @@ | ||||||
|         <property name="margin_bottom">7</property> |         <property name="margin_bottom">7</property> | ||||||
|         <property name="orientation">vertical</property> |         <property name="orientation">vertical</property> | ||||||
|         <property name="spacing">5</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> |         <child> | ||||||
|           <object class="GtkPaned" id="hpaned"> |           <object class="GtkPaned" id="hpaned"> | ||||||
|             <property name="visible">True</property> |             <property name="visible">True</property> | ||||||
|  | @ -104,7 +103,68 @@ | ||||||
|                 <property name="can_focus">False</property> |                 <property name="can_focus">False</property> | ||||||
|                 <property name="margin_right">4</property> |                 <property name="margin_right">4</property> | ||||||
|                 <property name="orientation">vertical</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> |                 <child> | ||||||
|                   <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> |                   <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> | ||||||
|                     <property name="width_request">200</property> |                     <property name="width_request">200</property> | ||||||
|  | @ -115,11 +175,28 @@ | ||||||
|                     <child> |                     <child> | ||||||
|                       <placeholder/> |                       <placeholder/> | ||||||
|                     </child> |                     </child> | ||||||
|  |                     <style> | ||||||
|  |                       <class name="scrolled-no-border"/> | ||||||
|  |                     </style> | ||||||
|                   </object> |                   </object> | ||||||
|                   <packing> |                   <packing> | ||||||
|                     <property name="expand">True</property> |                     <property name="expand">True</property> | ||||||
|                     <property name="fill">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> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|                 <child> |                 <child> | ||||||
|  | @ -138,12 +215,11 @@ | ||||||
|                           <object class="GtkImage"> |                           <object class="GtkImage"> | ||||||
|                             <property name="visible">True</property> |                             <property name="visible">True</property> | ||||||
|                             <property name="can_focus">False</property> |                             <property name="can_focus">False</property> | ||||||
|                             <property name="icon_name">face-smile</property> |                             <property name="icon_name">face-smile-symbolic</property> | ||||||
|                           </object> |                           </object> | ||||||
|                         </child> |                         </child> | ||||||
|                         <style> |                         <style> | ||||||
|                           <class name="msgtextview-button"/> |                           <class name="chatcontrol-actionbar-button"/> | ||||||
|                           <class name="left"/> |  | ||||||
|                         </style> |                         </style> | ||||||
|                       </object> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|  | @ -153,7 +229,32 @@ | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|                     <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> | ||||||
|                     <child> |                     <child> | ||||||
|                       <object class="GtkButton" id="authentication_button"> |                       <object class="GtkButton" id="authentication_button"> | ||||||
|  | @ -172,8 +273,7 @@ | ||||||
|                           </object> |                           </object> | ||||||
|                         </child> |                         </child> | ||||||
|                         <style> |                         <style> | ||||||
|                           <class name="msgtextview-button"/> |                           <class name="chatcontrol-actionbar-button"/> | ||||||
|                           <class name="right"/> |  | ||||||
|                         </style> |                         </style> | ||||||
|                       </object> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|  | @ -183,190 +283,19 @@ | ||||||
|                         <property name="position">2</property> |                         <property name="position">2</property> | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </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> |                     <child> | ||||||
|                       <object class="GtkButton" id="formattings_button"> |                       <placeholder/> | ||||||
|                         <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> |  | ||||||
|                     </child> |                     </child> | ||||||
|                     <child> |                     <child> | ||||||
|                       <object class="GtkComboBox" id="label_selector"> |                       <object class="GtkComboBox" id="label_selector"> | ||||||
|                         <property name="visible">True</property> |                         <property name="visible">True</property> | ||||||
|                         <property name="can_focus">False</property> |                         <property name="can_focus">False</property> | ||||||
|  |                         <property name="no_show_all">True</property> | ||||||
|                       </object> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|                         <property name="expand">True</property> |                         <property name="expand">True</property> | ||||||
|                         <property name="fill">True</property> |                         <property name="fill">True</property> | ||||||
|                         <property name="position">7</property> |                         <property name="position">4</property> | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|                     <child> |                     <child> | ||||||
|  | @ -383,18 +312,22 @@ | ||||||
|                             <property name="icon_name">channel-secure-symbolic</property> |                             <property name="icon_name">channel-secure-symbolic</property> | ||||||
|                           </object> |                           </object> | ||||||
|                         </child> |                         </child> | ||||||
|  |                         <style> | ||||||
|  |                           <class name="chatcontrol-actionbar-button"/> | ||||||
|  |                         </style> | ||||||
|                       </object> |                       </object> | ||||||
|                       <packing> |                       <packing> | ||||||
|                         <property name="expand">False</property> |                         <property name="expand">False</property> | ||||||
|                         <property name="fill">True</property> |                         <property name="fill">True</property> | ||||||
|                         <property name="position">8</property> |                         <property name="pack_type">end</property> | ||||||
|  |                         <property name="position">5</property> | ||||||
|                       </packing> |                       </packing> | ||||||
|                     </child> |                     </child> | ||||||
|                   </object> |                   </object> | ||||||
|                   <packing> |                   <packing> | ||||||
|                     <property name="expand">False</property> |                     <property name="expand">False</property> | ||||||
|                     <property name="fill">True</property> |                     <property name="fill">False</property> | ||||||
|                     <property name="position">2</property> |                     <property name="position">3</property> | ||||||
|                   </packing> |                   </packing> | ||||||
|                 </child> |                 </child> | ||||||
|               </object> |               </object> | ||||||
|  | @ -422,6 +355,9 @@ | ||||||
|                     </child> |                     </child> | ||||||
|                   </object> |                   </object> | ||||||
|                 </child> |                 </child> | ||||||
|  |                 <style> | ||||||
|  |                   <class name="scrolled-no-border"/> | ||||||
|  |                 </style> | ||||||
|               </object> |               </object> | ||||||
|               <packing> |               <packing> | ||||||
|                 <property name="resize">False</property> |                 <property name="resize">False</property> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <!-- Generated with glade 3.18.3 --> | <!-- Generated with glade 3.20.0 --> | ||||||
| <interface> | <interface> | ||||||
|   <requires lib="gtk+" version="3.12"/> |   <requires lib="gtk+" version="3.12"/> | ||||||
|   <object class="GtkEventBox" id="chat_tab_ebox"> |   <object class="GtkEventBox" id="chat_tab_ebox"> | ||||||
|  | @ -28,10 +28,10 @@ | ||||||
|             <property name="width_request">70</property> |             <property name="width_request">70</property> | ||||||
|             <property name="visible">True</property> |             <property name="visible">True</property> | ||||||
|             <property name="can_focus">False</property> |             <property name="can_focus">False</property> | ||||||
|             <property name="xalign">0</property> |  | ||||||
|             <property name="use_markup">True</property> |             <property name="use_markup">True</property> | ||||||
|             <property name="ellipsize">end</property> |             <property name="ellipsize">end</property> | ||||||
|             <property name="max_width_chars">9</property> |             <property name="max_width_chars">9</property> | ||||||
|  |             <property name="xalign">0</property> | ||||||
|           </object> |           </object> | ||||||
|           <packing> |           <packing> | ||||||
|             <property name="expand">False</property> |             <property name="expand">False</property> | ||||||
|  | @ -64,15 +64,35 @@ | ||||||
|       </object> |       </object> | ||||||
|     </child> |     </child> | ||||||
|   </object> |   </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"> |   <object class="GtkApplicationWindow" id="message_window"> | ||||||
|  |     <property name="name">MessageWindow</property> | ||||||
|     <property name="can_focus">False</property> |     <property name="can_focus">False</property> | ||||||
|     <property name="default_width">480</property> |     <property name="default_width">480</property> | ||||||
|     <property name="default_height">440</property> |     <property name="default_height">440</property> | ||||||
|  |     <property name="show_menubar">False</property> | ||||||
|     <child> |     <child> | ||||||
|       <object class="GtkNotebook" id="notebook"> |       <object class="GtkNotebook" id="notebook"> | ||||||
|         <property name="visible">True</property> |         <property name="visible">True</property> | ||||||
|         <property name="can_focus">True</property> |         <property name="can_focus">True</property> | ||||||
|         <property name="margin_top">2</property> |  | ||||||
|         <property name="scrollable">True</property> |         <property name="scrollable">True</property> | ||||||
|       </object> |       </object> | ||||||
|     </child> |     </child> | ||||||
|  |  | ||||||
|  | @ -426,23 +426,6 @@ | ||||||
|                             <property name="top_attach">2</property> |                             <property name="top_attach">2</property> | ||||||
|                           </packing> |                           </packing> | ||||||
|                         </child> |                         </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> |                         <child> | ||||||
|                           <object class="GtkCheckButton" id="xhtml_checkbutton"> |                           <object class="GtkCheckButton" id="xhtml_checkbutton"> | ||||||
|                             <property name="label" translatable="yes">_Ignore rich content in incoming messages</property> |                             <property name="label" translatable="yes">_Ignore rich content in incoming messages</property> | ||||||
|  | @ -457,7 +440,7 @@ | ||||||
|                           </object> |                           </object> | ||||||
|                           <packing> |                           <packing> | ||||||
|                             <property name="left_attach">0</property> |                             <property name="left_attach">0</property> | ||||||
|                             <property name="top_attach">4</property> |                             <property name="top_attach">3</property> | ||||||
|                             <property name="width">2</property> |                             <property name="width">2</property> | ||||||
|                           </packing> |                           </packing> | ||||||
|                         </child> |                         </child> | ||||||
|  | @ -474,7 +457,7 @@ | ||||||
|                           </object> |                           </object> | ||||||
|                           <packing> |                           <packing> | ||||||
|                             <property name="left_attach">0</property> |                             <property name="left_attach">0</property> | ||||||
|                             <property name="top_attach">5</property> |                             <property name="top_attach">4</property> | ||||||
|                             <property name="width">2</property> |                             <property name="width">2</property> | ||||||
|                           </packing> |                           </packing> | ||||||
|                         </child> |                         </child> | ||||||
|  | @ -490,7 +473,7 @@ | ||||||
|                           </object> |                           </object> | ||||||
|                           <packing> |                           <packing> | ||||||
|                             <property name="left_attach">0</property> |                             <property name="left_attach">0</property> | ||||||
|                             <property name="top_attach">6</property> |                             <property name="top_attach">5</property> | ||||||
|                             <property name="width">2</property> |                             <property name="width">2</property> | ||||||
|                           </packing> |                           </packing> | ||||||
|                         </child> |                         </child> | ||||||
|  | @ -555,7 +538,7 @@ | ||||||
|                           </object> |                           </object> | ||||||
|                           <packing> |                           <packing> | ||||||
|                             <property name="left_attach">0</property> |                             <property name="left_attach">0</property> | ||||||
|                             <property name="top_attach">7</property> |                             <property name="top_attach">6</property> | ||||||
|                             <property name="width">2</property> |                             <property name="width">2</property> | ||||||
|                           </packing> |                           </packing> | ||||||
|                         </child> |                         </child> | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
|   <requires lib="gtk+" version="3.12"/> |   <requires lib="gtk+" version="3.12"/> | ||||||
|   <object class="GtkAccelGroup" id="accelgroup1"/> |   <object class="GtkAccelGroup" id="accelgroup1"/> | ||||||
|   <object class="GtkApplicationWindow" id="roster_window"> |   <object class="GtkApplicationWindow" id="roster_window"> | ||||||
|  |     <property name="name">RosterWindow</property> | ||||||
|     <property name="width_request">85</property> |     <property name="width_request">85</property> | ||||||
|     <property name="height_request">200</property> |     <property name="height_request">200</property> | ||||||
|     <property name="can_focus">False</property> |     <property name="can_focus">False</property> | ||||||
|  | @ -115,4 +116,28 @@ | ||||||
|       </object> |       </object> | ||||||
|     </child> |     </child> | ||||||
|   </object> |   </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> | </interface> | ||||||
|  |  | ||||||
|  | @ -1,28 +1,31 @@ | ||||||
| /* Gajim Application CSS File */ | /* Gajim Application CSS File */ | ||||||
| 
 | 
 | ||||||
| .msgtextview-button {  | 
 | ||||||
|  | .chatcontrol-actionbar-button {  | ||||||
| 	padding: 0px 5px 0px 5px; | 	padding: 0px 5px 0px 5px; | ||||||
| 	background-color: @theme_base_color;  | 	background-color: @theme_base_color;  | ||||||
| 	border: 1px solid; | 	border: none; | ||||||
| 	border-radius: 0px;  |  | ||||||
| 	border-color: @borders; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| .msgtextview-button:hover, .msgtextview-button:checked {  | .scrolled-no-border {border: none} | ||||||
| 	color: @theme_base_color; | .scrolled-no-border undershoot.top, undershoot.bottom { background-image: none; } | ||||||
| 	border-color: @borders; | 
 | ||||||
| 	text-shadow: none; | .actionbar-no-border box {border: none} | ||||||
| 	-gtk-icon-shadow: none; | 
 | ||||||
| 	box-shadow: none; | .actionbar-no-border button {  | ||||||
|  | 	padding: 0px; | ||||||
|  | 	background-color: @theme_base_color;  | ||||||
|  | 	border: none; | ||||||
| 	background-image: none; | 	background-image: none; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | #MessageWindow, #RosterWindow paned { background-color:  @theme_base_color; } | ||||||
| 
 | 
 | ||||||
| .msgtextview-button.left { border-right: none; } | .scrolledtextview { border:none; } | ||||||
| .msgtextview-button.right { border-left: none; } |  | ||||||
| 
 | 
 | ||||||
| .scrolledtextview { border-left:none; } | .chatcontrol-separator {margin-bottom: 6px;} | ||||||
| .scrolledtextview.authentication { border-right:none; } | 
 | ||||||
|  | #SubjectPopover box { padding: 10px; } | ||||||
| 
 | 
 | ||||||
| /* VCardWindow */ | /* VCardWindow */ | ||||||
| .VCard-GtkLinkButton { padding-left: 5px; border-left: none; } | .VCard-GtkLinkButton { padding-left: 5px; border-left: none; } | ||||||
|  |  | ||||||
|  | @ -289,6 +289,7 @@ class EmoticonPopover(Gtk.Popover): | ||||||
|             self.append_emoticon(child.get_child().get_text()) |             self.append_emoticon(child.get_child().get_text()) | ||||||
| 
 | 
 | ||||||
|     def append_emoticon(self, pix): |     def append_emoticon(self, pix): | ||||||
|  |         self.text_widget.remove_placeholder() | ||||||
|         buffer_ = self.text_widget.get_buffer() |         buffer_ = self.text_widget.get_buffer() | ||||||
|         if buffer_.get_char_count(): |         if buffer_.get_char_count(): | ||||||
|             buffer_.insert_at_cursor(' ') |             buffer_.insert_at_cursor(' ') | ||||||
|  |  | ||||||
|  | @ -212,8 +212,15 @@ class GajimApplication(Gtk.Application): | ||||||
|         builder = Gtk.Builder() |         builder = Gtk.Builder() | ||||||
|         builder.set_translation_domain(i18n.APP) |         builder.set_translation_domain(i18n.APP) | ||||||
|         builder.add_from_file(path) |         builder.add_from_file(path) | ||||||
|         self.set_menubar(builder.get_object("menubar")) |         menubar = builder.get_object("menubar") | ||||||
|         self.set_app_menu(builder.get_object("appmenu")) |         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): |     def do_activate(self): | ||||||
|         Gtk.Application.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 GdkPixbuf | ||||||
| from gi.repository import Pango | from gi.repository import Pango | ||||||
| from gi.repository import GLib | from gi.repository import GLib | ||||||
|  | from gi.repository import Gio | ||||||
| from gajim import gtkgui_helpers | from gajim import gtkgui_helpers | ||||||
| from gajim import gui_menu_builder | from gajim import gui_menu_builder | ||||||
| from gajim import message_control | from gajim import message_control | ||||||
|  | @ -251,15 +252,6 @@ class PrivateChatControl(ChatControl): | ||||||
|             return |             return | ||||||
|         self.show_avatar() |         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): |     def update_contact(self): | ||||||
|         self.contact = self.gc_contact.as_contact() |         self.contact = self.gc_contact.as_contact() | ||||||
| 
 | 
 | ||||||
|  | @ -274,20 +266,6 @@ class PrivateChatControl(ChatControl): | ||||||
| 
 | 
 | ||||||
|         self.session.negotiate_e2e(False) |         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): |     def got_disconnected(self): | ||||||
|         ChatControl.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 |         # Keep error dialog instance to be sure to have only once at a time | ||||||
|         self.error_dialog = None |         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.emoticons_button = self.xml.get_object('emoticons_button') | ||||||
|         self.toggle_emoticons() |         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 = self.xml.get_object('formattings_button') | ||||||
|         formattings_button.set_sensitive(False) |         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 |         self.current_tooltip = None | ||||||
|         if parent_win is not None: |         if parent_win is not None: | ||||||
|             # On AutoJoin with minimize Groupchats are created without parent |             # 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.set_tooltip() | ||||||
|  |             self.add_actions() | ||||||
| 
 | 
 | ||||||
|         widget = self.xml.get_object('list_treeview') |         widget = self.xml.get_object('list_treeview') | ||||||
|         id_ = widget.connect('row_expanded', self.on_list_treeview_row_expanded) |         id_ = widget.connect('row_expanded', self.on_list_treeview_row_expanded) | ||||||
|  | @ -399,8 +339,6 @@ class GroupchatControl(ChatControlBase): | ||||||
|         if not self.name: |         if not self.name: | ||||||
|             self.name = self.room_jid.split('@')[0] |             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'), |         self.widget_set_visible(self.xml.get_object('banner_eventbox'), | ||||||
|             app.config.get('hide_groupchat_banner')) |             app.config.get('hide_groupchat_banner')) | ||||||
|         self.widget_set_visible(self.xml.get_object('list_scrolledwindow'), |         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) |         id_ = self.hpaned.connect('notify', self.on_hpaned_notify) | ||||||
|         self.handlers[id_] = self.hpaned |         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 |         # set the position of the current hpaned | ||||||
|         hpaned_position = app.config.get('gc-hpaned-position') |         hpaned_position = app.config.get('gc-hpaned-position') | ||||||
|         self.hpaned.set_position(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)) |             gui_menu_builder.get_encryption_menu(self.control_id, self.type_id)) | ||||||
|         self.set_encryption_menu_icon() |         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, |         app.ged.register_event_handler('gc-presence-received', ged.GUI1, | ||||||
|             self._nec_gc_presence_received) |             self._nec_gc_presence_received) | ||||||
|         app.ged.register_event_handler('gc-message-received', ged.GUI1, |         app.ged.register_event_handler('gc-message-received', ged.GUI1, | ||||||
|  | @ -538,14 +496,199 @@ class GroupchatControl(ChatControlBase): | ||||||
| 
 | 
 | ||||||
|         self.update_ui() |         self.update_ui() | ||||||
|         self.widget.show_all() |         self.widget.show_all() | ||||||
| 
 |  | ||||||
|         # PluginSystem: adding GUI extension point for this GroupchatControl |         # PluginSystem: adding GUI extension point for this GroupchatControl | ||||||
|         # instance object |         # instance object | ||||||
|         app.plugin_manager.gui_extension_point('groupchat_control', self) |         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): |     def on_groupchat_maximize(self): | ||||||
|         self.set_tooltip() |         self.set_tooltip() | ||||||
|         self.add_window_actions() |         self.add_window_actions() | ||||||
|  |         self.add_actions() | ||||||
|  |         self.update_actions() | ||||||
|         self.set_lock_image() |         self.set_lock_image() | ||||||
|         self._schedule_activity_timers() |         self._schedule_activity_timers() | ||||||
| 
 | 
 | ||||||
|  | @ -823,10 +966,6 @@ class GroupchatControl(ChatControlBase): | ||||||
|         self.authentication_button.set_tooltip_text(tooltip) |         self.authentication_button.set_tooltip_text(tooltip) | ||||||
|         self.widget_set_visible(self.authentication_button, not visible) |         self.widget_set_visible(self.authentication_button, not visible) | ||||||
|         context = self.msg_scrolledwindow.get_style_context() |         context = self.msg_scrolledwindow.get_style_context() | ||||||
|         if visible: |  | ||||||
|             context.add_class('authentication') |  | ||||||
|         else: |  | ||||||
|             context.remove_class('authentication') |  | ||||||
|         self.lock_image.set_sensitive(visible) |         self.lock_image.set_sensitive(visible) | ||||||
| 
 | 
 | ||||||
|     def _on_authentication_button_clicked(self, widget): |     def _on_authentication_button_clicked(self, widget): | ||||||
|  | @ -886,7 +1025,6 @@ class GroupchatControl(ChatControlBase): | ||||||
|         room jid, subject |         room jid, subject | ||||||
|         """ |         """ | ||||||
|         self.name_label.set_ellipsize(Pango.EllipsizeMode.END) |         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() |         font_attrs, font_attrs_small = self.get_font_attrs() | ||||||
|         if self.is_continued: |         if self.is_continued: | ||||||
|             name = self.get_continued_conversation_name() |             name = self.get_continued_conversation_name() | ||||||
|  | @ -896,169 +1034,10 @@ class GroupchatControl(ChatControlBase): | ||||||
|         self.name_label.set_markup(text) |         self.name_label.set_markup(text) | ||||||
| 
 | 
 | ||||||
|         if self.subject: |         if self.subject: | ||||||
|             subject = helpers.reduce_chars_newlines(self.subject, max_lines=2) |             subject = GLib.markup_escape_text(self.subject) | ||||||
|             subject = GLib.markup_escape_text(subject) |  | ||||||
|             subject_text = self.urlfinder.sub(self.make_href, subject) |             subject_text = self.urlfinder.sub(self.make_href, subject) | ||||||
|             subject_text = '<span %s>%s</span>' % (font_attrs_small, |             subject_text = '<span>%s</span>' % subject_text | ||||||
|                 subject_text) |             self.subject_button.get_popover().set_text(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() |  | ||||||
| 
 | 
 | ||||||
|     def _nec_vcard_published(self, obj): |     def _nec_vcard_published(self, obj): | ||||||
|         if obj.conn.name != self.account: |         if obj.conn.name != self.account: | ||||||
|  | @ -1379,6 +1358,11 @@ class GroupchatControl(ChatControlBase): | ||||||
|         else: |         else: | ||||||
|             self.print_conversation(text) |             self.print_conversation(text) | ||||||
| 
 | 
 | ||||||
|  |         if obj.subject == '': | ||||||
|  |             self.subject_button.hide() | ||||||
|  |         else: | ||||||
|  |             self.subject_button.show() | ||||||
|  | 
 | ||||||
|     def _nec_gc_config_changed_received(self, obj): |     def _nec_gc_config_changed_received(self, obj): | ||||||
|         # statuscode is a list |         # statuscode is a list | ||||||
|         # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify |         # 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 = self.xml.get_object('formattings_button') | ||||||
|         formattings_button.set_sensitive(True) |         formattings_button.set_sensitive(True) | ||||||
|         change_nick_button = self.xml.get_object('change_nick_button') | 
 | ||||||
|         change_nick_button.set_sensitive(True) |         self.update_actions() | ||||||
|         change_subject_button = self.xml.get_object('change_subject_button') |  | ||||||
|         change_subject_button.set_sensitive(True) |  | ||||||
| 
 | 
 | ||||||
|     def got_disconnected(self): |     def got_disconnected(self): | ||||||
|         formattings_button = self.xml.get_object('formattings_button') |         formattings_button = self.xml.get_object('formattings_button') | ||||||
|         formattings_button.set_sensitive(False) |         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.list_treeview.set_model(None) | ||||||
|         self.model.clear() |         self.model.clear() | ||||||
|         nick_list = app.contacts.get_nick_list(self.account, self.room_jid) |         nick_list = app.contacts.get_nick_list(self.account, self.room_jid) | ||||||
|  | @ -1512,6 +1490,8 @@ class GroupchatControl(ChatControlBase): | ||||||
|             if ar_to: |             if ar_to: | ||||||
|                 self.autorejoin = GLib.timeout_add_seconds(ar_to, self.rejoin) |                 self.autorejoin = GLib.timeout_add_seconds(ar_to, self.rejoin) | ||||||
| 
 | 
 | ||||||
|  |         self.update_actions() | ||||||
|  | 
 | ||||||
|     def rejoin(self): |     def rejoin(self): | ||||||
|         if not self.autorejoin: |         if not self.autorejoin: | ||||||
|             return False |             return False | ||||||
|  | @ -1904,6 +1884,11 @@ class GroupchatControl(ChatControlBase): | ||||||
|                     st += ' (' + obj.status + ')' |                     st += ' (' + obj.status + ')' | ||||||
|                 self.print_conversation(st, graphics=False) |                 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, |     def add_contact_to_roster(self, nick, show, role, affiliation, status, | ||||||
|     jid='', avatar_sha=None): |     jid='', avatar_sha=None): | ||||||
|         role_name = helpers.get_uf_role(role, plural=True) |         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, |             _('Please specify the new subject:'), input_str=self.subject, | ||||||
|             ok_handler=on_ok, transient_for=self.parent_win.window) |             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, |     def _on_drag_data_received(self, widget, context, x, y, selection, | ||||||
|     target_type, timestamp): |     target_type, timestamp): | ||||||
|         # Invite contact to groupchat |         # Invite contact to groupchat | ||||||
|  | @ -2913,3 +2837,40 @@ class GroupchatControl(ChatControlBase): | ||||||
|             self.grant_owner(widget, jid) |             self.grant_owner(widget, jid) | ||||||
|         else: |         else: | ||||||
|             self.revoke_owner(widget, jid) |             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) |     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) |     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) |     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): | def get_icon_pixmap(icon_name, size=16, color=None, quiet=False): | ||||||
|     try: |     try: | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ import gi | ||||||
| gi.require_version('GtkSpell', '3.0') | gi.require_version('GtkSpell', '3.0') | ||||||
| from gi.repository import GtkSpell | from gi.repository import GtkSpell | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def ensure_attached(func): | def ensure_attached(func): | ||||||
|     def f(self, *args, **kwargs): |     def f(self, *args, **kwargs): | ||||||
|         if self.spell: |         if self.spell: | ||||||
|  |  | ||||||
|  | @ -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): | def get_bookmarks_menu(account, rebuild=False): | ||||||
|     if not app.connections[account].bookmarks: |     if not app.connections[account].bookmarks: | ||||||
|         return None |         return None | ||||||
|  | @ -708,7 +767,11 @@ def get_account_menu(account): | ||||||
| def build_accounts_menu(): | def build_accounts_menu(): | ||||||
|     menubar = app.app.get_menubar() |     menubar = app.app.get_menubar() | ||||||
|     # Accounts Submenu |     # 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() |     acc_menu.remove_all() | ||||||
|     accounts_list = sorted(app.contacts.get_accounts()) |     accounts_list = sorted(app.contacts.get_accounts()) | ||||||
|     if not accounts_list: |     if not accounts_list: | ||||||
|  | @ -721,8 +784,8 @@ def build_accounts_menu(): | ||||||
|                 acc, get_account_menu(acc)) |                 acc, get_account_menu(acc)) | ||||||
|     else: |     else: | ||||||
|         acc_menu = get_account_menu(accounts_list[0]) |         acc_menu = get_account_menu(accounts_list[0]) | ||||||
|         menubar.remove(0) |         menubar.remove(menu_position) | ||||||
|         menubar.insert_submenu(0, 'Accounts', acc_menu) |         menubar.insert_submenu(menu_position, 'Accounts', acc_menu) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def build_bookmark_menu(account): | def build_bookmark_menu(account): | ||||||
|  | @ -731,8 +794,12 @@ def build_bookmark_menu(account): | ||||||
|     if not bookmark_menu: |     if not bookmark_menu: | ||||||
|         return |         return | ||||||
| 
 | 
 | ||||||
|  |     menu_position = 0 | ||||||
|  |     if os.name == 'nt': | ||||||
|  |         menu_position = 1 | ||||||
|  | 
 | ||||||
|     # Accounts Submenu |     # 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 |     # We have more than one Account active | ||||||
|     if acc_menu.get_item_link(0, 'submenu'): |     if acc_menu.get_item_link(0, 'submenu'): | ||||||
|  |  | ||||||
|  | @ -57,7 +57,6 @@ class MessageControl(object): | ||||||
|         self.widget_name = widget_name |         self.widget_name = widget_name | ||||||
|         self.contact = contact |         self.contact = contact | ||||||
|         self.account = account |         self.account = account | ||||||
|         self.hide_chat_buttons = False |  | ||||||
|         self.resource = resource |         self.resource = resource | ||||||
|         # control_id is a unique id for the control, |         # control_id is a unique id for the control, | ||||||
|         # its used as action name for actions that belong to a control |         # its used as action name for actions that belong to a control | ||||||
|  | @ -175,12 +174,6 @@ class MessageControl(object): | ||||||
|         """ |         """ | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     def chat_buttons_set_visible(self, state): |  | ||||||
|         """ |  | ||||||
|         Derived classes MAY implement this |  | ||||||
|         """ |  | ||||||
|         self.hide_chat_buttons = state |  | ||||||
| 
 |  | ||||||
|     def got_connected(self): |     def got_connected(self): | ||||||
|         pass |         pass | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -24,7 +24,6 @@ | ||||||
| import gc | import gc | ||||||
| 
 | 
 | ||||||
| from gi.repository import Gtk | from gi.repository import Gtk | ||||||
| from gi.repository import GObject |  | ||||||
| from gi.repository import GLib | from gi.repository import GLib | ||||||
| from gi.repository import Pango | from gi.repository import Pango | ||||||
| 
 | 
 | ||||||
|  | @ -37,6 +36,7 @@ class MessageTextView(Gtk.TextView): | ||||||
|     chat/groupchat windows |     chat/groupchat windows | ||||||
|     """ |     """ | ||||||
|     UNDO_LIMIT = 20 |     UNDO_LIMIT = 20 | ||||||
|  |     PLACEHOLDER = _('Write a message..') | ||||||
| 
 | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         Gtk.TextView.__init__(self) |         Gtk.TextView.__init__(self) | ||||||
|  | @ -64,6 +64,9 @@ class MessageTextView(Gtk.TextView): | ||||||
|         self.color_tags = [] |         self.color_tags = [] | ||||||
|         self.fonts_tags = [] |         self.fonts_tags = [] | ||||||
|         self.other_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'] = _buffer.create_tag('bold') | ||||||
|         self.other_tags['bold'].set_property('weight', Pango.Weight.BOLD) |         self.other_tags['bold'].set_property('weight', Pango.Weight.BOLD) | ||||||
|         self.begin_tags['bold'] = '<strong>' |         self.begin_tags['bold'] = '<strong>' | ||||||
|  | @ -82,6 +85,30 @@ class MessageTextView(Gtk.TextView): | ||||||
|         self.end_tags['strike'] = '</span>' |         self.end_tags['strike'] = '</span>' | ||||||
| 
 | 
 | ||||||
|         self.connect_after('paste-clipboard', self.after_paste_clipboard) |         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 _on_focus_in(self, *args): | ||||||
|  |         buf = self.get_buffer() | ||||||
|  |         start, end = buf.get_bounds() | ||||||
|  |         text = buf.get_text(start, end, True) | ||||||
|  |         if text == self.PLACEHOLDER: | ||||||
|  |             buf.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): |     def after_paste_clipboard(self, textview): | ||||||
|         buffer_ = textview.get_buffer() |         buffer_ = textview.get_buffer() | ||||||
|  |  | ||||||
|  | @ -81,7 +81,6 @@ class MessageWindow(object): | ||||||
|         self.xml = gtkgui_helpers.get_gtk_builder('%s.ui' % self.widget_name) |         self.xml = gtkgui_helpers.get_gtk_builder('%s.ui' % self.widget_name) | ||||||
|         self.window = self.xml.get_object(self.widget_name) |         self.window = self.xml.get_object(self.widget_name) | ||||||
|         self.window.set_application(app.app) |         self.window.set_application(app.app) | ||||||
|         self.window.set_show_menubar(False) |  | ||||||
|         self.notebook = self.xml.get_object('notebook') |         self.notebook = self.xml.get_object('notebook') | ||||||
|         self.parent_paned = None |         self.parent_paned = None | ||||||
| 
 | 
 | ||||||
|  | @ -94,17 +93,26 @@ class MessageWindow(object): | ||||||
|             if app.config.get('roster_on_the_right'): |             if app.config.get('roster_on_the_right'): | ||||||
|                 child1 = self.parent_paned.get_child1() |                 child1 = self.parent_paned.get_child1() | ||||||
|                 self.parent_paned.remove(child1) |                 self.parent_paned.remove(child1) | ||||||
|                 self.parent_paned.add(self.notebook) |                 self.parent_paned.pack1(self.notebook, resize=False) | ||||||
|                 self.parent_paned.pack1(self.notebook, resize=False, |                 self.parent_paned.pack2(child1) | ||||||
|                     shrink=True) |  | ||||||
|                 self.parent_paned.pack2(child1, resize=True, shrink=True) |  | ||||||
|             else: |             else: | ||||||
|                 self.parent_paned.add(self.notebook) |                 self.parent_paned.pack2(self.notebook) | ||||||
|                 self.parent_paned.pack2(self.notebook, resize=True, shrink=True) |  | ||||||
|             self.window.lookup_action('show-roster').set_enabled(True) |             self.window.lookup_action('show-roster').set_enabled(True) | ||||||
|             orig_window.destroy() |             orig_window.destroy() | ||||||
|             del orig_window |             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 |         # NOTE: we use 'connect_after' here because in | ||||||
|         # MessageWindowMgr._new_window we register handler that saves window |         # MessageWindowMgr._new_window we register handler that saves window | ||||||
|         # state when closing it, and it should be called before |         # 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.notebook.set_show_border(app.config.get('tabs_border')) | ||||||
|         self.show_icon() |         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): |     def change_account_name(self, old_name, new_name): | ||||||
|         if old_name in self._controls: |         if old_name in self._controls: | ||||||
|             self._controls[new_name] = self._controls[old_name] |             self._controls[new_name] = self._controls[old_name] | ||||||
|  | @ -324,6 +335,7 @@ class MessageWindow(object): | ||||||
|             self.notebook.show_all() |             self.notebook.show_all() | ||||||
|         else: |         else: | ||||||
|             self.window.show_all() |             self.window.show_all() | ||||||
|  | 
 | ||||||
|         # NOTE: we do not call set_control_active(True) since we don't know |         # NOTE: we do not call set_control_active(True) since we don't know | ||||||
|         # whether the tab is the active one. |         # whether the tab is the active one. | ||||||
|         self.show_title() |         self.show_title() | ||||||
|  | @ -436,9 +448,6 @@ class MessageWindow(object): | ||||||
|             elif chr(keyval) in st: # ALT + 1,2,3.. |             elif chr(keyval) in st: # ALT + 1,2,3.. | ||||||
|                 self.notebook.set_current_page(st.index(chr(keyval))) |                 self.notebook.set_current_page(st.index(chr(keyval))) | ||||||
|                 return True |                 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 |             elif keyval == Gdk.KEY_m: # ALT + M show emoticons menu | ||||||
|                 control.emoticons_button.get_popover().show() |                 control.emoticons_button.get_popover().show() | ||||||
|                 return True |                 return True | ||||||
|  | @ -570,6 +579,7 @@ class MessageWindow(object): | ||||||
|         ask any confirmation |         ask any confirmation | ||||||
|         """ |         """ | ||||||
|         def close(ctrl): |         def close(ctrl): | ||||||
|  |             self.remove_headermenu(self.notebook, ctrl) | ||||||
|             if reason is not None: # We are leaving gc with a status message |             if reason is not None: # We are leaving gc with a status message | ||||||
|                 ctrl.shutdown(reason) |                 ctrl.shutdown(reason) | ||||||
|             else: # We are leaving gc without status message or it's a chat |             else: # We are leaving gc without status message or it's a chat | ||||||
|  | @ -607,6 +617,7 @@ class MessageWindow(object): | ||||||
| 
 | 
 | ||||||
|         def on_minimize(ctrl): |         def on_minimize(ctrl): | ||||||
|             if method != self.CLOSE_COMMAND: |             if method != self.CLOSE_COMMAND: | ||||||
|  |                 self.remove_headermenu(self.notebook, ctrl) | ||||||
|                 ctrl.minimize() |                 ctrl.minimize() | ||||||
|                 self.check_tabs() |                 self.check_tabs() | ||||||
|                 return |                 return | ||||||
|  | @ -618,6 +629,13 @@ class MessageWindow(object): | ||||||
|         else: |         else: | ||||||
|             ctrl.allow_shutdown(method, on_yes, on_no, on_minimize) |             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): |     def check_tabs(self): | ||||||
|         if self.parent_paned: |         if self.parent_paned: | ||||||
|             # Do nothing in single window mode |             # Do nothing in single window mode | ||||||
|  | @ -822,6 +840,8 @@ class MessageWindow(object): | ||||||
| 
 | 
 | ||||||
|     def popup_menu(self, event): |     def popup_menu(self, event): | ||||||
|         menu = self.get_active_control().prepare_context_menu() |         menu = self.get_active_control().prepare_context_menu() | ||||||
|  |         if menu is None: | ||||||
|  |             return | ||||||
|         # show the menu |         # show the menu | ||||||
|         menu.attach_to_widget(app.interface.roster.window, None) |         menu.attach_to_widget(app.interface.roster.window, None) | ||||||
|         menu.show_all() |         menu.show_all() | ||||||
|  | @ -836,6 +856,7 @@ class MessageWindow(object): | ||||||
|         new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num)) |         new_ctrl = self._widget_to_control(notebook.get_nth_page(page_num)) | ||||||
|         new_ctrl.set_control_active(True) |         new_ctrl.set_control_active(True) | ||||||
|         self.show_title(control = new_ctrl) |         self.show_title(control = new_ctrl) | ||||||
|  |         self.set_header_menu(new_ctrl.control_menu) | ||||||
| 
 | 
 | ||||||
|         control = self.get_active_control() |         control = self.get_active_control() | ||||||
|         if isinstance(control, ChatControlBase): |         if isinstance(control, ChatControlBase): | ||||||
|  | @ -879,6 +900,7 @@ class MessageWindow(object): | ||||||
| 
 | 
 | ||||||
|         if isinstance(control, ChatControlBase): |         if isinstance(control, ChatControlBase): | ||||||
|             # we forwarded it to message textview |             # we forwarded it to message textview | ||||||
|  |             control.msg_textview.remove_placeholder() | ||||||
|             control.msg_textview.event(event) |             control.msg_textview.event(event) | ||||||
|             control.msg_textview.grab_focus() |             control.msg_textview.grab_focus() | ||||||
| 
 | 
 | ||||||
|  | @ -1289,6 +1311,7 @@ class MessageWindowMgr(GObject.GObject): | ||||||
|                 gtkgui_helpers.resize_window(w.window, |                 gtkgui_helpers.resize_window(w.window, | ||||||
|                         app.config.get('roster_width'), |                         app.config.get('roster_width'), | ||||||
|                         app.config.get('roster_height')) |                         app.config.get('roster_height')) | ||||||
|  |                 self.hide_header_bar(self.parent_win) | ||||||
| 
 | 
 | ||||||
|         self._windows = {} |         self._windows = {} | ||||||
| 
 | 
 | ||||||
|  | @ -1298,8 +1321,16 @@ class MessageWindowMgr(GObject.GObject): | ||||||
|                 mw = self.create_window(ctrl.contact, ctrl.account, |                 mw = self.create_window(ctrl.contact, ctrl.account, | ||||||
|                                         ctrl.type_id) |                                         ctrl.type_id) | ||||||
|             ctrl.parent_win = mw |             ctrl.parent_win = mw | ||||||
|  |             ctrl.add_actions() | ||||||
|  |             ctrl.update_actions() | ||||||
|             mw.new_tab(ctrl) |             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): |     def save_opened_controls(self): | ||||||
|         if not app.config.get('remember_opened_chat_controls'): |         if not app.config.get('remember_opened_chat_controls'): | ||||||
|             return |             return | ||||||
|  |  | ||||||
|  | @ -5696,9 +5696,18 @@ class RosterWindow: | ||||||
|         application.add_window(self.window) |         application.add_window(self.window) | ||||||
|         self.add_actions() |         self.add_actions() | ||||||
|         self.hpaned = self.xml.get_object('roster_hpaned') |         self.hpaned = self.xml.get_object('roster_hpaned') | ||||||
|  | 
 | ||||||
|         app.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned) |         app.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned) | ||||||
|         app.interface.msg_win_mgr.connect('window-delete', |         app.interface.msg_win_mgr.connect('window-delete', | ||||||
|             self.on_message_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 |         self.advanced_menus = [] # We keep them to destroy them | ||||||
|         if app.config.get('roster_window_skip_taskbar'): |         if app.config.get('roster_window_skip_taskbar'): | ||||||
|             self.window.set_property('skip-taskbar-hint', True) |             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