Philipp Hörist pushed to branch gajim_1.1 at gajim / gajim
Commits: 488f3c55 by Philipp Hörist at 2018-10-11T18:01:48Z Fix gettext not supporting Pathlib yet
- - - - - c2012d63 by Philipp Hörist at 2018-10-11T18:01:48Z GajimRemote: Fix chatstate-received callback
- - - - - 306a7c4b by Philipp Hörist at 2018-10-11T18:01:48Z Better fix for aa50da05
- - - - - acc5f0b6 by André Apitzsch at 2018-10-11T18:01:48Z Flatpak: Update dependencies
and switch to yaml format to improve readability
- - - - - 7ef05d5d by Philipp Hörist at 2018-10-11T18:01:48Z GajimRemote: pass combined chatstate
- - - - - a6db988e by Philipp Hörist at 2018-10-11T18:01:48Z Raise min nbxmpp version
- - - - - e3cf8c00 by Philipp Hörist at 2018-10-11T18:01:49Z Add property to distinguish pm contacts
- - - - - 59a5f20a by Philipp Hörist at 2018-10-11T18:01:49Z Fix a few things regarding chatstates
- Fix wrong chatstates issued because of textview changed signal - Fix chatstates in PMs
- - - - - 85e8867a by Daniel Brötzmann at 2018-10-11T18:01:49Z polish-change-status-dialog
- - - - - 796da7b2 by Philipp Hörist at 2018-10-11T18:01:49Z Dont overwrite user plugins with base plugins
- Dont load plugins without short_name - Check with the short_name if a plugin is already loaded
- - - - - 369abc25 by Philipp Hörist at 2018-10-11T18:01:49Z Reset chatstate correctly if contact goes offline
- - - - -
13 changed files:
- flatpak/README.md - + flatpak/org.gajim.Gajim.yaml - gajim/chat_control.py - gajim/chat_control_base.py - gajim/common/contacts.py - gajim/common/i18n.py - gajim/common/modules/chatstates.py - gajim/data/gui/change_status_message_dialog.ui - gajim/gajim.py - gajim/message_textview.py - gajim/plugins/pluginmanager.py - gajim/remote_control.py - − org.gajim.Gajim.json
Changes:
===================================== flatpak/README.md ===================================== @@ -23,9 +23,9 @@ Replace install path `~/Gajim/gajim_flatpak` with an install path of your choice *Note: Remove `--user` if you want a system-wide installation.*
1. `flatpak --user remote-add --from gnome https://sdk.gnome.org/gnome.flatpakrepo%60 -2. `flatpak --user install gnome org.gnome.Platform//3.28` -3. `flatpak --user install gnome org.gnome.Sdk//3.28` -4. `flatpak-builder --repo=gajim_flatpak_repo ~/Gajim/gajim_flatpak ~/Gajim/org.gajim.Gajim.json` +2. `flatpak --user install gnome org.gnome.Platform//3.30` +3. `flatpak --user install gnome org.gnome.Sdk//3.30` +4. `flatpak-builder --repo=gajim_flatpak_repo ~/Gajim/gajim_flatpak ~/Gajim/flatpak/org.gajim.Gajim.yaml` 5. `flatpak --user remote-add --no-gpg-verify gajim_flatpak_repo gajim_flatpak_repo` 6. `flatpak --user install gajim_flatpak_repo org.gajim.Gajim` 7. `flatpak run org.gajim.Gajim` @@ -51,7 +51,7 @@ In this example, we use `git` to update the repository. You can also download th
### Install and update Gajim
-1. `flatpak-builder --repo=gajim_flatpak_repo ~/Gajim/gajim_flatpak ~/Gajim/org.gajim.Gajim.json` +1. `flatpak-builder --repo=gajim_flatpak_repo ~/Gajim/gajim_flatpak ~/Gajim/flatpak/org.gajim.Gajim.yaml` 2. `flatpak --user update` 3. `flatpak run org.gajim.Gajim`
===================================== flatpak/org.gajim.Gajim.yaml ===================================== @@ -0,0 +1,250 @@ +app-id: org.gajim.Gajim +runtime: org.gnome.Platform +runtime-version: 3.30 +sdk: org.gnome.Sdk +command: gajim +tags: nightly +desktop-file-name-prefix: '(Nightly) ' +finish-args: + - --share=ipc + - --share=network + - --socket=x11 + - --socket=wayland + - --socket=pulseaudio + - --system-talk-name=org.freedesktop.GeoClue2 + - --system-talk-name=org.freedesktop.login1 + - --talk-name=org.gnome.ScreenSaver + - --talk-name=org.mpris.MediaPlayer2.* + # Keyring + - --talk-name=org.freedesktop.secrets + # dconf + - --filesystem=xdg-run/dconf + - --filesystem=~/.config/dconf:ro + - --talk-name=ca.desrt.dconf + - --env=DCONF_USER_CONFIG_DIR=.config/dconf + # extensions + - --env=PYTHONPATH=/app/plugins/lib/python3.7/site-packages + +add-extensions: + org.gajim.Gajim.Plugin: + directory: plugins + merge-dirs: lib/python3.7/site-packages + subdirectories: true + no-autodownload: true + autodelete: true + +build-options: + cflags: -O2 -g + cxxflags: -O2 -g + +cleanup: + - '/bin/easy*' + - /include + - /lib/debug + - /lib/pkgconfig + - /lib/python2.7 + - /share/aclocal + - /share/doc + - /share/gtk-doc + - /share/man + - '*.a' + - '*.la' + +modules: + - name: python3-pycparser + buildsystem: simple + build-commands: + - python3 setup.py install --prefix=/app + sources: + - type: archive + url: https://files.pythonhosted.org/packages/68/9e/49196946aee219aead1290e00d1e7f... + sha256: a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3 + + - name: python3-cffi + buildsystem: simple + build-commands: + - python3 setup.py install --prefix=/app + sources: + - type: archive + url: https://files.pythonhosted.org/packages/e7/a7/4cd50e57cc6f436f1cc3a7e8fa700f... + sha256: e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4 + + - name: python3-six + buildsystem: simple + build-commands: + - pip3 install --prefix=/app six-1.11.0-py2.py3-none-any.whl + sources: + - type: file + url: https://pypi.python.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7c... + sha256: 832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb + + - name: python3-pyparsing + buildsystem: simple + build-commands: + - pip3 install --prefix=/app pyparsing-2.2.2-py2.py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/2b/4a/f06b45ab9690d4c37641ec776f7ad6... + sha256: d29593d8ebe7b57d6967b62494f8c72b03ac0262b1eed63826c6f788b3606401 + + - name: python3-packaging + buildsystem: simple + build-commands: + - pip3 install --prefix=/app packaging-18.0-py2.py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/89/d1/92e6df2e503a69df9faab187c68458... + sha256: f95a1e147590f204328170981833854229bb2912ac3d5f89e2a8ccd2834800c9 + + - name: python3-appdirs + buildsystem: simple + build-commands: + - pip3 install --prefix=/app appdirs-1.4.3-py2.py3-none-any.whl + sources: + - type: file + url: https://pypi.python.org/packages/56/eb/810e700ed1349edde4cbdc1b2a21e28cdf115... + sha256: d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e + + - name: python3-setuptools + ensure-writable: + - easy-install.pth + - setuptools.pth + buildsystem: simple + build-commands: + - python3 setup.py install --prefix=/app + sources: + - type: archive + url: https://files.pythonhosted.org/packages/6e/9c/6a003320b00ef237f94aa74e4ad66c... + sha256: acbc5740dd63f243f46c2b4b8e2c7fd92259c2ddb55a4115b16418a2ed371b15 + + - name: python3-asn1crypto + buildsystem: simple + build-commands: + - pip3 install --prefix=/app asn1crypto-0.24.0-py2.py3-none-any.whl + sources: + - type: file + url: https://pypi.python.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd... + sha256: 2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87 + + - name: python3-idna + buildsystem: simple + build-commands: + - pip3 install --prefix=/app idna-2.7-py2.py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355... + sha256: 156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e + + - name: python3-cryptography + ensure-writable: + - easy-install.pth + buildsystem: simple + build-commands: + - python3 setup.py install --prefix=/app + sources: + - type: archive + url: https://files.pythonhosted.org/packages/22/21/233e38f74188db94e8451ef6385754... + sha256: 8d10113ca826a4c29d5b85b2c4e045ffa8bad74fb525ee0eceb1d38d4c70dfd6 + + - name: python3-pyopenssl + buildsystem: simple + build-commands: + - pip3 install --prefix=/app pyOpenSSL-18.0.0-py2.py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/96/af/9d29e6bd40823061aea2e0574ccb2f... + sha256: 26ff56a6b5ecaf3a2a59f132681e2a80afcc76b4f902f612f518f92c2a1bf854 + + - name: python3-dbus-python + build-options: + env: + PYTHON_VERSION: '3' + sources: + - type: archive + url: https://files.pythonhosted.org/packages/3f/e7/4edb582d1ffd5ac3c84188deea32e9... + sha256: abf12bbb765e300bf8e2a1b2f32f85949eab06998dbda127952c31cb63957b6f + + - name: python3-jeepney + buildsystem: simple + build-commands: + - pip3 install --prefix=/app jeepney-0.4-py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/2b/f7/ff23b9b59534f501d47c327576aadd... + sha256: f6f8b1428403b4afad04b6b82f9ab9fc426c253d7504c9031c41712a2c01dc74 + + - name: python3-secretstorage + buildsystem: simple + build-commands: + - pip3 install --prefix=/app SecretStorage-3.1.0-py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/d8/e8/80975fd281764c80b2eb581a7f25d2... + sha256: 20196abd1a9d1310df7573d58ca6e7ed9292218c98ca3638eea07beb16080343 + + - name: python3-entrypoints + buildsystem: simple + build-commands: + - pip3 install --prefix=/app entrypoints-0.2.3-py2.py3-none-any.whl + sources: + - type: file + url: https://files.pythonhosted.org/packages/cc/8b/4eefa9b47f1910b3d2081da67726b0... + sha256: 10ad569bb245e7e2ba425285b9fa3e8178a0dc92fc53b1e1c553805e15a8825b + + - name: python3-keyring + buildsystem: simple + build-commands: + - pip3 install --prefix=/app keyring-15.1.0-py2.py3-none-any.whl + cleanup: + - /bin + sources: + - type: file + url: https://files.pythonhosted.org/packages/6c/3f/7a3f780dfa9ee5708507090ce15d67... + sha256: 16dddc3edaeb2703aaf5588a0b488b62f162e26f1877b6faf3a3db4b7712df61 + + - name: python3-cssutils + buildsystem: simple + build-commands: + - pip3 install --prefix=/app cssutils-1.0.2-py3-none-any.whl + cleanup: + - /bin + sources: + - type: file + url: https://files.pythonhosted.org/packages/6b/15/a9fb9010f58d1c55dd0b7779db2334... + sha256: c74dbe19c92f5052774eadb15136263548dd013250f1ed1027988e7fef125c8d + + - name: gspell + cleanup: + - /bin + sources: + - type: archive + url: https://download.gnome.org/sources/gspell/1.8/gspell-1.8.1.tar.xz + sha256: 819a1d23c7603000e73f5e738bdd284342e0cd345fb0c7650999c31ec741bbe5 + modules: + - name: enchant2 + cleanup: + - /bin + sources: + - type: archive + url: https://github.com/AbiWord/enchant/releases/download/v2.2.3/enchant-2.2.3.ta... + sha256: abd8e915675cff54c0d4da5029d95c528362266557c61c7149d53fa069b8076d + + - name: python3-nbxmpp + buildsystem: simple + build-commands: + - python3 setup.py install --prefix=/app + sources: + - type: archive + url: https://files.pythonhosted.org/packages/24/54/23a475a0d7d3664ea21b14ce907245... + sha256: 8c2b4b8aac1a8c6d07c1e30af542fde20a70a9b8c7c04017e9cea0db654437c6 + + - name: gajim + buildsystem: simple + build-commands: + - pip3 install --prefix=/app . + - touch /app/share/run-as-flatpak + sources: + - type: git + url: https://dev.gajim.org/gajim/gajim.git + post-install: + - install -d /app/plugins
===================================== gajim/chat_control.py ===================================== @@ -134,11 +134,6 @@ class ChatControl(ChatControlBase): self.show_avatar()
# Hook up signals - message_tv_buffer = self.msg_textview.get_buffer() - id_ = message_tv_buffer.connect('changed', - self._on_message_tv_buffer_changed) - self.handlers[id_] = message_tv_buffer - widget = self.xml.get_object('avatar_eventbox') widget.set_property('height-request', AvatarSize.CHAT)
@@ -680,7 +675,11 @@ class ChatControl(ChatControlBase): status_escaped = GLib.markup_escape_text(status_reduced)
st = app.config.get('displayed_chat_state_notifications') - cs = app.contacts.get_combined_chatstate(self.account, self.contact.jid) + if self.TYPE_ID == 'pm': + cs = self.gc_contact.chatstate + else: + cs = app.contacts.get_combined_chatstate( + self.account, self.contact.jid) if cs and st in ('composing_only', 'all'): if contact.show == 'offline': chatstate = '' @@ -1152,13 +1151,21 @@ class ChatControl(ChatControlBase): if event.account != self.account: return
- if event.jid != self.contact.jid: - return + if self.TYPE_ID == 'pm': + if event.contact != self.gc_contact: + return + else: + if event.contact.jid != self.contact.jid: + return
self.draw_banner_text() + # update chatstate in tab for this chat - chatstate = app.contacts.get_combined_chatstate( - self.account, self.contact.jid) + if event.contact.is_gc_contact: + chatstate = event.contact.chatstate + else: + chatstate = app.contacts.get_combined_chatstate( + self.account, self.contact.jid) self.parent_win.redraw_tab(self, chatstate)
def _nec_caps_received(self, obj): @@ -1239,12 +1246,6 @@ class ChatControl(ChatControlBase):
dialogs.TransformChatToMUC(self.account, [c.jid], [dropped_jid])
- def _on_message_tv_buffer_changed(self, textbuffer): - super()._on_message_tv_buffer_changed(textbuffer) - if textbuffer.get_char_count() and self.encryption: - app.plugin_manager.extension_point( - 'typing' + self.encryption, self) - def restore_conversation(self): jid = self.contact.jid # don't restore lines if it's a transport
===================================== gajim/chat_control_base.py ===================================== @@ -341,10 +341,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): con = app.connections[self.account] con.get_module('Chatstate').set_active(self.contact.jid)
- message_tv_buffer = self.msg_textview.get_buffer() - id_ = message_tv_buffer.connect('changed', + id_ = self.msg_textview.connect('text-changed', self._on_message_tv_buffer_changed) - self.handlers[id_] = message_tv_buffer + self.handlers[id_] = self.msg_textview if parent_win is not None: id_ = parent_win.window.connect('motion-notify-event', self._on_window_motion_notify) @@ -837,10 +836,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): con = app.connections[self.account] con.get_module('Chatstate').set_mouse_activity(self.contact)
- def _on_message_tv_buffer_changed(self, *args): + def _on_message_tv_buffer_changed(self, textview, textbuffer): + if textbuffer.get_char_count() and self.encryption: + app.plugin_manager.extension_point( + 'typing' + self.encryption, self) + con = app.connections[self.account] con.get_module('Chatstate').set_keyboard_activity(self.contact) - if not self.msg_textview.has_text(): + if not textview.has_text(): con.get_module('Chatstate').set_chatstate(self.contact, Chatstate.ACTIVE) return
===================================== gajim/common/contacts.py ===================================== @@ -60,6 +60,8 @@ class CommonContact(XMPPEntity): # this is contact's chatstate self._chatstate = chatstate
+ self._is_pm_contact = False + @property def show(self): return self._show @@ -91,6 +93,10 @@ class CommonContact(XMPPEntity): def is_gc_contact(self): return isinstance(self, GC_Contact)
+ @property + def is_pm_contact(self): + return self._is_pm_contact + def get_full_jid(self): raise NotImplementedError
@@ -117,7 +123,8 @@ class Contact(CommonContact): """ def __init__(self, jid, account, name='', groups=None, show='', status='', sub='', ask='', resource='', priority=0, keyID='', client_caps=None, - chatstate=None, idle_time=None, avatar_sha=None, groupchat=False): + chatstate=None, idle_time=None, avatar_sha=None, groupchat=False, + is_pm_contact=False): if not isinstance(jid, str): print('no str') if groups is None: @@ -130,6 +137,7 @@ class Contact(CommonContact): self.groups = [i if i else _('General') for i in set(groups)] # filter duplicate values self.avatar_sha = avatar_sha self._is_groupchat = groupchat + self._is_pm_contact = is_pm_contact
self.sub = sub self.ask = ask @@ -227,7 +235,8 @@ class GC_Contact(CommonContact): """ return Contact(jid=self.get_full_jid(), account=self.account, name=self.name, groups=[], show=self.show, status=self.status, - sub='none', client_caps=self.client_caps, avatar_sha=self.avatar_sha) + sub='none', client_caps=self.client_caps, avatar_sha=self.avatar_sha, + is_pm_contact=True)
class LegacyContactsAPI:
===================================== gajim/common/i18n.py ===================================== @@ -67,7 +67,7 @@ def iter_locale_dirs(): continue found_paths.append(locale_dir) if locale_dir.is_dir(): - yield locale_dir + yield str(locale_dir)
def initialize_direction_mark(): from gi.repository import Gtk
===================================== gajim/common/modules/chatstates.py ===================================== @@ -74,42 +74,58 @@ class Chatstate: return
full_jid = stanza.getFrom() - jid = full_jid.getStripped()
if self._con.get_own_jid().bareMatch(full_jid): return
- contact = app.contacts.get_contact_from_full_jid( - self._account, str(full_jid)) - if contact is None or contact.is_gc_contact: + contact = app.contacts.get_gc_contact( + self._account, full_jid.getStripped(), full_jid.getResource()) + if contact is None: + contact = app.contacts.get_contact_from_full_jid( + self._account, str(full_jid)) + if contact is None: return
if contact.chatstate is None: return
+ if contact.is_gc_contact: + jid = contact.get_full_jid() + else: + jid = contact.jid + contact.chatstate = None - self._chatstates.pop(contact.jid, None) - self._last_mouse_activity.pop(contact.jid, None) + self._chatstates.pop(jid, None) + self._last_mouse_activity.pop(jid, None) + self._last_keyboard_activity.pop(jid, None)
log.info('Reset chatstate for %s', jid)
app.nec.push_outgoing_event( NetworkEvent('chatstate-received', account=self._account, - jid=jid)) + contact=contact))
def delegate(self, event: Any) -> None: if self._con.get_own_jid().bareMatch(event.jid) or event.sent: # Dont show chatstates from our own resources return
+ if event.mtype == 'groupchat': + # Not implemented yet + return + chatstate = parse_chatstate(event.stanza) if chatstate is None: return
- contact = app.contacts.get_contact_from_full_jid( - self._account, event.fjid) - if contact is None or contact.is_gc_contact: + if event.muc_pm: + contact = app.contacts.get_gc_contact( + self._account, event.jid, event.resource) + else: + contact = app.contacts.get_contact_from_full_jid( + self._account, event.fjid) + if contact is None: return
contact.chatstate = chatstate @@ -117,7 +133,7 @@ class Chatstate: app.nec.push_outgoing_event( NetworkEvent('chatstate-received', account=self._account, - jid=event.jid)) + contact=contact))
def _check_last_interaction(self) -> GLib.SOURCE_CONTINUE: setting = app.config.get('outgoing_chat_state_notifications') @@ -125,14 +141,16 @@ class Chatstate: return GLib.SOURCE_CONTINUE
now = time.time() - for jid, time_ in self._last_mouse_activity.items(): + for jid in list(self._last_mouse_activity.keys()): + time_ = self._last_mouse_activity[jid] current_state = self._chatstates.get(jid) if current_state is None: self._last_mouse_activity.pop(jid, None) - return GLib.SOURCE_CONTINUE + self._last_keyboard_activity.pop(jid, None) + continue
if current_state in (State.GONE, State.INACTIVE): - return GLib.SOURCE_CONTINUE + continue
new_chatstate = None if now - time_ > INACTIVE_AFTER: @@ -147,9 +165,16 @@ class Chatstate: if self._chatstates.get(jid) != new_chatstate: contact = app.contacts.get_contact(self._account, jid) if contact is None: - self._last_mouse_activity.pop(jid, None) - return GLib.SOURCE_CONTINUE - self.set_chatstate(contact, new_chatstate) + room, nick = app.get_room_and_nick_from_fjid(jid) + contact = app.contacts.get_gc_contact( + self._account, room, nick) + if contact is not None: + contact = contact.as_contact() + else: + self._last_mouse_activity.pop(jid, None) + self._last_keyboard_activity.pop(jid, None) + continue + self.set_chatstate(contact, new_chatstate)
return GLib.SOURCE_CONTINUE
@@ -191,13 +216,15 @@ class Chatstate: chatstate=str(State.GONE))) self._chatstates.pop(contact.jid, None) self._last_mouse_activity.pop(contact.jid, None) + self._last_keyboard_activity.pop(contact.jid, None) return
if not contact.is_groupchat(): # Dont leak presence to contacts # which are not allowed to see our status - if contact and contact.sub in ('to', 'none'): - return + if not contact.is_pm_contact: + if contact and contact.sub in ('to', 'none'): + return
if contact.show == 'offline': return
===================================== gajim/data/gui/change_status_message_dialog.ui ===================================== @@ -1,29 +1,32 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.18.3 --> +<!-- Generated with glade 3.22.1 --> <interface> - <requires lib="gtk+" version="3.12"/> + <requires lib="gtk+" version="3.18"/> <object class="GtkDialog" id="change_status_message_dialog"> <property name="can_focus">False</property> - <property name="border_width">6</property> + <property name="border_width">18</property> <property name="default_width">270</property> <property name="default_height">330</property> <property name="type_hint">dialog</property> <signal name="key-press-event" handler="on_change_status_message_dialog_key_press_event" swapped="no"/> + <child> + <placeholder/> + </child> <child internal-child="vbox"> - <object class="GtkBox" id="dialog-vbox5"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="orientation">vertical</property> - <property name="spacing">6</property> + <property name="spacing">12</property> <child internal-child="action_area"> <object class="GtkButtonBox" id="dialog-action_area"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="margin_top">6</property> <property name="layout_style">end</property> <child> <object class="GtkButton" id="cancel_button"> <property name="label">gtk-cancel</property> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="can_default">True</property> @@ -39,13 +42,15 @@ <child> <object class="GtkButton" id="ok_button"> <property name="label">gtk-ok</property> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="can_default">True</property> <property name="has_default">True</property> <property name="receives_default">False</property> <property name="use_stock">True</property> + <style> + <class name="suggested-action"/> + </style> </object> <packing> <property name="expand">False</property> @@ -62,109 +67,94 @@ </packing> </child> <child> - <object class="GtkFrame" id="frame38"> + <object class="GtkFrame"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="label_xalign">0</property> <property name="shadow_type">none</property> <child> - <object class="GtkBox" id="vbox112"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="border_width">6</property> + <property name="margin_top">6</property> <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkScrolledWindow" id="scrolledwindow24"> + <object class="GtkBox"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">in</property> + <property name="can_focus">False</property> <child> - <object class="GtkTextView" id="message_textview"> + <object class="GtkScrolledWindow"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="wrap_mode">word</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTextView" id="message_textview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="wrap_mode">word</property> + <property name="left_margin">6</property> + <property name="right_margin">6</property> + <property name="top_margin">6</property> + <property name="bottom_margin">6</property> + </object> + </child> </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButtonBox" id="hbuttonbox33"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> <child> <object class="GtkButton" id="save_as_preset_button"> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="can_default">True</property> <property name="receives_default">False</property> + <property name="tooltip_text" translatable="yes">Save this message as a preset</property> <signal name="clicked" handler="on_save_as_preset_button_clicked" swapped="no"/> <child> - <object class="GtkBox" id="hbox3021"> + <object class="GtkImage"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="spacing">2</property> - <child> - <object class="GtkImage" id="image1369"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-save-as</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label380"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Save as Preset...</property> - <property name="use_underline">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> + <property name="stock">gtk-save-as</property> </object> </child> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">0</property> + <property name="position">1</property> </packing> </child> </object> <packing> - <property name="expand">False</property> + <property name="expand">True</property> <property name="fill">True</property> - <property name="position">1</property> + <property name="position">0</property> </packing> </child> <child> <object class="GtkGrid" id="under_table"> <property name="visible">True</property> <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="margin_top">6</property> <property name="row_spacing">6</property> - <property name="column_spacing">6</property> + <property name="column_spacing">12</property> <child> - <object class="GtkLabel" id="label165"> + <object class="GtkLabel"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Preset messages:</property> + <property name="halign">end</property> + <property name="label" translatable="yes">Preset _messages</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">message_combobox</property> + <style> + <class name="dim-label"/> + </style> </object> <packing> <property name="left_attach">0</property> @@ -175,8 +165,14 @@ <object class="GtkLabel" id="activity_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Activity:</property> + <property name="halign">end</property> + <property name="margin_top">6</property> + <property name="label" translatable="yes">Ac_tivity</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">activity_button</property> + <style> + <class name="dim-label"/> + </style> </object> <packing> <property name="left_attach">0</property> @@ -187,8 +183,13 @@ <object class="GtkLabel" id="mood_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Mood:</property> + <property name="halign">end</property> + <property name="label" translatable="yes">M_ood</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">mood_button</property> + <style> + <class name="dim-label"/> + </style> </object> <packing> <property name="left_attach">0</property> @@ -197,6 +198,7 @@ </child> <child> <object class="GtkComboBox" id="message_combobox"> + <property name="width_request">200</property> <property name="visible">True</property> <property name="can_focus">False</property> <property name="hexpand">True</property> @@ -209,13 +211,13 @@ </child> <child> <object class="GtkButton" id="activity_button"> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> + <property name="margin_top">6</property> <signal name="clicked" handler="on_activity_button_clicked" swapped="no"/> <child> - <object class="GtkBox" id="hbox1"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="spacing">6</property> @@ -235,8 +237,8 @@ <object class="GtkLabel" id="activity_button_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0</property> <property name="wrap">True</property> + <property name="xalign">0</property> </object> <packing> <property name="expand">True</property> @@ -254,13 +256,12 @@ </child> <child> <object class="GtkButton" id="mood_button"> - <property name="use_action_appearance">False</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="receives_default">True</property> <signal name="clicked" handler="on_mood_button_clicked" swapped="no"/> <child> - <object class="GtkBox" id="hbox2"> + <object class="GtkBox"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="spacing">6</property> @@ -280,8 +281,8 @@ <object class="GtkLabel" id="mood_button_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="xalign">0</property> <property name="wrap">True</property> + <property name="xalign">0</property> </object> <packing> <property name="expand">True</property> @@ -301,17 +302,23 @@ <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">2</property> + <property name="position">1</property> </packing> </child> </object> </child> <child type="label"> - <object class="GtkLabel" id="label383"> + <object class="GtkLabel"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes"><b>Type your new status message</b></property> - <property name="use_markup">True</property> + <property name="label" translatable="yes">_Type your new status message</property> + <property name="use_underline">True</property> + <property name="justify">center</property> + <property name="mnemonic_widget">message_textview</property> + <style> + <class name="dim-label"/> + <class name="bold"/> + </style> </object> </child> </object>
===================================== gajim/gajim.py ===================================== @@ -27,7 +27,7 @@ from distutils.version import LooseVersion as V # Install _() in namespace from gajim.common import i18n
-_MIN_NBXMPP_VER = "0.6.7" +_MIN_NBXMPP_VER = "0.6.8" _MIN_GTK_VER = "3.22.0"
===================================== gajim/message_textview.py ===================================== @@ -21,6 +21,7 @@ import gc
from gi.repository import Gtk from gi.repository import GLib +from gi.repository import GObject from gi.repository import Pango
from gajim.common import app @@ -36,6 +37,10 @@ class MessageTextView(Gtk.TextView): Class for the message textview (where user writes new messages) for chat/groupchat windows """ + __gsignals__ = { + 'text-changed': (GObject.SIGNAL_RUN_LAST, None, (Gtk.TextBuffer,)) + } + UNDO_LIMIT = 20 PLACEHOLDER = _('Write a message…')
@@ -60,6 +65,9 @@ class MessageTextView(Gtk.TextView): self.undo_pressed = False
buffer_ = self.get_buffer() + buffer_.connect('changed', self._on_buffer_changed) + self._last_text = '' + self.begin_tags = {} self.end_tags = {} self.color_tags = [] @@ -94,6 +102,16 @@ class MessageTextView(Gtk.TextView): buffer_.insert_with_tags( start, self.PLACEHOLDER, self.placeholder_tag)
+ def _on_buffer_changed(self, *args): + text = self.get_text() + # Because of inserting and removing PLACEHOLDER text + # the signal becomes unreliable when determining if the user + # typed in new text. So we send our own signal depending on if + # there is real new text that is not the PLACEHOLDER + if text != self._last_text: + self.emit('text-changed', self.get_buffer()) + self._last_text = text + def has_text(self): buf = self.get_buffer() start, end = buf.get_bounds()
===================================== gajim/plugins/pluginmanager.py ===================================== @@ -110,7 +110,7 @@ class PluginManager(metaclass=Singleton): '''
for path in reversed(configpaths.get_plugin_dirs()): - pc = PluginManager.scan_dir_for_plugins(path) + pc = self.scan_dir_for_plugins(path) self.add_plugins(pc)
@log_calls('PluginManager') @@ -137,7 +137,6 @@ class PluginManager(metaclass=Singleton): and adding class from reloaded module or ignoring adding plug-in? ''' plugin = plugin_class() - if plugin not in self.plugins: if not self._plugin_has_entry_in_global_config(plugin): self._create_plugin_entry_in_global_config(plugin) @@ -520,9 +519,8 @@ class PluginManager(metaclass=Singleton): def _set_plugin_active_in_global_config(self, plugin, active=True): app.config.set_per('plugins', plugin.short_name, 'active', active)
- @staticmethod @log_calls('PluginManager') - def scan_dir_for_plugins(path, scan_dirs=True, package=False): + def scan_dir_for_plugins(self, path, scan_dirs=True, package=False): r''' Scans given directory for plugin classes.
@@ -584,6 +582,21 @@ class PluginManager(metaclass=Singleton): elem_name, exc_info=True) continue
+ short_name = conf.get('info', 'short_name', fallback=None) + if short_name is None: + log.error('No short_name defined for %s', elem_name) + + # Check if the plugin is already loaded + try: + for plugin in self.plugins: + if plugin.short_name == short_name: + raise PluginAlreadyLoaded( + 'Skip Plugin %s because its ' + 'already loaded' % elem_name) + except PluginAlreadyLoaded as error: + log.warning(error) + continue + min_v = conf.get('info', 'min_gajim_version', fallback=None) max_v = conf.get('info', 'max_gajim_version', fallback=None)
@@ -734,3 +747,7 @@ class PluginManager(metaclass=Singleton): for plugin in self.plugins: if plugin.__path__ in plugin_dir: return plugin + + +class PluginAlreadyLoaded(Exception): + pass
===================================== gajim/remote_control.py ===================================== @@ -324,8 +324,14 @@ class GajimRemote(Server): self.on_message_sent)
def on_chatstate_received(self, obj): - self.raise_signal('ChatState', (obj.conn.name, [ - obj.jid, obj.fjid, obj.stanza, obj.resource, obj.chatstate])) + if obj.contact.is_gc_contact: + jid = obj.contact.get_full_jid() + chatstate = obj.contact.chatstate + else: + jid = obj.contact.jid + chatstate = app.contacts.get_combined_chatstate( + obj.account, obj.contact.jid) + self.raise_signal('ChatState', (obj.account, [jid, chatstate]))
def on_message_sent(self, obj): try:
===================================== org.gajim.Gajim.json deleted ===================================== @@ -1,361 +0,0 @@ -{ - "app-id": "org.gajim.Gajim", - "runtime": "org.gnome.Platform", - "runtime-version": "3.28", - "sdk": "org.gnome.Sdk", - "command": "gajim", - "tags": ["nightly"], - "desktop-file-name-prefix": "(Nightly) ", - "finish-args": [ - "--share=ipc", - "--share=network", - "--socket=x11", - "--socket=wayland", - "--socket=pulseaudio", - "--system-talk-name=org.freedesktop.GeoClue2", - "--system-talk-name=org.freedesktop.login1", - "--talk-name=org.gnome.ScreenSaver", - "--talk-name=org.mpris.MediaPlayer2.*", - /* Keyring */ - "--talk-name=org.freedesktop.secrets", - /* dconf */ - "--filesystem=xdg-run/dconf", - "--filesystem=~/.config/dconf:ro", - "--talk-name=ca.desrt.dconf", - "--env=DCONF_USER_CONFIG_DIR=.config/dconf", - /* extensions */ - "--env=PYTHONPATH=/app/plugins/lib/python3.5/site-packages" - ], - "add-extensions": { - "org.gajim.Gajim.Plugin": { - "directory": "plugins", - "merge-dirs": "lib/python3.5/site-packages", - "subdirectories": true, - "no-autodownload": true, - "autodelete": true - } - }, - "build-options": { - "cflags": "-O2 -g", - "cxxflags": "-O2 -g" - }, - "cleanup": [ - "/bin/easy*", - "/include", - "/lib/debug", - "/lib/pkgconfig", - "/lib/python2.7", - "/share/aclocal", - "/share/doc", - "/share/man", - "*.a", - "*.la" - ], - "modules": [ - { - "name": "python3-pycparser", - "buildsystem": "simple", - "build-commands": [ - "python3 setup.py install --prefix=/app" - ], - "sources": [ - { - "type": "archive", - "url": "https://pypi.python.org/packages/8c/2d/aad7f16146f4197a11f8e91fb81df177adcc2...", - "sha256": "99a8ca03e29851d96616ad0404b4aad7d9ee16f25c9f9708a11faf2810f7b226" - } - ] - }, - { - "name": "python3-cffi", - "buildsystem": "simple", - "build-commands": [ - "python3 setup.py install --prefix=/app" - ], - "sources": [ - { - "type": "archive", - "url": "https://files.pythonhosted.org/packages/e7/a7/4cd50e57cc6f436f1cc3a7e8fa700f...", - "sha256": "e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4" - } - ] - }, - { - "name": "python3-six", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app six-1.11.0-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/67/4b/141a581104b1f6397bfa78ac9d43d8ad29a7c...", - "sha256": "832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - } - ] - }, - { - "name": "python3-pyparsing", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app pyparsing-2.2.0-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/6a/8a/718fd7d3458f9fab8e67186b00abdd345b639...", - "sha256": "fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" - } - ] - }, - { - "name": "python3-packaging", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app packaging-17.1-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/ad/c2/b500ea05d5f9f361a562f089fc91f7...", - "sha256": "e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0" - } - ] - }, - { - "name": "python3-appdirs", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app appdirs-1.4.3-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/56/eb/810e700ed1349edde4cbdc1b2a21e28cdf115...", - "sha256": "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e" - } - ] - }, - { - "name": "python3-setuptools", - "ensure-writable": [ - "easy-install.pth", - "setuptools.pth" - ], - "buildsystem": "simple", - "build-commands": [ - "python3 setup.py install --prefix=/app" - ], - "sources": [ - { - "type": "archive", - "url": "https://files.pythonhosted.org/packages/d3/3e/1d74cdcb393b68ab9ee18d78c11ae6...", - "sha256": "012adb8e25fbfd64c652e99e7bab58799a3aaf05d39ab38561f69190a909015f" - } - ] - }, - { - "name": "python3-asn1crypto", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app asn1crypto-0.24.0-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://pypi.python.org/packages/ea/cd/35485615f45f30a510576f1a56d1e0a7ad7bd...", - "sha256": "2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87" - } - ] - }, - { - "name": "python3-idna", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app idna-2.7-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355...", - "sha256": "156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e" - } - ] - }, - { - "name": "python3-cryptography", - "ensure-writable": [ - "easy-install.pth" - ], - "buildsystem": "simple", - "build-commands": [ - "python3 setup.py install --prefix=/app" - ], - "sources": [ - { - "type": "archive", - "url": "https://files.pythonhosted.org/packages/22/21/233e38f74188db94e8451ef6385754...", - "sha256": "8d10113ca826a4c29d5b85b2c4e045ffa8bad74fb525ee0eceb1d38d4c70dfd6" - } - ] - }, - { - "name": "python3-pyopenssl", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app pyOpenSSL-18.0.0-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/96/af/9d29e6bd40823061aea2e0574ccb2f...", - "sha256": "26ff56a6b5ecaf3a2a59f132681e2a80afcc76b4f902f612f518f92c2a1bf854" - } - ] - }, - { - "name": "python3-dbus-python", - "build-options": { - "env": { - "PYTHON_VERSION": "3" - } - }, - "sources": [ - { - "type": "archive", - "url": "https://files.pythonhosted.org/packages/3f/e7/4edb582d1ffd5ac3c84188deea32e9...", - "sha256": "abf12bbb765e300bf8e2a1b2f32f85949eab06998dbda127952c31cb63957b6f" - } - ] - }, - { - "name": "python3-jeepney", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app jeepney-0.3.1-py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/a9/21/c07060fac57d8d4932ccb19f96b244...", - "sha256": "e7b961fe9dfa1ed4c576d3bb2d70a9276dace0e16ebed9da09e4d288fe1d3b2a" - } - ] - }, - { - "name": "python3-secretstorage", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app SecretStorage-3.0.1-py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/f3/40/3cbd8b15c8f98b5c6d2480fb0087b0...", - "sha256": "1bbf5b85a718854916d1c151fa33e6f667e3c005e033ea46d4123384d233b137" - } - ] - }, - { - "name": "python3-entrypoints", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app entrypoints-0.2.3-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/cc/8b/4eefa9b47f1910b3d2081da67726b0...", - "sha256": "10ad569bb245e7e2ba425285b9fa3e8178a0dc92fc53b1e1c553805e15a8825b" - } - ] - }, - { - "name": "python3-keyring", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app keyring-13.2.1-py2.py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/88/a8/341fa4686e98bf10289eb4eaea89e8...", - "sha256": "6e01954fd3e404820e1fade262ee661974051551ed08c899ffc5e88bb9df288e" - } - ] - }, - { - "name": "python3-cssutils", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app cssutils-1.0.2-py3-none-any.whl" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/6b/15/a9fb9010f58d1c55dd0b7779db2334...", - "sha256": "c74dbe19c92f5052774eadb15136263548dd013250f1ed1027988e7fef125c8d" - } - ] - }, - { - "name": "gspell", - "cleanup": [ - "/bin" - ], - "sources": [ - { - "type": "archive", - "url": "https://download.gnome.org/sources/gspell/1.8/gspell-1.8.1.tar.xz", - "sha256": "819a1d23c7603000e73f5e738bdd284342e0cd345fb0c7650999c31ec741bbe5" - } - ], - "modules": [ - { - "name": "enchant2", - "cleanup": [ - "/bin" - ], - "sources": [ - { - "type": "archive", - "url": "https://github.com/AbiWord/enchant/releases/download/v2.2.3/enchant-2.2.3.ta...", - "sha256": "abd8e915675cff54c0d4da5029d95c528362266557c61c7149d53fa069b8076d" - } - ] - } - ] - }, - { - "name": "python3-nbxmpp", - "buildsystem": "simple", - "build-commands": [ - "python3 setup.py install --prefix=/app" - ], - "sources": [ - { - "type": "archive", - "url": "https://files.pythonhosted.org/packages/b2/2d/680b61de01eaaadfe1b57fe8a21cdf...", - "sha256": "5b291d9604854b611479657867e2d5118b65f0e58280bcd19fb539c955245a39" - } - ] - }, - { - "name": "gajim", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --prefix=/app .", - "touch /app/share/run-as-flatpak" - ], - "sources": [ - { - "type": "git", - "url": "https://dev.gajim.org/gajim/gajim.git" - } - ], - "post-install": [ - "install -d /app/plugins" - ] - } - ] -}
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/a04637105d9929cfba0cbfa3007aa94a5c...