21 Commits

Author SHA1 Message Date
ac07cb529f reconnection bug fixed 2017-04-22 22:35:32 +03:00
4f77e2c105 @cached 2017-04-17 22:04:22 +03:00
47ce9252b7 Merge pull request #41 from nurupo/fix-md-formatting
Fix markdown formatting
2017-04-12 17:16:06 +03:00
9153836ead Fix markdown formatting 2017-04-12 10:10:48 -04:00
19fb905554 some fixes 2017-04-06 21:28:34 +03:00
3ef581bc5d some messages improvements 2017-04-05 23:46:32 +03:00
f897c7ce8d unlock screen crash fixed 2017-03-27 00:04:32 +03:00
ba390eda91 message splitting bug fix 2017-03-26 18:28:30 +03:00
5fea3e918d plugin reloading refactoring 2017-03-25 18:21:25 +03:00
7cc404ce52 plugins improvements 2017-03-15 23:17:38 +03:00
8a502b4082 block user option in friend menu and translations update 2017-03-08 13:37:19 +03:00
b83ea6be18 reconnection bug fix 2017-03-08 13:19:41 +03:00
85554eacd1 docs updates 2017-03-04 23:35:46 +03:00
8bbefff6c7 history - travis tests fix 2017-03-04 23:18:26 +03:00
019165aeac unsent files fix 2017-03-04 22:15:42 +03:00
0cfb8efefa reconnection fixes 2017-03-03 22:09:45 +03:00
b227ed627a more tests 2017-02-27 21:44:35 +03:00
f41b5e5c97 history test 2017-02-25 23:53:33 +03:00
bc9ec04171 version number fix 2017-02-20 23:47:55 +03:00
05e4184c5d travis fix 2017-02-20 22:03:35 +03:00
3194099f59 default config file moved to app dir 2017-02-20 21:33:04 +03:00
27 changed files with 523 additions and 358 deletions

View File

@ -2,6 +2,7 @@ language: python
python:
- "3.4"
before_install:
- sudo apt-get update
- sudo apt-get install -y checkinstall build-essential
- sudo apt-get install portaudio19-dev
- sudo apt-get install libconfig-dev libvpx-dev check -qq

View File

@ -2,13 +2,13 @@
Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pure Python3.
[![Release](https://img.shields.io/github/release/xveduk/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/releases/latest)
[![Stars](https://img.shields.io/github/stars/xveduk/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/stargazers)
[![Open issues](https://img.shields.io/github/issues/xveduk/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/issues)
[![Release](https://img.shields.io/github/release/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/releases/latest)
[![Stars](https://img.shields.io/github/stars/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/stargazers)
[![Open issues](https://img.shields.io/github/issues/toxygen-project/toxygen.svg?style=flat)](https://github.com/toxygen-project/toxygen/issues)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://raw.githubusercontent.com/toxygen-project/toxygen/master/LICENSE.md)
[![Build Status](https://travis-ci.org/toxygen-project/toxygen.svg?branch=master)](https://travis-ci.org/toxygen-project/toxygen)
### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md) - [Compile](/docs/compile.md) - [Contact](/docs/contact.md)
### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md) - [Compile](/docs/compile.md) - [Contact](/docs/contact.md) - [Updater](https://github.com/toxygen-project/toxygen_updater)
### Supported OS:
@ -18,7 +18,7 @@ Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pu
- [x] 1v1 messages
- [x] File transfers
- [x] Audio
- [x] Audio calls
- [x] Plugins support
- [x] Chat history
- [x] Emoticons
@ -42,7 +42,7 @@ Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pu
- [x] Changing nospam
- [x] File resuming
- [x] Read receipts
- [ ] Video
- [ ] Video calls
- [ ] Desktop sharing
- [ ] Group chats

View File

@ -5,6 +5,7 @@ You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/)
Install PyInstaller:
``pip3 install pyinstaller``
Compile Toxygen:
``pyinstaller --windowed --icon images/icon.ico main.py``
Don't forget to copy /images/, /sounds/, /translations/, /styles/, /smileys/, /stickers/, /plugins/ (and /libs/libtox.dll, /libs/libsodium.a on Windows) to /dist/main/

View File

@ -1,20 +1,20 @@
#Issues
# Issues
Help us find all bugs in Toxygen! Please provide following info:
- OS
- Toxygen version
- Toxygen executable info - .py or precompiled binary
- Toxygen executable info - .py or precompiled binary, how was it installed in system
- Steps to reproduce the bug
Want to see new feature in Toxygen? [Ask for it!](https://github.com/xveduk/toxygen/issues)
Want to see new feature in Toxygen? [Ask for it!](https://github.com/toxygen-project/toxygen/issues)
#Pull requests
# Pull requests
Developer? Feel free to open pull request. Our dev team is small so we glad to get help.
Don't know what to do? Improve UI, fix [issues](https://github.com/xveduk/toxygen/issues) or implement features from our TODO list.
Don't know what to do? Improve UI, fix [issues](https://github.com/toxygen-project/toxygen/issues) or implement features from our TODO list.
You can find our TODO's in code, issues list and [here](/README.md). Also you can implement [plugins](/docs/plugins.md) for Toxygen.
#Translations
# Translations
Help us translate Toxygen! Translation can be created using pyside-lupdate (``pyside-lupdate toxygen.pro``) and QT Linguist.

View File

@ -1,7 +1,7 @@
# How to install Toxygen
## Use precompiled binary:
[Check our releases page](https://github.com/xveduk/toxygen/releases)
[Check our releases page](https://github.com/toxygen-project/toxygen/releases)
## Using pip3

View File

@ -1,4 +1,4 @@
#Plugins API
# Plugins API
In Toxygen plugin is single python (supported Python 3.0 - 3.4) module (.py file) and directory with data associated with it.
Every module must contain one class derived from PluginSuperClass defined in [plugin_super_class.py](/src/plugins/plugin_super_class.py). Instance of this class will be created by PluginLoader class (defined in [plugin_support.py](/src/plugin_support.py) ). This class can enable/disable plugins and send data to it.
@ -18,7 +18,7 @@ All plugin's data should be stored in following structure:
```
Plugin MUST override:
- __init__ with params: tox (Tox instance), profile (Profile instance), settings (Settings instance), encrypt_save (ToxEncryptSave instance). Call super().__init__ with params plugin_full_name, plugin_short_name, tox, profile, settings, encrypt_save.
- __init__ with params: tox (Tox instance), profile (Profile instance), settings (Settings instance), encrypt_save (ToxES instance). Call super().__init__ with params plugin_full_name, plugin_short_name, tox, profile, settings, encrypt_save.
Plugin can override following methods:
- get_description - this method should return plugin description.
@ -51,7 +51,7 @@ Exceptions:
Plugin's methods MUST NOT raise exceptions.
#Examples
# Examples
You can find examples in [official repo](https://github.com/ingvar1995/toxygen_plugins)
You can find examples in [official repo](https://github.com/toxygen-project/toxygen_plugins)

View File

@ -1,22 +1,22 @@
#Plugins
# Plugins
Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python 3.4 module (.py file) and directory with plugin's data which provide some additional functionality.
#How to write plugin
# How to write plugin
Check [Plugin API](/docs/plugin_api.md) for more info
#How to install plugin
# How to install plugin
Toxygen comes without preinstalled plugins.
1. Put plugin and directory with its data into /src/plugins/ or import it via GUI (In menu: Plugins -> Import plugin)
2. Restart Toxygen
1. Put plugin and directory with its data into /src/plugins/ or import it via GUI (In menu: Plugins => Import plugin)
2. Restart Toxygen or choose Plugins => Reload plugins in menu.
##Note: /src/plugins/ should contain plugin_super_class.py and __init__.py
## Note: /src/plugins/ should contain plugin_super_class.py and __init__.py
#Plugins list
# Plugins list
WARNING: It is unsecure to install plugin not from this list!
[Main repo](https://github.com/ingvar1995/toxygen_plugins)
[Main repo](https://github.com/toxygen-project/toxygen_plugins)

View File

@ -1,4 +1,4 @@
#Smileys
# Smileys
Toxygen support smileys. Smiley is small picture which replaces some symbol or combination of symbols. If you want to create your own smiley pack, create directory in src/smileys/. This directory must contain images with smileys and config.json. Example of config.json:
@ -6,8 +6,8 @@ Toxygen support smileys. Smiley is small picture which replaces some symbol or c
Animated smileys (.gif) are supported too.
#Stickers
# Stickers
Sticker is inline image. If you want to create your own smiley pack, create directory in src/stickers/ and place your stickers there.
Users can import plugins and stickers packs using menu: Settings -> Interface
Users can import smileys and stickers using menu: Settings -> Interface

View File

@ -1,7 +1,10 @@
from toxygen.profile import *
from toxygen.tox_dns import tox_dns
from toxygen.history import History
from toxygen.smileys import SmileyLoader
from toxygen.messages import *
import toxygen.toxes as encr
import toxygen.messages as m
import toxygen.util as util
import time
@ -58,47 +61,68 @@ class TestEncryption:
lib.set_password(password)
copy_data = data[:]
new_data = lib.pass_encrypt(data)
assert lib.is_data_encrypted(new_data)
new_data = lib.pass_decrypt(new_data)
assert copy_data == new_data
class TestFriend:
class TestSmileys:
def create_singletons(self):
def test_loading(self):
settings = {'smiley_pack': 'default', 'smileys': True}
sm = SmileyLoader(settings)
assert sm.get_smileys_path() is not None
l = sm.get_packs_list()
assert len(l) == 4
def create_singletons():
folder = util.curr_directory() + '/abc'
Settings._instance = Settings.get_default_settings()
ProfileHelper('abc', 'test')
if not os.path.exists(folder):
os.makedirs(folder)
ProfileHelper(folder, 'test')
def create_friend(self, name, status_message, number, tox_id):
def create_friend(name, status_message, number, tox_id):
friend = Friend(None, number, name, status_message, None, tox_id)
return friend
def test_friend_creation(self):
self.create_singletons()
def create_random_friend():
name, status_message, number = 'Friend', 'I am friend!', 0
tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6'
friend = self.create_friend(name, status_message, number, tox_id)
friend = create_friend(name, status_message, number, tox_id)
return friend
class TestFriend:
def test_friend_creation(self):
create_singletons()
name, status_message, number = 'Friend', 'I am friend!', 0
tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6'
friend = create_friend(name, status_message, number, tox_id)
assert friend.name == name
assert friend.tox_id == tox_id
assert friend.status_message == status_message
assert friend.number == number
def test_friend_corr(self):
self.create_singletons()
name, status_message, number = 'Friend', 'I am friend!', 0
tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6'
friend = self.create_friend(name, status_message, number, tox_id)
create_singletons()
friend = create_random_friend()
t = time.time()
friend.append_message(m.InfoMessage('Info message', t))
friend.append_message(m.TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t + 0.001, 0))
friend.append_message(m.TextMessage('Hello!', MESSAGE_OWNER['FRIEND'], t + 0.002, 0))
friend.append_message(InfoMessage('Info message', t))
friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t + 0.001, 0))
friend.append_message(TextMessage('Hello!', MESSAGE_OWNER['FRIEND'], t + 0.002, 0))
assert friend.get_last_message_text() == 'Hello! It is test!'
assert len(friend.get_corr()) == 3
assert len(friend.get_corr_for_saving()) == 2
friend.append_message(m.TextMessage('Not sent', MESSAGE_OWNER['NOT_SENT'], t + 0.002, 0))
friend.append_message(TextMessage('Not sent', MESSAGE_OWNER['NOT_SENT'], t + 0.002, 0))
arr = friend.get_unsent_messages_for_saving()
assert len(arr) == 1
assert arr[0][0] == 'Not sent'
tm = m.TransferMessage(MESSAGE_OWNER['FRIEND'],
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
time.time(),
TOX_FILE_TRANSFER_STATE['RUNNING'],
100, 'file_name', friend.number, 0)
@ -106,8 +130,48 @@ class TestFriend:
friend.clear_corr()
assert len(friend.get_corr()) == 1
assert len(friend.get_corr_for_saving()) == 0
friend.append_message(m.TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t, 0))
friend.append_message(TextMessage('Hello! It is test!', MESSAGE_OWNER['ME'], t, 0))
assert len(friend.get_corr()) == 2
assert len(friend.get_corr_for_saving()) == 1
# TODO: more friend tests and history test
def test_history_search(self):
create_singletons()
friend = create_random_friend()
message = 'Hello! It is test!'
friend.append_message(TextMessage(message, MESSAGE_OWNER['ME'], time.time(), 0))
last_message = friend.get_last_message_text()
assert last_message == message
result = friend.search_string('e[m|s]')
assert result is not None
result = friend.search_string('tox')
assert result is None
class TestHistory:
def test_history(self):
create_singletons()
db_name = 'my_name'
name, status_message, number = 'Friend', 'I am friend!', 0
tox_id = '76518406F6A9F2217E8DC487CC783C25CC16A15EB36FF32E335A235342C48A39218F515C39A6'
friend = create_friend(name, status_message, number, tox_id)
history = History(db_name)
history.add_friend_to_db(friend.tox_id)
assert history.friend_exists_in_db(friend.tox_id)
text_message = 'Test!'
t = time.time()
friend.append_message(TextMessage(text_message, MESSAGE_OWNER['ME'], t, 0))
messages = friend.get_corr_for_saving()
history.save_messages_to_db(friend.tox_id, messages)
getter = history.messages_getter(friend.tox_id)
messages = getter.get_all()
assert len(messages) == 1
assert messages[0][0] == text_message
assert messages[0][1] == MESSAGE_OWNER['ME']
assert messages[0][-1] == 0
history.delete_message(friend.tox_id, t)
getter = history.messages_getter(friend.tox_id)
messages = getter.get_all()
assert len(messages) == 0
history.delete_friend_from_db(friend.tox_id)
assert not history.friend_exists_in_db(friend.tox_id)

View File

@ -63,6 +63,7 @@ class IncomingCallWidget(widgets.CenteredWidget):
def __init__(self):
QtCore.QThread.__init__(self)
self.a = None
def run(self):
class AudioFile:

View File

@ -109,6 +109,10 @@ class BaseContact:
def get_pixmap(self):
return self._widget.avatar_label.pixmap()
# -----------------------------------------------------------------------------------------------------------------
# Widgets
# -----------------------------------------------------------------------------------------------------------------
def init_widget(self):
if self._widget is not None:
self._widget.name.setText(self._name)

View File

@ -141,4 +141,3 @@ class AV:
if state & TOXAV_FRIEND_CALL_STATE['ACCEPTING_A']:
self._calls[friend_number] |= 1

View File

@ -30,7 +30,6 @@ class Contact(basecontact.BaseContact):
self._corr = []
self._unsaved_messages = 0
self._history_loaded = self._new_actions = False
self._receipts = 0
self._curr_text = self._search_string = ''
self._search_index = 0
@ -50,6 +49,8 @@ class Contact(basecontact.BaseContact):
"""
if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')):
return
if self._message_getter is None:
return
data = list(self._message_getter.get(PAGE_SIZE))
if data is not None and len(data):
data.reverse()
@ -138,12 +139,15 @@ class Contact(basecontact.BaseContact):
"""
Delete old messages (reduces RAM usage if messages saving is not enabled)
"""
old = filter(lambda x: x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None),
self._corr[:-SAVE_MESSAGES])
old = list(old)
l = max(len(self._corr) - SAVE_MESSAGES, 0) - len(old)
self._unsaved_messages -= l
self._corr = old + self._corr[-SAVE_MESSAGES:]
def save_message(x):
if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None):
return True
return x.get_owner() == MESSAGE_OWNER['NOT_SENT']
old = filter(save_message, self._corr[:-SAVE_MESSAGES])
self._corr = list(old) + self._corr[-SAVE_MESSAGES:]
text_messages = filter(lambda x: x.get_type() <= 1, self._corr)
self._unsaved_messages = min(self._unsaved_messages, len(list(text_messages)))
self._search_index = 0
def clear_corr(self, save_unsent=False):
@ -176,7 +180,7 @@ class Contact(basecontact.BaseContact):
while True:
l = len(self._corr)
for i in range(self._search_index - 1, -l - 1, -1):
if type(self._corr[i]) is not TextMessage:
if self._corr[i].get_type() > 1:
continue
message = self._corr[i].get_data()[0]
if re.search(self._search_string, message, re.IGNORECASE) is not None:
@ -191,7 +195,7 @@ class Contact(basecontact.BaseContact):
if not self._search_index:
return None
for i in range(self._search_index + 1, 0):
if type(self._corr[i]) is not TextMessage:
if self._corr[i].get_type() > 1:
continue
message = self._corr[i].get_data()[0]
if re.search(self._search_string, message, re.IGNORECASE) is not None:

View File

@ -1,5 +1,6 @@
import contact
from messages import *
import os
class Friend(contact.Contact):
@ -37,6 +38,15 @@ class Friend(contact.Contact):
def clear_unsent_files(self):
self._corr = list(filter(lambda x: type(x) is not UnsentFile, self._corr))
def remove_invalid_unsent_files(self):
def is_valid(message):
if type(message) is not UnsentFile:
return True
if message.get_data()[1] is not None:
return True
return os.path.exists(message.get_data()[0])
self._corr = list(filter(is_valid, self._corr))
def delete_one_unsent_file(self, time):
self._corr = list(filter(lambda x: not (type(x) is UnsentFile and x.get_data()[2] == time), self._corr))

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from sqlite3 import connect
import settings
from os import chdir
@ -9,7 +12,7 @@ PAGE_SIZE = 42
TIMEOUT = 11
SAVE_MESSAGES = 150
SAVE_MESSAGES = 250
MESSAGE_OWNER = {
'ME': 0,
@ -133,14 +136,15 @@ class History:
db.rollback()
finally:
db.close()
pass
def delete_message(self, tox_id, time):
start, end = str(time - 0.01), str(time + 0.01)
chdir(settings.ProfileHelper.get_path())
db = connect(self._name + '.hstr', timeout=TIMEOUT)
try:
cursor = db.cursor()
cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time = ' + str(time) + ';')
cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time < ' + end + ' AND unix_time > ' +
start + ';')
db.commit()
except:
print('Database is locked!')

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import sys
from loginscreen import LoginScreen
import profile
@ -238,17 +240,22 @@ class Toxygen:
exit = m.addAction(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
def show_window():
s = Settings.get_instance()
def show():
if not self.ms.isActiveWindow():
self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
self.ms.activateWindow()
self.ms.show()
if not Settings.get_instance().locked:
if not s.locked:
show()
else:
def correct_pass():
show()
Settings.get_instance().locked = False
s.locked = False
s.unlockScreen = False
if not s.unlockScreen:
s.unlockScreen = True
self.p = UnlockAppScreen(toxes.ToxES.get_instance(), correct_pass)
self.p.show()

View File

@ -65,6 +65,7 @@ class MainWindow(QtGui.QMainWindow, Singleton):
self.audioSettings = QtGui.QAction(Form)
self.pluginData = QtGui.QAction(Form)
self.importPlugin = QtGui.QAction(Form)
self.reloadPlugins = QtGui.QAction(Form)
self.lockApp = QtGui.QAction(Form)
self.menuProfile.addAction(self.actionAdd_friend)
self.menuProfile.addAction(self.actionSettings)
@ -77,6 +78,7 @@ class MainWindow(QtGui.QMainWindow, Singleton):
self.menuSettings.addAction(self.updateSettings)
self.menuPlugins.addAction(self.pluginData)
self.menuPlugins.addAction(self.importPlugin)
self.menuPlugins.addAction(self.reloadPlugins)
self.menuAbout.addAction(self.actionAbout_program)
self.profile_button.setMenu(self.menuProfile)
@ -96,6 +98,7 @@ class MainWindow(QtGui.QMainWindow, Singleton):
self.pluginData.triggered.connect(self.plugins_menu)
self.lockApp.triggered.connect(self.lock_app)
self.importPlugin.triggered.connect(self.import_plugin)
self.reloadPlugins.triggered.connect(self.reload_plugins)
Form.setLayout(box)
QtCore.QMetaObject.connectSlotsByName(Form)
@ -140,6 +143,7 @@ class MainWindow(QtGui.QMainWindow, Singleton):
d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5}
self.online_contacts.setCurrentIndex(d[ind])
self.importPlugin.setText(QtGui.QApplication.translate("MainWindow", "Import plugin", None, QtGui.QApplication.UnicodeUTF8))
self.reloadPlugins.setText(QtGui.QApplication.translate("MainWindow", "Reload plugins", None, QtGui.QApplication.UnicodeUTF8))
def setup_right_bottom(self, Form):
Form.resize(650, 60)
@ -460,6 +464,11 @@ class MainWindow(QtGui.QMainWindow, Singleton):
self.update_s = UpdateSettings()
self.update_s.show()
def reload_plugins(self):
plugin_loader = plugin_support.PluginLoader.get_instance()
if plugin_loader is not None:
plugin_loader.reload()
def import_plugin(self):
import util
directory = QtGui.QFileDialog.getExistingDirectory(self,
@ -596,14 +605,18 @@ class MainWindow(QtGui.QMainWindow, Singleton):
auto_accept_item = self.listMenu.addAction(auto)
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
block_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Block friend', None, QtGui.QApplication.UnicodeUTF8))
notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
plugins_loader = plugin_support.PluginLoader.get_instance()
if plugins_loader is not None:
submenu = plugins_loader.get_menu(self.listMenu, num)
if len(submenu):
plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
plug.addActions(submenu)
self.connect(set_alias_item, QtCore.SIGNAL("triggered()"), lambda: self.set_alias(num))
self.connect(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num))
self.connect(block_item, QtCore.SIGNAL("triggered()"), lambda: self.block_friend(num))
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
@ -652,6 +665,10 @@ class MainWindow(QtGui.QMainWindow, Singleton):
def remove_friend(self, num):
self.profile.delete_friend(num)
def block_friend(self, num):
friend = self.profile.get_friend(num)
self.profile.block_user(friend.tox_id)
def copy_friend_key(self, num):
tox_id = self.profile.friend_public_key(num)
clipboard = QtGui.QApplication.clipboard()

View File

@ -363,11 +363,7 @@ class WelcomeScreen(CenteredWidget):
text = QtGui.QApplication.translate('WelcomeScreen',
'Since v0.1.3 Toxygen supports plugins. <a href="https://github.com/xveduk/toxygen/blob/master/docs/plugins.md">Read more</a>',
None, QtGui.QApplication.UnicodeUTF8)
elif num == 6:
text = QtGui.QApplication.translate('WelcomeScreen',
'New in Toxygen v0.2.6:<br>Updater<br>Better contact sorting<br>Plugins improvements',
None, QtGui.QApplication.UnicodeUTF8)
elif num == 7:
elif num in (6, 7):
text = QtGui.QApplication.translate('WelcomeScreen',
'Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.',
None, QtGui.QApplication.UnicodeUTF8)
@ -520,10 +516,12 @@ class SearchScreen(QtGui.QWidget):
@staticmethod
def not_found(text):
mbox = QtGui.QMessageBox()
mbox.setText(QtGui.QApplication.translate("MainWindow",
'Text "{}" was not found'.format(text),
mbox_text = QtGui.QApplication.translate("MainWindow",
'Text "{}" was not found',
None,
QtGui.QApplication.UnicodeUTF8))
QtGui.QApplication.UnicodeUTF8)
mbox.setText(mbox_text.format(text))
mbox.setWindowTitle(QtGui.QApplication.translate("MainWindow",
'Not found',
None,

View File

@ -51,7 +51,8 @@ class PluginLoader(util.Singleton):
continue
for elem in dir(module):
obj = getattr(module, elem)
if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin: # looking for plugin class in module
# looking for plugin class in module
if inspect.isclass(obj) and hasattr(obj, 'is_plugin') and obj.is_plugin:
print('Plugin', elem)
try: # create instance of plugin class
inst = obj(self._tox, self._profile, self._settings, self._encr)
@ -165,3 +166,8 @@ class PluginLoader(util.Singleton):
if self._plugins[key][1]:
self._plugins[key][0].close()
del self._plugins[key]
def reload(self):
print('Reloading plugins')
self.stop()
self.load()

View File

@ -41,6 +41,7 @@ class Profile(basecontact.BaseContact, Singleton):
self._call = calls.AV(tox.AV) # object with data about calls
self._incoming_calls = set()
self._load_history = True
self._waiting_for_reconnection = False
self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages)
settings = Settings.get_instance()
self._sorting = settings['sorting']
@ -86,8 +87,9 @@ class Profile(basecontact.BaseContact, Singleton):
super(Profile, self).set_status(status)
if status is not None:
self._tox.self_set_status(status)
else:
QtCore.QTimer.singleShot(45000, self.reconnect)
elif not self._waiting_for_reconnection:
self._waiting_for_reconnection = True
QtCore.QTimer.singleShot(50000, self.reconnect)
def set_name(self, value):
if self.name == value:
@ -218,6 +220,7 @@ class Profile(basecontact.BaseContact, Singleton):
except:
pass
friend = self._contacts[value]
friend.remove_invalid_unsent_files()
if self._active_friend != value:
self._screen.messageEdit.setPlainText(friend.curr_text)
self._active_friend = value
@ -328,6 +331,7 @@ class Profile(basecontact.BaseContact, Singleton):
def send_files(self, friend_number):
friend = self.get_friend_by_number(friend_number)
friend.remove_invalid_unsent_files()
files = friend.get_unsent_files()
try:
for fl in files:
@ -409,25 +413,25 @@ class Profile(basecontact.BaseContact, Singleton):
for message in messages:
self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8'))
friend.inc_receipts()
except:
pass
except Exception as ex:
log('Sending pending messages failed with ' + str(ex))
def split_and_send(self, number, message_type, message):
"""
Message splitting
Message splitting. Message length cannot be > TOX_MAX_MESSAGE_LENGTH
:param number: friend's number
:param message_type: type of message
:param message: message text
"""
while len(message) > TOX_MAX_MESSAGE_LENGTH:
size = TOX_MAX_MESSAGE_LENGTH * 4 / 5
size = TOX_MAX_MESSAGE_LENGTH * 4 // 5
last_part = message[size:TOX_MAX_MESSAGE_LENGTH]
if ' ' in last_part:
index = last_part.index(' ')
elif ',' in last_part:
index = last_part.index(',')
elif '.' in last_part:
index = last_part.index('.')
if b' ' in last_part:
index = last_part.index(b' ')
elif b',' in last_part:
index = last_part.index(b',')
elif b'.' in last_part:
index = last_part.index(b'.')
else:
index = TOX_MAX_MESSAGE_LENGTH - size - 1
index += size + 1
@ -855,9 +859,11 @@ class Profile(basecontact.BaseContact, Singleton):
self.update_filtration()
def reconnect(self):
self._waiting_for_reconnection = False
if self.status is None or all(list(map(lambda x: x.status is None, self._contacts))) and len(self._contacts):
self._waiting_for_reconnection = True
self.reset(self._screen.reset)
QtCore.QTimer.singleShot(45000, self.reconnect)
QtCore.QTimer.singleShot(50000, self.reconnect)
def close(self):
for friend in self._contacts:

View File

@ -35,6 +35,7 @@ class Settings(dict, Singleton):
smileys.SmileyLoader(self)
self.locked = False
self.closing = False
self.unlockScreen = False
p = pyaudio.PyAudio()
input_devices = output_devices = 0
for i in range(p.get_device_count()):
@ -49,7 +50,7 @@ class Settings(dict, Singleton):
@staticmethod
def get_auto_profile():
p = Settings.get_default_path() + 'toxygen.json'
p = Settings.get_global_settings_path()
if os.path.isfile(p):
with open(p) as fl:
data = fl.read()
@ -60,7 +61,7 @@ class Settings(dict, Singleton):
@staticmethod
def set_auto_profile(path, name):
p = Settings.get_default_path() + 'toxygen.json'
p = Settings.get_global_settings_path()
if os.path.isfile(p):
with open(p) as fl:
data = fl.read()
@ -74,7 +75,7 @@ class Settings(dict, Singleton):
@staticmethod
def reset_auto_profile():
p = Settings.get_default_path() + 'toxygen.json'
p = Settings.get_global_settings_path()
if os.path.isfile(p):
with open(p) as fl:
data = fl.read()
@ -89,15 +90,8 @@ class Settings(dict, Singleton):
@staticmethod
def is_active_profile(path, name):
path = path + name + '.tox'
settings = Settings.get_default_path() + 'toxygen.json'
if os.path.isfile(settings):
with open(settings) as fl:
data = fl.read()
data = json.loads(data)
if 'active_profile' in data:
return path in data['active_profile']
return False
path = path + name + '.lock'
return os.path.isfile(path)
@staticmethod
def get_default_settings():
@ -176,37 +170,19 @@ class Settings(dict, Singleton):
fl.write(text)
def close(self):
path = Settings.get_default_path() + 'toxygen.json'
profile_path = ProfileHelper.get_path()
path = str(profile_path + str(self.name) + '.lock')
if os.path.isfile(path):
with open(path) as fl:
data = fl.read()
app_settings = json.loads(data)
try:
app_settings['active_profile'].remove(str(ProfileHelper.get_path() + self.name + '.tox'))
except:
pass
data = json.dumps(app_settings)
with open(path, 'w') as fl:
fl.write(data)
os.remove(path)
def set_active_profile(self):
"""
Mark current profile as active
"""
path = Settings.get_default_path() + 'toxygen.json'
if os.path.isfile(path):
with open(path) as fl:
data = fl.read()
app_settings = json.loads(data)
else:
app_settings = {}
if 'active_profile' not in app_settings:
app_settings['active_profile'] = []
profile_path = ProfileHelper.get_path()
app_settings['active_profile'].append(str(profile_path + str(self.name) + '.tox'))
data = json.dumps(app_settings)
path = str(profile_path + str(self.name) + '.lock')
with open(path, 'w') as fl:
fl.write(data)
fl.write('active')
def export(self, path):
text = json.dumps(self)
@ -216,6 +192,10 @@ class Settings(dict, Singleton):
def update_path(self):
self.path = ProfileHelper.get_path() + self.name + '.json'
@staticmethod
def get_global_settings_path():
return curr_directory() + '/toxygen.json'
@staticmethod
def get_default_path():
if system() == 'Windows':

View File

@ -84,98 +84,98 @@ can produce IP leak</source>
<context>
<name>MainWindow</name>
<message>
<location filename="mainscreen.py" line="118"/>
<location filename="mainscreen.py" line="123"/>
<source>Profile</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="124"/>
<location filename="mainscreen.py" line="129"/>
<source>Settings</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="414"/>
<location filename="mainscreen.py" line="426"/>
<source>About</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="117"/>
<location filename="mainscreen.py" line="122"/>
<source>Add contact</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="119"/>
<location filename="mainscreen.py" line="124"/>
<source>Privacy</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="120"/>
<location filename="mainscreen.py" line="125"/>
<source>Interface</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="121"/>
<location filename="mainscreen.py" line="126"/>
<source>Notifications</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="122"/>
<location filename="mainscreen.py" line="127"/>
<source>Network</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="123"/>
<location filename="mainscreen.py" line="128"/>
<source>About program</source>
<translation></translation>
</message>
<message>
<location filename="profile.py" line="816"/>
<location filename="profile.py" line="829"/>
<source>User {} wants to add you to contact list. Message:
{}</source>
<translation></translation>
</message>
<message>
<location filename="profile.py" line="818"/>
<location filename="profile.py" line="831"/>
<source>Friend request</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="509"/>
<location filename="mainscreen.py" line="528"/>
<source>Choose file</source>
<translation>Choose file</translation>
</message>
<message>
<location filename="mainscreen.py" line="574"/>
<location filename="mainscreen.py" line="593"/>
<source>Disallow auto accept</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="575"/>
<location filename="mainscreen.py" line="594"/>
<source>Allow auto accept</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="577"/>
<location filename="mainscreen.py" line="596"/>
<source>Set alias</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="580"/>
<location filename="mainscreen.py" line="599"/>
<source>Clear history</source>
<translation></translation>
</message>
<message>
<location filename="mainscreen.py" line="590"/>
<location filename="mainscreen.py" line="609"/>
<source>Remove friend</source>
<translation></translation>
</message>
<message>
<location filename="profile.py" line="655"/>
<location filename="profile.py" line="668"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation>Enter new alias for friend {} or leave empty to use friend&apos;s name:</translation>
</message>
<message>
<location filename="mainscreen.py" line="125"/>
<location filename="mainscreen.py" line="130"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
@ -185,24 +185,24 @@ can produce IP leak</source>
<translation type="obsolete">Find contact</translation>
</message>
<message>
<location filename="profile.py" line="788"/>
<location filename="profile.py" line="801"/>
<source>Friend added</source>
<translation>Friend added</translation>
</message>
<message>
<location filename="mainscreen.py" line="415"/>
<location filename="mainscreen.py" line="427"/>
<source>Toxygen is Tox client written on Python.
Version: </source>
<translation>Toxygen is Tox client written on Python.
Version:</translation>
</message>
<message>
<location filename="profile.py" line="789"/>
<location filename="profile.py" line="802"/>
<source>Friend added without sending friend request</source>
<translation>Friend added without sending friend request</translation>
</message>
<message>
<location filename="mainscreen.py" line="632"/>
<location filename="mainscreen.py" line="655"/>
<source>Choose folder</source>
<translation>Choose folder</translation>
</message>
@ -217,47 +217,47 @@ Version:</translation>
<translation type="obsolete">Send file</translation>
</message>
<message>
<location filename="mainscreen.py" line="128"/>
<location filename="mainscreen.py" line="133"/>
<source>Send message</source>
<translation>Send message</translation>
</message>
<message>
<location filename="mainscreen.py" line="129"/>
<location filename="mainscreen.py" line="134"/>
<source>Start audio call with friend</source>
<translation>Start audio call with friend</translation>
</message>
<message>
<location filename="mainscreen.py" line="595"/>
<location filename="mainscreen.py" line="617"/>
<source>Plugins</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<location filename="mainscreen.py" line="118"/>
<source>List of plugins</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="127"/>
<location filename="mainscreen_widgets.py" line="462"/>
<source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="131"/>
<location filename="mainscreen.py" line="136"/>
<source>All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="132"/>
<location filename="mainscreen.py" line="137"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="591"/>
<location filename="mainscreen.py" line="611"/>
<source>Notes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="616"/>
<location filename="mainscreen.py" line="639"/>
<source>Notes about user</source>
<translation type="unfinished"></translation>
</message>
@ -307,42 +307,42 @@ Version:</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="303"/>
<location filename="profile.py" line="315"/>
<source>User {} is now known as {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="list_items.py" line="167"/>
<location filename="list_items.py" line="168"/>
<source>Delete message</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="111"/>
<location filename="mainscreen.py" line="116"/>
<source>Lock</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="482"/>
<location filename="mainscreen.py" line="501"/>
<source>Cannot lock app</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="485"/>
<location filename="mainscreen.py" line="504"/>
<source>Error. Profile password is not set.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="585"/>
<location filename="mainscreen.py" line="604"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="586"/>
<location filename="mainscreen.py" line="605"/>
<source>Status message</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="587"/>
<location filename="mainscreen.py" line="606"/>
<source>Public key</source>
<translation type="unfinished"></translation>
</message>
@ -367,65 +367,85 @@ Version:</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="140"/>
<location filename="mainscreen.py" line="145"/>
<source>Import plugin</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="460"/>
<location filename="mainscreen.py" line="479"/>
<source>Choose folder with plugin</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="469"/>
<location filename="mainscreen.py" line="488"/>
<source>Restart Toxygen</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="472"/>
<location filename="mainscreen.py" line="491"/>
<source>Plugin will be loaded after restart</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="list_items.py" line="48"/>
<location filename="list_items.py" line="49"/>
<source>Quote selected text</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="579"/>
<location filename="mainscreen.py" line="598"/>
<source>Chat history</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="581"/>
<location filename="mainscreen.py" line="600"/>
<source>Export as text</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="582"/>
<location filename="mainscreen.py" line="601"/>
<source>Export as HTML</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="126"/>
<location filename="mainscreen.py" line="131"/>
<source>Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="133"/>
<location filename="mainscreen.py" line="138"/>
<source>Online first</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="135"/>
<location filename="mainscreen.py" line="140"/>
<source>Online and by name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="136"/>
<location filename="mainscreen.py" line="141"/>
<source>Online first and by name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="610"/>
<source>Block friend</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="528"/>
<source>Not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="524"/>
<source>Text &quot;{}&quot; was not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="146"/>
<source>Reload plugins</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MenuWindow</name>
@ -758,30 +778,25 @@ Version:</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="374"/>
<location filename="mainscreen_widgets.py" line="370"/>
<source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="386"/>
<location filename="mainscreen_widgets.py" line="382"/>
<source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="378"/>
<location filename="mainscreen_widgets.py" line="374"/>
<source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="382"/>
<location filename="mainscreen_widgets.py" line="378"/>
<source>Use right click on inline image to save it</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="370"/>
<source>New in Toxygen v0.2.6:&lt;br&gt;Updater&lt;br&gt;Better contact sorting&lt;br&gt;Plugins improvements</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>audioSettingsForm</name>
@ -804,32 +819,32 @@ Version:</translation>
<context>
<name>incoming_call</name>
<message>
<location filename="profile.py" line="1214"/>
<location filename="profile.py" line="1229"/>
<source>Incoming video call</source>
<translation>Incoming video call</translation>
</message>
<message>
<location filename="profile.py" line="1217"/>
<location filename="profile.py" line="1232"/>
<source>Incoming audio call</source>
<translation>Incoming audio call</translation>
</message>
<message>
<location filename="profile.py" line="1195"/>
<location filename="profile.py" line="1210"/>
<source>Outgoing video call</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1198"/>
<location filename="profile.py" line="1213"/>
<source>Outgoing audio call</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1248"/>
<location filename="profile.py" line="1263"/>
<source>Call declined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1250"/>
<location filename="profile.py" line="1265"/>
<source>Call finished</source>
<translation type="unfinished"></translation>
</message>
@ -985,7 +1000,7 @@ Version:</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="288"/>
<location filename="main.py" line="290"/>
<source>Update for Toxygen was found. Download and install it?</source>
<translation type="unfinished"></translation>
</message>
@ -1122,32 +1137,32 @@ Version:</translation>
<context>
<name>tray</name>
<message>
<location filename="main.py" line="228"/>
<location filename="main.py" line="229"/>
<source>Open Toxygen</source>
<translation></translation>
</message>
<message>
<location filename="main.py" line="237"/>
<location filename="main.py" line="238"/>
<source>Exit</source>
<translation></translation>
</message>
<message>
<location filename="main.py" line="229"/>
<location filename="main.py" line="230"/>
<source>Set status</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="230"/>
<location filename="main.py" line="231"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="231"/>
<location filename="main.py" line="232"/>
<source>Away</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="232"/>
<location filename="main.py" line="233"/>
<source>Busy</source>
<translation type="unfinished"></translation>
</message>

View File

@ -84,58 +84,58 @@ can produce IP leak</source>
<context>
<name>MainWindow</name>
<message>
<location filename="mainscreen.py" line="118"/>
<location filename="mainscreen.py" line="123"/>
<source>Profile</source>
<translation>Profile</translation>
</message>
<message>
<location filename="mainscreen.py" line="124"/>
<location filename="mainscreen.py" line="129"/>
<source>Settings</source>
<translation>Paramêtres</translation>
</message>
<message>
<location filename="mainscreen.py" line="414"/>
<location filename="mainscreen.py" line="426"/>
<source>About</source>
<translation>À Propos</translation>
</message>
<message>
<location filename="mainscreen.py" line="117"/>
<location filename="mainscreen.py" line="122"/>
<source>Add contact</source>
<translation>Rajouter un contact</translation>
</message>
<message>
<location filename="mainscreen.py" line="119"/>
<location filename="mainscreen.py" line="124"/>
<source>Privacy</source>
<translation>Confidentialité</translation>
</message>
<message>
<location filename="mainscreen.py" line="120"/>
<location filename="mainscreen.py" line="125"/>
<source>Interface</source>
<translation>Interface</translation>
</message>
<message>
<location filename="mainscreen.py" line="121"/>
<location filename="mainscreen.py" line="126"/>
<source>Notifications</source>
<translation>Notifications</translation>
</message>
<message>
<location filename="mainscreen.py" line="122"/>
<location filename="mainscreen.py" line="127"/>
<source>Network</source>
<translation>Réseau</translation>
</message>
<message>
<location filename="mainscreen.py" line="123"/>
<location filename="mainscreen.py" line="128"/>
<source>About program</source>
<translation>À propos du programme</translation>
</message>
<message>
<location filename="profile.py" line="816"/>
<location filename="profile.py" line="829"/>
<source>User {} wants to add you to contact list. Message:
{}</source>
<translation>L&apos;Utilisateur {} veut vout rajouter à sa liste de contacts. Message : {}</translation>
</message>
<message>
<location filename="profile.py" line="818"/>
<location filename="profile.py" line="831"/>
<source>Friend request</source>
<translation>Demande d&apos;amis</translation>
</message>
@ -145,27 +145,27 @@ can produce IP leak</source>
<translation type="obsolete">Toxygen est un client Tox écris en Python 2.7. Version : </translation>
</message>
<message>
<location filename="mainscreen.py" line="509"/>
<location filename="mainscreen.py" line="528"/>
<source>Choose file</source>
<translation>Choisir un fichier</translation>
</message>
<message>
<location filename="mainscreen.py" line="574"/>
<location filename="mainscreen.py" line="593"/>
<source>Disallow auto accept</source>
<translation>Désactiver l&apos;auto-réception</translation>
</message>
<message>
<location filename="mainscreen.py" line="575"/>
<location filename="mainscreen.py" line="594"/>
<source>Allow auto accept</source>
<translation>Activer l&apos;auto-réception</translation>
</message>
<message>
<location filename="mainscreen.py" line="577"/>
<location filename="mainscreen.py" line="596"/>
<source>Set alias</source>
<translation>Définir un alias</translation>
</message>
<message>
<location filename="mainscreen.py" line="580"/>
<location filename="mainscreen.py" line="599"/>
<source>Clear history</source>
<translation>Vider l&apos;historique</translation>
</message>
@ -175,17 +175,17 @@ can produce IP leak</source>
<translation type="obsolete">Copier la clé publique</translation>
</message>
<message>
<location filename="mainscreen.py" line="590"/>
<location filename="mainscreen.py" line="609"/>
<source>Remove friend</source>
<translation>Retirer un ami</translation>
</message>
<message>
<location filename="profile.py" line="655"/>
<location filename="profile.py" line="668"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation>Entrez un nouvel alias pour l&apos;ami {} ou laissez vide pour garder son nom de base :</translation>
</message>
<message>
<location filename="mainscreen.py" line="125"/>
<location filename="mainscreen.py" line="130"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
@ -195,24 +195,24 @@ can produce IP leak</source>
<translation type="obsolete">Trouver le contact</translation>
</message>
<message>
<location filename="profile.py" line="788"/>
<location filename="profile.py" line="801"/>
<source>Friend added</source>
<translation>Ami rajouté</translation>
</message>
<message>
<location filename="mainscreen.py" line="415"/>
<location filename="mainscreen.py" line="427"/>
<source>Toxygen is Tox client written on Python.
Version: </source>
<translation>Toxygen est un client Tox écrit en Python.
Version :</translation>
</message>
<message>
<location filename="profile.py" line="789"/>
<location filename="profile.py" line="802"/>
<source>Friend added without sending friend request</source>
<translation>Ami rajouté sans avoir envoyé de demande</translation>
</message>
<message>
<location filename="mainscreen.py" line="632"/>
<location filename="mainscreen.py" line="655"/>
<source>Choose folder</source>
<translation>Choisir le dossier</translation>
</message>
@ -227,47 +227,47 @@ Version :</translation>
<translation type="obsolete">Envoyer le fichier</translation>
</message>
<message>
<location filename="mainscreen.py" line="128"/>
<location filename="mainscreen.py" line="133"/>
<source>Send message</source>
<translation>Envoyer le message</translation>
</message>
<message>
<location filename="mainscreen.py" line="129"/>
<location filename="mainscreen.py" line="134"/>
<source>Start audio call with friend</source>
<translation>Lancer un appel audio avec un ami</translation>
</message>
<message>
<location filename="mainscreen.py" line="595"/>
<location filename="mainscreen.py" line="617"/>
<source>Plugins</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<location filename="mainscreen.py" line="118"/>
<source>List of plugins</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="127"/>
<location filename="mainscreen_widgets.py" line="462"/>
<source>Search</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="131"/>
<location filename="mainscreen.py" line="136"/>
<source>All</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="132"/>
<location filename="mainscreen.py" line="137"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="591"/>
<location filename="mainscreen.py" line="611"/>
<source>Notes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="616"/>
<location filename="mainscreen.py" line="639"/>
<source>Notes about user</source>
<translation type="unfinished"></translation>
</message>
@ -317,42 +317,42 @@ Version :</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="303"/>
<location filename="profile.py" line="315"/>
<source>User {} is now known as {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="list_items.py" line="167"/>
<location filename="list_items.py" line="168"/>
<source>Delete message</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="111"/>
<location filename="mainscreen.py" line="116"/>
<source>Lock</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="482"/>
<location filename="mainscreen.py" line="501"/>
<source>Cannot lock app</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="485"/>
<location filename="mainscreen.py" line="504"/>
<source>Error. Profile password is not set.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="585"/>
<location filename="mainscreen.py" line="604"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="586"/>
<location filename="mainscreen.py" line="605"/>
<source>Status message</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="587"/>
<location filename="mainscreen.py" line="606"/>
<source>Public key</source>
<translation type="unfinished"></translation>
</message>
@ -377,65 +377,85 @@ Version :</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="140"/>
<location filename="mainscreen.py" line="145"/>
<source>Import plugin</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="460"/>
<location filename="mainscreen.py" line="479"/>
<source>Choose folder with plugin</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="469"/>
<location filename="mainscreen.py" line="488"/>
<source>Restart Toxygen</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="472"/>
<location filename="mainscreen.py" line="491"/>
<source>Plugin will be loaded after restart</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="list_items.py" line="48"/>
<location filename="list_items.py" line="49"/>
<source>Quote selected text</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="579"/>
<location filename="mainscreen.py" line="598"/>
<source>Chat history</source>
<translation type="unfinished">Historique de chat</translation>
</message>
<message>
<location filename="mainscreen.py" line="581"/>
<location filename="mainscreen.py" line="600"/>
<source>Export as text</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="582"/>
<location filename="mainscreen.py" line="601"/>
<source>Export as HTML</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="126"/>
<location filename="mainscreen.py" line="131"/>
<source>Updates</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="133"/>
<location filename="mainscreen.py" line="138"/>
<source>Online first</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="135"/>
<location filename="mainscreen.py" line="140"/>
<source>Online and by name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="136"/>
<location filename="mainscreen.py" line="141"/>
<source>Online first and by name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="610"/>
<source>Block friend</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="528"/>
<source>Not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="524"/>
<source>Text &quot;{}&quot; was not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen.py" line="146"/>
<source>Reload plugins</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MenuWindow</name>
@ -768,30 +788,25 @@ Version :</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="374"/>
<location filename="mainscreen_widgets.py" line="370"/>
<source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="386"/>
<location filename="mainscreen_widgets.py" line="382"/>
<source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="378"/>
<location filename="mainscreen_widgets.py" line="374"/>
<source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="382"/>
<location filename="mainscreen_widgets.py" line="378"/>
<source>Use right click on inline image to save it</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="370"/>
<source>New in Toxygen v0.2.6:&lt;br&gt;Updater&lt;br&gt;Better contact sorting&lt;br&gt;Plugins improvements</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>audioSettingsForm</name>
@ -814,32 +829,32 @@ Version :</translation>
<context>
<name>incoming_call</name>
<message>
<location filename="profile.py" line="1214"/>
<location filename="profile.py" line="1229"/>
<source>Incoming video call</source>
<translation>Appel vidéo entrant</translation>
</message>
<message>
<location filename="profile.py" line="1217"/>
<location filename="profile.py" line="1232"/>
<source>Incoming audio call</source>
<translation>Appel audio entrant</translation>
</message>
<message>
<location filename="profile.py" line="1195"/>
<location filename="profile.py" line="1210"/>
<source>Outgoing video call</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1198"/>
<location filename="profile.py" line="1213"/>
<source>Outgoing audio call</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1248"/>
<location filename="profile.py" line="1263"/>
<source>Call declined</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="profile.py" line="1250"/>
<location filename="profile.py" line="1265"/>
<source>Call finished</source>
<translation type="unfinished"></translation>
</message>
@ -1000,7 +1015,7 @@ Version :</translation>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="288"/>
<location filename="main.py" line="290"/>
<source>Update for Toxygen was found. Download and install it?</source>
<translation type="unfinished"></translation>
</message>
@ -1137,32 +1152,32 @@ Version :</translation>
<context>
<name>tray</name>
<message>
<location filename="main.py" line="228"/>
<location filename="main.py" line="229"/>
<source>Open Toxygen</source>
<translation>Ouvrir Toxygen</translation>
</message>
<message>
<location filename="main.py" line="237"/>
<location filename="main.py" line="238"/>
<source>Exit</source>
<translation>Quitter</translation>
</message>
<message>
<location filename="main.py" line="229"/>
<location filename="main.py" line="230"/>
<source>Set status</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="230"/>
<location filename="main.py" line="231"/>
<source>Online</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="231"/>
<location filename="main.py" line="232"/>
<source>Away</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="main.py" line="232"/>
<location filename="main.py" line="233"/>
<source>Busy</source>
<translation type="unfinished"></translation>
</message>

Binary file not shown.

View File

@ -87,84 +87,84 @@ can produce IP leak</source>
<context>
<name>MainWindow</name>
<message>
<location filename="mainscreen.py" line="118"/>
<location filename="mainscreen.py" line="123"/>
<source>Profile</source>
<translation>Профиль</translation>
</message>
<message>
<location filename="mainscreen.py" line="124"/>
<location filename="mainscreen.py" line="129"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="mainscreen.py" line="414"/>
<location filename="mainscreen.py" line="426"/>
<source>About</source>
<translation>О программе</translation>
</message>
<message>
<location filename="mainscreen.py" line="117"/>
<location filename="mainscreen.py" line="122"/>
<source>Add contact</source>
<translation>Добавить контакт</translation>
</message>
<message>
<location filename="mainscreen.py" line="119"/>
<location filename="mainscreen.py" line="124"/>
<source>Privacy</source>
<translation>Приватность</translation>
</message>
<message>
<location filename="mainscreen.py" line="120"/>
<location filename="mainscreen.py" line="125"/>
<source>Interface</source>
<translation>Интерфейс</translation>
</message>
<message>
<location filename="mainscreen.py" line="121"/>
<location filename="mainscreen.py" line="126"/>
<source>Notifications</source>
<translation>Уведомления</translation>
</message>
<message>
<location filename="mainscreen.py" line="122"/>
<location filename="mainscreen.py" line="127"/>
<source>Network</source>
<translation>Сеть</translation>
</message>
<message>
<location filename="mainscreen.py" line="123"/>
<location filename="mainscreen.py" line="128"/>
<source>About program</source>
<translation>О программе</translation>
</message>
<message>
<location filename="profile.py" line="816"/>
<location filename="profile.py" line="829"/>
<source>User {} wants to add you to contact list. Message:
{}</source>
<translation>Пользователь {} хочет добавить Вас в список контактов. Сообщение:
{}</translation>
</message>
<message>
<location filename="profile.py" line="818"/>
<location filename="profile.py" line="831"/>
<source>Friend request</source>
<translation>Запрос на добавление в друзья</translation>
</message>
<message>
<location filename="mainscreen.py" line="509"/>
<location filename="mainscreen.py" line="528"/>
<source>Choose file</source>
<translation>Выберите файл</translation>
</message>
<message>
<location filename="mainscreen.py" line="574"/>
<location filename="mainscreen.py" line="593"/>
<source>Disallow auto accept</source>
<translation>Запретить автоматическое получение файлов</translation>
</message>
<message>
<location filename="mainscreen.py" line="575"/>
<location filename="mainscreen.py" line="594"/>
<source>Allow auto accept</source>
<translation>Разрешить автоматическое получение файлов</translation>
</message>
<message>
<location filename="mainscreen.py" line="577"/>
<location filename="mainscreen.py" line="596"/>
<source>Set alias</source>
<translation>Изменить псевдоним</translation>
</message>
<message>
<location filename="mainscreen.py" line="580"/>
<location filename="mainscreen.py" line="599"/>
<source>Clear history</source>
<translation>Очистить историю</translation>
</message>
@ -174,17 +174,17 @@ can produce IP leak</source>
<translation type="obsolete">Копировать публичный ключ</translation>
</message>
<message>
<location filename="mainscreen.py" line="590"/>
<location filename="mainscreen.py" line="609"/>
<source>Remove friend</source>
<translation>Удалить друга</translation>
</message>
<message>
<location filename="profile.py" line="655"/>
<location filename="profile.py" line="668"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation>
</message>
<message>
<location filename="mainscreen.py" line="125"/>
<location filename="mainscreen.py" line="130"/>
<source>Audio</source>
<translation>Аудио</translation>
</message>
@ -194,23 +194,23 @@ can produce IP leak</source>
<translation type="obsolete">Найти контакт</translation>
</message>
<message>
<location filename="profile.py" line="788"/>
<location filename="profile.py" line="801"/>
<source>Friend added</source>
<translation>Друг добавлен</translation>
</message>
<message>
<location filename="mainscreen.py" line="415"/>
<location filename="mainscreen.py" line="427"/>
<source>Toxygen is Tox client written on Python.
Version: </source>
<translation>Toxygen - клиент для мессенджера Tox, написанный на Python. Версия: </translation>
</message>
<message>
<location filename="profile.py" line="789"/>
<location filename="profile.py" line="802"/>
<source>Friend added without sending friend request</source>
<translation>Друг добавлен без отправки запроса на добавление в друзья</translation>
</message>
<message>
<location filename="mainscreen.py" line="632"/>
<location filename="mainscreen.py" line="655"/>
<source>Choose folder</source>
<translation>Выбрать папку</translation>
</message>
@ -225,47 +225,47 @@ Version: </source>
<translation type="obsolete">Отправить файл</translation>
</message>
<message>
<location filename="mainscreen.py" line="128"/>
<location filename="mainscreen.py" line="133"/>
<source>Send message</source>
<translation>Отправить сообщение</translation>
</message>
<message>
<location filename="mainscreen.py" line="129"/>
<location filename="mainscreen.py" line="134"/>
<source>Start audio call with friend</source>
<translation>Начать аудиозвонок с другом</translation>
</message>
<message>
<location filename="mainscreen.py" line="595"/>
<location filename="mainscreen.py" line="617"/>
<source>Plugins</source>
<translation>Плагины</translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<location filename="mainscreen.py" line="118"/>
<source>List of plugins</source>
<translation>Список плагинов</translation>
</message>
<message>
<location filename="mainscreen.py" line="127"/>
<location filename="mainscreen_widgets.py" line="462"/>
<source>Search</source>
<translation>Поиск</translation>
</message>
<message>
<location filename="mainscreen.py" line="131"/>
<location filename="mainscreen.py" line="136"/>
<source>All</source>
<translation>Все</translation>
</message>
<message>
<location filename="mainscreen.py" line="132"/>
<location filename="mainscreen.py" line="137"/>
<source>Online</source>
<translation>Онлайн</translation>
</message>
<message>
<location filename="mainscreen.py" line="591"/>
<location filename="mainscreen.py" line="611"/>
<source>Notes</source>
<translation>Заметки</translation>
</message>
<message>
<location filename="mainscreen.py" line="616"/>
<location filename="mainscreen.py" line="639"/>
<source>Notes about user</source>
<translation>Заметки о пользователе</translation>
</message>
@ -315,42 +315,42 @@ Version: </source>
<translation>Сохранить</translation>
</message>
<message>
<location filename="profile.py" line="303"/>
<location filename="profile.py" line="315"/>
<source>User {} is now known as {}</source>
<translation>Пользователь {} сейчас известен как {}</translation>
</message>
<message>
<location filename="list_items.py" line="167"/>
<location filename="list_items.py" line="168"/>
<source>Delete message</source>
<translation>Удалить сообщение</translation>
</message>
<message>
<location filename="mainscreen.py" line="111"/>
<location filename="mainscreen.py" line="116"/>
<source>Lock</source>
<translation>Заблокировать</translation>
</message>
<message>
<location filename="mainscreen.py" line="482"/>
<location filename="mainscreen.py" line="501"/>
<source>Cannot lock app</source>
<translation>Невозможно заблокировать приложение</translation>
</message>
<message>
<location filename="mainscreen.py" line="485"/>
<location filename="mainscreen.py" line="504"/>
<source>Error. Profile password is not set.</source>
<translation>Ошибка. Пароль профиля не установлен.</translation>
</message>
<message>
<location filename="mainscreen.py" line="585"/>
<location filename="mainscreen.py" line="604"/>
<source>Name</source>
<translation>Имя</translation>
</message>
<message>
<location filename="mainscreen.py" line="586"/>
<location filename="mainscreen.py" line="605"/>
<source>Status message</source>
<translation>Статус</translation>
</message>
<message>
<location filename="mainscreen.py" line="587"/>
<location filename="mainscreen.py" line="606"/>
<source>Public key</source>
<translation>Публичный ключ</translation>
</message>
@ -375,65 +375,85 @@ Version: </source>
<translation>Выберите папку с паком смайлов</translation>
</message>
<message>
<location filename="mainscreen.py" line="140"/>
<location filename="mainscreen.py" line="145"/>
<source>Import plugin</source>
<translation>Импортировать плагин</translation>
</message>
<message>
<location filename="mainscreen.py" line="460"/>
<location filename="mainscreen.py" line="479"/>
<source>Choose folder with plugin</source>
<translation>Выберите папку с плагином</translation>
</message>
<message>
<location filename="mainscreen.py" line="469"/>
<location filename="mainscreen.py" line="488"/>
<source>Restart Toxygen</source>
<translation>Перезапустите Toxygen</translation>
</message>
<message>
<location filename="mainscreen.py" line="472"/>
<location filename="mainscreen.py" line="491"/>
<source>Plugin will be loaded after restart</source>
<translation>Плагин будет загружен после перезапуска</translation>
</message>
<message>
<location filename="list_items.py" line="48"/>
<location filename="list_items.py" line="49"/>
<source>Quote selected text</source>
<translation>Цитировать выбранный текст</translation>
</message>
<message>
<location filename="mainscreen.py" line="579"/>
<location filename="mainscreen.py" line="598"/>
<source>Chat history</source>
<translation>История чата</translation>
</message>
<message>
<location filename="mainscreen.py" line="581"/>
<location filename="mainscreen.py" line="600"/>
<source>Export as text</source>
<translation>Экспортировать как текст</translation>
</message>
<message>
<location filename="mainscreen.py" line="582"/>
<location filename="mainscreen.py" line="601"/>
<source>Export as HTML</source>
<translation>Экспортировать как HTML</translation>
</message>
<message>
<location filename="mainscreen.py" line="126"/>
<location filename="mainscreen.py" line="131"/>
<source>Updates</source>
<translation>Обновления</translation>
</message>
<message>
<location filename="mainscreen.py" line="133"/>
<location filename="mainscreen.py" line="138"/>
<source>Online first</source>
<translation>Сначала онлайн</translation>
</message>
<message>
<location filename="mainscreen.py" line="135"/>
<location filename="mainscreen.py" line="140"/>
<source>Online and by name</source>
<translation>Онлайн и по имени</translation>
</message>
<message>
<location filename="mainscreen.py" line="136"/>
<location filename="mainscreen.py" line="141"/>
<source>Online first and by name</source>
<translation>Сначала онлайн и по имени</translation>
</message>
<message>
<location filename="mainscreen.py" line="610"/>
<source>Block friend</source>
<translation>Заблокировать друга</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="528"/>
<source>Not found</source>
<translation>Не найдено</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="524"/>
<source>Text &quot;{}&quot; was not found</source>
<translation>Текст &quot;{}&quot; не был найден</translation>
</message>
<message>
<location filename="mainscreen.py" line="146"/>
<source>Reload plugins</source>
<translation>Перезагрузить плагины</translation>
</message>
</context>
<context>
<name>MenuWindow</name>
@ -796,12 +816,12 @@ Version: </source>
<translation>Используйте Настройки -&gt; Интерфейс для настройки интерфейса.</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="374"/>
<location filename="mainscreen_widgets.py" line="370"/>
<source>Toxygen supports faux offline messages and file transfers. Send message or file to offline friend and he will get it later.</source>
<translation>Toxygen поддерживает псевдооффлайн сообщения и файл трансферы.</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="386"/>
<location filename="mainscreen_widgets.py" line="382"/>
<source>Set new NoSpam to avoid spam friend requests: Profile -&gt; Settings -&gt; Set new NoSpam.</source>
<translation>Установите новый NoSpam, чтобы избежать спам запросов в друзья: Профиль-&gt;Настройки-&gt;Новый NoSpam.</translation>
</message>
@ -811,12 +831,12 @@ Version: </source>
<translation type="obsolete">Новое в Toxygen 0.2.3:&lt;br&gt;Соответствие TCS&lt;br&gt;Импорт плагинов, смайлов и стикеров&lt;br&gt;Исправления ошибок</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="378"/>
<location filename="mainscreen_widgets.py" line="374"/>
<source>Delete single message in chat: make right click on spinner or message time and choose &quot;Delete&quot; in menu</source>
<translation>Чтобы удалить отдельное сообщение в чате сделайте правый клик на спиннер или время сообщения и выберите &quot;Удалить&quot; в меню</translation>
</message>
<message>
<location filename="mainscreen_widgets.py" line="382"/>
<location filename="mainscreen_widgets.py" line="378"/>
<source>Use right click on inline image to save it</source>
<translation>Правый клик на инлайн изображении позволит сохранить его</translation>
</message>
@ -828,7 +848,7 @@ Version: </source>
<message>
<location filename="mainscreen_widgets.py" line="370"/>
<source>New in Toxygen v0.2.6:&lt;br&gt;Updater&lt;br&gt;Better contact sorting&lt;br&gt;Plugins improvements</source>
<translation>Новое в Toxygen v0.2.6:&lt;br&gt;Поддержка обновлений&lt;br&gt;Улучшенная сортировка контактов&lt;br&gt;Улучшения в работе плагинов</translation>
<translation type="obsolete">Новое в Toxygen v0.2.6:&lt;br&gt;Поддержка обновлений&lt;br&gt;Улучшенная сортировка контактов&lt;br&gt;Улучшения в работе плагинов</translation>
</message>
</context>
<context>
@ -852,32 +872,32 @@ Version: </source>
<context>
<name>incoming_call</name>
<message>
<location filename="profile.py" line="1214"/>
<location filename="profile.py" line="1229"/>
<source>Incoming video call</source>
<translation>Входящий видеозвонок</translation>
</message>
<message>
<location filename="profile.py" line="1217"/>
<location filename="profile.py" line="1232"/>
<source>Incoming audio call</source>
<translation>Входящий аудиозвонок</translation>
</message>
<message>
<location filename="profile.py" line="1195"/>
<location filename="profile.py" line="1210"/>
<source>Outgoing video call</source>
<translation>Исходящий видеозвонок</translation>
</message>
<message>
<location filename="profile.py" line="1198"/>
<location filename="profile.py" line="1213"/>
<source>Outgoing audio call</source>
<translation>Исходящий аудиозвонок</translation>
</message>
<message>
<location filename="profile.py" line="1248"/>
<location filename="profile.py" line="1263"/>
<source>Call declined</source>
<translation>Звонок отменен</translation>
</message>
<message>
<location filename="profile.py" line="1250"/>
<location filename="profile.py" line="1265"/>
<source>Call finished</source>
<translation>Звонок завершен</translation>
</message>
@ -1038,7 +1058,7 @@ Version: </source>
<translation>Ошибка сохранения профиля! Toxygen имеет разрешение на запись в данную папку?</translation>
</message>
<message>
<location filename="main.py" line="288"/>
<location filename="main.py" line="290"/>
<source>Update for Toxygen was found. Download and install it?</source>
<translation>Обновление для Toxygen было найдено. Загрузить и установить его?</translation>
</message>
@ -1077,7 +1097,7 @@ Version: </source>
<message>
<location filename="passwordscreen.py" line="65"/>
<source>Password:</source>
<translation>Пароль</translation>
<translation>Пароль:</translation>
</message>
<message>
<location filename="passwordscreen.py" line="66"/>
@ -1176,32 +1196,32 @@ Version: </source>
<context>
<name>tray</name>
<message>
<location filename="main.py" line="228"/>
<location filename="main.py" line="229"/>
<source>Open Toxygen</source>
<translation>Открыть Toxygen</translation>
</message>
<message>
<location filename="main.py" line="237"/>
<location filename="main.py" line="238"/>
<source>Exit</source>
<translation>Выход</translation>
</message>
<message>
<location filename="main.py" line="229"/>
<location filename="main.py" line="230"/>
<source>Set status</source>
<translation>Изменить статус</translation>
</message>
<message>
<location filename="main.py" line="230"/>
<location filename="main.py" line="231"/>
<source>Online</source>
<translation>Онлайн</translation>
</message>
<message>
<location filename="main.py" line="231"/>
<location filename="main.py" line="232"/>
<source>Away</source>
<translation>Нет на месте</translation>
</message>
<message>
<location filename="main.py" line="232"/>
<location filename="main.py" line="233"/>
<source>Busy</source>
<translation>Занят</translation>
</message>

View File

@ -1,10 +1,25 @@
# -*- coding: utf-8 -*-
import os
import time
import shutil
import sys
import re
program_version = '0.2.7'
program_version = '0.2.8'
def cached(func):
saved_result = None
def wrapped_func():
nonlocal saved_result
if saved_result is None:
saved_result = func()
return saved_result
return wrapped_func
def log(data):
@ -12,6 +27,7 @@ def log(data):
fl.write(str(data) + '\n')
@cached
def curr_directory():
return os.path.dirname(os.path.realpath(__file__))
@ -46,9 +62,8 @@ def convert_time(t):
return '%02d:%02d' % (h, m)
@cached
def time_offset():
if hasattr(time_offset, 'offset'):
return time_offset.offset
hours = int(time.strftime('%H'))
minutes = int(time.strftime('%M'))
sec = int(time.time()) - time.timezone
@ -56,7 +71,6 @@ def time_offset():
h, m = divmod(m, 60)
d, h = divmod(h, 24)
result = hours * 60 + minutes - h * 60 - m
time_offset.offset = result
return result

View File

@ -137,4 +137,3 @@ class MultilineEdit(CenteredWidget):
def button_click(self):
self.save(self.edit.toPlainText())
self.close()