Commit 02c16764 authored by Martin's avatar Martin

Imported upstream master from 2017-11-04

parent 55278263
......@@ -1268,8 +1268,9 @@ class ChatControl(ChatControlBase):
if target_type == self.TARGET_TYPE_URI_LIST:
if not c.resource: # If no resource is known, we can't send a file
return
uri = selection.get_data().strip()
uri_splitted = uri.split() # we may have more than one file dropped
# we may have more than one file dropped
uri_splitted = selection.get_uris()
for uri in uri_splitted:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
if os.path.isfile(path): # is it file?
......@@ -1384,7 +1385,7 @@ class ChatControl(ChatControlBase):
self.print_conversation(event.message, kind, tim=event.time,
encrypted=event.encrypted, subject=event.subject,
xhtml=event.xhtml, displaymarking=event.displaymarking,
correct_id=event.correct_id)
correct_id=event.correct_id, additional_data=event.additional_data)
if isinstance(event.msg_log_id, int):
message_ids.append(event.msg_log_id)
......
......@@ -318,12 +318,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
# add MessageTextView to UI and connect signals
self.msg_textview = MessageTextView()
self.msg_scrolledwindow = ScrolledWindow()
self.msg_scrolledwindow.set_max_content_height(100)
self.msg_scrolledwindow.set_propagate_natural_height(True)
self.msg_scrolledwindow.get_style_context().add_class('scrolledtextview')
self.msg_scrolledwindow.set_property('shadow_type', Gtk.ShadowType.IN)
self.msg_scrolledwindow.add(self.msg_textview)
hbox = self.xml.get_object('hbox')
......@@ -1345,6 +1340,14 @@ class ScrolledWindow(Gtk.ScrolledWindow):
def __init__(self, *args, **kwargs):
Gtk.ScrolledWindow.__init__(self, *args, **kwargs)
self.set_overlay_scrolling(False)
self.set_max_content_height(100)
self.set_propagate_natural_height(True)
self.get_style_context().add_class('scrolled-no-border')
self.get_style_context().add_class('no-scroll-indicator')
self.get_style_context().add_class('scrollbar-style')
self.set_shadow_type(Gtk.ShadowType.IN)
def do_get_preferred_height(self):
min_height, natural_height = Gtk.ScrolledWindow.do_get_preferred_height(self)
child = self.get_child()
......
......@@ -237,6 +237,7 @@ class Config:
'notification_position_y': [opt_int, -1],
'muc_highlight_words': [opt_str, '', _('A semicolon-separated list of words that will be highlighted in group chats.')],
'quit_on_roster_x_button': [opt_bool, False, _('If True, quits Gajim when X button of Window Manager is clicked. This setting is taken into account only if notification icon is used.')],
'hide_on_roster_x_button': [opt_bool, False, _('If True, Gajim hides the Roster window on pressing the X button instead of minimizing into the Dock.')],
'show_unread_tab_icon': [opt_bool, False, _('If True, Gajim will display an icon on each tab containing unread messages. Depending on the theme, this icon may be animated.')],
'show_status_msgs_in_roster': [opt_bool, True, _('If True, Gajim will display the status message, if not empty, for every contact under the contact name in roster window.'), True],
'show_avatars_in_roster': [opt_bool, True, '', True],
......
......@@ -127,6 +127,18 @@ class HelperEvent:
self.chatstate = child.getName()
break
def get_oob_data(self, stanza):
oob_node = stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
if oob_node is not None:
if 'gajim' not in self.additional_data:
self.additional_data['gajim'] = {}
oob_url = oob_node.getTagData('url')
if oob_url is not None:
self.additional_data['gajim']['oob_url'] = oob_url
oob_desc = oob_node.getTagData('desc')
if oob_desc is not None:
self.additional_data['gajim']['oob_desc'] = oob_desc
class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
name = 'http-auth-received'
base_network_events = []
......@@ -1097,6 +1109,9 @@ class MamDecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
# For example Chatstates, Receipts, Chatmarkers
log.debug('Received MAM message without text')
return
self.get_oob_data(self.msg_)
self.is_pm = app.logger.jid_is_room_jid(self.with_.getStripped())
if self.is_pm is None:
# Check if this event is triggered after a disco, so we dont
......@@ -1124,7 +1139,7 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
base_network_events = ['raw-message-received']
def init(self):
self.additional_data = None
self.additional_data = {}
def generate(self):
self.conn = self.base_event.conn
......@@ -1399,8 +1414,7 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
def generate(self):
self.stanza = self.msg_obj.stanza
if not hasattr(self, 'additional_data'):
self.additional_data = self.msg_obj.additional_data
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.id_
self.jid = self.msg_obj.jid
self.fjid = self.msg_obj.fjid
......@@ -1452,19 +1466,7 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.get_chatstate()
oob_node = self.stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
self.oob_url = None
self.oob_desc = None
if oob_node:
self.oob_url = oob_node.getTagData('url')
self.oob_desc = oob_node.getTagData('desc')
if self.oob_url:
self.msgtxt += '\n'
if self.oob_desc:
self.msgtxt += self.oob_desc
else:
self.msgtxt += _('URL:')
self.msgtxt += ' ' + self.oob_url
self.get_oob_data(self.stanza)
replace = self.stanza.getTag('replace', namespace=nbxmpp.NS_CORRECT)
if replace:
......@@ -1506,7 +1508,9 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
def generate(self):
self.stanza = self.msg_obj.stanza
if not hasattr(self, 'additional_data'):
if not hasattr(self.msg_obj, 'additional_data'):
self.additional_data = {}
else:
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.stanza.getID()
self.fjid = self.msg_obj.fjid
......@@ -2696,7 +2700,7 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
base_network_events = []
def init(self):
self.additional_data = None
self.additional_data = {}
self.message = None
self.keyID = None
self.type_ = 'chat'
......
......@@ -24,7 +24,7 @@ class OptionKind(IntEnum):
class OptionType(IntEnum):
ACCOUNT_CONFIG = 0
CONFIG = 1
BOOL = 2
VALUE = 2
ACTION = 3
DIALOG = 4
......
......@@ -83,6 +83,8 @@ class ChatEvent(Event):
self.form_node = form_node
self.displaymarking = displaymarking
self.sent_forwarded = sent_forwarded
if additional_data is None:
additional_data = {}
self.additional_data = additional_data
class NormalEvent(ChatEvent):
......
......@@ -132,9 +132,8 @@ class Logger:
Row = namedtuple("Row", fields)
named_row = Row(*row)
if 'additional_data' in fields:
if named_row.additional_data is not None:
named_row = named_row._replace(
additional_data=json.loads(named_row.additional_data))
named_row = named_row._replace(
additional_data=json.loads(named_row.additional_data or '{}'))
return named_row
def dispatch(self, event, error):
......@@ -917,7 +916,9 @@ class Logger:
app.config.set_per('accounts', account_name, 'roster_version', '')
account_jid = app.get_jid_from_account(account_name)
account_jid_id = self.get_jid_id(account_jid)
# Execute get_jid_id() because this ensures on new accounts that the
# jid_id will be created
self.get_jid_id(account_jid, type_=JIDConstant.NORMAL_TYPE)
# Delete old roster
self.remove_roster(account_jid)
......@@ -1113,7 +1114,13 @@ class Logger:
a field in the `logs` table
"""
jid_id = self.get_jid_id(jid, kind=kind)
if 'additional_data' in kwargs:
if not kwargs['additional_data']:
del kwargs['additional_data']
else:
kwargs['additional_data'] = json.dumps(kwargs["additional_data"])
sql = '''
INSERT INTO logs (jid_id, time, kind, {columns})
VALUES (?, ?, ?, {values})
......
......@@ -133,7 +133,8 @@ class ConnectionArchive313:
app.logger.insert_into_logs(
obj.with_, obj.timestamp, obj.kind,
unread=False,
message=obj.msgtxt)
message=obj.msgtxt,
additional_data=obj.additional_data)
def get_query_id(self):
self.mam_query_id = self.connection.getAnID()
......
......@@ -844,6 +844,18 @@ class ConversationTextview(GObject.GObject):
# We impose an arbitrary limit of 100 specials per message.
specials_limit = 100
# add oob text to the end
try:
gajim_data = additional_data['gajim']
oob_url = gajim_data['oob_url']
except KeyError:
pass
else:
oob_desc = additional_data['gajim'].get('oob_desc', None)
if oob_desc is None:
oob_desc = _('URL:')
otext += '\n{} {}'.format(oob_desc, oob_url)
# basic: links + mail + formatting is always checked (we like that)
if app.config.get('emoticons_theme') and graphics:
# search for emoticons & urls
......@@ -892,7 +904,7 @@ class ConversationTextview(GObject.GObject):
# PluginSystem: adding GUI extension point for ConversationTextview
self.plugin_modified = False
app.plugin_manager.extension_point('print_special_text', self,
special_text, other_tags, graphics, additional_data)
special_text, other_tags, graphics, additional_data, iter_)
if self.plugin_modified:
return
......@@ -1072,6 +1084,8 @@ class ConversationTextview(GObject.GObject):
"""
Print 'chat' type messages
"""
if additional_data is None:
additional_data = {}
buffer_ = self.tv.get_buffer()
buffer_.begin_user_action()
insert_mark = None
......
......@@ -572,22 +572,39 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<style>
<class name="chatcontrol-separator-top"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="height_request">60</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="overlay_scrolling">False</property>
<child>
<placeholder/>
</child>
<style>
<class name="scrolled-no-border"/>
<class name="no-scroll-indicator"/>
<class name="scrollbar-style"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
<child>
......@@ -601,7 +618,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
<child>
......@@ -836,7 +853,7 @@ audio-mic-volume-low</property>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
</object>
......
......@@ -165,6 +165,20 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<style>
<class name="chatcontrol-separator-top"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="conversation_scrolledwindow">
<property name="width_request">200</property>
......@@ -172,17 +186,20 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="overlay_scrolling">False</property>
<child>
<placeholder/>
</child>
<style>
<class name="scrolled-no-border"/>
<class name="no-scroll-indicator"/>
<class name="scrollbar-style"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
<child>
......@@ -196,7 +213,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
<child>
......@@ -327,7 +344,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
<property name="position">4</property>
</packing>
</child>
</object>
......@@ -342,6 +359,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin_left">4</property>
<property name="margin_bottom">5</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
......@@ -357,6 +375,7 @@
</child>
<style>
<class name="scrolled-no-border"/>
<class name="no-scroll-indicator"/>
</style>
</object>
<packing>
......
......@@ -8,10 +8,12 @@
}
.scrolled-no-border {border: none}
.scrolled-no-border undershoot.top, undershoot.bottom { background-image: none; }
.no-scroll-indicator undershoot.top, undershoot.bottom { background-image: none; }
.scrollbar-style scrollbar trough {background-color:@theme_base_color; }
.scrollbar-style scrollbar {border:none; }
.scrollbar-style slider {min-width: 3px; background-color: #a0a3a4}
.actionbar-no-border box {border: none}
.actionbar-no-border button {
padding: 0px;
background-color: @theme_base_color;
......@@ -21,9 +23,9 @@
#MessageWindow, #RosterWindow paned { background-color: @theme_base_color; }
.scrolledtextview { border:none; }
.chatcontrol-separator-top {margin-top: 5px;}
.chatcontrol-separator {margin-bottom: 6px;}
.chatcontrol-separator {margin-bottom: 5px;}
#SubjectPopover box { padding: 10px; }
......
......@@ -3439,24 +3439,24 @@ class XMLConsoleWindow(Gtk.Window):
def on_filter_options(self, *args):
options = [
Option(OptionKind.SWITCH, 'Presence',
OptionType.BOOL, self.presence,
OptionType.VALUE, self.presence,
callback=self.on_option, data='presence'),
Option(OptionKind.SWITCH, 'Message',
OptionType.BOOL, self.message,
OptionType.VALUE, self.message,
callback=self.on_option, data='message'),
Option(OptionKind.SWITCH, 'Iq', OptionType.BOOL, self.iq,
Option(OptionKind.SWITCH, 'Iq', OptionType.VALUE, self.iq,
callback=self.on_option, data='iq'),
Option(OptionKind.SWITCH, 'Stream\nManagement',
OptionType.BOOL, self.stream,
OptionType.VALUE, self.stream,
callback=self.on_option, data='stream'),
Option(OptionKind.SWITCH, 'In', OptionType.BOOL, self.incoming,
Option(OptionKind.SWITCH, 'In', OptionType.VALUE, self.incoming,
callback=self.on_option, data='incoming'),
Option(OptionKind.SWITCH, 'Out', OptionType.BOOL, self.outgoing,
Option(OptionKind.SWITCH, 'Out', OptionType.VALUE, self.outgoing,
callback=self.on_option, data='outgoing'),
]
......
......@@ -53,15 +53,14 @@ from gajim.common import i18n
from gajim.common import logging_helpers
from gajim.common import crypto
MIN_NBXMPP_VER = "0.5.6"
MIN_NBXMPP_VER = "0.6.0"
class GajimApplication(Gtk.Application):
'''Main class handling activation and command line.'''
def __init__(self):
Gtk.Application.__init__(self, application_id='org.gajim.Gajim',
flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
Gtk.Application.__init__(self, application_id='org.gajim.Gajim')
self.add_main_option('version', ord('V'), GLib.OptionFlags.NONE,
GLib.OptionArg.NONE,
......@@ -94,6 +93,10 @@ class GajimApplication(Gtk.Application):
GLib.OptionArg.STRING_ARRAY,
"")
self.connect('handle-local-options', self._handle_local_options)
self.connect('startup', self._startup)
self.connect('activate', self._activate)
self.profile = ''
self.config_path = None
self.profile_separation = False
......@@ -103,9 +106,7 @@ class GajimApplication(Gtk.Application):
if GLib.get_application_name() != 'Gajim':
GLib.set_application_name('Gajim')
def do_startup(self):
Gtk.Application.do_startup(self)
def _startup(self, application):
from gajim import gtkexcepthook
gtkexcepthook.init()
......@@ -222,8 +223,10 @@ class GajimApplication(Gtk.Application):
menubar.prepend_submenu('Gajim', appmenu)
self.set_menubar(menubar)
def do_activate(self):
Gtk.Application.do_activate(self)
def _activate(self, application):
if self.interface is not None:
self.interface.roster.window.present()
return
from gajim.gui_interface import Interface
from gajim import gtkgui_helpers
self.interface = Interface()
......@@ -243,7 +246,8 @@ class GajimApplication(Gtk.Application):
from gajim.common import app
app.logger.commit()
def do_handle_local_options(self, options: GLib.VariantDict) -> int:
def _handle_local_options(self, application,
options: GLib.VariantDict) -> int:
logging_helpers.init()
......@@ -277,12 +281,6 @@ class GajimApplication(Gtk.Application):
return 0
return -1
def do_command_line(self, command_line: Gio.ApplicationCommandLine) -> int:
Gtk.Application.do_command_line(self, command_line)
if not command_line.get_is_remote():
self.activate()
return 0
def show_warnings(self):
import traceback
import warnings
......
......@@ -1097,23 +1097,29 @@ class GroupchatControl(ChatControlBase):
self.is_anonymous = False
if not obj.nick:
# message from server
self.print_conversation(obj.msgtxt, tim=obj.timestamp,
xhtml=obj.xhtml_msgtxt, displaymarking=obj.displaymarking)
self.print_conversation(
obj.msgtxt, tim=obj.timestamp,
xhtml=obj.xhtml_msgtxt, displaymarking=obj.displaymarking,
additional_data=obj.additional_data)
else:
# message from someone
if obj.has_timestamp:
# don't print xhtml if it's an old message.
# Like that xhtml messages are grayed too.
self.print_old_conversation(obj.msgtxt, contact=obj.nick,
self.print_old_conversation(
obj.msgtxt, contact=obj.nick,
tim=obj.timestamp, xhtml=None, encrypted=obj.encrypted,
displaymarking=obj.displaymarking, msg_stanza_id=obj.id_)
displaymarking=obj.displaymarking, msg_stanza_id=obj.id_,
additional_data=obj.additional_data)
else:
if obj.nick == self.nick:
self.last_sent_txt = obj.msgtxt
self.print_conversation(obj.msgtxt, contact=obj.nick,
self.print_conversation(
obj.msgtxt, contact=obj.nick,
tim=obj.timestamp, xhtml=obj.xhtml_msgtxt,
displaymarking=obj.displaymarking, encrypted=obj.encrypted,
correct_id=obj.correct_id, msg_stanza_id=obj.id_)
correct_id=obj.correct_id, msg_stanza_id=obj.id_,
additional_data=obj.additional_data)
obj.needs_highlight = self.needs_visual_notification(obj.msgtxt)
def on_private_message(self, nick, sent, msg, tim, xhtml, session, msg_log_id=None,
......@@ -1168,7 +1174,10 @@ class GroupchatControl(ChatControlBase):
return None
def print_old_conversation(self, text, contact='', tim=None, xhtml = None,
displaymarking=None, msg_stanza_id=None, encrypted=None):
displaymarking=None, msg_stanza_id=None, encrypted=None, additional_data=None):
if additional_data is None:
additional_data = {}
if contact:
if contact == self.nick: # it's us
kind = 'outgoing'
......@@ -1185,11 +1194,11 @@ class GroupchatControl(ChatControlBase):
small_attr, small_attr + ['restored_message'],
small_attr + ['restored_message'], count_as_new=False, xhtml=xhtml,
displaymarking=displaymarking, msg_stanza_id=msg_stanza_id,
encrypted=encrypted)
encrypted=encrypted, additional_data=additional_data)
def print_conversation(self, text, contact='', tim=None, xhtml=None,
graphics=True, displaymarking=None, correct_id=None, msg_stanza_id=None,
encrypted=None):
encrypted=None, additional_data=None):
"""
Print a line in the conversation
......@@ -1197,6 +1206,8 @@ class GroupchatControl(ChatControlBase):
(contact = 'info' in such a case).
If contact is not set: it's a message from the server or help.
"""
if additional_data is None:
additional_data = {}
other_tags_for_name = []
other_tags_for_text = []
if contact:
......@@ -1251,7 +1262,8 @@ class GroupchatControl(ChatControlBase):
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
other_tags_for_name, [], other_tags_for_text, xhtml=xhtml,
graphics=graphics, displaymarking=displaymarking,
correct_id=correct_id, msg_stanza_id=msg_stanza_id, encrypted=encrypted)
correct_id=correct_id, msg_stanza_id=msg_stanza_id, encrypted=encrypted,
additional_data=additional_data)
def get_nb_unread(self):
type_events = ['printed_marked_gc_msg']
......
......@@ -141,6 +141,11 @@ class HistoryWindow:
xml.connect_signals(self)
self.window.show_all()
# PluginSystem: adding GUI extension point for
# HistoryWindow instance object
app.plugin_manager.gui_extension_point(
'history_window', self)
def _fill_completion_dict(self):
"""
Fill completion_dict for key auto completion. Then load history for
......@@ -246,6 +251,10 @@ class HistoryWindow:
return account
def on_history_window_destroy(self, widget):
# PluginSystem: removing GUI extension points connected with
# HistoryWindow instance object
app.plugin_manager.remove_gui_extension_point(
'history_window', self)
self.history_textview.del_handlers()
del app.interface.instances['logs']
......
......@@ -8,7 +8,8 @@ from gajim import dialogs
class OptionsDialog(Gtk.ApplicationWindow):
def __init__(self, parent, title, flags, options, account):
def __init__(self, parent, title, flags, options, account,
extend=None):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app.app)
self.set_show_menubar(False)
......@@ -23,7 +24,7 @@ class OptionsDialog(Gtk.ApplicationWindow):
elif flags == Gtk.DialogFlags.DESTROY_WITH_PARENT:
self.set_destroy_with_parent(True)
self.listbox = OptionsBox(account)
self.listbox = OptionsBox(account, extend)
self.listbox.set_hexpand(True)
self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
......@@ -50,7 +51,7 @@ class OptionsDialog(Gtk.ApplicationWindow):
class OptionsBox(Gtk.ListBox):
def __init__(self, account):
def __init__(self, account, extend=None):
Gtk.ListBox.__init__(self)
self.set_name('OptionsBox')
self.account = account
......@@ -72,6 +73,10 @@ class OptionsBox(Gtk.ListBox):
OptionKind.GPG: GPGOption,
}
if extend is not None:
for option, callback in extend:
self.map[option] = callback
def add_option(self, option):
if option.props is not None:
listitem = self.map[option.kind](
......@@ -168,7 +173,7 @@ class GenericOption(Gtk.Grid):
def __get_value(type_, value, account):
if value is None:
return
if type_ == OptionType.BOOL:
if type_ == OptionType.VALUE:
return value
elif type_ == OptionType.CONFIG:
return app.config.get(value)
......
......@@ -2406,7 +2406,10 @@ class RosterWindow:
x, y = self.window.get_position()
app.config.set('roster_x-position', x)
app.config.set('roster_y-position', y)
self.window.iconify()
if app.config.get('hide_on_roster_x_button'):
self.window.hide()
else:
self.window.iconify()
elif app.config.get('quit_on_roster_x_button'):
self.on_quit_request()
else:
......@@ -2720,7 +2723,7 @@ class RosterWindow:
tim=obj.timestamp, encrypted=obj.encrypted, subject=obj.subject,
xhtml=obj.xhtml, displaymarking=obj.displaymarking,
msg_log_id=obj.msg_log_id, msg_stanza_id=obj.id_, correct_id=obj.correct_id,
xep0184_id=xep0184_id)
xep0184_id=xep0184_id, additional_data=obj.additional_data)
if obj.msg_log_id:
pw = obj.session.control.parent_win
end = obj.session.control.was_at_the_end
......@@ -3755,7 +3758,10 @@ class RosterWindow:
'quit_on_roster_x_button') and ((app.interface.systray_enabled and\
app.config.get('trayicon') == 'always') or app.config.get(
'allow_hide_roster')):
self.window.iconify()
if app.config.get('hide_on_roster_x_button'):
self.window.hide()
else:
self.window.iconify()
elif event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == \
Gdk.KEY_i:
treeselection = self.tree.get_selection()
......
......@@ -273,7 +273,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
displaymarking=obj.displaymarking,
sent_forwarded=obj.forwarded and obj.sent,
show_in_roster=obj.show_in_roster,
show_in_systray=obj.show_in_systray)
show_in_systray=obj.show_in_systray,
additional_data=obj.additional_data)
app.events.add_event(self.conn.name, fjid, event)
......
......@@ -269,7 +269,7 @@ setup(
data_files=data_files,
install_requires=[
'dbus-python;sys_platform=="linux"',
'nbxmpp',
'nbxmpp>=0.6.0',
'pyOpenSSL>=0.12',
'pyasn1',
],
......