Philipp Hörist pushed to branch chat_markers at gajim / gajim
Commits:
-
0cba9f53
by Daniel Brötzmann at 2019-09-29T19:42:30Z
-
5db3dc55
by Philipp Hörist at 2019-10-01T18:08:37Z
-
be5c65de
by Yann Leboulanger at 2019-10-01T19:45:09Z
-
b3c67112
by Yann Leboulanger at 2019-10-01T19:45:09Z
-
4032986b
by Philipp Hörist at 2019-10-01T19:45:09Z
-
9c17266f
by Yann Leboulanger at 2019-10-01T19:45:09Z
-
12bc8165
by Yann Leboulanger at 2019-10-01T19:45:09Z
-
f28a7812
by Philipp Hörist at 2019-10-01T21:36:38Z
17 changed files:
- gajim/app_actions.py
- gajim/application.py
- gajim/chat_control.py
- gajim/chat_control_base.py
- gajim/common/app.py
- gajim/common/connection.py
- gajim/common/events.py
- gajim/common/modules/__init__.py
- + gajim/common/modules/chat_markers.py
- gajim/common/modules/message.py
- gajim/data/gui/application_menu.ui
- gajim/data/gui/xml_console_window.ui → gajim/data/gui/xml_console.ui
- gajim/groupchat_control.py
- gajim/gtk/xml_console.py
- gajim/gui_interface.py
- gajim/gui_menu_builder.py
- gajim/session.py
Changes:
| ... | ... | @@ -249,12 +249,8 @@ def on_server_info(action, param): |
| 249 | 249 |
|
| 250 | 250 |
|
| 251 | 251 |
def on_xml_console(action, param):
|
| 252 |
- account = param.get_string()
|
|
| 253 |
- window = app.get_app_window(XMLConsoleWindow, account)
|
|
| 254 |
- if window is None:
|
|
| 255 |
- XMLConsoleWindow(account)
|
|
| 256 |
- else:
|
|
| 257 |
- window.present()
|
|
| 252 |
+ XMLConsoleWindow()
|
|
| 253 |
+ |
|
| 258 | 254 |
|
| 259 | 255 |
|
| 260 | 256 |
def on_manage_proxies(action, param):
|
| ... | ... | @@ -431,6 +431,7 @@ class GajimApplication(Gtk.Application): |
| 431 | 431 |
('history-manager', app_actions.on_history_manager),
|
| 432 | 432 |
('preferences', app_actions.on_preferences),
|
| 433 | 433 |
('plugins', app_actions.on_plugins),
|
| 434 |
+ ('xml-console', app_actions.on_xml_console),
|
|
| 434 | 435 |
('file-transfer', app_actions.on_file_transfers),
|
| 435 | 436 |
('history', app_actions.on_history),
|
| 436 | 437 |
('shortcuts', app_actions.on_keyboard_shortcuts),
|
| ... | ... | @@ -496,9 +497,7 @@ class GajimApplication(Gtk.Application): |
| 496 | 497 |
from gajim import app_actions as a
|
| 497 | 498 |
|
| 498 | 499 |
if account == 'Local':
|
| 499 |
- return [
|
|
| 500 |
- ('-xml-console', a.on_xml_console, 'always', 's')
|
|
| 501 |
- ]
|
|
| 500 |
+ return []
|
|
| 502 | 501 |
|
| 503 | 502 |
return [
|
| 504 | 503 |
('-bookmarks', a.on_bookmarks, 'online', 's'),
|
| ... | ... | @@ -507,7 +506,6 @@ class GajimApplication(Gtk.Application): |
| 507 | 506 |
('-add-contact', a.on_add_contact, 'online', 'as'),
|
| 508 | 507 |
('-services', a.on_service_disco, 'online', 's'),
|
| 509 | 508 |
('-profile', a.on_profile, 'feature', 's'),
|
| 510 |
- ('-xml-console', a.on_xml_console, 'always', 's'),
|
|
| 511 | 509 |
('-server-info', a.on_server_info, 'online', 's'),
|
| 512 | 510 |
('-archive', a.on_mam_preferences, 'feature', 's'),
|
| 513 | 511 |
('-pep-config', a.on_pep_config, 'online', 's'),
|
| ... | ... | @@ -1284,7 +1284,8 @@ class ChatControl(ChatControlBase): |
| 1284 | 1284 |
self.add_message(event.message, kind, tim=event.time,
|
| 1285 | 1285 |
encrypted=event.encrypted, subject=event.subject,
|
| 1286 | 1286 |
xhtml=event.xhtml, displaymarking=event.displaymarking,
|
| 1287 |
- correct_id=event.correct_id, additional_data=event.additional_data)
|
|
| 1287 |
+ correct_id=event.correct_id, message_id=event.message_id,
|
|
| 1288 |
+ additional_data=event.additional_data)
|
|
| 1288 | 1289 |
if isinstance(event.msg_log_id, int):
|
| 1289 | 1290 |
message_ids.append(event.msg_log_id)
|
| 1290 | 1291 |
|
| ... | ... | @@ -322,6 +322,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 322 | 322 |
self.received_history_pos = 0
|
| 323 | 323 |
self.orig_msg = None
|
| 324 | 324 |
|
| 325 |
+ # For XEP-0333
|
|
| 326 |
+ self.last_msg_id = None
|
|
| 327 |
+ |
|
| 325 | 328 |
self.set_emoticon_popover()
|
| 326 | 329 |
|
| 327 | 330 |
# Attach speller
|
| ... | ... | @@ -1022,6 +1025,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 1022 | 1025 |
if restored:
|
| 1023 | 1026 |
return
|
| 1024 | 1027 |
|
| 1028 |
+ if message_id:
|
|
| 1029 |
+ self.last_msg_id = message_id
|
|
| 1030 |
+ |
|
| 1025 | 1031 |
if kind == 'incoming':
|
| 1026 | 1032 |
if not self.type_id == message_control.TYPE_GC or \
|
| 1027 | 1033 |
app.config.notify_for_muc(jid) or \
|
| ... | ... | @@ -1065,6 +1071,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 1065 | 1071 |
event_type.type_, self.contact.jid)
|
| 1066 | 1072 |
|
| 1067 | 1073 |
event = event_type(text, subject, self, msg_log_id,
|
| 1074 |
+ msg_id=message_id,
|
|
| 1068 | 1075 |
show_in_roster=show_in_roster,
|
| 1069 | 1076 |
show_in_systray=show_in_systray)
|
| 1070 | 1077 |
app.events.add_event(self.account, full_jid, event)
|
| ... | ... | @@ -1220,6 +1227,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 1220 | 1227 |
types=type_):
|
| 1221 | 1228 |
# There were events to remove
|
| 1222 | 1229 |
self.redraw_after_event_removed(jid)
|
| 1230 |
+ # XEP-0333 Send <displayed> tag.
|
|
| 1231 |
+ con.get_module('ChatMarkers').send_displayed_marker(
|
|
| 1232 |
+ self.get_full_jid(), self.last_msg_id,
|
|
| 1233 |
+ self.type_id == message_control.TYPE_GC)
|
|
| 1234 |
+ self.last_msg_id = None
|
|
| 1223 | 1235 |
# send chatstate inactive to the one we're leaving
|
| 1224 | 1236 |
# and active to the one we visit
|
| 1225 | 1237 |
if self.msg_textview.has_text():
|
| ... | ... | @@ -1262,8 +1274,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 1262 | 1274 |
# we are at the end
|
| 1263 | 1275 |
if not app.events.remove_events(
|
| 1264 | 1276 |
self.account, jid, types=types_list):
|
| 1277 |
+ con = app.connections[self.account]
|
|
| 1265 | 1278 |
# There were events to remove
|
| 1266 | 1279 |
self.redraw_after_event_removed(jid)
|
| 1280 |
+ # XEP-0333 Send <displayed> tag.
|
|
| 1281 |
+ con.get_module('ChatMarkers').send_displayed_marker(
|
|
| 1282 |
+ self.get_full_jid(), self.last_msg_id,
|
|
| 1283 |
+ self.type_id == message_control.TYPE_GC)
|
|
| 1284 |
+ self.last_msg_id = None
|
|
| 1267 | 1285 |
|
| 1268 | 1286 |
def _on_scrollbar_button_release(self, scrollbar, event):
|
| 1269 | 1287 |
if event.get_button()[1] != 1:
|
| ... | ... | @@ -1328,6 +1346,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): |
| 1328 | 1346 |
We just removed a 'printed_*' event, redraw contact in roster or
|
| 1329 | 1347 |
gc_roster and titles in roster and msg_win
|
| 1330 | 1348 |
"""
|
| 1349 |
+ if not self.parent_win: # minimized groupchat
|
|
| 1350 |
+ return
|
|
| 1331 | 1351 |
self.parent_win.redraw_tab(self)
|
| 1332 | 1352 |
self.parent_win.show_title()
|
| 1333 | 1353 |
# TODO : get the contact and check get_show_in_roster()
|
| ... | ... | @@ -152,6 +152,7 @@ gajim_common_features = [ |
| 152 | 152 |
nbxmpp.NS_BYTESTREAM,
|
| 153 | 153 |
nbxmpp.NS_FILE,
|
| 154 | 154 |
nbxmpp.NS_MUC,
|
| 155 |
+ nbxmpp.NS_CHATMARKERS,
|
|
| 155 | 156 |
nbxmpp.NS_COMMANDS,
|
| 156 | 157 |
nbxmpp.NS_DISCO_INFO,
|
| 157 | 158 |
nbxmpp.NS_LAST,
|
| ... | ... | @@ -308,6 +308,9 @@ class CommonConnection: |
| 308 | 308 |
obj.session.last_send = time.time()
|
| 309 | 309 |
msg_iq.setThread(obj.session.thread_id)
|
| 310 | 310 |
|
| 311 |
+ if obj.message:
|
|
| 312 |
+ msg_iq.setMarkable()
|
|
| 313 |
+ |
|
| 311 | 314 |
self._push_stanza_message_outgoing(obj, msg_iq)
|
| 312 | 315 |
|
| 313 | 316 |
def _push_stanza_message_outgoing(self, obj, msg_iq):
|
| ... | ... | @@ -62,7 +62,7 @@ class Event: |
| 62 | 62 |
class ChatEvent(Event):
|
| 63 | 63 |
type_ = 'chat'
|
| 64 | 64 |
def __init__(self, message, subject, kind, time_, encrypted, resource,
|
| 65 |
- msg_log_id, correct_id=None, xhtml=None, session=None, form_node=None,
|
|
| 65 |
+ msg_log_id, correct_id=None, message_id=None, xhtml=None, session=None, form_node=None,
|
|
| 66 | 66 |
displaymarking=None, sent_forwarded=False, show_in_roster=False,
|
| 67 | 67 |
show_in_systray=True, additional_data=None):
|
| 68 | 68 |
Event.__init__(self, time_, show_in_roster=show_in_roster,
|
| ... | ... | @@ -74,6 +74,7 @@ class ChatEvent(Event): |
| 74 | 74 |
self.encrypted = encrypted
|
| 75 | 75 |
self.resource = resource
|
| 76 | 76 |
self.msg_log_id = msg_log_id
|
| 77 |
+ self.message_id = message_id
|
|
| 77 | 78 |
self.correct_id = correct_id
|
| 78 | 79 |
self.xhtml = xhtml
|
| 79 | 80 |
self.session = session
|
| ... | ... | @@ -94,13 +95,14 @@ class PmEvent(ChatEvent): |
| 94 | 95 |
class PrintedChatEvent(Event):
|
| 95 | 96 |
type_ = 'printed_chat'
|
| 96 | 97 |
def __init__(self, message, subject, control, msg_log_id, time_=None,
|
| 97 |
- show_in_roster=False, show_in_systray=True):
|
|
| 98 |
+ message_id=None, show_in_roster=False, show_in_systray=True):
|
|
| 98 | 99 |
Event.__init__(self, time_, show_in_roster=show_in_roster,
|
| 99 | 100 |
show_in_systray=show_in_systray)
|
| 100 | 101 |
self.message = message
|
| 101 | 102 |
self.subject = subject
|
| 102 | 103 |
self.control = control
|
| 103 | 104 |
self.msg_log_id = msg_log_id
|
| 105 |
+ self.message_id = message_id
|
|
| 104 | 106 |
|
| 105 | 107 |
class PrintedGcMsgEvent(PrintedChatEvent):
|
| 106 | 108 |
type_ = 'printed_gc_msg'
|
| ... | ... | @@ -40,6 +40,7 @@ MODULES = [ |
| 40 | 40 |
'bookmarks',
|
| 41 | 41 |
'caps',
|
| 42 | 42 |
'carbons',
|
| 43 |
+ 'chat_markers',
|
|
| 43 | 44 |
'chatstates',
|
| 44 | 45 |
'delimiter',
|
| 45 | 46 |
'discovery',
|
| 1 |
+# This file is part of Gajim.
|
|
| 2 |
+#
|
|
| 3 |
+# Gajim is free software; you can redistribute it and/or modify
|
|
| 4 |
+# it under the terms of the GNU General Public License as published
|
|
| 5 |
+# by the Free Software Foundation; version 3 only.
|
|
| 6 |
+#
|
|
| 7 |
+# Gajim is distributed in the hope that it will be useful,
|
|
| 8 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 9 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 10 |
+# GNU General Public License for more details.
|
|
| 11 |
+#
|
|
| 12 |
+# You should have received a copy of the GNU General Public License
|
|
| 13 |
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 14 |
+ |
|
| 15 |
+# Chat Markers (XEP-0333)
|
|
| 16 |
+ |
|
| 17 |
+import nbxmpp
|
|
| 18 |
+from nbxmpp.structs import StanzaHandler
|
|
| 19 |
+ |
|
| 20 |
+from gajim.common import app
|
|
| 21 |
+from gajim.common.nec import NetworkEvent
|
|
| 22 |
+from gajim.common.modules.base import BaseModule
|
|
| 23 |
+ |
|
| 24 |
+ |
|
| 25 |
+class ChatMarkers(BaseModule):
|
|
| 26 |
+ |
|
| 27 |
+ _nbxmpp_extends = 'ChatMarkers'
|
|
| 28 |
+ |
|
| 29 |
+ def __init__(self, con):
|
|
| 30 |
+ BaseModule.__init__(self, con)
|
|
| 31 |
+ |
|
| 32 |
+ self.handlers = [
|
|
| 33 |
+ StanzaHandler(name='message',
|
|
| 34 |
+ callback=self._process_message_marker,
|
|
| 35 |
+ ns=nbxmpp.NS_CHATMARKERS,
|
|
| 36 |
+ priority=48),
|
|
| 37 |
+ ]
|
|
| 38 |
+ |
|
| 39 |
+ def _process_message_marker(self, _con, stanza, properties):
|
|
| 40 |
+ if not properties.is_displayed_marker:
|
|
| 41 |
+ return
|
|
| 42 |
+ |
|
| 43 |
+ if properties.type.is_error:
|
|
| 44 |
+ return
|
|
| 45 |
+ |
|
| 46 |
+ self._log.info('Displayed: %s, id: %s',
|
|
| 47 |
+ properties.jid, properties.marker.id)
|
|
| 48 |
+ |
|
| 49 |
+ app.nec.push_outgoing_event(
|
|
| 50 |
+ NetworkEvent('displayed-marker-received',
|
|
| 51 |
+ account=self._account,
|
|
| 52 |
+ jid=properties.jid,
|
|
| 53 |
+ type=properties.type,
|
|
| 54 |
+ marker_id=properties.marker.id))
|
|
| 55 |
+ |
|
| 56 |
+ def send_marker(self, jid, marker, id_, is_gc):
|
|
| 57 |
+ if is_gc:
|
|
| 58 |
+ message = nbxmpp.Message(to=jid, typ='groupchat')
|
|
| 59 |
+ else:
|
|
| 60 |
+ message = nbxmpp.Message(to=jid, typ='chat')
|
|
| 61 |
+ message.setTag(marker, namespace=nbxmpp.NS_CHATMARKERS,
|
|
| 62 |
+ attrs={'id': id_})
|
|
| 63 |
+ self._nbxmpp().send(message)
|
|
| 64 |
+ |
|
| 65 |
+ def send_displayed_marker(self, jid, id_, is_gc):
|
|
| 66 |
+ self.send_marker(jid, 'displayed', id_, is_gc)
|
|
| 67 |
+ |
|
| 68 |
+ |
|
| 69 |
+def get_instance(*args, **kwargs):
|
|
| 70 |
+ return ChatMarkers(*args, **kwargs), 'ChatMarkers'
|
| ... | ... | @@ -173,7 +173,7 @@ class Message(BaseModule): |
| 173 | 173 |
'timestamp': properties.timestamp,
|
| 174 | 174 |
'delayed': properties.user_timestamp is not None,
|
| 175 | 175 |
'muc_pm': properties.is_muc_pm,
|
| 176 |
- 'gc_control': gc_control
|
|
| 176 |
+ 'gc_control': gc_control,
|
|
| 177 | 177 |
}
|
| 178 | 178 |
|
| 179 | 179 |
app.nec.push_incoming_event(NetworkEvent('update-client-info',
|
| ... | ... | @@ -28,6 +28,11 @@ |
| 28 | 28 |
</item>
|
| 29 | 29 |
</section>
|
| 30 | 30 |
<section>
|
| 31 |
+ <item>
|
|
| 32 |
+ <attribute name="label" translatable="yes">XML Console</attribute>
|
|
| 33 |
+ <attribute name="action">app.xml-console</attribute>
|
|
| 34 |
+ <attribute name="accel"><Primary><Shift>X</attribute>
|
|
| 35 |
+ </item>
|
|
| 31 | 36 |
<item>
|
| 32 | 37 |
<attribute name="label" translatable="yes">File Transfer</attribute>
|
| 33 | 38 |
<attribute name="action">app.file-transfer</attribute>
|
| ... | ... | @@ -75,16 +75,12 @@ |
| 75 | 75 |
<property name="visible">True</property>
|
| 76 | 76 |
<property name="can_focus">False</property>
|
| 77 | 77 |
<property name="title" translatable="yes">XML Console</property>
|
| 78 |
- <property name="has_subtitle">False</property>
|
|
| 79 | 78 |
<property name="show_close_button">True</property>
|
| 80 | 79 |
<child>
|
| 81 |
- <object class="GtkSwitch">
|
|
| 80 |
+ <object class="GtkImage">
|
|
| 82 | 81 |
<property name="visible">True</property>
|
| 83 |
- <property name="can_focus">True</property>
|
|
| 84 |
- <property name="halign">start</property>
|
|
| 85 |
- <property name="valign">center</property>
|
|
| 86 |
- <property name="active">True</property>
|
|
| 87 |
- <signal name="notify::active" handler="_on_enable" swapped="no"/>
|
|
| 82 |
+ <property name="can_focus">False</property>
|
|
| 83 |
+ <property name="icon_name">utilities-terminal-symbolic</property>
|
|
| 88 | 84 |
</object>
|
| 89 | 85 |
</child>
|
| 90 | 86 |
</object>
|
| ... | ... | @@ -847,7 +847,8 @@ class GroupchatControl(ChatControlBase): |
| 847 | 847 |
|
| 848 | 848 |
def on_private_message(self, nick, sent, msg, tim, xhtml, session,
|
| 849 | 849 |
msg_log_id=None, encrypted=False,
|
| 850 |
- displaymarking=None):
|
|
| 850 |
+ displaymarking=None, message_id=None):
|
|
| 851 |
+ |
|
| 851 | 852 |
# Do we have a queue?
|
| 852 | 853 |
fjid = self.room_jid + '/' + nick
|
| 853 | 854 |
|
| ... | ... | @@ -862,7 +863,9 @@ class GroupchatControl(ChatControlBase): |
| 862 | 863 |
session=session,
|
| 863 | 864 |
form_node=None,
|
| 864 | 865 |
displaymarking=displaymarking,
|
| 865 |
- sent_forwarded=sent)
|
|
| 866 |
+ sent_forwarded=sent,
|
|
| 867 |
+ message_id=message_id)
|
|
| 868 |
+ |
|
| 866 | 869 |
app.events.add_event(self.account, fjid, event)
|
| 867 | 870 |
|
| 868 | 871 |
autopopup = app.config.get('autopopup')
|
| ... | ... | @@ -1106,7 +1109,8 @@ class GroupchatControl(ChatControlBase): |
| 1106 | 1109 |
self.on_private_message(
|
| 1107 | 1110 |
nick, obj.sent, obj.msgtxt, obj.timestamp,
|
| 1108 | 1111 |
obj.xhtml, self.session, msg_log_id=obj.msg_log_id,
|
| 1109 |
- encrypted=obj.encrypted, displaymarking=obj.displaymarking)
|
|
| 1112 |
+ encrypted=obj.encrypted, displaymarking=obj.displaymarking,
|
|
| 1113 |
+ message_id=obj.message_id)
|
|
| 1110 | 1114 |
|
| 1111 | 1115 |
def _nec_ping(self, obj):
|
| 1112 | 1116 |
if self.contact.jid != obj.contact.room_jid:
|
| ... | ... | @@ -35,7 +35,7 @@ from gajim.gtk.const import SettingType |
| 35 | 35 |
|
| 36 | 36 |
|
| 37 | 37 |
class XMLConsoleWindow(Gtk.ApplicationWindow):
|
| 38 |
- def __init__(self, account):
|
|
| 38 |
+ def __init__(self):
|
|
| 39 | 39 |
Gtk.ApplicationWindow.__init__(self)
|
| 40 | 40 |
self.set_application(app.app)
|
| 41 | 41 |
self.set_position(Gtk.WindowPosition.CENTER)
|
| ... | ... | @@ -44,8 +44,7 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 44 | 44 |
self.set_show_menubar(False)
|
| 45 | 45 |
self.set_name('XMLConsoleWindow')
|
| 46 | 46 |
|
| 47 |
- self.account = account
|
|
| 48 |
- self.enabled = True
|
|
| 47 |
+ self.selected_account = None
|
|
| 49 | 48 |
self.presence = True
|
| 50 | 49 |
self.message = True
|
| 51 | 50 |
self.iq = True
|
| ... | ... | @@ -55,24 +54,21 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 55 | 54 |
self.filter_dialog = None
|
| 56 | 55 |
self.last_stanza = None
|
| 57 | 56 |
|
| 58 |
- self._ui = get_builder('xml_console_window.ui')
|
|
| 59 |
- |
|
| 60 |
- jid = app.get_jid_from_account(account)
|
|
| 57 |
+ self._ui = get_builder('xml_console.ui')
|
|
| 61 | 58 |
self.set_titlebar(self._ui.headerbar)
|
| 62 |
- self._ui.headerbar.set_subtitle(jid)
|
|
| 63 |
- |
|
| 59 |
+ self._set_titlebar()
|
|
| 64 | 60 |
self.add(self._ui.box)
|
| 65 | 61 |
|
| 66 | 62 |
self._ui.paned.set_position(self._ui.paned.get_property('max-position'))
|
| 67 | 63 |
|
| 68 | 64 |
button = get_image_button(
|
| 69 |
- 'edit-clear-all-symbolic', _('Clear'))
|
|
| 70 |
- button.connect('clicked', self._on_clear)
|
|
| 65 |
+ 'applications-system-symbolic', _('Filter'))
|
|
| 66 |
+ button.connect('clicked', self._on_filter_options)
|
|
| 71 | 67 |
self._ui.actionbar.pack_start(button)
|
| 72 | 68 |
|
| 73 | 69 |
button = get_image_button(
|
| 74 |
- 'applications-system-symbolic', _('Filter'))
|
|
| 75 |
- button.connect('clicked', self._on_filter_options)
|
|
| 70 |
+ 'edit-clear-symbolic', _('Clear'))
|
|
| 71 |
+ button.connect('clicked', self._on_clear)
|
|
| 76 | 72 |
self._ui.actionbar.pack_start(button)
|
| 77 | 73 |
|
| 78 | 74 |
button = get_image_button(
|
| ... | ... | @@ -84,7 +80,7 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 84 | 80 |
button.connect('clicked', self._on_paste_last)
|
| 85 | 81 |
self._ui.actionbar.pack_start(button)
|
| 86 | 82 |
|
| 87 |
- button = get_image_button('mail-send-symbolic', _('Send'))
|
|
| 83 |
+ button = Gtk.Button.new_with_mnemonic(_('_Send'))
|
|
| 88 | 84 |
button.connect('clicked', self._on_send)
|
| 89 | 85 |
self._ui.actionbar.pack_end(button)
|
| 90 | 86 |
|
| ... | ... | @@ -105,6 +101,13 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 105 | 101 |
app.ged.register_event_handler(
|
| 106 | 102 |
'stanza-sent', ged.GUI1, self._nec_stanza_sent)
|
| 107 | 103 |
|
| 104 |
+ def _set_titlebar(self):
|
|
| 105 |
+ if self.selected_account is None:
|
|
| 106 |
+ title = _('All Accounts')
|
|
| 107 |
+ else:
|
|
| 108 |
+ title = app.get_jid_from_account(self.selected_account)
|
|
| 109 |
+ self._ui.headerbar.set_subtitle(title)
|
|
| 110 |
+ |
|
| 108 | 111 |
def _create_tags(self):
|
| 109 | 112 |
buffer_ = self._ui.textview.get_buffer()
|
| 110 | 113 |
in_color = app.css_config.get_value(
|
| ... | ... | @@ -160,12 +163,12 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 160 | 163 |
self._ui.input_entry.grab_focus()
|
| 161 | 164 |
|
| 162 | 165 |
def _on_send(self, *args):
|
| 163 |
- if not app.account_is_connected(self.account):
|
|
| 166 |
+ if not app.account_is_connected(self.selected_account):
|
|
| 164 | 167 |
# If offline or connecting
|
| 165 | 168 |
ErrorDialog(
|
| 166 | 169 |
_('Connection not available'),
|
| 167 | 170 |
_('Please make sure you are connected with \'%s\'.') %
|
| 168 |
- self.account)
|
|
| 171 |
+ self.selected_account)
|
|
| 169 | 172 |
return
|
| 170 | 173 |
buffer_ = self._ui.input_entry.get_buffer()
|
| 171 | 174 |
begin_iter, end_iter = buffer_.get_bounds()
|
| ... | ... | @@ -184,7 +187,7 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 184 | 187 |
# stream management
|
| 185 | 188 |
node = nbxmpp.Protocol(node=stanza,
|
| 186 | 189 |
attrs={'xmlns': 'jabber:client'})
|
| 187 |
- app.connections[self.account].connection.send(node)
|
|
| 190 |
+ app.connections[self.selected_account].connection.send(node)
|
|
| 188 | 191 |
self.last_stanza = stanza
|
| 189 | 192 |
buffer_.set_text('')
|
| 190 | 193 |
|
| ... | ... | @@ -207,32 +210,46 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 207 | 210 |
if self.filter_dialog:
|
| 208 | 211 |
self.filter_dialog.present()
|
| 209 | 212 |
return
|
| 210 |
- options = [
|
|
| 213 |
+ |
|
| 214 |
+ # Get all accounts for settings combobox
|
|
| 215 |
+ accounts = app.get_accounts_sorted()
|
|
| 216 |
+ combo_accounts = []
|
|
| 217 |
+ for account in accounts:
|
|
| 218 |
+ label = app.get_account_label(account)
|
|
| 219 |
+ combo_accounts.append((account, label))
|
|
| 220 |
+ combo_accounts.insert(0, (None, _('All Accounts')))
|
|
| 221 |
+ |
|
| 222 |
+ settings = [
|
|
| 223 |
+ Setting(SettingKind.COMBO, _('Account'),
|
|
| 224 |
+ SettingType.VALUE, self.selected_account,
|
|
| 225 |
+ callback=self._set_account,
|
|
| 226 |
+ props={'combo_items': combo_accounts}),
|
|
| 227 |
+ |
|
| 211 | 228 |
Setting(SettingKind.SWITCH, 'Presence',
|
| 212 | 229 |
SettingType.VALUE, self.presence,
|
| 213 |
- callback=self._on_option, data='presence'),
|
|
| 230 |
+ callback=self._on_setting, data='presence'),
|
|
| 214 | 231 |
|
| 215 | 232 |
Setting(SettingKind.SWITCH, 'Message',
|
| 216 | 233 |
SettingType.VALUE, self.message,
|
| 217 |
- callback=self._on_option, data='message'),
|
|
| 234 |
+ callback=self._on_setting, data='message'),
|
|
| 218 | 235 |
|
| 219 | 236 |
Setting(SettingKind.SWITCH, 'IQ', SettingType.VALUE, self.iq,
|
| 220 |
- callback=self._on_option, data='iq'),
|
|
| 237 |
+ callback=self._on_setting, data='iq'),
|
|
| 221 | 238 |
|
| 222 | 239 |
Setting(SettingKind.SWITCH, 'Stream Management',
|
| 223 | 240 |
SettingType.VALUE, self.stream,
|
| 224 |
- callback=self._on_option, data='stream'),
|
|
| 241 |
+ callback=self._on_setting, data='stream'),
|
|
| 225 | 242 |
|
| 226 | 243 |
Setting(SettingKind.SWITCH, 'In', SettingType.VALUE, self.incoming,
|
| 227 |
- callback=self._on_option, data='incoming'),
|
|
| 244 |
+ callback=self._on_setting, data='incoming'),
|
|
| 228 | 245 |
|
| 229 | 246 |
Setting(SettingKind.SWITCH, 'Out', SettingType.VALUE, self.outgoing,
|
| 230 |
- callback=self._on_option, data='outgoing'),
|
|
| 247 |
+ callback=self._on_setting, data='outgoing'),
|
|
| 231 | 248 |
]
|
| 232 | 249 |
|
| 233 |
- self.filter_dialog = SettingsDialog(self, 'Filter',
|
|
| 250 |
+ self.filter_dialog = SettingsDialog(self, _('Filter'),
|
|
| 234 | 251 |
Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
| 235 |
- options, self.account)
|
|
| 252 |
+ settings, self.selected_account)
|
|
| 236 | 253 |
self.filter_dialog.connect('destroy', self._on_filter_destroyed)
|
| 237 | 254 |
|
| 238 | 255 |
def _on_filter_destroyed(self, win):
|
| ... | ... | @@ -247,10 +264,11 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 247 | 264 |
app.ged.remove_event_handler(
|
| 248 | 265 |
'stanza-sent', ged.GUI1, self._nec_stanza_sent)
|
| 249 | 266 |
|
| 250 |
- def _on_enable(self, switch, param):
|
|
| 251 |
- self.enabled = switch.get_active()
|
|
| 267 |
+ def _set_account(self, value, data):
|
|
| 268 |
+ self.selected_account = value
|
|
| 269 |
+ self._set_titlebar()
|
|
| 252 | 270 |
|
| 253 |
- def _on_option(self, value, data):
|
|
| 271 |
+ def _on_setting(self, value, data):
|
|
| 254 | 272 |
setattr(self, data, value)
|
| 255 | 273 |
value = not value
|
| 256 | 274 |
table = self._ui.textview.get_buffer().get_tag_table()
|
| ... | ... | @@ -263,19 +281,21 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 263 | 281 |
tag.set_property('invisible', value)
|
| 264 | 282 |
|
| 265 | 283 |
def _nec_stanza_received(self, obj):
|
| 266 |
- if obj.conn.name != self.account:
|
|
| 267 |
- return
|
|
| 268 |
- self._print_stanza(obj.stanza_str, 'incoming')
|
|
| 284 |
+ if self.selected_account is not None:
|
|
| 285 |
+ if obj.conn.name != self.selected_account:
|
|
| 286 |
+ return
|
|
| 287 |
+ self._print_stanza(obj, 'incoming')
|
|
| 269 | 288 |
|
| 270 | 289 |
def _nec_stanza_sent(self, obj):
|
| 271 |
- if obj.conn.name != self.account:
|
|
| 272 |
- return
|
|
| 273 |
- self._print_stanza(obj.stanza_str, 'outgoing')
|
|
| 290 |
+ if self.selected_account is not None:
|
|
| 291 |
+ if obj.conn.name != self.selected_account:
|
|
| 292 |
+ return
|
|
| 293 |
+ self._print_stanza(obj, 'outgoing')
|
|
| 274 | 294 |
|
| 275 |
- def _print_stanza(self, stanza, kind):
|
|
| 295 |
+ def _print_stanza(self, obj, kind):
|
|
| 296 |
+ account = app.get_jid_from_account(obj.conn.name)
|
|
| 297 |
+ stanza = obj.stanza_str
|
|
| 276 | 298 |
# Kind must be 'incoming' or 'outgoing'
|
| 277 |
- if not self.enabled:
|
|
| 278 |
- return
|
|
| 279 | 299 |
if not stanza:
|
| 280 | 300 |
return
|
| 281 | 301 |
|
| ... | ... | @@ -294,9 +314,10 @@ class XMLConsoleWindow(Gtk.ApplicationWindow): |
| 294 | 314 |
elif stanza.startswith('<r') or stanza.startswith('<a'):
|
| 295 | 315 |
type_ = 'stream'
|
| 296 | 316 |
|
| 297 |
- stanza = '<!-- {kind} {time} -->\n{stanza}\n\n'.format(
|
|
| 317 |
+ stanza = '<!-- {kind} {time} ({account}) -->\n{stanza}\n\n'.format(
|
|
| 298 | 318 |
kind=kind.capitalize(),
|
| 299 | 319 |
time=time.strftime('%c'),
|
| 320 |
+ account=account,
|
|
| 300 | 321 |
stanza=stanza.replace('><', '>\n<'))
|
| 301 | 322 |
buffer_.insert_with_tags_by_name(end_iter, stanza, type_, kind)
|
| 302 | 323 |
|
| ... | ... | @@ -285,6 +285,44 @@ class Interface: |
| 285 | 285 |
if ctrl and ctrl.session and len(obj.contact_list) > 1:
|
| 286 | 286 |
ctrl.remove_session(ctrl.session)
|
| 287 | 287 |
|
| 288 |
+ def handle_event_chat_marker_received(self, event):
|
|
| 289 |
+ if event.type.is_groupchat:
|
|
| 290 |
+ types = ['printed_gc_msg', 'printed_marked_gc_msg']
|
|
| 291 |
+ control = app.interface.msg_win_mgr.get_control(event.jid,
|
|
| 292 |
+ event.account)
|
|
| 293 |
+ if not control:
|
|
| 294 |
+ if event.jid in app.interface.minimized_controls[event.account]:
|
|
| 295 |
+ control = app.interface.minimized_controls[event.account][event.jid]
|
|
| 296 |
+ if not control:
|
|
| 297 |
+ return
|
|
| 298 |
+ if obj.resource != control.nick:
|
|
| 299 |
+ return
|
|
| 300 |
+ event_jid = event.jid
|
|
| 301 |
+ |
|
| 302 |
+ else:
|
|
| 303 |
+ |
|
| 304 |
+ types = ['chat', 'pm', 'printed_chat', 'printed_pm']
|
|
| 305 |
+ if event.properties.carbon_type != 'sent':
|
|
| 306 |
+ return
|
|
| 307 |
+ |
|
| 308 |
+ control = app.interface.msg_win_mgr.get_control(event.jid,
|
|
| 309 |
+ event.account)
|
|
| 310 |
+ if not control:
|
|
| 311 |
+ control = app.interface.msg_win_mgr.get_gc_control(
|
|
| 312 |
+ event.jid, event.account)
|
|
| 313 |
+ event_jid = event.jid
|
|
| 314 |
+ |
|
| 315 |
+ # Compare with control.last_msg_id.
|
|
| 316 |
+ events_ = app.events.get_events(event.account, event_jid, types)
|
|
| 317 |
+ if not events_:
|
|
| 318 |
+ return
|
|
| 319 |
+ if events_[-1].msg_id != event.marker_id:
|
|
| 320 |
+ return
|
|
| 321 |
+ if not app.events.remove_events(event.account, event_jid, types=types):
|
|
| 322 |
+ # There were events to remove
|
|
| 323 |
+ if control:
|
|
| 324 |
+ control.redraw_after_event_removed(event.jid)
|
|
| 325 |
+ |
|
| 288 | 326 |
def handle_event_msgerror(self, obj):
|
| 289 | 327 |
#'MSGERROR' (account, (jid, error_code, error_msg, msg, time[session]))
|
| 290 | 328 |
account = obj.conn.name
|
| ... | ... | @@ -1173,6 +1211,7 @@ class Interface: |
| 1173 | 1211 |
'unsubscribed-presence-received': [
|
| 1174 | 1212 |
self.handle_event_unsubscribed_presence],
|
| 1175 | 1213 |
'zeroconf-name-conflict': [self.handle_event_zc_name_conflict],
|
| 1214 |
+ 'displayed-marker-received': [self.handle_event_chat_marker_received],
|
|
| 1176 | 1215 |
}
|
| 1177 | 1216 |
|
| 1178 | 1217 |
def register_core_handlers(self):
|
| ... | ... | @@ -678,8 +678,6 @@ def get_account_menu(account): |
| 678 | 678 |
('-pep-config', _('PEP Configuration')),
|
| 679 | 679 |
('-sync-history', _('Synchronise History')),
|
| 680 | 680 |
('-privacylists', _('Privacy Lists')),
|
| 681 |
- |
|
| 682 |
- ('-xml-console', _('XML Console'))
|
|
| 683 | 681 |
]),
|
| 684 | 682 |
(_('Admin'), [
|
| 685 | 683 |
('-send-server-message', _('Send Server Message…')),
|
| ... | ... | @@ -689,10 +687,6 @@ def get_account_menu(account): |
| 689 | 687 |
]),
|
| 690 | 688 |
]
|
| 691 | 689 |
|
| 692 |
- zeroconf_menu = [
|
|
| 693 |
- ('-xml-console', _('XML Console')),
|
|
| 694 |
- ]
|
|
| 695 |
- |
|
| 696 | 690 |
def build_menu(preset):
|
| 697 | 691 |
menu = Gio.Menu()
|
| 698 | 692 |
for item in preset:
|
| ... | ... | @@ -713,8 +707,6 @@ def get_account_menu(account): |
| 713 | 707 |
menu.append_submenu(label, submenu)
|
| 714 | 708 |
return menu
|
| 715 | 709 |
|
| 716 |
- if account == 'Local':
|
|
| 717 |
- return build_menu(zeroconf_menu)
|
|
| 718 | 710 |
return build_menu(account_menu)
|
| 719 | 711 |
|
| 720 | 712 |
|
| ... | ... | @@ -741,8 +733,9 @@ def build_accounts_menu(): |
| 741 | 733 |
acc_menu.append_item(modify_account_item)
|
| 742 | 734 |
for acc in accounts_list:
|
| 743 | 735 |
label = escape_mnemonic(app.get_account_label(acc))
|
| 744 |
- acc_menu.append_submenu(
|
|
| 745 |
- label, get_account_menu(acc))
|
|
| 736 |
+ if acc != 'Local':
|
|
| 737 |
+ acc_menu.append_submenu(
|
|
| 738 |
+ label, get_account_menu(acc))
|
|
| 746 | 739 |
else:
|
| 747 | 740 |
acc_menu = get_account_menu(accounts_list[0])
|
| 748 | 741 |
modify_account_item = Gio.MenuItem.new(_('_Modify Account…'),
|
| ... | ... | @@ -253,7 +253,9 @@ class ChatControlSession: |
| 253 | 253 |
if do_event:
|
| 254 | 254 |
event = event_t(obj.msgtxt, obj.subject, obj.mtype, obj.timestamp,
|
| 255 | 255 |
obj.encrypted, obj.resource, obj.msg_log_id,
|
| 256 |
- correct_id=(obj.id_, obj.correct_id), xhtml=obj.xhtml,
|
|
| 256 |
+ correct_id=obj.correct_id,
|
|
| 257 |
+ message_id=obj.message_id,
|
|
| 258 |
+ xhtml=obj.xhtml,
|
|
| 257 | 259 |
session=self, form_node=obj.form_node,
|
| 258 | 260 |
displaymarking=obj.displaymarking,
|
| 259 | 261 |
sent_forwarded=obj.forwarded and obj.sent,
|