Compare commits

..

1 commit

Author SHA1 Message Date
5e0c4f5d5c add plural-affirmative aliases for /me 2019-06-01 11:24:08 -04:00
85 changed files with 24662 additions and 24988 deletions

View file

@ -1,29 +1,3 @@
Gajim 1.1.3 (24 April 2019)
New
* Add a mobile phone indicator to the chat window
* Rework HTTPUpload dialog
* Add a "paste as quote" option to the message input
Bug fixes
* #8822 Fix memory leak when using spell checker
* #9514 Fix jingle filetransfers not working in some circumstances
* #9573 Dont leak DNS query when connecting over proxy
* #9578 Determine Windows version more reliably
* #9622 Fix an error while quitting Gajim
* #9633 Fix an error while sending a file
* #9637 Restore window size correctly on wayland
* #9660 GPG Agent setting is ignored
* #9645 Make zeroconf IPV6 compatible
* Improve dark theme colors
* Fix access to GnuPG keys
* Use UUID4 item ids for pubsub posts
* Dont send invalid show values
* Windows: Dont override format region settings
* Various smaller improvements
Gajim 1.1.2 (15 January 2019) Gajim 1.1.2 (15 January 2019)
Bug fixes Bug fixes

View file

@ -32,8 +32,8 @@ build_script:
bash "git clone C:/projects/gajim C:/msys64/home/appveyor/gajim" bash "git clone C:/projects/gajim C:/msys64/home/appveyor/gajim"
bash "C:/msys64/home/appveyor/gajim/win/build.sh $($env:MSYS_ARCH)" bash "C:/msys64/home/appveyor/gajim/win/build.sh $($env:MSYS_ARCH)"
Push-AppveyorArtifact "$($env:BUILDROOT)/Gajim.exe" -FileName "Gajim-1.1.2-$($env:ARCH)-$($env:TIME_STRING).exe" Push-AppveyorArtifact "$($env:BUILDROOT)/Gajim.exe" -FileName "Gajim-1.1.1-$($env:ARCH)-$($env:TIME_STRING).exe"
Push-AppveyorArtifact "$($env:BUILDROOT)/Gajim-Portable.exe" -FileName "Gajim-Portable-1.1.2-$($env:ARCH)-$($env:TIME_STRING).exe" Push-AppveyorArtifact "$($env:BUILDROOT)/Gajim-Portable.exe" -FileName "Gajim-Portable-1.1.1-$($env:ARCH)-$($env:TIME_STRING).exe"
# on_finish: # on_finish:
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))

View file

@ -101,7 +101,6 @@
<content_attribute id="money-gambling">none</content_attribute> <content_attribute id="money-gambling">none</content_attribute>
</content_rating> </content_rating>
<releases> <releases>
<release version="1.1.3" date="2019-04-23" />
<release version="1.1.2" date="2019-01-15" /> <release version="1.1.2" date="2019-01-15" />
<release version="1.1.1" date="2018-12-23" /> <release version="1.1.1" date="2018-12-23" />
<release version="1.1.0" date="2018-11-06" /> <release version="1.1.0" date="2018-11-06" />

View file

@ -3,6 +3,8 @@ runtime: org.gnome.Platform
runtime-version: 3.30 runtime-version: 3.30
sdk: org.gnome.Sdk sdk: org.gnome.Sdk
command: gajim command: gajim
tags: nightly
desktop-file-name-prefix: '(Nightly) '
finish-args: finish-args:
- --share=ipc - --share=ipc
- --share=network - --share=network
@ -20,8 +22,6 @@ finish-args:
- --filesystem=~/.config/dconf:ro - --filesystem=~/.config/dconf:ro
- --talk-name=ca.desrt.dconf - --talk-name=ca.desrt.dconf
- --env=DCONF_USER_CONFIG_DIR=.config/dconf - --env=DCONF_USER_CONFIG_DIR=.config/dconf
# GnuPG
- --filesystem=~/.gnupg
# extensions # extensions
- --env=PYTHONPATH=/app/plugins/lib/python3.7/site-packages - --env=PYTHONPATH=/app/plugins/lib/python3.7/site-packages
@ -62,8 +62,8 @@ modules:
- pip3 install --prefix=/app . - pip3 install --prefix=/app .
sources: sources:
- type: archive - type: archive
url: https://files.pythonhosted.org/packages/64/7c/27367b38e6cc3e1f49f193deb761fe75cda9f95da37b67b422e62281fcac/cffi-1.12.2.tar.gz url: https://files.pythonhosted.org/packages/e7/a7/4cd50e57cc6f436f1cc3a7e8fa700ff9b8b4d471620629074913e3735fb2/cffi-1.11.5.tar.gz
sha256: e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7 sha256: e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4
- name: python3-asn1crypto - name: python3-asn1crypto
buildsystem: simple buildsystem: simple
@ -77,11 +77,11 @@ modules:
- name: python3-idna - name: python3-idna
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --prefix=/app idna-2.8-py2.py3-none-any.whl - pip3 install --prefix=/app idna-2.7-py2.py3-none-any.whl
sources: sources:
- type: file - type: file
url: https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl url: https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py2.py3-none-any.whl
sha256: ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c sha256: 156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e
- name: python3-cryptography - name: python3-cryptography
buildsystem: simple buildsystem: simple
@ -89,17 +89,17 @@ modules:
- pip3 install --prefix=/app . - pip3 install --prefix=/app .
sources: sources:
- type: archive - type: archive
url: https://files.pythonhosted.org/packages/07/ca/bc827c5e55918ad223d59d299fff92f3563476c3b00d0a9157d9c0217449/cryptography-2.6.1.tar.gz url: https://files.pythonhosted.org/packages/22/21/233e38f74188db94e8451ef6385754a98f3cad9b59bedf3a8e8b14988be4/cryptography-2.3.1.tar.gz
sha256: 26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6 sha256: 8d10113ca826a4c29d5b85b2c4e045ffa8bad74fb525ee0eceb1d38d4c70dfd6
- name: python3-pyopenssl - name: python3-pyopenssl
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --prefix=/app pyOpenSSL-19.0.0-py2.py3-none-any.whl - pip3 install --prefix=/app pyOpenSSL-18.0.0-py2.py3-none-any.whl
sources: sources:
- type: file - type: file
url: https://files.pythonhosted.org/packages/01/c8/ceb170d81bd3941cbeb9940fc6cc2ef2ca4288d0ca8929ea4db5905d904d/pyOpenSSL-19.0.0-py2.py3-none-any.whl url: https://files.pythonhosted.org/packages/96/af/9d29e6bd40823061aea2e0574ccb2fcf72bfd6130ce53d32773ec375458c/pyOpenSSL-18.0.0-py2.py3-none-any.whl
sha256: c727930ad54b10fc157015014b666f2d8b41f70c0d03e83ab67624fd3dd5d1e6 sha256: 26ff56a6b5ecaf3a2a59f132681e2a80afcc76b4f902f612f518f92c2a1bf854
- name: python3-dbus-python - name: python3-dbus-python
build-options: build-options:
@ -122,31 +122,31 @@ modules:
- name: python3-secretstorage - name: python3-secretstorage
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --prefix=/app SecretStorage-3.1.1-py3-none-any.whl - pip3 install --prefix=/app SecretStorage-3.1.0-py3-none-any.whl
sources: sources:
- type: file - type: file
url: https://files.pythonhosted.org/packages/82/59/cb226752e20d83598d7fdcabd7819570b0329a61db07cfbdd21b2ef546e3/SecretStorage-3.1.1-py3-none-any.whl url: https://files.pythonhosted.org/packages/d8/e8/80975fd281764c80b2eb581a7f25d2109786e273b8925e8161bd2d06d10a/SecretStorage-3.1.0-py3-none-any.whl
sha256: 7a119fb52a88e398dbb22a4b3eb39b779bfbace7e4153b7bc6e5954d86282a8a sha256: 20196abd1a9d1310df7573d58ca6e7ed9292218c98ca3638eea07beb16080343
- name: python3-entrypoints - name: python3-entrypoints
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --prefix=/app entrypoints-0.3-py2.py3-none-any.whl - pip3 install --prefix=/app entrypoints-0.2.3-py2.py3-none-any.whl
sources: sources:
- type: file - type: file
url: https://files.pythonhosted.org/packages/ac/c6/44694103f8c221443ee6b0041f69e2740d89a25641e62fb4f2ee568f2f9c/entrypoints-0.3-py2.py3-none-any.whl url: https://files.pythonhosted.org/packages/cc/8b/4eefa9b47f1910b3d2081da67726b066e379b04ca897acfe9f92bac56147/entrypoints-0.2.3-py2.py3-none-any.whl
sha256: 589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19 sha256: 10ad569bb245e7e2ba425285b9fa3e8178a0dc92fc53b1e1c553805e15a8825b
- name: python3-keyring - name: python3-keyring
buildsystem: simple buildsystem: simple
build-commands: build-commands:
- pip3 install --prefix=/app keyring-18.0.0-py2.py3-none-any.whl - pip3 install --prefix=/app keyring-16.0.2-py2.py3-none-any.whl
cleanup: cleanup:
- /bin - /bin
sources: sources:
- type: file - type: file
url: https://files.pythonhosted.org/packages/a1/28/0058032477bfdf2003e605d175629963759220661615443e20711446bfa7/keyring-18.0.0-py2.py3-none-any.whl url: https://files.pythonhosted.org/packages/5f/cb/dc7b2215cd82b77e7b8b48abd8989c1b09990d4c91a3ccfdc18a61157b36/keyring-16.0.2-py2.py3-none-any.whl
sha256: ca33f5ccc542b9ffaa196ee9a33488069e5e7eac77d5b81969f8a3ce74d0230c sha256: 2a5cf5e596cbf8b66b98b8df2c214adfe21e6e18baa82006b2c482bd0c4be94c
- name: python3-cssutils - name: python3-cssutils
buildsystem: simple buildsystem: simple
@ -182,8 +182,8 @@ modules:
- pip3 install --prefix=/app . - pip3 install --prefix=/app .
sources: sources:
- type: archive - type: archive
url: https://files.pythonhosted.org/packages/d6/01/34b2a441926780f26edd21490158afe0eb76beae4efbb6bc4d3323eae69a/nbxmpp-0.6.10.tar.gz url: https://files.pythonhosted.org/packages/24/54/23a475a0d7d3664ea21b14ce907245dc390496f31d229a9aac2ae20c7c28/nbxmpp-0.6.8.tar.gz
sha256: cd73417777e4847fdd8d0d96c7cafc606952edbd2b9d52a2a72bb2aaa04d08ef sha256: 8c2b4b8aac1a8c6d07c1e30af542fde20a70a9b8c7c04017e9cea0db654437c6
- name: gajim - name: gajim
buildsystem: simple buildsystem: simple
@ -193,6 +193,5 @@ modules:
sources: sources:
- type: git - type: git
url: https://dev.gajim.org/gajim/gajim.git url: https://dev.gajim.org/gajim/gajim.git
branch: gajim_1.1
post-install: post-install:
- install -d /app/plugins - install -d /app/plugins

View file

@ -1,7 +1,7 @@
import os import os
import subprocess import subprocess
__version__ = "1.1.3" __version__ = "1.1.2"
IS_FLATPAK = False IS_FLATPAK = False
if os.path.exists('/app/share/run-as-flatpak'): if os.path.exists('/app/share/run-as-flatpak'):

View file

@ -379,7 +379,6 @@ class GajimApplication(Gtk.Application):
act = Gio.SimpleAction.new_stateful( act = Gio.SimpleAction.new_stateful(
'agent', None, 'agent', None,
GLib.Variant.new_boolean(app.config.get('use_gpg_agent'))) GLib.Variant.new_boolean(app.config.get('use_gpg_agent')))
act.connect('change-state', app_actions.on_use_pgp_agent)
self.add_action(act) self.add_action(act)
# General Actions # General Actions

View file

@ -225,9 +225,6 @@ class ChatControl(ChatControlBase):
app.ged.register_event_handler('pep-received', ged.GUI1, app.ged.register_event_handler('pep-received', ged.GUI1,
self._nec_pep_received) self._nec_pep_received)
app.ged.register_event_handler('update-client-info', ged.GUI1,
self._on_update_client_info)
if self.TYPE_ID == message_control.TYPE_CHAT: if self.TYPE_ID == message_control.TYPE_CHAT:
# Dont connect this when PrivateChatControl is used # Dont connect this when PrivateChatControl is used
app.ged.register_event_handler('update-roster-avatar', ged.GUI1, app.ged.register_event_handler('update-roster-avatar', ged.GUI1,
@ -438,17 +435,6 @@ class ChatControl(ChatControlBase):
else: else:
self.update_pep(obj.pep_type) self.update_pep(obj.pep_type)
def _on_update_client_info(self, event):
if event.account != self.account:
return
if event.jid != self.contact.jid:
return
contact = app.contacts.get_contact(
self.account, event.jid, event.resource)
if contact is None:
return
self.xml.get_object('phone_image').set_visible(contact.uses_phone)
def _update_jingle(self, jingle_type): def _update_jingle(self, jingle_type):
if jingle_type not in ('audio', 'video'): if jingle_type not in ('audio', 'video'):
return return
@ -1077,9 +1063,6 @@ class ChatControl(ChatControlBase):
app.ged.remove_event_handler('pep-received', ged.GUI1, app.ged.remove_event_handler('pep-received', ged.GUI1,
self._nec_pep_received) self._nec_pep_received)
app.ged.remove_event_handler('update-client-info', ged.GUI1,
self._on_update_client_info)
if self.TYPE_ID == message_control.TYPE_CHAT: if self.TYPE_ID == message_control.TYPE_CHAT:
app.ged.remove_event_handler('update-roster-avatar', ged.GUI1, app.ged.remove_event_handler('update-roster-avatar', ged.GUI1,
self._nec_update_avatar) self._nec_update_avatar)

View file

@ -323,6 +323,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self.set_emoticon_popover() self.set_emoticon_popover()
# Attach speller # Attach speller
self.spell_checker = None
self.set_speller() self.set_speller()
self.conv_textview.tv.show() self.conv_textview.tv.show()
@ -473,15 +474,15 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
if gspell_lang is None: if gspell_lang is None:
return return
spell_checker = Gspell.Checker.new(gspell_lang) self.spell_checker = Gspell.Checker.new(gspell_lang)
spell_buffer = Gspell.TextBuffer.get_from_gtk_text_buffer( spell_buffer = Gspell.TextBuffer.get_from_gtk_text_buffer(
self.msg_textview.get_buffer()) self.msg_textview.get_buffer())
spell_buffer.set_spell_checker(spell_checker) spell_buffer.set_spell_checker(self.spell_checker)
spell_view = Gspell.TextView.get_from_gtk_text_view(self.msg_textview) spell_view = Gspell.TextView.get_from_gtk_text_view(self.msg_textview)
spell_view.set_inline_spell_checking(False) spell_view.set_inline_spell_checking(False)
spell_view.set_enable_language_menu(True) spell_view.set_enable_language_menu(True)
spell_checker.connect('notify::language', self.on_language_changed) self.spell_checker.connect('notify::language', self.on_language_changed)
def get_speller_language(self): def get_speller_language(self):
per_type = 'contacts' per_type = 'contacts'
@ -559,27 +560,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
id_ = item.connect('activate', self.msg_textview.clear) id_ = item.connect('activate', self.msg_textview.clear)
self.handlers[id_] = item self.handlers[id_] = item
paste_item = Gtk.MenuItem.new_with_label(_('Paste as quote'))
id_ = paste_item.connect('activate', self.paste_clipboard_as_quote)
self.handlers[id_] = paste_item
menu.append(paste_item)
menu.show_all() menu.show_all()
def insert_as_quote(self, text: str) -> None: def on_quote(self, widget, text):
self.msg_textview.remove_placeholder() self.msg_textview.remove_placeholder()
text = '> ' + text.replace('\n', '\n> ') + '\n' text = '>' + text.replace('\n', '\n>') + '\n'
message_buffer = self.msg_textview.get_buffer() message_buffer = self.msg_textview.get_buffer()
message_buffer.insert_at_cursor(text) message_buffer.insert_at_cursor(text)
def paste_clipboard_as_quote(self, _item: Gtk.MenuItem) -> None:
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
text = clipboard.wait_for_text()
self.insert_as_quote(text)
def on_quote(self, widget, text):
self.insert_as_quote(text)
# moved from ChatControl # moved from ChatControl
def _on_banner_eventbox_button_press_event(self, widget, event): def _on_banner_eventbox_button_press_event(self, widget, event):
""" """

View file

@ -74,7 +74,7 @@ class StandardCommonCommands(CommandContainer):
def say(self, message): def say(self, message):
self.send(message) self.send(message)
@command('we','us',raw=True) @command('we', 'us', raw=True)
@doc(_("Send action (in the third person) to the current chat")) @doc(_("Send action (in the third person) to the current chat"))
def me(self, action): def me(self, action):
self.send("/me %s" % action) self.send("/me %s" % action)

View file

@ -39,13 +39,11 @@ from distutils.version import LooseVersion as V
from collections import namedtuple from collections import namedtuple
import nbxmpp import nbxmpp
from gi.repository import Gdk
import gajim import gajim
from gajim.common import config as c_config from gajim.common import config as c_config
from gajim.common import configpaths from gajim.common import configpaths
from gajim.common import ged as ged_module from gajim.common import ged as ged_module
from gajim.common.const import Display
from gajim.common.contacts import LegacyContactsAPI from gajim.common.contacts import LegacyContactsAPI
from gajim.common.events import Events from gajim.common.events import Events
from gajim.common.types import NetworkEventsControllerT # pylint: disable=unused-import from gajim.common.types import NetworkEventsControllerT # pylint: disable=unused-import
@ -150,7 +148,6 @@ socks5queue = None
gupnp_igd = None gupnp_igd = None
gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'} gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'}
gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE, gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE,
nbxmpp.NS_MUC, nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC_ADMIN, nbxmpp.NS_MUC_OWNER, nbxmpp.NS_MUC, nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC_ADMIN, nbxmpp.NS_MUC_OWNER,
nbxmpp.NS_MUC_CONFIG, nbxmpp.NS_COMMANDS, nbxmpp.NS_DISCO_INFO, 'ipv6', nbxmpp.NS_MUC_CONFIG, nbxmpp.NS_COMMANDS, nbxmpp.NS_DISCO_INFO, 'ipv6',
@ -195,18 +192,6 @@ def is_installed(dependency):
def is_flatpak(): def is_flatpak():
return gajim.IS_FLATPAK return gajim.IS_FLATPAK
def is_display(display):
# XWayland reports as Display X11, so try with env var
is_wayland = os.environ.get('XDG_SESSION_TYPE') == 'wayland'
if is_wayland and display == Display.WAYLAND:
return True
default = Gdk.Display.get_default()
if default is None:
log('gajim').warning('Could not determine window manager')
return False
return default.__class__.__name__ == display.value
def disable_dependency(dependency): def disable_dependency(dependency):
_dependencies[dependency] = False _dependencies[dependency] = False

View file

@ -77,14 +77,6 @@ def client_supports(client_caps, requested_feature):
return requested_feature not in FEATURE_BLACKLIST return requested_feature not in FEATURE_BLACKLIST
return False return False
def get_client_identity(client_caps):
lookup_item = client_caps.get_cache_lookup_strategy()
cache_item = lookup_item(capscache)
for identity in cache_item.identities:
if identity.get('category') == 'client':
return identity.get('type')
def create_suitable_client_caps(node, caps_hash, hash_method, fjid=None): def create_suitable_client_caps(node, caps_hash, hash_method, fjid=None):
""" """
Create and return a suitable ClientCaps object for the given node, Create and return a suitable ClientCaps object for the given node,

View file

@ -905,6 +905,10 @@ class Connection(CommonConnection, ConnectionHandlers):
] ]
self._hostname = hostname self._hostname = hostname
if h:
app.resolver.resolve('_xmppconnect.' + helpers.idn_to_ascii(h),
self._on_resolve_txt, type_='txt')
if use_srv and self._proxy is None: if use_srv and self._proxy is None:
self._srv_hosts = [] self._srv_hosts = []
@ -914,9 +918,6 @@ class Connection(CommonConnection, ConnectionHandlers):
for service in services: for service in services:
record_name = '_' + service + '._tcp.' + helpers.idn_to_ascii(h) record_name = '_' + service + '._tcp.' + helpers.idn_to_ascii(h)
app.resolver.resolve(record_name, self._on_resolve_srv) app.resolver.resolve(record_name, self._on_resolve_srv)
app.resolver.resolve('_xmppconnect.' + helpers.idn_to_ascii(h),
self._on_resolve_txt, type_='txt')
else: else:
self._connect_to_next_host() self._connect_to_next_host()

View file

@ -193,13 +193,6 @@ class SyncThreshold(IntEnum):
return str(self.value) return str(self.value)
class Display(Enum):
X11 = 'X11Display'
WAYLAND = 'GdkWaylandDisplay'
WIN32 = 'GdkWin32Display'
QUARTZ = 'GdkQuartzDisplay'
ACTIVITIES = { ACTIVITIES = {
'doing_chores': { 'doing_chores': {
'category': _('Doing Chores'), 'category': _('Doing Chores'),

View file

@ -116,10 +116,6 @@ class CommonContact(XMPPEntity):
return False return False
return caps_cache.client_supports(self.client_caps, requested_feature) return caps_cache.client_supports(self.client_caps, requested_feature)
@property
def uses_phone(self):
return caps_cache.get_client_identity(self.client_caps) == 'phone'
class Contact(CommonContact): class Contact(CommonContact):
""" """

View file

@ -42,7 +42,6 @@ import logging
import json import json
import shutil import shutil
import collections import collections
from io import StringIO
from datetime import datetime, timedelta from datetime import datetime, timedelta
from distutils.version import LooseVersion as V from distutils.version import LooseVersion as V
from encodings.punycode import punycode_encode from encodings.punycode import punycode_encode
@ -52,17 +51,11 @@ import nbxmpp
from nbxmpp.stringprepare import nameprep from nbxmpp.stringprepare import nameprep
import precis_i18n.codec # pylint: disable=unused-import import precis_i18n.codec # pylint: disable=unused-import
from gajim.common import app
from gajim.common import caps_cache from gajim.common import caps_cache
from gajim.common import configpaths from gajim.common import configpaths
from gajim.common.i18n import Q_ from gajim.common.i18n import Q_
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.i18n import ngettext from gajim.common.i18n import ngettext
from gajim.common.const import Display
if app.is_installed('PYCURL'):
import pycurl
log = logging.getLogger('gajim.c.helpers') log = logging.getLogger('gajim.c.helpers')
@ -559,6 +552,12 @@ def datetime_tuple(timestamp):
tim = tim.timetuple() tim = tim.timetuple()
return tim return tim
from gajim.common import app
if app.is_installed('PYCURL'):
import pycurl
from io import StringIO
def convert_bytes(string): def convert_bytes(string):
suffix = '' suffix = ''
# IEC standard says KiB = 1024 bytes KB = 1000 bytes # IEC standard says KiB = 1024 bytes KB = 1000 bytes
@ -1535,14 +1534,3 @@ class AdditionalDataDict(collections.UserDict):
del _dict[key] del _dict[key]
except KeyError: except KeyError:
return return
def save_roster_position(window):
if not app.config.get('save-roster-position'):
return
if app.is_display(Display.WAYLAND):
return
x_pos, y_pos = window.get_position()
log.debug('Save roster position: %s %s', x_pos, y_pos)
app.config.set('roster_x-position', x_pos)
app.config.set('roster_y-position', y_pos)

View file

@ -151,18 +151,13 @@ def ngettext(s_sing, s_plural, n, replace_sing=None, replace_plural=None):
try: try:
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
except locale.Error as error: except locale.Error as error:
print(error, file=sys.stderr) print(error)
try: try:
LANG = get_default_lang() LANG = get_default_lang()
if os.name == 'nt': print('Found default language: %s' % LANG)
# Set the env var on Windows because gettext.find() uses it to
# find the translation
# Use LANGUAGE instead of LANG, LANG sets LC_ALL and thus
# doesn't retain other region settings like LC_TIME
os.environ['LANGUAGE'] = LANG
except Exception as error: except Exception as error:
print('Failed to determine default language', file=sys.stderr) print('Failed to determine default language')
import traceback import traceback
traceback.print_exc() traceback.print_exc()
@ -178,6 +173,6 @@ for dir_ in iter_locale_dirs():
else: else:
break break
else: else:
print('No translations found', file=sys.stderr) print('No translations found')
print('Dirs searched: %s' % get_locale_dirs(), file=sys.stderr) print('Dirs searched: %s' % get_locale_dirs())
_ = _translation.gettext _ = _translation.gettext

View file

@ -445,8 +445,6 @@ class JingleSession:
if child.getName() == 'checksum': if child.getName() == 'checksum':
hash_ = child.getTag('file').getTag(name='hash', hash_ = child.getTag('file').getTag(name='hash',
namespace=nbxmpp.NS_HASHES_2) namespace=nbxmpp.NS_HASHES_2)
if hash_ is None:
continue
algo = hash_.getAttr('algo') algo = hash_.getAttr('algo')
if algo in nbxmpp.Hashes2.supported: if algo in nbxmpp.Hashes2.supported:
file_props = FilesProp.getFileProp(self.connection.name, file_props = FilesProp.getFileProp(self.connection.name,

View file

@ -100,7 +100,7 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
flags = (SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3 | SSL.OP_SINGLE_DH_USE \ flags = (SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3 | SSL.OP_SINGLE_DH_USE \
| SSL.OP_NO_TICKET) | SSL.OP_NO_TICKET)
ctx.set_options(flags) ctx.set_options(flags)
ctx.set_cipher_list(b'HIGH:!aNULL:!3DES') ctx.set_cipher_list('HIGH:!aNULL:!3DES')
if fingerprint == 'server': # for testing purposes only if fingerprint == 'server': # for testing purposes only
ctx.set_verify(SSL.VERIFY_NONE|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, ctx.set_verify(SSL.VERIFY_NONE|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,

View file

@ -28,7 +28,7 @@ def parseLogLevel(arg):
return int(arg) return int(arg)
if arg.isupper() and hasattr(logging, arg): if arg.isupper() and hasattr(logging, arg):
return getattr(logging, arg) return getattr(logging, arg)
print(_('%s is not a valid loglevel') % repr(arg), file=sys.stderr) print(_('%s is not a valid loglevel') % repr(arg))
return 0 return 0
def parseLogTarget(arg): def parseLogTarget(arg):
@ -69,8 +69,7 @@ def parseAndSetLogLevels(arg):
target = parseLogTarget(target.strip()) target = parseLogTarget(target.strip())
if target: if target:
logging.getLogger(target).setLevel(level) logging.getLogger(target).setLevel(level)
print("Logger %s level set to %d" % (target, level), print("Logger %s level set to %d" % (target, level))
file=sys.stderr)
class colors: class colors:

View file

@ -207,11 +207,6 @@ class Message:
'gc_control': gc_control 'gc_control': gc_control
} }
app.nec.push_incoming_event(NetworkEvent('update-client-info',
account=self._account,
jid=jid,
resource=resource))
event = MessageReceivedEvent(None, **event_attr) event = MessageReceivedEvent(None, **event_attr)
app.nec.push_incoming_event(event) app.nec.push_incoming_event(event)

View file

@ -175,10 +175,6 @@ class Presence:
def get_presence(self, to=None, typ=None, priority=None, def get_presence(self, to=None, typ=None, priority=None,
show=None, status=None, nick=None, caps=True, show=None, status=None, nick=None, caps=True,
sign=None, idle_time=None): sign=None, idle_time=None):
if show not in ('chat', 'away', 'xa', 'dnd'):
# Gajim sometimes passes invalid show values here
# until this is fixed this is a workaround
show = None
presence = nbxmpp.Presence(to, typ, priority, show, status) presence = nbxmpp.Presence(to, typ, priority, show, status)
if nick is not None: if nick is not None:
nick_tag = presence.setTag('nick', namespace=nbxmpp.NS_NICK) nick_tag = presence.setTag('nick', namespace=nbxmpp.NS_NICK)

View file

@ -92,10 +92,7 @@ class Register:
error = stanza.getErrorMsg() error = stanza.getErrorMsg()
log.info('Error: %s', error) log.info('Error: %s', error)
if error_cb() is not None: if error_cb() is not None:
form = is_form = None error_cb()(error)
if stanza.getTagAttr('error', 'type') == 'modify':
form, is_form = self._get_register_form(stanza)
error_cb()(error, form, is_form)
return return
self._con.get_module('Presence').subscribe(agent, auto_auth=True) self._con.get_module('Presence').subscribe(agent, auto_auth=True)
@ -119,7 +116,8 @@ class Register:
iq, self._register_info_response, {'success_cb': weak_success_cb, iq, self._register_info_response, {'success_cb': weak_success_cb,
'error_cb': weak_error_cb}) 'error_cb': weak_error_cb})
def _register_info_response(self, _con, stanza, success_cb, error_cb): @staticmethod
def _register_info_response(_con, stanza, success_cb, error_cb):
if not nbxmpp.isResultNode(stanza): if not nbxmpp.isResultNode(stanza):
error = stanza.getErrorMsg() error = stanza.getErrorMsg()
log.info('Error: %s', error) log.info('Error: %s', error)
@ -127,28 +125,18 @@ class Register:
error_cb()(error) error_cb()(error)
else: else:
log.info('Register form received') log.info('Register form received')
form = stanza.getQuery().getTag('x', namespace=nbxmpp.NS_DATA)
is_form = form is not None
if not is_form:
form = {}
for field in stanza.getQueryPayload():
if not isinstance(field, nbxmpp.Node):
continue
form[field.getName()] = field.getData()
if success_cb() is not None: if success_cb() is not None:
form, is_form = self._get_register_form(stanza)
success_cb()(form, is_form) success_cb()(form, is_form)
@staticmethod
def _get_register_form(stanza):
query = stanza.getTag('query')
if not query:
return None, False
form = query.getTag('x', namespace=nbxmpp.NS_DATA)
is_form = form is not None
if not is_form:
form = {}
for field in query.getPayload():
if not isinstance(field, nbxmpp.Node):
continue
form[field.getName()] = field.getData()
return form, is_form
def get_instance(*args, **kwargs): def get_instance(*args, **kwargs):
return Register(*args, **kwargs), 'Register' return Register(*args, **kwargs), 'Register'

View file

@ -27,7 +27,6 @@ import os
import sys import sys
import re import re
import logging import logging
from pathlib import Path
from gajim.common import app from gajim.common import app
from gajim.common import caps_cache from gajim.common import caps_cache
@ -105,22 +104,25 @@ class OptionsParser:
fd.write(s + ' = ' + value + '\n') fd.write(s + ' = ' + value + '\n')
def write(self): def write(self):
config_path = Path(self.__filename) (base_dir, filename) = os.path.split(self.__filename)
tempfile = 'temp_%s' % config_path.name self.__tempfile = os.path.join(base_dir, '.' + filename)
temp_filepath = config_path.parent / tempfile
try: try:
with open(str(temp_filepath), 'w', encoding='utf-8') as file: with open(self.__tempfile, 'w', encoding='utf-8') as f:
app.config.foreach(self.write_line, file) app.config.foreach(self.write_line, f)
except IOError: except IOError as e:
log.exception('Failed to write config file') return str(e)
return
if os.path.exists(self.__filename):
if os.name == 'nt':
# win32 needs this
try:
os.remove(self.__filename)
except Exception as e:
return str(e)
try: try:
temp_filepath.replace(config_path) os.rename(self.__tempfile, self.__filename)
except Exception: except IOError as e:
log.exception('Failed to replace config file') return str(e)
else:
log.info('Successful saved config file')
def update_config(self, old_version, new_version): def update_config(self, old_version, new_version):
old_version_list = old_version.split('.') # convert '0.x.y' to (0, x, y) old_version_list = old_version.split('.') # convert '0.x.y' to (0, x, y)

View file

@ -23,7 +23,7 @@ import struct
import hashlib import hashlib
import os import os
import time import time
import sys import platform
import logging import logging
from errno import EWOULDBLOCK from errno import EWOULDBLOCK
from errno import ENOBUFS from errno import ENOBUFS
@ -296,7 +296,7 @@ class SocksQueue:
def activate_proxy(self, idx): def activate_proxy(self, idx):
if not self.isHashInSockObjs(self.senders, idx): if not self.isHashInSockObjs(self.senders, idx):
return return
for key in list(self.senders): for key in self.senders:
if idx in key: if idx in key:
sender = self.senders[key] sender = self.senders[key]
if sender.file_props.type_ != 's': if sender.file_props.type_ != 's':
@ -687,11 +687,13 @@ class Socks5:
OpenSSL.SSL.WantX509LookupError) as e: OpenSSL.SSL.WantX509LookupError) as e:
log.info('SSL rehandshake request: %s', repr(e)) log.info('SSL rehandshake request: %s', repr(e))
raise e raise e
except OpenSSL.SSL.SysCallError:
return self._on_send_exception()
except Exception as e: except Exception as e:
if e.errno not in (EINTR, ENOBUFS, EWOULDBLOCK): if e.errno not in (EINTR, ENOBUFS, EWOULDBLOCK):
return self._on_send_exception() # peer stopped reading
self.state = 8 # end connection
self.disconnect()
self.file_props.error = -1
return -1
self.size += lenn self.size += lenn
current_time = time.time() current_time = time.time()
self.file_props.elapsed_time += current_time - \ self.file_props.elapsed_time += current_time - \
@ -717,13 +719,6 @@ class Socks5:
self.disconnect() self.disconnect()
return -1 return -1
def _on_send_exception(self):
# peer stopped reading
self.state = 8 # end connection
self.disconnect()
self.file_props.error = -1
return -1
def get_file_contents(self, timeout): def get_file_contents(self, timeout):
""" """
Read file contents from socket and write them to file Read file contents from socket and write them to file
@ -1436,7 +1431,7 @@ class Socks5Listener(IdleObject):
# Under windows Vista, we need that to listen on ipv6 AND ipv4 # Under windows Vista, we need that to listen on ipv6 AND ipv4
# Doesn't work under windows XP # Doesn't work under windows XP
if os.name == 'nt': if os.name == 'nt':
if sys.getwindowsversion().major >= 6: # Win Vista + if int(platform.win32_ver()[0]) >= 6: # Win Vista +
# 47 is socket.IPPROTO_IPV6 # 47 is socket.IPPROTO_IPV6
# 27 is socket.IPV6_V6ONLY under windows, but not defined ... # 27 is socket.IPV6_V6ONLY under windows, but not defined ...
self._serv.setsockopt(41, 27, 0) self._serv.setsockopt(41, 27, 0)

View file

@ -26,6 +26,7 @@ from gajim.common.zeroconf import zeroconf
from nbxmpp.protocol import * from nbxmpp.protocol import *
import socket import socket
import platform
import ssl import ssl
import errno import errno
import sys import sys
@ -72,7 +73,7 @@ class ZeroconfListener(IdleObject):
self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if os.name == 'nt': if os.name == 'nt':
if sys.getwindowsversion().major >= 6: # Win Vista + if int(platform.win32_ver()[0]) >= 6: # Win Vista +
# 47 is socket.IPPROTO_IPV6 # 47 is socket.IPPROTO_IPV6
# 27 is socket.IPV6_V6ONLY under windows, but not defined ... # 27 is socket.IPV6_V6ONLY under windows, but not defined ...
self._serv.setsockopt(41, 27, 0) self._serv.setsockopt(41, 27, 0)

View file

@ -17,6 +17,7 @@
import logging import logging
import select import select
import socket
import re import re
from gajim.common.i18n import _ from gajim.common.i18n import _
@ -156,16 +157,16 @@ class Zeroconf:
self.queried.append(True) self.queried.append(True)
def getaddrinfo_callback(self, sdRef, flags, interfaceIndex, errorCode, def query_record_callback(self, sdRef, flags, interfaceIndex, errorCode,
hosttarget, address, ttl): hosttarget, rrtype, rrclass, rdata, ttl):
if errorCode != pybonjour.kDNSServiceErr_NoError: if errorCode != pybonjour.kDNSServiceErr_NoError:
log.error('Error in getaddrinfo_callback: %s', str(errorCode)) log.error('Error in query_record_callback: %s', str(errorCode))
return return
fullname, port, txtRecord = self.resolved_contacts[hosttarget] fullname, port, txtRecord = self.resolved_contacts[hosttarget]
txt = pybonjour.TXTRecord.parse(txtRecord) txt = pybonjour.TXTRecord.parse(txtRecord)
ip = address[1] ip = socket.inet_ntoa(rdata)
name, bare_name, protocol, domain = self._parse_name(fullname) name, bare_name, protocol, domain = self._parse_name(fullname)
@ -206,18 +207,20 @@ class Zeroconf:
self.resolved_contacts[hosttarget] = (fullname, port, txtRecord) self.resolved_contacts[hosttarget] = (fullname, port, txtRecord)
try: try:
getaddrinfo_sdRef = \ query_sdRef = None
pybonjour.DNSServiceGetAddrInfo( query_sdRef = \
pybonjour.DNSServiceQueryRecord(
interfaceIndex=interfaceIndex, interfaceIndex=interfaceIndex,
hostname=hosttarget, fullname=hosttarget,
callBack=self.getaddrinfo_callback) rrtype=pybonjour.kDNSServiceType_A,
callBack=self.query_record_callback)
while not self.queried: while not self.queried:
ready = select.select([getaddrinfo_sdRef], [], [], resolve_timeout) ready = select.select([query_sdRef], [], [], resolve_timeout)
if getaddrinfo_sdRef not in ready[0]: if query_sdRef not in ready[0]:
log.warning('GetAddrInfo timed out') log.warning('Query record timed out')
break break
pybonjour.DNSServiceProcessResult(getaddrinfo_sdRef) pybonjour.DNSServiceProcessResult(query_sdRef)
else: else:
self.queried.pop() self.queried.pop()
@ -228,8 +231,8 @@ class Zeroconf:
self.error_CB(_('Error while adding service. %s') % error) self.error_CB(_('Error while adding service. %s') % error)
finally: finally:
if getaddrinfo_sdRef: if query_sdRef:
getaddrinfo_sdRef.close() query_sdRef.close()
self.resolved.append(True) self.resolved.append(True)

View file

@ -407,13 +407,12 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkGrid"> <object class="GtkBox" id="banner_vbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="margin_left">5</property> <property name="border_width">5</property>
<property name="hexpand">True</property> <property name="orientation">vertical</property>
<property name="row_spacing">2</property>
<child> <child>
<object class="GtkLabel" id="banner_name_label"> <object class="GtkLabel" id="banner_name_label">
<property name="name">ChatControl-BannerNameLabel</property> <property name="name">ChatControl-BannerNameLabel</property>
@ -424,9 +423,9 @@
<property name="xalign">0</property> <property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="expand">True</property>
<property name="top_attach">0</property> <property name="fill">True</property>
<property name="width">2</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -434,7 +433,6 @@
<property name="name">ChatControl-BannerLabel</property> <property name="name">ChatControl-BannerLabel</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label">label</property> <property name="label">label</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
<property name="selectable">True</property> <property name="selectable">True</property>
@ -442,27 +440,14 @@
<signal name="populate-popup" handler="on_banner_label_populate_popup" swapped="no"/> <signal name="populate-popup" handler="on_banner_label_populate_popup" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="expand">True</property>
<property name="top_attach">1</property> <property name="fill">True</property>
</packing> <property name="position">1</property>
</child>
<child>
<object class="GtkImage" id="phone_image">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">The last message was written on a mobile client</property>
<property name="halign">start</property>
<property name="margin_right">4</property>
<property name="icon_name">phone-apple-iphone-symbolic</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>

View file

@ -311,7 +311,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Chat</property> <property name="label" translatable="yes">Chat</property>
<style> <style>
<class name="dim-label"/> <class name="dim-label"/>
@ -327,7 +326,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Record history for this chat</property> <property name="tooltip_text" translatable="yes">Record history for this chat</property>
<property name="valign">center</property>
<signal name="notify::active" handler="on_log_history_checkbutton_toggled" swapped="no"/> <signal name="notify::active" handler="on_log_history_checkbutton_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
@ -339,7 +337,6 @@
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property>
<property name="margin_left">6</property> <property name="margin_left">6</property>
<property name="label" translatable="yes">Record History</property> <property name="label" translatable="yes">Record History</property>
<style> <style>
@ -356,7 +353,6 @@
<property name="width_request">400</property> <property name="width_request">400</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="model">liststore1</property> <property name="model">liststore1</property>
<property name="tearoff_title" translatable="yes">Ttitle</property> <property name="tearoff_title" translatable="yes">Ttitle</property>

View file

@ -1,20 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 --> <!-- Generated with glade 3.20.1 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.14"/>
<object class="GtkBox" id="box"> <object class="GtkBox" id="box">
<property name="width_request">300</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="border_width">18</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">6</property>
<child> <child>
<object class="GtkImage"> <object class="GtkAlignment" id="alignment1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="icon_name">document-send-symbolic</property> <property name="top_padding">8</property>
<property name="icon_size">6</property> <property name="bottom_padding">4</property>
<property name="left_padding">8</property>
<property name="right_padding">8</property>
<child>
<object class="GtkLabel" id="label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="variant" value="normal"/>
</attributes>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -23,14 +32,21 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="label"> <object class="GtkAlignment" id="alignment2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_top">6</property> <property name="top_padding">4</property>
<property name="label">&lt;placeholder&gt;</property> <property name="bottom_padding">4</property>
<style> <property name="left_padding">8</property>
<class name="bold"/> <property name="right_padding">8</property>
</style> <child>
<object class="GtkProgressBar" id="progressbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pulse_step">0.10000000149</property>
<property name="show_text">True</property>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -38,52 +54,5 @@
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="progress_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">&lt;progress&gt;</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="progressbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">6</property>
<property name="pulse_step">0.10000000149</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel_upload_button">
<property name="label" translatable="yes">Cancel Upload</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="margin_top">6</property>
<signal name="clicked" handler="on_cancel_upload_button_clicked" swapped="no"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object> </object>
</interface> </interface>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 --> <!-- Generated with glade 3.22.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkWindow" id="plugins_window"> <object class="GtkWindow" id="plugins_window">
@ -13,9 +13,6 @@
<property name="type_hint">dialog</property> <property name="type_hint">dialog</property>
<signal name="destroy" handler="on_plugins_window_destroy" swapped="no"/> <signal name="destroy" handler="on_plugins_window_destroy" swapped="no"/>
<signal name="key-press-event" handler="on_key_press_event" swapped="no"/> <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
<child type="titlebar">
<placeholder/>
</child>
<child> <child>
<object class="GtkNotebook" id="plugins_notebook"> <object class="GtkNotebook" id="plugins_notebook">
<property name="visible">True</property> <property name="visible">True</property>
@ -235,7 +232,6 @@
<property name="wrap">True</property> <property name="wrap">True</property>
<property name="wrap_mode">word-char</property> <property name="wrap_mode">word-char</property>
<property name="selectable">True</property> <property name="selectable">True</property>
<property name="xalign">0</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -379,6 +375,9 @@
</child> </child>
</object> </object>
</child> </child>
<child type="titlebar">
<placeholder/>
</child>
</object> </object>
<object class="GtkTextBuffer" id="textbuffer1"> <object class="GtkTextBuffer" id="textbuffer1">
<property name="text" translatable="yes">Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.</property> <property name="text" translatable="yes">Plug-in decription should be displayed here. This text will be erased during PluginsWindow initialization.</property>

View file

@ -10,8 +10,7 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property> <property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property> <property name="shadow_type">in</property>
<property name="min_content_height">260</property> <property name="min_content_height">200</property>
<property name="overlay_scrolling">False</property>
<child> <child>
<object class="GtkViewport"> <object class="GtkViewport">
<property name="visible">True</property> <property name="visible">True</property>

View file

@ -2,7 +2,6 @@
"servers":[ "servers":[
"0nl1ne.at", "0nl1ne.at",
"404.city", "404.city",
"blabber.im",
"brauchen.info", "brauchen.info",
"chatme.im", "chatme.im",
"comm.unicate.me", "comm.unicate.me",

View file

@ -1,11 +1,11 @@
.gajim-incoming-nickname { .gajim-incoming-nickname {
color: rgb(207, 49, 47) color: rgb(164, 0, 0)
} }
.gajim-outgoing-nickname { .gajim-outgoing-nickname {
color: rgb(38, 139, 210) color: rgb(52, 101, 164)
} }
.gajim-url { .gajim-url {
color: rgb(53, 132, 228) color: rgb(117, 80, 123)
} }
.gajim-highlight-message { .gajim-highlight-message {
color: rgb(245, 121, 0) color: rgb(245, 121, 0)
@ -15,4 +15,4 @@
} }
.gajim-status-message { .gajim-status-message {
color: rgb(115, 210, 22) color: rgb(115, 210, 22)
} }

View file

@ -55,7 +55,6 @@ from gajim.common.exceptions import GajimGeneralException
# Compat with Gajim 1.0.3 for plugins # Compat with Gajim 1.0.3 for plugins
from gajim.gtk.dialogs import * from gajim.gtk.dialogs import *
from gajim.gtk.add_contact import AddNewContactWindow from gajim.gtk.add_contact import AddNewContactWindow
from gajim.gtk.util import get_builder
log = logging.getLogger('gajim.dialogs') log = logging.getLogger('gajim.dialogs')
@ -1734,18 +1733,22 @@ class ProgressWindow(Gtk.ApplicationWindow):
self.set_position(Gtk.WindowPosition.CENTER) self.set_position(Gtk.WindowPosition.CENTER)
self.set_show_menubar(False) self.set_show_menubar(False)
self.set_title(_('File Transfer')) self.set_title(_('File Transfer'))
self.set_default_size(250, -1)
self.event = file.event self.event = file.event
self.file = file self.file = file
self._ui = get_builder('httpupload_progress_dialog.ui') self.xml = gtkgui_helpers.get_gtk_builder(
'httpupload_progress_dialog.ui')
self.add(self._ui.box) self.label = self.xml.get_object('label')
self.progressbar = self.xml.get_object('progressbar')
self.add(self.xml.get_object('box'))
self.pulse = GLib.timeout_add(100, self._pulse_progressbar) self.pulse = GLib.timeout_add(100, self._pulse_progressbar)
self.show_all() self.show_all()
self.connect('destroy', self._on_destroy) self.connect('destroy', self._on_destroy)
self._ui.connect_signals(self)
app.ged.register_event_handler('httpupload-progress', ged.CORE, app.ged.register_event_handler('httpupload-progress', ged.CORE,
self._on_httpupload_progress) self._on_httpupload_progress)
@ -1753,23 +1756,20 @@ class ProgressWindow(Gtk.ApplicationWindow):
if self.file != obj.file: if self.file != obj.file:
return return
if obj.status == 'request': if obj.status == 'request':
self._ui.label.set_text(_('Requesting HTTP Upload Slot…')) self.label.set_text(_('Requesting HTTP Upload Slot…'))
elif obj.status == 'close': elif obj.status == 'close':
self.destroy() self.destroy()
elif obj.status == 'upload': elif obj.status == 'upload':
self._ui.label.set_text(_('Uploading file via HTTP File Upload…')) self.label.set_text(_('Uploading file via HTTP File Upload…'))
elif obj.status == 'update': elif obj.status == 'update':
self.update_progress(obj.seen, obj.total) self.update_progress(obj.seen, obj.total)
elif obj.status == 'encrypt': elif obj.status == 'encrypt':
self._ui.label.set_text(_('Encrypting file…')) self.label.set_text(_('Encrypting file…'))
def _pulse_progressbar(self): def _pulse_progressbar(self):
self._ui.progressbar.pulse() self.progressbar.pulse()
return True return True
def on_cancel_upload_button_clicked(self, widget):
self.destroy()
def _on_destroy(self, *args): def _on_destroy(self, *args):
self.event.set() self.event.set()
if self.pulse: if self.pulse:
@ -1783,9 +1783,6 @@ class ProgressWindow(Gtk.ApplicationWindow):
if self.pulse: if self.pulse:
GLib.source_remove(self.pulse) GLib.source_remove(self.pulse)
self.pulse = None self.pulse = None
self._ui.progressbar.set_fraction(float(seen) / total) pct = (float(seen) / total) * 100.0
size_total = round(total / (1024 * 1024), 1) self.progressbar.set_fraction(float(seen) / total)
size_progress = round(seen / (1024 * 1024), 1) self.progressbar.set_text(str(int(pct)) + "%")
self._ui.progress_label.set_text(
_('%(progress)s of %(total)s MiB sent') % \
{'progress': str(size_progress), 'total': str(size_total)})

View file

@ -27,7 +27,7 @@ from distutils.version import LooseVersion as V
# Install _() in namespace # Install _() in namespace
from gajim.common import i18n from gajim.common import i18n
_MIN_NBXMPP_VER = "0.6.10" _MIN_NBXMPP_VER = "0.6.9"
_MIN_GTK_VER = "3.22.0" _MIN_GTK_VER = "3.22.0"

View file

@ -17,8 +17,6 @@
'''Window to create new post for discussion groups service.''' '''Window to create new post for discussion groups service.'''
import uuid
from gajim.common import app from gajim.common import app
from nbxmpp import Node from nbxmpp import Node
from gajim import gtkgui_helpers from gajim import gtkgui_helpers
@ -67,7 +65,7 @@ class GroupsPostWindow:
# publish it to node # publish it to node
con = app.connections[self.account] con = app.connections[self.account]
con.get_module('PubSub').send_pb_publish( con.get_module('PubSub').send_pb_publish(
self.servicejid, self.groupid, item, str(uuid.uuid4())) self.servicejid, self.groupid, item, '0')
# close the window # close the window
self.window.destroy() self.window.destroy()

View file

@ -564,7 +564,6 @@ class AccountCreationWizard:
def create_vars(self, config): def create_vars(self, config):
app.config.add_per('accounts', self.account) app.config.add_per('accounts', self.account)
config['account_label'] = '%s@%s' % (config['name'], config['hostname'])
if not config['savepass']: if not config['savepass']:
config['password'] = '' config['password'] = ''

View file

@ -33,9 +33,7 @@ from gajim.common import app
from gajim.common import helpers from gajim.common import helpers
from gajim.common import exceptions from gajim.common import exceptions
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.const import ShowConstant from gajim.common.const import ShowConstant, KindConstant
from gajim.common.const import KindConstant
from gajim.common.const import StyleAttr
from gajim import conversation_textview from gajim import conversation_textview
@ -81,9 +79,7 @@ class HistoryWindow:
account, used_in_history_window=True) account, used_in_history_window=True)
scrolledwindow.add(self.history_textview.tv) scrolledwindow.add(self.history_textview.tv)
self.history_buffer = self.history_textview.tv.get_buffer() self.history_buffer = self.history_textview.tv.get_buffer()
highlight_color = app.css_config.get_value( self.history_buffer.create_tag('highlight', background='yellow')
'.gajim-highlight-message', StyleAttr.COLOR)
self.history_buffer.create_tag('highlight', background=highlight_color)
self.history_buffer.create_tag('invisible', invisible=True) self.history_buffer.create_tag('invisible', invisible=True)
self.checkbutton = xml.get_object('log_history_checkbutton') self.checkbutton = xml.get_object('log_history_checkbutton')
self.show_status_checkbutton = xml.get_object('show_status_checkbutton') self.show_status_checkbutton = xml.get_object('show_status_checkbutton')

View file

@ -75,14 +75,6 @@ class ServiceRegistration(Gtk.Assistant):
sidebar = main_box.get_children()[0] sidebar = main_box.get_children()[0]
main_box.remove(sidebar) main_box.remove(sidebar)
def _build_dataform(self, form, is_form):
if not is_form:
from gajim import config
return config.FakeDataForm(form)
dataform = dataforms.extend_form(node=form)
return DataFormWidget(dataform)
def _on_page_change(self, assistant, page): def _on_page_change(self, assistant, page):
if self.get_current_page() == Page.REQUEST: if self.get_current_page() == Page.REQUEST:
self._con.get_module('Register').get_register_form( self._con.get_module('Register').get_register_form(
@ -94,22 +86,23 @@ class ServiceRegistration(Gtk.Assistant):
def _on_get_success(self, form, is_form): def _on_get_success(self, form, is_form):
log.info('Show Form page') log.info('Show Form page')
self._is_form = is_form self._is_form = is_form
self._data_form_widget = self._build_dataform(form, is_form) if is_form:
dataform = dataforms.extend_form(node=form)
self._data_form_widget = DataFormWidget(dataform)
else:
from gajim import config
self._data_form_widget = config.FakeDataForm(form)
self.get_nth_page(Page.FORM).set_form(self._data_form_widget) page = self.get_nth_page(Page.FORM)
page.pack_start(self._data_form_widget, True, True, 0)
self._data_form_widget.show_all()
self.set_current_page(Page.FORM) self.set_current_page(Page.FORM)
def _on_error(self, error_text, form=None, is_form=False): def _on_error(self, error_text):
if form is not None: log.info('Show Error page')
log.info('Show Form page') page = self.get_nth_page(Page.ERROR)
self._is_form = is_form page.set_text(error_text)
self._data_form_widget = self._build_dataform(form, is_form) self.set_current_page(Page.ERROR)
self.get_nth_page(Page.FORM).set_form(self._data_form_widget, error_text=error_text)
self.set_current_page(Page.FORM)
else:
log.info('Show Error page')
self.get_nth_page(Page.ERROR).set_text(error_text)
self.set_current_page(Page.ERROR)
def _on_cancel(self, widget): def _on_cancel(self, widget):
self.destroy() self.destroy()
@ -166,25 +159,6 @@ class FormPage(Gtk.Box):
def __init__(self): def __init__(self):
super().__init__(orientation=Gtk.Orientation.VERTICAL) super().__init__(orientation=Gtk.Orientation.VERTICAL)
self._form = None
self._label = Gtk.Label()
self._label.set_no_show_all(True)
self._label.get_style_context().add_class('error-color')
self.pack_end(self._label, False, False, 0)
def set_form(self, form, error_text=None):
if self._form is not None:
self.remove(self._form)
self._form.destroy()
self._label.hide()
self._form = form
if error_text is not None:
self._label.set_text(error_text)
self._label.show()
self.pack_start(form, True, True, 0)
self._form.show_all()
class SuccessfulPage(Gtk.Box): class SuccessfulPage(Gtk.Box):

View file

@ -30,7 +30,6 @@ from gajim.common import app
from gajim.common import configpaths from gajim.common import configpaths
from gajim.common import i18n from gajim.common import i18n
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.const import Display
_icon_theme = Gtk.IconTheme.get_default() _icon_theme = Gtk.IconTheme.get_default()
_icon_theme.append_search_path(configpaths.get('ICONS')) _icon_theme.append_search_path(configpaths.get('ICONS'))
@ -118,17 +117,11 @@ def get_iconset_name_for(name: str) -> str:
def get_total_screen_geometry() -> Tuple[int, int]: def get_total_screen_geometry() -> Tuple[int, int]:
total_width = 0 screen = Gdk.Screen.get_default()
total_height = 0 window = Gdk.Screen.get_root_window(screen)
display = Gdk.Display.get_default() width, height = window.get_width(), window.get_height()
monitors = display.get_n_monitors() log.debug('Get screen geometry: %s %s', width, height)
for num in range(0, monitors): return width, height
monitor = display.get_monitor(num)
geometry = monitor.get_geometry()
total_width += geometry.width
total_height = max(total_height, geometry.height)
log.debug('Get screen geometry: %s %s', total_width, total_height)
return total_width, total_height
def resize_window(window: Gtk.Window, width: int, height: int) -> None: def resize_window(window: Gtk.Window, width: int, height: int) -> None:
@ -162,16 +155,6 @@ def move_window(window: Gtk.Window, pos_x: int, pos_y: int) -> None:
window.move(pos_x, pos_y) window.move(pos_x, pos_y)
def restore_roster_position(window):
if not app.config.get('save-roster-position'):
return
if app.is_display(Display.WAYLAND):
return
move_window(window,
app.config.get('roster_x-position'),
app.config.get('roster_y-position'))
def get_completion_liststore(entry: Gtk.Entry) -> Gtk.ListStore: def get_completion_liststore(entry: Gtk.Entry) -> Gtk.ListStore:
""" """
Create a completion model for entry widget completion list consists of Create a completion model for entry widget completion list consists of

View file

@ -85,7 +85,7 @@ class ExceptionDialog():
traceback.print_exception(type_, value, tb, None, trace) traceback.print_exception(type_, value, tb, None, trace)
self.text = self.get_issue_text(trace.getvalue()) self.text = self.get_issue_text(trace.getvalue())
buffer_.set_text(self.text) buffer_.set_text(self.text)
print(self.text, file=sys.stderr) print(self.text)
self.exception_view.set_editable(False) self.exception_view.set_editable(False)
self.dialog.show() self.dialog.show()
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>. # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import gc
from gi.repository import Gtk from gi.repository import Gtk
from gi.repository import GLib from gi.repository import GLib
from gi.repository import GObject from gi.repository import GObject
@ -406,6 +408,9 @@ class MessageTextView(Gtk.TextView):
self.add_child_at_anchor(image, anchor) self.add_child_at_anchor(image, anchor)
buffer_.insert_at_cursor(' ') buffer_.insert_at_cursor(' ')
def destroy(self):
GLib.idle_add(gc.collect)
def clear(self, widget=None): def clear(self, widget=None):
""" """
Clear text in the textview Clear text in the textview

View file

@ -75,10 +75,31 @@ class Notification:
Handle notifications Handle notifications
""" """
def __init__(self): def __init__(self):
self._dbus_available = False
self.daemon_capabilities = ['actions'] self.daemon_capabilities = ['actions']
self._detect_dbus_caps() # Detect if actions are supported by the notification daemon
if sys.platform not in ('win32', 'darwin'):
def on_proxy_ready(source, res, data=None):
try:
proxy = Gio.DBusProxy.new_finish(res)
self.daemon_capabilities = proxy.GetCapabilities()
except GLib.Error as e:
if e.domain == 'g-dbus-error-quark':
log.info('Notifications D-Bus connection failed: %s',
e.message)
else:
raise
else:
log.debug('Notifications D-Bus connected')
log.debug('Connecting to Notifications D-Bus')
Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION,
Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
None,
'org.freedesktop.Notifications',
'/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
None, on_proxy_ready)
app.ged.register_event_handler( app.ged.register_event_handler(
'notification', ged.GUI2, self._nec_notification) 'notification', ged.GUI2, self._nec_notification)
@ -86,30 +107,6 @@ class Notification:
'our-show', ged.GUI2, self._nec_our_status) 'our-show', ged.GUI2, self._nec_our_status)
app.events.event_removed_subscribe(self._on_event_removed) app.events.event_removed_subscribe(self._on_event_removed)
def _detect_dbus_caps(self):
if sys.platform in ('win32', 'darwin'):
return
def on_proxy_ready(_source, res, _data=None):
try:
proxy = Gio.DBusProxy.new_finish(res)
self.daemon_capabilities = proxy.GetCapabilities()
except GLib.Error:
log.exception('Notifications D-Bus connection failed')
else:
self._dbus_available = True
log.info('Notifications D-Bus connected')
log.info('Connecting to Notifications D-Bus')
Gio.DBusProxy.new_for_bus(Gio.BusType.SESSION,
Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
None,
'org.freedesktop.Notifications',
'/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
None,
on_proxy_ready)
def _nec_notification(self, obj): def _nec_notification(self, obj):
if obj.do_popup: if obj.do_popup:
icon_name = self._get_icon_name(obj) icon_name = self._get_icon_name(obj)
@ -173,9 +170,6 @@ class Notification:
app.interface.roster.popup_notification_windows.append(instance) app.interface.roster.popup_notification_windows.append(instance)
return return
if not self._dbus_available:
return
scale = gtkgui_helpers.get_monitor_scale_factor() scale = gtkgui_helpers.get_monitor_scale_factor()
icon_pixbuf = gtkgui_helpers.gtk_icon_theme.load_icon_for_scale( icon_pixbuf = gtkgui_helpers.gtk_icon_theme.load_icon_for_scale(
icon_name, 48, scale, 0) icon_name, 48, scale, 0)
@ -226,9 +220,8 @@ class Notification:
app.app.send_notification(notif_id, notification) app.app.send_notification(notif_id, notification)
def withdraw(self, *args): def withdraw(self, *args):
if not self._dbus_available: if sys.platform != 'win32':
return app.app.withdraw_notification(self._id(*args))
app.app.withdraw_notification(self._id(*args))
def _id(self, *args): def _id(self, *args):
return ','.join(args) return ','.join(args)

View file

@ -36,12 +36,10 @@ from gajim.gtk.dialogs import YesNoDialog
from gajim.gtk.filechoosers import ArchiveChooserDialog from gajim.gtk.filechoosers import ArchiveChooserDialog
from gajim.common import app from gajim.common import app
from gajim.common import configpaths from gajim.common import configpaths
from gajim.common.exceptions import PluginsystemError
from gajim.common.helpers import launch_browser_mailer
from gajim.plugins.helpers import log_calls from gajim.plugins.helpers import log_calls
from gajim.plugins.helpers import GajimPluginActivateException from gajim.plugins.helpers import GajimPluginActivateException
from gajim.plugins.plugins_i18n import _ from gajim.plugins.plugins_i18n import _
from gajim.common.exceptions import PluginsystemError
@unique @unique
@ -65,21 +63,14 @@ class PluginsWindow:
widgets_to_extract = ('plugins_notebook', 'plugin_name_label', widgets_to_extract = ('plugins_notebook', 'plugin_name_label',
'plugin_version_label', 'plugin_authors_label', 'plugin_version_label', 'plugin_authors_label',
'plugin_homepage_linkbutton', 'install_plugin_button', 'plugin_homepage_linkbutton', 'uninstall_plugin_button',
'uninstall_plugin_button', 'configure_plugin_button', 'configure_plugin_button', 'installed_plugins_treeview',
'installed_plugins_treeview', 'available_text', 'available_text', 'available_text_label')
'available_text_label')
for widget_name in widgets_to_extract: for widget_name in widgets_to_extract:
setattr(self, widget_name, builder.get_object(widget_name)) setattr(self, widget_name, builder.get_object(widget_name))
self.plugin_description_textview = builder.get_object('description') self.plugin_description_textview = builder.get_object('description')
# Disable 'Install from ZIP' for Flatpak installs
if app.is_flatpak():
self.install_plugin_button.set_tooltip_text(
_('Click to view Gajim\'s wiki page on how to install plugins in Flatpak.'))
self.installed_plugins_model = Gtk.ListStore(object, str, bool, bool, self.installed_plugins_model = Gtk.ListStore(object, str, bool, bool,
GdkPixbuf.Pixbuf) GdkPixbuf.Pixbuf)
self.installed_plugins_treeview.set_model(self.installed_plugins_model) self.installed_plugins_treeview.set_model(self.installed_plugins_model)
@ -246,10 +237,6 @@ class PluginsWindow:
@log_calls('PluginsWindow') @log_calls('PluginsWindow')
def on_install_plugin_button_clicked(self, widget): def on_install_plugin_button_clicked(self, widget):
if app.is_flatpak():
launch_browser_mailer('url', 'https://dev.gajim.org/gajim/gajim/wikis/help/flathub')
return
def show_warn_dialog(): def show_warn_dialog():
text = _('Archive is malformed') text = _('Archive is malformed')
dialog = WarningDialog(text, '', transient_for=self.window) dialog = WarningDialog(text, '', transient_for=self.window)

View file

@ -406,9 +406,9 @@ class PluginManager(metaclass=Singleton):
return return
for con in app.connections.values(): for con in app.connections.values():
for module in plugin.modules: for module in plugin.modules:
instance, name = module.get_instance(con)
if not module.zeroconf and con.name == 'Local': if not module.zeroconf and con.name == 'Local':
continue continue
instance, name = module.get_instance(con)
modules.register_single(con, instance, name) modules.register_single(con, instance, name)
# If handlers have been registered, register the # If handlers have been registered, register the
@ -558,9 +558,9 @@ class PluginManager(metaclass=Singleton):
return return
for module in plugin.modules: for module in plugin.modules:
instance, name = module.get_instance(con)
if not module.zeroconf and con.name == 'Local': if not module.zeroconf and con.name == 'Local':
continue continue
instance, name = module.get_instance(con)
modules.register_single(con, instance, name) modules.register_single(con, instance, name)
def _plugin_is_active_in_global_config(self, plugin): def _plugin_is_active_in_global_config(self, plugin):

View file

@ -59,7 +59,6 @@ from gajim.common import helpers
from gajim.common import idle from gajim.common import idle
from gajim.common.exceptions import GajimGeneralException from gajim.common.exceptions import GajimGeneralException
from gajim.common import i18n from gajim.common import i18n
from gajim.common.helpers import save_roster_position
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.const import PEPEventType, AvatarSize, StyleAttr from gajim.common.const import PEPEventType, AvatarSize, StyleAttr
from gajim.common.dbus import location from gajim.common.dbus import location
@ -84,8 +83,6 @@ from gajim.gtk.service_registration import ServiceRegistration
from gajim.gtk.history import HistoryWindow from gajim.gtk.history import HistoryWindow
from gajim.gtk.accounts import AccountsWindow from gajim.gtk.accounts import AccountsWindow
from gajim.gtk.util import restore_roster_position
log = logging.getLogger('gajim.roster') log = logging.getLogger('gajim.roster')
@ -2410,7 +2407,11 @@ class RosterWindow:
if not app.config.get('quit_on_roster_x_button') and ( if not app.config.get('quit_on_roster_x_button') and (
(app.interface.systray_enabled and app.config.get('trayicon') != \ (app.interface.systray_enabled and app.config.get('trayicon') != \
'on_event') or app.config.get('allow_hide_roster')): 'on_event') or app.config.get('allow_hide_roster')):
save_roster_position(self.window) if app.config.get('save-roster-position'):
x, y = self.window.get_position()
log.debug('Save roster position (get_position): %s %s', x, y)
app.config.set('roster_x-position', x)
app.config.set('roster_y-position', y)
if os.name == 'nt' or app.config.get('hide_on_roster_x_button'): if os.name == 'nt' or app.config.get('hide_on_roster_x_button'):
self.window.hide() self.window.hide()
else: else:
@ -2435,7 +2436,11 @@ class RosterWindow:
# in case show_roster_on_start is False and roster is never shown # in case show_roster_on_start is False and roster is never shown
# window.window is None # window.window is None
if self.window.get_window() is not None: if self.window.get_window() is not None:
save_roster_position(self.window) if app.config.get('save-roster-position'):
x, y = self.window.get_window().get_root_origin()
log.debug('Save roster position (get_root_origin): %s %s', x, y)
app.config.set('roster_x-position', x)
app.config.set('roster_y-position', y)
width, height = self.window.get_size() width, height = self.window.get_size()
app.config.set('roster_width', width) app.config.set('roster_width', width)
app.config.set('roster_height', height) app.config.set('roster_height', height)
@ -2496,11 +2501,8 @@ class RosterWindow:
self.send_pep(acct, pep_dict) self.send_pep(acct, pep_dict)
def on_continue2(message, pep_dict): def on_continue2(message, pep_dict):
if 'file_transfers' not in app.interface.instances:
on_continue3(message, pep_dict)
return
# check if there is an active file transfer # check if there is an active file transfer
from gajim.common.protocol.bytestream import is_transfer_active from gajim.common.protocol.bytestream import (is_transfer_active)
files_props = app.interface.instances['file_transfers'].\ files_props = app.interface.instances['file_transfers'].\
files_props files_props
transfer_active = False transfer_active = False
@ -5701,11 +5703,13 @@ class RosterWindow:
if len(app.connections) < 2: if len(app.connections) < 2:
# Do not merge accounts if only one exists # Do not merge accounts if only one exists
self.regroup = False self.regroup = False
gtkgui_helpers.resize_window(self.window, gtkgui_helpers.resize_window(self.window,
app.config.get('roster_width'), app.config.get('roster_width'),
app.config.get('roster_height')) app.config.get('roster_height'))
restore_roster_position(self.window) if app.config.get('save-roster-position'):
gtkgui_helpers.move_window(self.window,
app.config.get('roster_x-position'),
app.config.get('roster_y-position'))
self.popups_notification_height = 0 self.popups_notification_height = 0
self.popup_notification_windows = [] self.popup_notification_windows = []

View file

@ -30,8 +30,6 @@ from gajim import gtkgui_helpers
from gajim.common import app from gajim.common import app
from gajim.common import helpers from gajim.common import helpers
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.helpers import save_roster_position
from gajim.gtk.util import restore_roster_position
from gajim.gtk.single_message import SingleMessageWindow from gajim.gtk.single_message import SingleMessageWindow
@ -373,11 +371,18 @@ class StatusIcon:
# No pending events, so toggle visible/hidden for roster window # No pending events, so toggle visible/hidden for roster window
if win.get_property('visible'): if win.get_property('visible'):
if win.get_property('has-toplevel-focus') or os.name == 'nt': if win.get_property('has-toplevel-focus') or os.name == 'nt':
save_roster_position(win) if app.config.get('save-roster-position'):
x, y = win.get_position()
app.config.set('roster_x-position', x)
app.config.set('roster_y-position', y)
win.hide() # else we hide it from VD that was visible in win.hide() # else we hide it from VD that was visible in
else: else:
win.show_all() if not win.get_property('visible'):
restore_roster_position(win) win.show_all()
if app.config.get('save-roster-position'):
gtkgui_helpers.move_window(win,
app.config.get('roster_x-position'),
app.config.get('roster_y-position'))
if not app.config.get('roster_window_skip_taskbar'): if not app.config.get('roster_window_skip_taskbar'):
win.set_property('skip-taskbar-hint', False) win.set_property('skip-taskbar-hint', False)
win.present_with_time(Gtk.get_current_event_time()) win.present_with_time(Gtk.get_current_event_time())
@ -389,9 +394,11 @@ class StatusIcon:
if not event: if not event:
return return
win = app.interface.roster.window win = app.interface.roster.window
if not win.get_property('visible'): if not win.get_property('visible') and app.config.get(
# Needed if we are in one window mode 'save-roster-position'):
restore_roster_position(win) gtkgui_helpers.move_window(win,
app.config.get('roster_x-position'),
app.config.get('roster_y-position'))
app.interface.handle_event(account, jid, event.type_) app.interface.handle_event(account, jid, event.type_)
def on_middle_click(self): def on_middle_click(self):

View file

@ -609,7 +609,8 @@ class FileTransfersTooltip():
self.sid = sid self.sid = sid
return False, self.widget return False, self.widget
def _create_tooltip(self, file_props, _sid): @staticmethod
def _create_tooltip(file_props, sid):
ft_table = Gtk.Table(2, 1) ft_table = Gtk.Table(2, 1)
ft_table.set_property('column-spacing', 2) ft_table.set_property('column-spacing', 2)
current_row = 1 current_row = 1
@ -641,7 +642,26 @@ class FileTransfersTooltip():
if not transfered_len: if not transfered_len:
transfered_len = 0 transfered_len = 0
properties.append((_('Transferred: '), helpers.convert_bytes(transfered_len))) properties.append((_('Transferred: '), helpers.convert_bytes(transfered_len)))
status = self._get_current_status(file_props) status = ''
if file_props.started:
status = _('Not started')
if file_props.stopped:
status = _('Stopped')
elif file_props.completed:
status = _('Completed')
elif not file_props.connected:
if file_props.completed:
status = _('Completed')
else:
if file_props.paused:
status = Q_('?transfer status:Paused')
elif file_props.stalled:
# stalled is not paused. it is like 'frozen' it stopped alone
status = _('Stalled')
else:
status = _('Transferring')
else:
status = _('Not started')
properties.append((_('Status: '), status)) properties.append((_('Status: '), status))
file_desc = file_props.desc or '' file_desc = file_props.desc or ''
properties.append((_('Description: '), GLib.markup_escape_text( properties.append((_('Description: '), GLib.markup_escape_text(
@ -666,24 +686,6 @@ class FileTransfersTooltip():
ft_table.show_all() ft_table.show_all()
return ft_table return ft_table
@staticmethod
def _get_current_status(file_props):
if file_props.stopped:
return _('Aborted')
if file_props.completed:
return _('Completed')
if file_props.paused:
return Q_('?transfer status:Paused')
if file_props.stalled:
# stalled is not paused. it is like 'frozen' it stopped alone
return _('Stalled')
if file_props.connected:
if file_props.started:
return _('Transferring')
return _('Not started')
return _('Not started')
def colorize_status(status): def colorize_status(status):
""" """

1211
po/be.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1217
po/bg.po

File diff suppressed because it is too large Load diff

1195
po/br.po

File diff suppressed because it is too large Load diff

1196
po/ca.po

File diff suppressed because it is too large Load diff

1217
po/cs.po

File diff suppressed because it is too large Load diff

1217
po/da.po

File diff suppressed because it is too large Load diff

1236
po/de.po

File diff suppressed because it is too large Load diff

1195
po/el.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1211
po/eo.po

File diff suppressed because it is too large Load diff

3847
po/es.po

File diff suppressed because it is too large Load diff

1211
po/eu.po

File diff suppressed because it is too large Load diff

1625
po/fr.po

File diff suppressed because it is too large Load diff

1211
po/gl.po

File diff suppressed because it is too large Load diff

1217
po/he.po

File diff suppressed because it is too large Load diff

1217
po/hr.po

File diff suppressed because it is too large Load diff

1217
po/hu.po

File diff suppressed because it is too large Load diff

1217
po/it.po

File diff suppressed because it is too large Load diff

1217
po/ja.po

File diff suppressed because it is too large Load diff

1217
po/kk.po

File diff suppressed because it is too large Load diff

1196
po/lt.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1236
po/nl.po

File diff suppressed because it is too large Load diff

1217
po/pl.po

File diff suppressed because it is too large Load diff

1613
po/pt.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1217
po/ru.po

File diff suppressed because it is too large Load diff

1217
po/sk.po

File diff suppressed because it is too large Load diff

1217
po/sr.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1217
po/sv.po

File diff suppressed because it is too large Load diff

1193
po/tr.po

File diff suppressed because it is too large Load diff

1217
po/uk.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -97,11 +97,10 @@ function install_deps {
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pyopenssl \ mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pyopenssl \
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-docutils \ mingw-w64-"${ARCH}"-"${PYTHON_ID}"-docutils \
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-certifi \ mingw-w64-"${ARCH}"-"${PYTHON_ID}"-certifi \
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-six \ mingw-w64-"${ARCH}"-"${PYTHON_ID}"-six
mingw-w64-"${ARCH}"-"${PYTHON_ID}"-pygments
PIP_REQUIREMENTS="\ PIP_REQUIREMENTS="\
git+https://dev.gajim.org/gajim/python-nbxmpp.git@nbxmpp_0.6 nbxmpp==0.6.9
git+https://dev.gajim.org/lovetox/pybonjour-python3.git git+https://dev.gajim.org/lovetox/pybonjour-python3.git
git+https://github.com/enthought/pywin32-ctypes.git git+https://github.com/enthought/pywin32-ctypes.git
keyring keyring