Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02507ab8b4 | ||
|
|
c27c35fcc0 | ||
|
|
a214ab12c7 | ||
|
|
b2fa484bb3 | ||
|
|
be4a592599 | ||
|
|
cfdf145160 | ||
|
|
948776c31c | ||
|
|
b2210c21cf | ||
|
|
3a835ceb7e | ||
|
|
21d5eed719 | ||
|
|
0fa7ca7b5c | ||
|
|
010c15a498 | ||
|
|
284311a91c | ||
|
|
a78f18cce5 | ||
|
|
d0b767c779 | ||
|
|
f0875b0415 | ||
|
|
c5ec90a8a4 | ||
|
|
1734ea02f9 | ||
|
|
b0e1aeb0f0 | ||
|
|
f445a79e11 | ||
|
|
29617c2168 | ||
|
|
e441b8ffd2 | ||
|
|
b588e19789 | ||
|
|
168d259ebd | ||
|
|
0db5195689 | ||
|
|
91b558775a | ||
|
|
4d1addfa63 | ||
|
|
0239b4f8ef | ||
|
|
60df9f7b1d | ||
|
|
2ed4e00d3e | ||
|
|
e6a0988c6d | ||
|
|
57b4e2af67 | ||
|
|
6192688e0c | ||
|
|
177e180e37 | ||
|
|
8d3db9f897 | ||
|
|
2903ed1877 | ||
|
|
a1fbd87719 | ||
|
|
be6acff18d | ||
|
|
81660a9ad4 | ||
|
|
0e103749a2 | ||
|
|
160e9dc72f | ||
|
|
c503cf4a72 | ||
|
|
575d626e03 | ||
|
|
c6d832ae92 | ||
|
|
78c376a0b0 | ||
|
|
8df2e3ebce | ||
|
|
845af6d37e | ||
|
|
5e1dfe6d36 | ||
|
|
2b8da1deb4 | ||
|
|
89be47dc72 | ||
|
|
f5782c2dcb | ||
|
|
7a5b1fc05e | ||
|
|
61b124dad4 | ||
|
|
ac1800703f | ||
|
|
89c4b2cdf6 | ||
|
|
ce40acc4a0 | ||
|
|
7a02ae3ef4 |
26
README.md
@@ -1,10 +1,13 @@
|
|||||||
# Toxygen
|
# Toxygen
|
||||||
Toxygen is simple [Tox](https://tox.chat/) client written on pure Python
|
Toxygen is cross-platform [Tox](https://tox.chat/) client written on Python
|
||||||
### [How to install](/docs/install.md)
|
|
||||||
|
|
||||||
### [Contributing](/docs/contributing.md)
|
[](https://github.com/xveduk/toxygen/releases/latest)
|
||||||
|
[](https://github.com/xveduk/toxygen/issues)
|
||||||
|
[](https://raw.githubusercontent.com/xveduk/toxygen/master/LICENSE.md)
|
||||||
|
|
||||||
# Supported OS:
|
### [Install](/docs/install.md) - [Contribute](/docs/contributing.md) - [Plugins](/docs/plugins.md)
|
||||||
|
|
||||||
|
### Supported OS:
|
||||||
- Windows
|
- Windows
|
||||||
- Linux
|
- Linux
|
||||||
|
|
||||||
@@ -13,8 +16,9 @@ Toxygen is simple [Tox](https://tox.chat/) client written on pure Python
|
|||||||
- [x] File transfers
|
- [x] File transfers
|
||||||
- [x] Audio
|
- [x] Audio
|
||||||
- [x] Chat history
|
- [x] Chat history
|
||||||
- [x] Name lookups (TOX DNS 4 support)
|
- [x] Screenshots
|
||||||
- [x] Profile import/export
|
- [x] Name lookups (toxme.io support)
|
||||||
|
- [x] Profile import and export
|
||||||
- [x] Inline images
|
- [x] Inline images
|
||||||
- [x] Message splitting
|
- [x] Message splitting
|
||||||
- [x] Proxy support
|
- [x] Proxy support
|
||||||
@@ -25,11 +29,11 @@ Toxygen is simple [Tox](https://tox.chat/) client written on pure Python
|
|||||||
- [x] Contact aliases
|
- [x] Contact aliases
|
||||||
- [x] Contact blocking
|
- [x] Contact blocking
|
||||||
- [x] Typing notifications
|
- [x] Typing notifications
|
||||||
|
- [x] Changing nospam
|
||||||
|
- [x] File resuming
|
||||||
|
- [x] Save file encryption
|
||||||
|
- [x] Plugins support
|
||||||
- [ ] Video
|
- [ ] Video
|
||||||
- [ ] Save file encryption
|
|
||||||
- [ ] File resuming
|
|
||||||
- [ ] Changing nospam
|
|
||||||
- [ ] Plugins support
|
|
||||||
- [ ] Group chats
|
- [ ] Group chats
|
||||||
- [ ] Read receipts
|
- [ ] Read receipts
|
||||||
- [ ] Faux offline messaging
|
- [ ] Faux offline messaging
|
||||||
@@ -43,7 +47,7 @@ Toxygen is simple [Tox](https://tox.chat/) client written on pure Python
|
|||||||
[Releases](https://github.com/xveduk/toxygen/releases)
|
[Releases](https://github.com/xveduk/toxygen/releases)
|
||||||
|
|
||||||
###Screenshots
|
###Screenshots
|
||||||
*Toxygen running on Ubuntu and Windows*
|
*Toxygen on Ubuntu and Windows*
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
#Compile Toxygen
|
||||||
|
|
||||||
You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/)
|
You can compile Toxygen using [PyInstaller](http://www.pyinstaller.org/)
|
||||||
|
|
||||||
Install PyInstaller:
|
Install PyInstaller:
|
||||||
``pip install pyinstaller``
|
``pip install pyinstaller``
|
||||||
|
|
||||||
|
|||||||
53
docs/plugin_api.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#Plugins API
|
||||||
|
|
||||||
|
In Toxygen plugin is single python 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.
|
||||||
|
|
||||||
|
Every plugin has it's own full name and unique short name (1-5 symbols). Main app can get it using special methods.
|
||||||
|
|
||||||
|
All plugin's data should be stored in following structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
/plugins/
|
||||||
|
|---plugin_short_name.py
|
||||||
|
|---/plugin_short_name/
|
||||||
|
|---settings.json
|
||||||
|
|---logs.txt
|
||||||
|
|---other_files
|
||||||
|
```
|
||||||
|
|
||||||
|
Plugin can override following methods:
|
||||||
|
- get_description - this method should return plugin description.
|
||||||
|
- get_menu - plugins allowed to add items in friend menu. You can open this menu making right click on friend in friends list. This method should return list of QAction's. Plugin must connect to QAction's triggered() signal.
|
||||||
|
- get_window - plugins can have GUI, this method should return window instance or None for plugins without GUI.
|
||||||
|
- start - plugin was started.
|
||||||
|
- stop - plugin was stopped.
|
||||||
|
- close - app is closing, stop plugin.
|
||||||
|
- command - new command to plugin. Command can be entered in message field in format '/plugin <plugin_short_name> <command>'. Command 'help' should show user list of supported commands.
|
||||||
|
- lossless_packet - callback - incoming lossless packet from friend.
|
||||||
|
- lossy_packet - callback - incoming lossy packet from friend.
|
||||||
|
- friend_connected - callback - friend became online.
|
||||||
|
|
||||||
|
Other methods:
|
||||||
|
- send_lossless - this method send custom lossless packet. Plugins MUST send lossless packets using this method.
|
||||||
|
- send_lossy - this method send custom lossy packet. Plugins MUST send lossy packets using this method.
|
||||||
|
- load_settings - loads settings stored in default location.
|
||||||
|
- save_settings - saves settings to default location.
|
||||||
|
- load_translator - loads translations. Translations must be stored in directory with plugin's data. Files with translations must have the same name as in main app.
|
||||||
|
|
||||||
|
About import:
|
||||||
|
|
||||||
|
import statement will not work in case you import module that wasn't previously imported by main program and user use precompiled binary. It's recommended to use imp module and dynamic import instead.
|
||||||
|
|
||||||
|
About GUI:
|
||||||
|
|
||||||
|
It's strictly recommended to support both PySide and PyQt4 in GUI.
|
||||||
|
|
||||||
|
Exceptions:
|
||||||
|
|
||||||
|
Plugin's methods should not raise exceptions.
|
||||||
|
|
||||||
|
#Examples
|
||||||
|
|
||||||
|
You can find example of a plugin in [official repo](https://github.com/ingvar1995/toxygen_plugins)
|
||||||
|
|
||||||
20
docs/plugins.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#Plugins
|
||||||
|
|
||||||
|
Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python module and directory with plugin's data which provide some additional functionality.
|
||||||
|
|
||||||
|
#How to write plugin
|
||||||
|
|
||||||
|
Check [Plugin API](/docs/plugin_api.md) for more info
|
||||||
|
|
||||||
|
#How to install plugin
|
||||||
|
|
||||||
|
Toxygen comes without preinstalled plugins.
|
||||||
|
|
||||||
|
1. Put plugin and directory with it's data into /src/plugins/
|
||||||
|
2. Restart Toxygen
|
||||||
|
|
||||||
|
#Plugins list
|
||||||
|
|
||||||
|
WARNING: It is unsecure to install plugin not from this list!
|
||||||
|
|
||||||
|
[Main repo](https://github.com/ingvar1995/toxygen_plugins)
|
||||||
BIN
docs/ubuntu.png
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 94 KiB |
BIN
docs/windows.png
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 317 KiB |
@@ -1,4 +1,7 @@
|
|||||||
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
import widgets
|
import widgets
|
||||||
import profile
|
import profile
|
||||||
import util
|
import util
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
|
try:
|
||||||
from PySide import QtCore
|
from PySide import QtCore
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore
|
||||||
from notifications import *
|
from notifications import *
|
||||||
from settings import Settings
|
from settings import Settings
|
||||||
from profile import Profile
|
from profile import Profile
|
||||||
from toxcore_enums_and_consts import *
|
from toxcore_enums_and_consts import *
|
||||||
from toxav_enums import *
|
from toxav_enums import *
|
||||||
from tox import bin_to_string
|
from tox import bin_to_string
|
||||||
from ctypes import c_char_p, cast, pointer
|
from plugin_support import PluginLoader
|
||||||
|
|
||||||
|
|
||||||
class InvokeEvent(QtCore.QEvent):
|
class InvokeEvent(QtCore.QEvent):
|
||||||
@@ -76,12 +79,13 @@ def friend_connection_status(tox, friend_num, new_status, user_data):
|
|||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
friend = profile.get_friend_by_number(friend_num)
|
friend = profile.get_friend_by_number(friend_num)
|
||||||
if new_status == TOX_CONNECTION['NONE']:
|
if new_status == TOX_CONNECTION['NONE']:
|
||||||
invoke_in_main_thread(friend.set_status, None)
|
invoke_in_main_thread(profile.friend_exit, friend_num)
|
||||||
invoke_in_main_thread(profile.update_filtration)
|
invoke_in_main_thread(profile.update_filtration)
|
||||||
if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
if Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
||||||
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
||||||
elif friend.status is None:
|
elif friend.status is None:
|
||||||
invoke_in_main_thread(profile.send_avatar, friend_num)
|
invoke_in_main_thread(profile.send_avatar, friend_num)
|
||||||
|
PluginLoader.get_instance().friend_online(friend_num)
|
||||||
|
|
||||||
|
|
||||||
def friend_name(tox, friend_num, name, size, user_data):
|
def friend_name(tox, friend_num, name, size, user_data):
|
||||||
@@ -183,11 +187,14 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us
|
|||||||
"""
|
"""
|
||||||
Incoming chunk
|
Incoming chunk
|
||||||
"""
|
"""
|
||||||
|
if not length:
|
||||||
invoke_in_main_thread(Profile.get_instance().incoming_chunk,
|
invoke_in_main_thread(Profile.get_instance().incoming_chunk,
|
||||||
friend_number,
|
friend_number,
|
||||||
file_number,
|
file_number,
|
||||||
position,
|
position,
|
||||||
chunk[:length] if length else None)
|
None)
|
||||||
|
else:
|
||||||
|
Profile.get_instance().incoming_chunk(friend_number, file_number, position,chunk[:length])
|
||||||
|
|
||||||
|
|
||||||
def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
|
def file_chunk_request(tox, friend_number, file_number, position, size, user_data):
|
||||||
@@ -210,6 +217,30 @@ def file_recv_control(tox, friend_number, file_number, file_control, user_data):
|
|||||||
"""
|
"""
|
||||||
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
||||||
Profile.get_instance().cancel_transfer(friend_number, file_number, True)
|
Profile.get_instance().cancel_transfer(friend_number, file_number, True)
|
||||||
|
elif file_control == TOX_FILE_CONTROL['PAUSE']:
|
||||||
|
Profile.get_instance().pause_transfer(friend_number, file_number, True)
|
||||||
|
elif file_control == TOX_FILE_CONTROL['RESUME']:
|
||||||
|
Profile.get_instance().resume_transfer(friend_number, file_number, True)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks - custom packets
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def lossless_packet(tox, friend_number, data, length, user_data):
|
||||||
|
"""
|
||||||
|
Incoming lossless packet
|
||||||
|
"""
|
||||||
|
plugin = PluginLoader.get_instance()
|
||||||
|
invoke_in_main_thread(plugin.callback_lossless, friend_number, data, length)
|
||||||
|
|
||||||
|
|
||||||
|
def lossy_packet(tox, friend_number, data, length, user_data):
|
||||||
|
"""
|
||||||
|
Incoming lossy packet
|
||||||
|
"""
|
||||||
|
plugin = PluginLoader.get_instance()
|
||||||
|
invoke_in_main_thread(plugin.callback_lossy, friend_number, data, length)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -217,7 +248,9 @@ def file_recv_control(tox, friend_number, file_number, file_control, user_data):
|
|||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def call_state(toxav, friend_number, mask, user_data):
|
def call_state(toxav, friend_number, mask, user_data):
|
||||||
"""New call state"""
|
"""
|
||||||
|
New call state
|
||||||
|
"""
|
||||||
print friend_number, mask
|
print friend_number, mask
|
||||||
if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']:
|
if mask == TOXAV_FRIEND_CALL_STATE['FINISHED'] or mask == TOXAV_FRIEND_CALL_STATE['ERROR']:
|
||||||
invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True)
|
invoke_in_main_thread(Profile.get_instance().stop_call, friend_number, True)
|
||||||
@@ -226,13 +259,17 @@ def call_state(toxav, friend_number, mask, user_data):
|
|||||||
|
|
||||||
|
|
||||||
def call(toxav, friend_number, audio, video, user_data):
|
def call(toxav, friend_number, audio, video, user_data):
|
||||||
"""Incoming call from friend"""
|
"""
|
||||||
|
Incoming call from friend
|
||||||
|
"""
|
||||||
print friend_number, audio, video
|
print friend_number, audio, video
|
||||||
invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number)
|
invoke_in_main_thread(Profile.get_instance().incoming_call, audio, video, friend_number)
|
||||||
|
|
||||||
|
|
||||||
def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data):
|
def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, audio_channels_count, rate, user_data):
|
||||||
"""New audio chunk"""
|
"""
|
||||||
|
New audio chunk
|
||||||
|
"""
|
||||||
print audio_samples_per_channel, audio_channels_count, rate
|
print audio_samples_per_channel, audio_channels_count, rate
|
||||||
Profile.get_instance().call.chunk(
|
Profile.get_instance().call.chunk(
|
||||||
''.join(chr(x) for x in samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
''.join(chr(x) for x in samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
||||||
@@ -272,3 +309,6 @@ def init_callbacks(tox, window, tray):
|
|||||||
toxav.callback_call(call, 0)
|
toxav.callback_call(call, 0)
|
||||||
toxav.callback_audio_receive_frame(callback_audio, 0)
|
toxav.callback_audio_receive_frame(callback_audio, 0)
|
||||||
|
|
||||||
|
tox.callback_friend_lossless_packet(lossless_packet, 0)
|
||||||
|
tox.callback_friend_lossy_packet(lossy_packet, 0)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ from os import remove, rename
|
|||||||
from time import time, sleep
|
from time import time, sleep
|
||||||
from tox import Tox
|
from tox import Tox
|
||||||
import settings
|
import settings
|
||||||
|
try:
|
||||||
from PySide import QtCore
|
from PySide import QtCore
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
|
|
||||||
TOX_FILE_TRANSFER_STATE = {
|
TOX_FILE_TRANSFER_STATE = {
|
||||||
@@ -12,11 +15,15 @@ TOX_FILE_TRANSFER_STATE = {
|
|||||||
'PAUSED': 1,
|
'PAUSED': 1,
|
||||||
'CANCELED': 2,
|
'CANCELED': 2,
|
||||||
'FINISHED': 3,
|
'FINISHED': 3,
|
||||||
|
'PAUSED_BY_FRIEND': 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class StateSignal(QtCore.QObject):
|
class StateSignal(QtCore.QObject):
|
||||||
signal = QtCore.Signal(int, float)
|
try:
|
||||||
|
signal = QtCore.Signal(int, float) # state and progress
|
||||||
|
except:
|
||||||
|
signal = QtCore.pyqtSignal(int, float) # state and progress - pyqt4
|
||||||
|
|
||||||
|
|
||||||
class FileTransfer(QtCore.QObject):
|
class FileTransfer(QtCore.QObject):
|
||||||
@@ -42,6 +49,9 @@ class FileTransfer(QtCore.QObject):
|
|||||||
def set_state_changed_handler(self, handler):
|
def set_state_changed_handler(self, handler):
|
||||||
self._state_changed.signal.connect(handler)
|
self._state_changed.signal.connect(handler)
|
||||||
|
|
||||||
|
def signal(self):
|
||||||
|
self._state_changed.signal.emit(self.state, self._done / self._size if self._size else 0)
|
||||||
|
|
||||||
def get_file_number(self):
|
def get_file_number(self):
|
||||||
return self._file_number
|
return self._file_number
|
||||||
|
|
||||||
@@ -58,12 +68,20 @@ class FileTransfer(QtCore.QObject):
|
|||||||
if hasattr(self, '_file'):
|
if hasattr(self, '_file'):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self._state_changed.signal.emit(TOX_FILE_CONTROL['CANCEL'], 1)
|
self.state = TOX_FILE_TRANSFER_STATE['CANCELED']
|
||||||
|
self._state_changed.signal.emit(self.state, 1)
|
||||||
|
|
||||||
|
def pause(self, by_friend):
|
||||||
|
if not by_friend:
|
||||||
|
self.send_control(TOX_FILE_CONTROL['PAUSE'])
|
||||||
|
else:
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']
|
||||||
|
self.signal()
|
||||||
|
|
||||||
def send_control(self, control):
|
def send_control(self, control):
|
||||||
if self._tox.file_control(self._friend_number, self._file_number, control):
|
if self._tox.file_control(self._friend_number, self._file_number, control):
|
||||||
self.state = control
|
self.state = control
|
||||||
self._state_changed.signal.emit(self.state, self._done / self._size if self._size else 0)
|
self.signal()
|
||||||
|
|
||||||
def get_file_id(self):
|
def get_file_id(self):
|
||||||
return self._tox.file_get_file_id(self._friend_number, self._file_number)
|
return self._tox.file_get_file_id(self._friend_number, self._file_number)
|
||||||
@@ -96,8 +114,9 @@ class SendTransfer(FileTransfer):
|
|||||||
data = self._file.read(size)
|
data = self._file.read(size)
|
||||||
self._tox.file_send_chunk(self._friend_number, self._file_number, position, data)
|
self._tox.file_send_chunk(self._friend_number, self._file_number, position, data)
|
||||||
self._done += size
|
self._done += size
|
||||||
self._state_changed.signal.emit(self.state, self._done / self._size)
|
self.signal()
|
||||||
else:
|
else:
|
||||||
|
if hasattr(self, '_file'):
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self._state_changed.signal.emit(self.state, 1)
|
||||||
@@ -208,7 +227,7 @@ class ReceiveToBuffer(FileTransfer):
|
|||||||
if position + l > self._data_size:
|
if position + l > self._data_size:
|
||||||
self._data_size = position + l
|
self._data_size = position + l
|
||||||
self._done += l
|
self._done += l
|
||||||
self._state_changed.signal.emit(self.state, self._done / self._size)
|
self.signal()
|
||||||
|
|
||||||
|
|
||||||
class ReceiveAvatar(ReceiveTransfer):
|
class ReceiveAvatar(ReceiveTransfer):
|
||||||
@@ -223,21 +242,19 @@ class ReceiveAvatar(ReceiveTransfer):
|
|||||||
if size > self.MAX_AVATAR_SIZE:
|
if size > self.MAX_AVATAR_SIZE:
|
||||||
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
remove(path + '.tmp')
|
remove(path + '.tmp')
|
||||||
elif exists(path):
|
elif not size:
|
||||||
if not size:
|
|
||||||
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['CANCELED']
|
|
||||||
self._file.close()
|
self._file.close()
|
||||||
|
if exists(path):
|
||||||
remove(path)
|
remove(path)
|
||||||
remove(path + '.tmp')
|
remove(path + '.tmp')
|
||||||
else:
|
elif exists(path):
|
||||||
hash = self.get_file_id()
|
hash = self.get_file_id()
|
||||||
with open(path) as fl:
|
with open(path) as fl:
|
||||||
data = fl.read()
|
data = fl.read()
|
||||||
existing_hash = Tox.hash(data)
|
existing_hash = Tox.hash(data)
|
||||||
if hash == existing_hash:
|
if hash == existing_hash:
|
||||||
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['CANCELED']
|
|
||||||
remove(path + '.tmp')
|
remove(path + '.tmp')
|
||||||
else:
|
else:
|
||||||
self.send_control(TOX_FILE_CONTROL['RESUME'])
|
self.send_control(TOX_FILE_CONTROL['RESUME'])
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
from sqlite3 import connect
|
from sqlite3 import connect
|
||||||
import settings
|
import settings
|
||||||
from os import chdir
|
from os import chdir
|
||||||
|
import os.path
|
||||||
|
from toxencryptsave import LibToxEncryptSave
|
||||||
|
|
||||||
|
|
||||||
PAGE_SIZE = 42
|
PAGE_SIZE = 42
|
||||||
@@ -17,6 +19,15 @@ class History(object):
|
|||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
chdir(settings.ProfileHelper.get_path())
|
chdir(settings.ProfileHelper.get_path())
|
||||||
|
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
||||||
|
if os.path.exists(path):
|
||||||
|
decr = LibToxEncryptSave.get_instance()
|
||||||
|
with open(path, 'rb') as fin:
|
||||||
|
data = fin.read()
|
||||||
|
if decr.is_data_encrypted(data):
|
||||||
|
data = decr.pass_decrypt(data)
|
||||||
|
with open(path, 'wb') as fout:
|
||||||
|
fout.write(data)
|
||||||
db = connect(name + '.hstr')
|
db = connect(name + '.hstr')
|
||||||
cursor = db.cursor()
|
cursor = db.cursor()
|
||||||
cursor.execute('CREATE TABLE IF NOT EXISTS friends('
|
cursor.execute('CREATE TABLE IF NOT EXISTS friends('
|
||||||
@@ -24,6 +35,16 @@ class History(object):
|
|||||||
')')
|
')')
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
encr = LibToxEncryptSave.get_instance()
|
||||||
|
if encr.has_password():
|
||||||
|
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
||||||
|
with open(path, 'rb') as fin:
|
||||||
|
data = fin.read()
|
||||||
|
data = encr.pass_encrypt(data)
|
||||||
|
with open(path, 'wb') as fout:
|
||||||
|
fout.write(data)
|
||||||
|
|
||||||
def export(self, directory):
|
def export(self, directory):
|
||||||
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
||||||
new_path = directory + self._name + '.hstr'
|
new_path = directory + self._name + '.hstr'
|
||||||
@@ -31,7 +52,6 @@ class History(object):
|
|||||||
data = fin.read()
|
data = fin.read()
|
||||||
with open(new_path, 'wb') as fout:
|
with open(new_path, 'wb') as fout:
|
||||||
fout.write(data)
|
fout.write(data)
|
||||||
print 'History exported to: {}'.format(new_path)
|
|
||||||
|
|
||||||
def add_friend_to_db(self, tox_id):
|
def add_friend_to_db(self, tox_id):
|
||||||
chdir(settings.ProfileHelper.get_path())
|
chdir(settings.ProfileHelper.get_path())
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 3.5 KiB |
BIN
src/images/pause.png
Executable file
|
After Width: | Height: | Size: 306 B |
BIN
src/images/resume.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
src/images/search.png
Executable file
|
After Width: | Height: | Size: 3.6 KiB |
@@ -6,7 +6,7 @@ class LibToxCore(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if system() == 'Linux':
|
if system() == 'Linux':
|
||||||
# be sure that libtoxcore and libsodium are installed in your os
|
# libtoxcore and libsodium must be installed in your os
|
||||||
self._libtoxcore = CDLL('libtoxcore.so')
|
self._libtoxcore = CDLL('libtoxcore.so')
|
||||||
elif system() == 'Windows':
|
elif system() == 'Windows':
|
||||||
self._libtoxcore = CDLL('libs/libtox.dll')
|
self._libtoxcore = CDLL('libs/libtox.dll')
|
||||||
@@ -21,7 +21,7 @@ class LibToxAV(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
if system() == 'Linux':
|
if system() == 'Linux':
|
||||||
# be sure that /usr/lib/libtoxav.so exists
|
# that /usr/lib/libtoxav.so must exists
|
||||||
self._libtoxav = CDLL('libtoxav.so')
|
self._libtoxav = CDLL('libtoxav.so')
|
||||||
elif system() == 'Windows':
|
elif system() == 'Windows':
|
||||||
# on Windows av api is in libtox.dll
|
# on Windows av api is in libtox.dll
|
||||||
@@ -31,3 +31,19 @@ class LibToxAV(object):
|
|||||||
|
|
||||||
def __getattr__(self, item):
|
def __getattr__(self, item):
|
||||||
return self._libtoxav.__getattr__(item)
|
return self._libtoxav.__getattr__(item)
|
||||||
|
|
||||||
|
|
||||||
|
class LibToxEncryptSave(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if system() == 'Linux':
|
||||||
|
# /usr/lib/libtoxencryptsave.so must exists
|
||||||
|
self._lib_tox_encrypt_save = CDLL('libtoxencryptsave.so')
|
||||||
|
elif system() == 'Windows':
|
||||||
|
# on Windows profile encryption api is in libtox.dll
|
||||||
|
self._lib_tox_encrypt_save = CDLL('libs/libtox.dll')
|
||||||
|
else:
|
||||||
|
raise OSError('Unknown system.')
|
||||||
|
|
||||||
|
def __getattr__(self, item):
|
||||||
|
return self._lib_tox_encrypt_save.__getattr__(item)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from toxcore_enums_and_consts import *
|
from toxcore_enums_and_consts import *
|
||||||
from PySide import QtGui, QtCore
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
import profile
|
import profile
|
||||||
from file_transfers import TOX_FILE_TRANSFER_STATE
|
from file_transfers import TOX_FILE_TRANSFER_STATE
|
||||||
from util import curr_directory, convert_time
|
from util import curr_directory, convert_time
|
||||||
@@ -136,17 +139,21 @@ class StatusCircle(QtGui.QWidget):
|
|||||||
|
|
||||||
class FileTransferItem(QtGui.QListWidget):
|
class FileTransferItem(QtGui.QListWidget):
|
||||||
|
|
||||||
def __init__(self, file_name, size, time, user, friend_number, file_number, state, parent=None):
|
def __init__(self, file_name, size, time, user, friend_number, file_number, state, width, parent=None):
|
||||||
|
|
||||||
QtGui.QListWidget.__init__(self, parent)
|
QtGui.QListWidget.__init__(self, parent)
|
||||||
self.resize(QtCore.QSize(620, 50))
|
self.resize(QtCore.QSize(width, 34))
|
||||||
if state != FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
|
if state == FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
|
||||||
self.setStyleSheet('QWidget { background-color: green; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
||||||
|
elif state in (FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED'], FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND']):
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
||||||
else:
|
else:
|
||||||
self.setStyleSheet('QWidget { background-color: #B40404; }')
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
|
|
||||||
|
self.state = state
|
||||||
|
|
||||||
self.name = DataLabel(self)
|
self.name = DataLabel(self)
|
||||||
self.name.setGeometry(QtCore.QRect(1, 15, 95, 20))
|
self.name.setGeometry(QtCore.QRect(3, 7, 95, 20))
|
||||||
self.name.setTextFormat(QtCore.Qt.PlainText)
|
self.name.setTextFormat(QtCore.Qt.PlainText)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
@@ -154,45 +161,50 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.name.setFont(font)
|
self.name.setFont(font)
|
||||||
self.name.setText(user)
|
self.name.setText(user)
|
||||||
self.name.setStyleSheet('QLabel { color: black; }')
|
|
||||||
|
|
||||||
self.time = QtGui.QLabel(self)
|
self.time = QtGui.QLabel(self)
|
||||||
self.time.setGeometry(QtCore.QRect(570, 2, 50, 46))
|
self.time.setGeometry(QtCore.QRect(width - 53, 7, 50, 20))
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
self.time.setFont(font)
|
self.time.setFont(font)
|
||||||
self.time.setText(convert_time(time))
|
self.time.setText(convert_time(time))
|
||||||
self.time.setStyleSheet('QLabel { color: black; }')
|
|
||||||
|
|
||||||
self.cancel = QtGui.QPushButton(self)
|
self.cancel = QtGui.QPushButton(self)
|
||||||
self.cancel.setGeometry(QtCore.QRect(500, 2, 46, 46))
|
self.cancel.setGeometry(QtCore.QRect(width - 120, 2, 30, 30))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png')
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.cancel.setIcon(icon)
|
self.cancel.setIcon(icon)
|
||||||
self.cancel.setIconSize(QtCore.QSize(30, 30))
|
self.cancel.setIconSize(QtCore.QSize(30, 30))
|
||||||
self.cancel.setVisible(state > 1)
|
self.cancel.setVisible(state > 1)
|
||||||
self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number))
|
self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number))
|
||||||
self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; }')
|
self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}')
|
||||||
|
|
||||||
self.accept = QtGui.QPushButton(self)
|
self.accept_or_pause = QtGui.QPushButton(self)
|
||||||
self.accept.setGeometry(QtCore.QRect(450, 2, 46, 46))
|
self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png')
|
if state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED']:
|
||||||
icon = QtGui.QIcon(pixmap)
|
self.accept_or_pause.setVisible(True)
|
||||||
self.accept.setIcon(icon)
|
self.button_update('accept')
|
||||||
self.accept.setIconSize(QtCore.QSize(30, 30))
|
elif state in (0, 1, 5):
|
||||||
self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number, size))
|
self.accept_or_pause.setVisible(False)
|
||||||
self.accept.setVisible(state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED'])
|
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']: # setup for continue
|
||||||
self.accept.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; }')
|
self.accept_or_pause.setVisible(True)
|
||||||
|
self.button_update('resume')
|
||||||
|
else: # pause
|
||||||
|
self.accept_or_pause.setVisible(True)
|
||||||
|
self.button_update('pause')
|
||||||
|
self.accept_or_pause.clicked.connect(lambda: self.accept_or_pause_transfer(friend_number, file_number, size))
|
||||||
|
|
||||||
|
self.accept_or_pause.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none}')
|
||||||
|
|
||||||
self.pb = QtGui.QProgressBar(self)
|
self.pb = QtGui.QProgressBar(self)
|
||||||
self.pb.setGeometry(QtCore.QRect(100, 15, 100, 20))
|
self.pb.setGeometry(QtCore.QRect(100, 7, 100, 20))
|
||||||
self.pb.setValue(0)
|
self.pb.setValue(0)
|
||||||
self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }')
|
self.pb.setStyleSheet('QProgressBar { background-color: #302F2F; }')
|
||||||
if state < 2:
|
if state < 2:
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
|
|
||||||
self.file_name = DataLabel(self)
|
self.file_name = DataLabel(self)
|
||||||
self.file_name.setGeometry(QtCore.QRect(210, 2, 230, 46))
|
self.file_name.setGeometry(QtCore.QRect(210, 7, width - 400, 20))
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
self.file_name.setFont(font)
|
self.file_name.setFont(font)
|
||||||
file_size = size / 1024
|
file_size = size / 1024
|
||||||
@@ -204,56 +216,103 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
file_size = '{}KB'.format(file_size)
|
file_size = '{}KB'.format(file_size)
|
||||||
file_data = u'{} {}'.format(file_size, file_name)
|
file_data = u'{} {}'.format(file_size, file_name)
|
||||||
self.file_name.setText(file_data)
|
self.file_name.setText(file_data)
|
||||||
self.file_name.setStyleSheet('QLabel { color: black; }')
|
|
||||||
self.saved_name = file_name
|
self.saved_name = file_name
|
||||||
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
self.paused = False
|
||||||
|
|
||||||
def cancel_transfer(self, friend_number, file_number):
|
def cancel_transfer(self, friend_number, file_number):
|
||||||
pr = profile.Profile.get_instance()
|
pr = profile.Profile.get_instance()
|
||||||
pr.cancel_transfer(friend_number, file_number)
|
pr.cancel_transfer(friend_number, file_number)
|
||||||
self.setStyleSheet('QListWidget { background-color: #B40404; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
||||||
self.cancel.setVisible(False)
|
self.cancel.setVisible(False)
|
||||||
self.accept.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
|
|
||||||
def accept_transfer(self, friend_number, file_number, size):
|
def accept_or_pause_transfer(self, friend_number, file_number, size):
|
||||||
directory = QtGui.QFileDialog.getExistingDirectory()
|
if self.state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED']:
|
||||||
|
directory = QtGui.QFileDialog.getExistingDirectory(self,
|
||||||
|
QtGui.QApplication.translate("MainWindow", 'Choose folder', None, QtGui.QApplication.UnicodeUTF8),
|
||||||
|
curr_directory(),
|
||||||
|
QtGui.QFileDialog.ShowDirsOnly)
|
||||||
if directory:
|
if directory:
|
||||||
pr = profile.Profile.get_instance()
|
pr = profile.Profile.get_instance()
|
||||||
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
|
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
|
||||||
self.accept.setVisible(False)
|
self.button_update('pause')
|
||||||
|
elif self.state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']: # resume
|
||||||
|
self.paused = False
|
||||||
|
profile.Profile.get_instance().resume_transfer(friend_number, file_number)
|
||||||
|
self.button_update('pause')
|
||||||
|
self.state = FILE_TRANSFER_MESSAGE_STATUS['OUTGOING']
|
||||||
|
else: # pause
|
||||||
|
self.paused = True
|
||||||
|
self.state = FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']
|
||||||
|
profile.Profile.get_instance().pause_transfer(friend_number, file_number)
|
||||||
|
self.button_update('resume')
|
||||||
|
self.accept_or_pause.clearFocus()
|
||||||
|
|
||||||
|
def button_update(self, path):
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(path))
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.accept_or_pause.setIcon(icon)
|
||||||
|
self.accept_or_pause.setIconSize(QtCore.QSize(30, 30))
|
||||||
|
|
||||||
|
def convert(self, state):
|
||||||
|
# convert TOX_FILE_TRANSFER_STATE to FILE_TRANSFER_MESSAGE_STATUS
|
||||||
|
d = {0: 2, 1: 6, 2: 1, 3: 0, 4: 5}
|
||||||
|
return d[state]
|
||||||
|
|
||||||
@QtCore.Slot(int, float)
|
@QtCore.Slot(int, float)
|
||||||
def update(self, state, progress):
|
def update(self, state, progress):
|
||||||
self.pb.setValue(int(progress * 100))
|
self.pb.setValue(int(progress * 100))
|
||||||
if state == TOX_FILE_TRANSFER_STATE['CANCELED']:
|
state = self.convert(state)
|
||||||
self.setStyleSheet('QListWidget { background-color: #B40404; }')
|
if self.state != state:
|
||||||
|
if state == FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
||||||
self.cancel.setVisible(False)
|
self.cancel.setVisible(False)
|
||||||
self.accept.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
elif state == TOX_FILE_TRANSFER_STATE['FINISHED']:
|
self.state = state
|
||||||
|
elif state == FILE_TRANSFER_MESSAGE_STATUS['FINISHED']:
|
||||||
|
self.accept_or_pause.setVisible(False)
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
self.cancel.setVisible(False)
|
self.cancel.setVisible(False)
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
|
self.state = state
|
||||||
|
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND']:
|
||||||
|
self.accept_or_pause.setVisible(False)
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
||||||
|
self.state = state
|
||||||
|
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']:
|
||||||
|
self.button_update('resume') # setup button continue
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
|
self.state = state
|
||||||
|
elif not self.paused: # active
|
||||||
|
self.accept_or_pause.setVisible(True) # setup to pause
|
||||||
|
self.button_update('pause')
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
|
||||||
class InlineImageItem(QtGui.QWidget):
|
class InlineImageItem(QtGui.QWidget):
|
||||||
|
|
||||||
def __init__(self, data, parent=None):
|
def __init__(self, data, width, parent=None):
|
||||||
|
|
||||||
QtGui.QWidget.__init__(self, parent)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.resize(QtCore.QSize(620, 500))
|
self.resize(QtCore.QSize(width, 500))
|
||||||
self._image_label = QtGui.QLabel(self)
|
self._image_label = QtGui.QLabel(self)
|
||||||
self._image_label.raise_()
|
self._image_label.raise_()
|
||||||
self._image_label.setAutoFillBackground(True)
|
self._image_label.setAutoFillBackground(True)
|
||||||
self._image_label.setScaledContents(False)
|
self._image_label.setScaledContents(False)
|
||||||
self.pixmap = QtGui.QPixmap()
|
self.pixmap = QtGui.QPixmap()
|
||||||
self.pixmap.loadFromData(QtCore.QByteArray(data), "PNG")
|
self.pixmap.loadFromData(QtCore.QByteArray(data), "PNG")
|
||||||
if self.pixmap.width() <= 600:
|
max_size = width - 40
|
||||||
|
if self.pixmap.width() <= max_size:
|
||||||
self._image_label.setPixmap(self.pixmap)
|
self._image_label.setPixmap(self.pixmap)
|
||||||
self.resize(QtCore.QSize(600, self.pixmap.height()))
|
self.resize(QtCore.QSize(max_size, self.pixmap.height()))
|
||||||
else:
|
else:
|
||||||
pixmap = self.pixmap.scaled(600, 600, QtCore.Qt.KeepAspectRatio)
|
pixmap = self.pixmap.scaled(max_size, max_size, QtCore.Qt.KeepAspectRatio)
|
||||||
self._image_label.setPixmap(pixmap)
|
self._image_label.setPixmap(pixmap)
|
||||||
self.resize(QtCore.QSize(600, pixmap.height()))
|
self.resize(QtCore.QSize(max_size, pixmap.height()))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
from widgets import *
|
from widgets import *
|
||||||
|
|
||||||
|
|
||||||
@@ -22,6 +25,7 @@ class LoginScreen(CenteredWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(LoginScreen, self).__init__()
|
super(LoginScreen, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.resize(400, 200)
|
self.resize(400, 200)
|
||||||
@@ -91,7 +95,7 @@ class LoginScreen(CenteredWidget):
|
|||||||
def update_select(self, data):
|
def update_select(self, data):
|
||||||
list_of_profiles = []
|
list_of_profiles = []
|
||||||
for elem in data:
|
for elem in data:
|
||||||
list_of_profiles.append(self.tr(elem))
|
list_of_profiles.append(elem)
|
||||||
self.comboBox.addItems(list_of_profiles)
|
self.comboBox.addItems(list_of_profiles)
|
||||||
self.create_only = not list_of_profiles
|
self.create_only = not list_of_profiles
|
||||||
|
|
||||||
|
|||||||
100
src/main.py
@@ -1,7 +1,10 @@
|
|||||||
import sys
|
import sys
|
||||||
from loginscreen import LoginScreen
|
from loginscreen import LoginScreen
|
||||||
from settings import *
|
from settings import *
|
||||||
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
from bootstrap import node_generator
|
from bootstrap import node_generator
|
||||||
from mainscreen import MainWindow
|
from mainscreen import MainWindow
|
||||||
from profile import tox_factory
|
from profile import tox_factory
|
||||||
@@ -9,13 +12,32 @@ from callbacks import init_callbacks
|
|||||||
from util import curr_directory, get_style
|
from util import curr_directory, get_style
|
||||||
import styles.style
|
import styles.style
|
||||||
import locale
|
import locale
|
||||||
|
import toxencryptsave
|
||||||
|
from passwordscreen import PasswordScreen
|
||||||
|
import profile
|
||||||
|
from plugin_support import PluginLoader
|
||||||
|
|
||||||
|
|
||||||
class Toxygen(object):
|
class Toxygen(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, path=None):
|
||||||
super(Toxygen, self).__init__()
|
super(Toxygen, self).__init__()
|
||||||
self.tox = self.ms = self.init = self.mainloop = self.avloop = None
|
self.tox = self.ms = self.init = self.mainloop = self.avloop = None
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def enter_pass(self, data):
|
||||||
|
"""
|
||||||
|
Show password screen
|
||||||
|
"""
|
||||||
|
tmp = [data]
|
||||||
|
p = PasswordScreen(toxencryptsave.LibToxEncryptSave.get_instance(), tmp)
|
||||||
|
p.show()
|
||||||
|
self.app.connect(self.app, QtCore.SIGNAL("lastWindowClosed()"), self.app, QtCore.SLOT("quit()"))
|
||||||
|
self.app.exec_()
|
||||||
|
if tmp[0] == data:
|
||||||
|
raise SystemExit()
|
||||||
|
else:
|
||||||
|
return tmp[0]
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
"""
|
"""
|
||||||
@@ -23,12 +45,24 @@ class Toxygen(object):
|
|||||||
"""
|
"""
|
||||||
app = QtGui.QApplication(sys.argv)
|
app = QtGui.QApplication(sys.argv)
|
||||||
app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
app.setWindowIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
||||||
|
self.app = app
|
||||||
|
|
||||||
# application color scheme
|
# application color scheme
|
||||||
with open(curr_directory() + '/styles/style.qss') as fl:
|
with open(curr_directory() + '/styles/style.qss') as fl:
|
||||||
dark_style = fl.read()
|
dark_style = fl.read()
|
||||||
app.setStyleSheet(dark_style)
|
app.setStyleSheet(dark_style)
|
||||||
|
|
||||||
|
encrypt_save = toxencryptsave.LibToxEncryptSave()
|
||||||
|
|
||||||
|
if self.path is not None:
|
||||||
|
path = os.path.dirname(self.path.encode(locale.getpreferredencoding())) + '/'
|
||||||
|
name = os.path.basename(self.path.encode(locale.getpreferredencoding()))[:-4]
|
||||||
|
data = ProfileHelper(path, name).open_profile()
|
||||||
|
if encrypt_save.is_data_encrypted(data):
|
||||||
|
data = self.enter_pass(data)
|
||||||
|
settings = Settings(name)
|
||||||
|
self.tox = tox_factory(data, settings)
|
||||||
|
else:
|
||||||
auto_profile = Settings.get_auto_profile()
|
auto_profile = Settings.get_auto_profile()
|
||||||
if not auto_profile:
|
if not auto_profile:
|
||||||
# show login screen if default profile not found
|
# show login screen if default profile not found
|
||||||
@@ -57,20 +91,24 @@ class Toxygen(object):
|
|||||||
self.tox = tox_factory()
|
self.tox = tox_factory()
|
||||||
self.tox.self_set_name(_login.name if _login.name else 'Toxygen User')
|
self.tox.self_set_name(_login.name if _login.name else 'Toxygen User')
|
||||||
self.tox.self_set_status_message('Toxing on Toxygen')
|
self.tox.self_set_status_message('Toxing on Toxygen')
|
||||||
ProfileHelper.save_profile(self.tox.get_savedata(), name)
|
ProfileHelper(Settings.get_default_path(), name).save_profile(self.tox.get_savedata())
|
||||||
path = Settings.get_default_path()
|
path = Settings.get_default_path()
|
||||||
settings = Settings(name)
|
settings = Settings(name)
|
||||||
else: # load existing profile
|
else: # load existing profile
|
||||||
path, name = _login.get_data()
|
path, name = _login.get_data()
|
||||||
if _login.default:
|
if _login.default:
|
||||||
Settings.set_auto_profile(path, name)
|
Settings.set_auto_profile(path, name)
|
||||||
data = ProfileHelper.open_profile(path, name)
|
data = ProfileHelper(path, name).open_profile()
|
||||||
|
if encrypt_save.is_data_encrypted(data):
|
||||||
|
data = self.enter_pass(data)
|
||||||
settings = Settings(name)
|
settings = Settings(name)
|
||||||
self.tox = tox_factory(data, settings)
|
self.tox = tox_factory(data, settings)
|
||||||
else:
|
else:
|
||||||
path, name = auto_profile
|
path, name = auto_profile
|
||||||
path = path.encode(locale.getpreferredencoding())
|
path = path.encode(locale.getpreferredencoding())
|
||||||
data = ProfileHelper.open_profile(path, name)
|
data = ProfileHelper(path, name).open_profile()
|
||||||
|
if encrypt_save.is_data_encrypted(data):
|
||||||
|
data = self.enter_pass(data)
|
||||||
settings = Settings(name)
|
settings = Settings(name)
|
||||||
self.tox = tox_factory(data, settings)
|
self.tox = tox_factory(data, settings)
|
||||||
|
|
||||||
@@ -98,27 +136,65 @@ class Toxygen(object):
|
|||||||
self.tray.setObjectName('tray')
|
self.tray.setObjectName('tray')
|
||||||
|
|
||||||
class Menu(QtGui.QMenu):
|
class Menu(QtGui.QMenu):
|
||||||
|
|
||||||
|
def newStatus(self, status):
|
||||||
|
profile.Profile.get_instance().set_status(status)
|
||||||
|
self.aboutToShow()
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
def aboutToShow(self):
|
||||||
|
status = profile.Profile.get_instance().status
|
||||||
|
act = self.act
|
||||||
|
if status is None:
|
||||||
|
self.actions()[1].setVisible(False)
|
||||||
|
else:
|
||||||
|
self.actions()[1].setVisible(True)
|
||||||
|
act.actions()[0].setChecked(False)
|
||||||
|
act.actions()[1].setChecked(False)
|
||||||
|
act.actions()[2].setChecked(False)
|
||||||
|
act.actions()[status].setChecked(True)
|
||||||
|
|
||||||
def languageChange(self, *args, **kwargs):
|
def languageChange(self, *args, **kwargs):
|
||||||
self.actions()[0].setText(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
|
self.actions()[0].setText(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.actions()[1].setText(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
|
self.actions()[1].setText(QtGui.QApplication.translate('tray', 'Set status', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.actions()[2].setText(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.act.actions()[0].setText(QtGui.QApplication.translate('tray', 'Online', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.act.actions()[1].setText(QtGui.QApplication.translate('tray', 'Away', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.act.actions()[2].setText(QtGui.QApplication.translate('tray', 'Busy', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
m = Menu()
|
m = Menu()
|
||||||
show = m.addAction(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
|
show = m.addAction(QtGui.QApplication.translate('tray', 'Open Toxygen', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
sub = m.addMenu(QtGui.QApplication.translate('tray', 'Set status', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
onl = sub.addAction(QtGui.QApplication.translate('tray', 'Online', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
away = sub.addAction(QtGui.QApplication.translate('tray', 'Away', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
busy = sub.addAction(QtGui.QApplication.translate('tray', 'Busy', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
onl.setCheckable(True)
|
||||||
|
away.setCheckable(True)
|
||||||
|
busy.setCheckable(True)
|
||||||
|
m.act = sub
|
||||||
exit = m.addAction(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
|
exit = m.addAction(QtGui.QApplication.translate('tray', 'Exit', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
def show_window():
|
def show_window():
|
||||||
if not self.ms.isActiveWindow():
|
if not self.ms.isActiveWindow():
|
||||||
self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
|
self.ms.setWindowState(self.ms.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
|
||||||
self.ms.activateWindow()
|
self.ms.activateWindow()
|
||||||
|
self.ms.show()
|
||||||
|
|
||||||
m.connect(show, QtCore.SIGNAL("triggered()"), show_window)
|
m.connect(show, QtCore.SIGNAL("triggered()"), show_window)
|
||||||
m.connect(exit, QtCore.SIGNAL("triggered()"), lambda: app.exit())
|
m.connect(exit, QtCore.SIGNAL("triggered()"), lambda: app.exit())
|
||||||
|
m.connect(m, QtCore.SIGNAL("aboutToShow()"), lambda: m.aboutToShow())
|
||||||
|
sub.connect(onl, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(0))
|
||||||
|
sub.connect(away, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(1))
|
||||||
|
sub.connect(busy, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(2))
|
||||||
self.tray.setContextMenu(m)
|
self.tray.setContextMenu(m)
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
self.ms.show()
|
self.ms.show()
|
||||||
QtGui.QApplication.setStyle(get_style(settings['theme'])) # set application style
|
QtGui.QApplication.setStyle(get_style(settings['theme'])) # set application style
|
||||||
|
|
||||||
|
plugin_helper = PluginLoader(self.tox, settings) # plugin support
|
||||||
|
plugin_helper.load()
|
||||||
|
|
||||||
# init thread
|
# init thread
|
||||||
self.init = self.InitThread(self.tox, self.ms, self.tray)
|
self.init = self.InitThread(self.tox, self.ms, self.tray)
|
||||||
self.init.start()
|
self.init.start()
|
||||||
@@ -128,16 +204,18 @@ class Toxygen(object):
|
|||||||
self.mainloop.start()
|
self.mainloop.start()
|
||||||
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
||||||
self.avloop.start()
|
self.avloop.start()
|
||||||
|
|
||||||
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
|
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
|
||||||
app.exec_()
|
app.exec_()
|
||||||
self.init.stop = True
|
self.init.stop = True
|
||||||
self.mainloop.stop = True
|
self.mainloop.stop = True
|
||||||
self.avloop.stop = True
|
self.avloop.stop = True
|
||||||
|
plugin_helper.stop()
|
||||||
self.mainloop.wait()
|
self.mainloop.wait()
|
||||||
self.init.wait()
|
self.init.wait()
|
||||||
self.avloop.wait()
|
self.avloop.wait()
|
||||||
data = self.tox.get_savedata()
|
data = self.tox.get_savedata()
|
||||||
ProfileHelper.save_profile(data)
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
settings.close()
|
settings.close()
|
||||||
del self.tox
|
del self.tox
|
||||||
|
|
||||||
@@ -153,7 +231,7 @@ class Toxygen(object):
|
|||||||
self.init.wait()
|
self.init.wait()
|
||||||
self.avloop.wait()
|
self.avloop.wait()
|
||||||
data = self.tox.get_savedata()
|
data = self.tox.get_savedata()
|
||||||
ProfileHelper.save_profile(data)
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
del self.tox
|
del self.tox
|
||||||
# create new tox instance
|
# create new tox instance
|
||||||
self.tox = tox_factory(data, Settings.get_instance())
|
self.tox = tox_factory(data, Settings.get_instance())
|
||||||
@@ -167,6 +245,10 @@ class Toxygen(object):
|
|||||||
|
|
||||||
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
||||||
self.avloop.start()
|
self.avloop.start()
|
||||||
|
|
||||||
|
plugin_helper = PluginLoader.get_instance()
|
||||||
|
plugin_helper.set_tox(self.tox)
|
||||||
|
|
||||||
return self.tox
|
return self.tox
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -252,6 +334,8 @@ class Toxygen(object):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# TODO: add command line options
|
if len(sys.argv) == 1:
|
||||||
toxygen = Toxygen()
|
toxygen = Toxygen()
|
||||||
|
else: # path to profile
|
||||||
|
toxygen = Toxygen(sys.argv[1])
|
||||||
toxygen.main()
|
toxygen.main()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
from menu import *
|
from menu import *
|
||||||
from profile import *
|
from profile import *
|
||||||
from list_items import *
|
from list_items import *
|
||||||
|
from widgets import QRightClickButton
|
||||||
|
import plugin_support
|
||||||
|
|
||||||
|
|
||||||
class MessageArea(QtGui.QPlainTextEdit):
|
class MessageArea(QtGui.QPlainTextEdit):
|
||||||
@@ -19,10 +21,10 @@ class MessageArea(QtGui.QPlainTextEdit):
|
|||||||
if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
|
if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
|
||||||
self.appendPlainText('')
|
self.appendPlainText('')
|
||||||
else:
|
else:
|
||||||
self.parent.send_message()
|
|
||||||
if self.timer.isActive():
|
if self.timer.isActive():
|
||||||
self.timer.stop()
|
self.timer.stop()
|
||||||
self.parent.profile.send_typing(False)
|
self.parent.profile.send_typing(False)
|
||||||
|
self.parent.send_message()
|
||||||
elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
|
elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
|
||||||
self.appendPlainText(Profile.get_instance().get_last_message())
|
self.appendPlainText(Profile.get_instance().get_last_message())
|
||||||
else:
|
else:
|
||||||
@@ -47,12 +49,16 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.menubar.setMinimumSize(self.width(), 25)
|
self.menubar.setMinimumSize(self.width(), 25)
|
||||||
self.menubar.setMaximumSize(self.width(), 25)
|
self.menubar.setMaximumSize(self.width(), 25)
|
||||||
self.menubar.setBaseSize(self.width(), 25)
|
self.menubar.setBaseSize(self.width(), 25)
|
||||||
|
|
||||||
self.menuProfile = QtGui.QMenu(self.menubar)
|
self.menuProfile = QtGui.QMenu(self.menubar)
|
||||||
self.menuProfile.setObjectName("menuProfile")
|
self.menuProfile.setObjectName("menuProfile")
|
||||||
self.menuSettings = QtGui.QMenu(self.menubar)
|
self.menuSettings = QtGui.QMenu(self.menubar)
|
||||||
self.menuSettings.setObjectName("menuSettings")
|
self.menuSettings.setObjectName("menuSettings")
|
||||||
|
self.menuPlugins = QtGui.QMenu(self.menubar)
|
||||||
|
self.menuPlugins.setObjectName("menuPlugins")
|
||||||
self.menuAbout = QtGui.QMenu(self.menubar)
|
self.menuAbout = QtGui.QMenu(self.menubar)
|
||||||
self.menuAbout.setObjectName("menuAbout")
|
self.menuAbout.setObjectName("menuAbout")
|
||||||
|
|
||||||
self.actionAdd_friend = QtGui.QAction(MainWindow)
|
self.actionAdd_friend = QtGui.QAction(MainWindow)
|
||||||
self.actionAdd_friend.setObjectName("actionAdd_friend")
|
self.actionAdd_friend.setObjectName("actionAdd_friend")
|
||||||
self.actionProfile_settings = QtGui.QAction(MainWindow)
|
self.actionProfile_settings = QtGui.QAction(MainWindow)
|
||||||
@@ -70,6 +76,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.actionSettings = QtGui.QAction(MainWindow)
|
self.actionSettings = QtGui.QAction(MainWindow)
|
||||||
self.actionSettings.setObjectName("actionSettings")
|
self.actionSettings.setObjectName("actionSettings")
|
||||||
self.audioSettings = QtGui.QAction(MainWindow)
|
self.audioSettings = QtGui.QAction(MainWindow)
|
||||||
|
self.pluginData = QtGui.QAction(MainWindow)
|
||||||
self.menuProfile.addAction(self.actionAdd_friend)
|
self.menuProfile.addAction(self.actionAdd_friend)
|
||||||
self.menuProfile.addAction(self.actionSettings)
|
self.menuProfile.addAction(self.actionSettings)
|
||||||
self.menuSettings.addAction(self.actionPrivacy_settings)
|
self.menuSettings.addAction(self.actionPrivacy_settings)
|
||||||
@@ -77,9 +84,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.menuSettings.addAction(self.actionNotifications)
|
self.menuSettings.addAction(self.actionNotifications)
|
||||||
self.menuSettings.addAction(self.actionNetwork)
|
self.menuSettings.addAction(self.actionNetwork)
|
||||||
self.menuSettings.addAction(self.audioSettings)
|
self.menuSettings.addAction(self.audioSettings)
|
||||||
|
self.menuPlugins.addAction(self.pluginData)
|
||||||
self.menuAbout.addAction(self.actionAbout_program)
|
self.menuAbout.addAction(self.actionAbout_program)
|
||||||
self.menubar.addAction(self.menuProfile.menuAction())
|
self.menubar.addAction(self.menuProfile.menuAction())
|
||||||
self.menubar.addAction(self.menuSettings.menuAction())
|
self.menubar.addAction(self.menuSettings.menuAction())
|
||||||
|
self.menubar.addAction(self.menuPlugins.menuAction())
|
||||||
self.menubar.addAction(self.menuAbout.menuAction())
|
self.menubar.addAction(self.menuAbout.menuAction())
|
||||||
|
|
||||||
self.actionAbout_program.triggered.connect(self.about_program)
|
self.actionAbout_program.triggered.connect(self.about_program)
|
||||||
@@ -90,13 +99,15 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.actionInterface_settings.triggered.connect(self.interface_settings)
|
self.actionInterface_settings.triggered.connect(self.interface_settings)
|
||||||
self.actionNotifications.triggered.connect(self.notification_settings)
|
self.actionNotifications.triggered.connect(self.notification_settings)
|
||||||
self.audioSettings.triggered.connect(self.audio_settings)
|
self.audioSettings.triggered.connect(self.audio_settings)
|
||||||
|
self.pluginData.triggered.connect(self.plugins_menu)
|
||||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
def languageChange(self, *args, **kwargs):
|
def languageChange(self, *args, **kwargs):
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.online_contacts.setText(QtGui.QApplication.translate("Form", "Online contacts", None, QtGui.QApplication.UnicodeUTF8))
|
self.menuPlugins.setTitle(QtGui.QApplication.translate("MainWindow", "Plugins", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.pluginData.setText(QtGui.QApplication.translate("MainWindow", "List of plugins", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.menuProfile.setTitle(QtGui.QApplication.translate("MainWindow", "Profile", None, QtGui.QApplication.UnicodeUTF8))
|
self.menuProfile.setTitle(QtGui.QApplication.translate("MainWindow", "Profile", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.menuSettings.setTitle(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
self.menuSettings.setTitle(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
|
self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
@@ -109,66 +120,88 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.actionAbout_program.setText(QtGui.QApplication.translate("MainWindow", "About program", None, QtGui.QApplication.UnicodeUTF8))
|
self.actionAbout_program.setText(QtGui.QApplication.translate("MainWindow", "About program", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.audioSettings.setText(QtGui.QApplication.translate("MainWindow", "Audio", None, QtGui.QApplication.UnicodeUTF8))
|
self.audioSettings.setText(QtGui.QApplication.translate("MainWindow", "Audio", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.contact_name.setPlaceholderText(QtGui.QApplication.translate("MainWindow", "Search", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.screenshotButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send screenshot", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.fileTransferButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send file", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.sendMessageButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send message", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.callButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Start audio call with friend", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.online_contacts.clear()
|
||||||
|
self.online_contacts.addItem(QtGui.QApplication.translate("MainWindow", "All", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.online_contacts.addItem(QtGui.QApplication.translate("MainWindow", "Online", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.online_contacts.setCurrentIndex(int(Settings.get_instance()['show_online_friends']))
|
||||||
|
|
||||||
def setup_right_bottom(self, Form):
|
def setup_right_bottom(self, Form):
|
||||||
Form.setObjectName("right_bottom")
|
Form.setObjectName("right_bottom")
|
||||||
Form.resize(650, 100)
|
Form.resize(650, 60)
|
||||||
self.messageEdit = MessageArea(Form, self)
|
self.messageEdit = MessageArea(Form, self)
|
||||||
self.messageEdit.setGeometry(QtCore.QRect(0, 10, 450, 110))
|
self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55))
|
||||||
self.messageEdit.setObjectName("messageEdit")
|
self.messageEdit.setObjectName("messageEdit")
|
||||||
self.screenshotButton = QtGui.QPushButton(Form)
|
font = QtGui.QFont()
|
||||||
self.screenshotButton.setGeometry(QtCore.QRect(460, 10, 90, 55))
|
font.setPointSize(10)
|
||||||
|
self.messageEdit.setFont(font)
|
||||||
|
|
||||||
|
self.screenshotButton = QRightClickButton(Form)
|
||||||
|
self.screenshotButton.setGeometry(QtCore.QRect(455, 3, 55, 55))
|
||||||
self.screenshotButton.setObjectName("screenshotButton")
|
self.screenshotButton.setObjectName("screenshotButton")
|
||||||
|
|
||||||
self.fileTransferButton = QtGui.QPushButton(Form)
|
self.fileTransferButton = QtGui.QPushButton(Form)
|
||||||
self.fileTransferButton.setGeometry(QtCore.QRect(460, 65, 90, 55))
|
self.fileTransferButton.setGeometry(QtCore.QRect(510, 3, 55, 55))
|
||||||
self.fileTransferButton.setObjectName("fileTransferButton")
|
self.fileTransferButton.setObjectName("fileTransferButton")
|
||||||
|
|
||||||
self.sendMessageButton = QtGui.QPushButton(Form)
|
self.sendMessageButton = QtGui.QPushButton(Form)
|
||||||
self.sendMessageButton.setGeometry(QtCore.QRect(550, 10, 70, 110))
|
self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55))
|
||||||
self.sendMessageButton.setObjectName("sendMessageButton")
|
self.sendMessageButton.setObjectName("sendMessageButton")
|
||||||
self.sendMessageButton.clicked.connect(self.send_message)
|
|
||||||
|
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/send.png')
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/send.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.sendMessageButton.setIcon(icon)
|
self.sendMessageButton.setIcon(icon)
|
||||||
self.sendMessageButton.setIconSize(QtCore.QSize(50, 100))
|
self.sendMessageButton.setIconSize(QtCore.QSize(45, 60))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/file.png')
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/file.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.fileTransferButton.setIcon(icon)
|
self.fileTransferButton.setIcon(icon)
|
||||||
self.fileTransferButton.setIconSize(QtCore.QSize(90, 40))
|
self.fileTransferButton.setIconSize(QtCore.QSize(40, 40))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/screenshot.png')
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/screenshot.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.screenshotButton.setIcon(icon)
|
self.screenshotButton.setIcon(icon)
|
||||||
self.screenshotButton.setIconSize(QtCore.QSize(90, 40))
|
self.screenshotButton.setIconSize(QtCore.QSize(40, 60))
|
||||||
|
|
||||||
self.fileTransferButton.clicked.connect(self.send_file)
|
self.fileTransferButton.clicked.connect(self.send_file)
|
||||||
self.screenshotButton.clicked.connect(self.send_screenshot)
|
self.screenshotButton.clicked.connect(self.send_screenshot)
|
||||||
|
self.sendMessageButton.clicked.connect(self.send_message)
|
||||||
|
self.connect(self.screenshotButton, QtCore.SIGNAL("rightClicked()"), lambda: self.send_screenshot(True))
|
||||||
|
|
||||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||||
|
|
||||||
def setup_left_bottom(self, Form):
|
def setup_left_center_menu(self, Form):
|
||||||
Form.setObjectName("left_bottom")
|
Form.resize(270, 25)
|
||||||
Form.resize(500, 60)
|
self.search_label = QtGui.QLabel(Form)
|
||||||
self.online_contacts = QtGui.QCheckBox(Form)
|
self.search_label.setGeometry(QtCore.QRect(3, 0, 25, 25))
|
||||||
self.online_contacts.setGeometry(QtCore.QRect(0, 0, 250, 20))
|
pixmap = QtGui.QPixmap(QtCore.QSize(25, 25))
|
||||||
self.online_contacts.setObjectName("online_contacts")
|
pixmap.load(curr_directory() + '/images/search.png')
|
||||||
self.online_contacts.clicked.connect(self.filtering)
|
self.search_label.setScaledContents(False)
|
||||||
|
self.search_label.setPixmap(pixmap.scaled(25, 25, QtCore.Qt.KeepAspectRatio))
|
||||||
|
|
||||||
self.contact_name = QtGui.QLineEdit(Form)
|
self.contact_name = QtGui.QLineEdit(Form)
|
||||||
self.contact_name.setGeometry(QtCore.QRect(0, 27, 270, 30))
|
self.contact_name.setGeometry(QtCore.QRect(30, 0, 120, 25))
|
||||||
self.contact_name.setObjectName("contact_name")
|
self.contact_name.setObjectName("contact_name")
|
||||||
self.contact_name.textChanged.connect(self.filtering)
|
self.contact_name.textChanged.connect(self.filtering)
|
||||||
|
self.online_contacts = QtGui.QComboBox(Form)
|
||||||
|
self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25))
|
||||||
|
self.online_contacts.activated[int].connect(lambda x: self.filtering())
|
||||||
|
|
||||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||||
|
|
||||||
def setup_left_top(self, Form):
|
def setup_left_top(self, Form):
|
||||||
Form.setObjectName("left_top")
|
Form.setObjectName("left_top")
|
||||||
Form.resize(500, 300)
|
|
||||||
Form.setCursor(QtCore.Qt.PointingHandCursor)
|
Form.setCursor(QtCore.Qt.PointingHandCursor)
|
||||||
Form.setMinimumSize(QtCore.QSize(250, 100))
|
Form.setMinimumSize(QtCore.QSize(250, 80))
|
||||||
Form.setMaximumSize(QtCore.QSize(250, 100))
|
Form.setMaximumSize(QtCore.QSize(250, 80))
|
||||||
Form.setBaseSize(QtCore.QSize(250, 100))
|
Form.setBaseSize(QtCore.QSize(250, 80))
|
||||||
self.avatar_label = Form.avatar_label = QtGui.QLabel(Form)
|
self.avatar_label = Form.avatar_label = QtGui.QLabel(Form)
|
||||||
self.avatar_label.setGeometry(QtCore.QRect(10, 20, 64, 64))
|
self.avatar_label.setGeometry(QtCore.QRect(5, 15, 64, 64))
|
||||||
self.avatar_label.setScaledContents(True)
|
self.avatar_label.setScaledContents(True)
|
||||||
self.name = Form.name = DataLabel(Form)
|
self.name = Form.name = DataLabel(Form)
|
||||||
Form.name.setGeometry(QtCore.QRect(80, 30, 150, 25))
|
Form.name.setGeometry(QtCore.QRect(80, 25, 150, 25))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
font.setPointSize(14)
|
font.setPointSize(14)
|
||||||
@@ -176,13 +209,13 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
Form.name.setFont(font)
|
Form.name.setFont(font)
|
||||||
Form.name.setObjectName("name")
|
Form.name.setObjectName("name")
|
||||||
self.status_message = Form.status_message = DataLabel(Form)
|
self.status_message = Form.status_message = DataLabel(Form)
|
||||||
Form.status_message.setGeometry(QtCore.QRect(80, 60, 170, 20))
|
Form.status_message.setGeometry(QtCore.QRect(80, 55, 170, 20))
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
Form.status_message.setFont(font)
|
Form.status_message.setFont(font)
|
||||||
Form.status_message.setObjectName("status_message")
|
Form.status_message.setObjectName("status_message")
|
||||||
self.connection_status = Form.connection_status = StatusCircle(self)
|
self.connection_status = Form.connection_status = StatusCircle(self)
|
||||||
Form.connection_status.setGeometry(QtCore.QRect(230, 34, 64, 64))
|
Form.connection_status.setGeometry(QtCore.QRect(230, 29, 64, 64))
|
||||||
Form.connection_status.setMinimumSize(QtCore.QSize(32, 32))
|
Form.connection_status.setMinimumSize(QtCore.QSize(32, 32))
|
||||||
Form.connection_status.setMaximumSize(QtCore.QSize(32, 32))
|
Form.connection_status.setMaximumSize(QtCore.QSize(32, 32))
|
||||||
Form.connection_status.setBaseSize(QtCore.QSize(32, 32))
|
Form.connection_status.setBaseSize(QtCore.QSize(32, 32))
|
||||||
@@ -194,12 +227,12 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def setup_right_top(self, Form):
|
def setup_right_top(self, Form):
|
||||||
Form.setObjectName("Form")
|
Form.setObjectName("Form")
|
||||||
Form.resize(650, 300)
|
Form.resize(650, 80)
|
||||||
self.account_avatar = QtGui.QLabel(Form)
|
self.account_avatar = QtGui.QLabel(Form)
|
||||||
self.account_avatar.setGeometry(QtCore.QRect(10, 20, 64, 64))
|
self.account_avatar.setGeometry(QtCore.QRect(10, 17, 64, 64))
|
||||||
self.account_avatar.setScaledContents(True)
|
self.account_avatar.setScaledContents(True)
|
||||||
self.account_name = DataLabel(Form)
|
self.account_name = DataLabel(Form)
|
||||||
self.account_name.setGeometry(QtCore.QRect(100, 30, 400, 25))
|
self.account_name.setGeometry(QtCore.QRect(100, 25, 400, 25))
|
||||||
self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
|
self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
@@ -208,7 +241,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.account_name.setFont(font)
|
self.account_name.setFont(font)
|
||||||
self.account_name.setObjectName("account_name")
|
self.account_name.setObjectName("account_name")
|
||||||
self.account_status = DataLabel(Form)
|
self.account_status = DataLabel(Form)
|
||||||
self.account_status.setGeometry(QtCore.QRect(100, 50, 400, 25))
|
self.account_status.setGeometry(QtCore.QRect(100, 45, 400, 25))
|
||||||
self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
|
self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse)
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
@@ -240,10 +273,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def setup_right_center(self, widget):
|
def setup_right_center(self, widget):
|
||||||
self.messages = QtGui.QListWidget(widget)
|
self.messages = QtGui.QListWidget(widget)
|
||||||
self.messages.setGeometry(0, 0, 620, 250)
|
self.messages.setGeometry(0, 0, 620, 310)
|
||||||
self.messages.setObjectName("messages")
|
self.messages.setObjectName("messages")
|
||||||
self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
|
||||||
self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
self.messages.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
|
||||||
def load(pos):
|
def load(pos):
|
||||||
if not pos:
|
if not pos:
|
||||||
@@ -254,46 +288,72 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def initUI(self, tox):
|
def initUI(self, tox):
|
||||||
self.setMinimumSize(920, 500)
|
self.setMinimumSize(920, 500)
|
||||||
self.setMaximumSize(920, 500)
|
|
||||||
self.setGeometry(400, 400, 920, 500)
|
self.setGeometry(400, 400, 920, 500)
|
||||||
self.setWindowTitle('Toxygen')
|
self.setWindowTitle('Toxygen')
|
||||||
|
os.chdir(curr_directory() + '/images/')
|
||||||
main = QtGui.QWidget()
|
main = QtGui.QWidget()
|
||||||
grid = QtGui.QGridLayout()
|
grid = QtGui.QGridLayout()
|
||||||
search = QtGui.QWidget()
|
search = QtGui.QWidget()
|
||||||
self.setup_left_bottom(search)
|
self.setup_left_center_menu(search)
|
||||||
grid.addWidget(search, 3, 0)
|
grid.addWidget(search, 1, 0)
|
||||||
name = QtGui.QWidget()
|
name = QtGui.QWidget()
|
||||||
self.setup_left_top(name)
|
self.setup_left_top(name)
|
||||||
grid.addWidget(name, 0, 0)
|
grid.addWidget(name, 0, 0)
|
||||||
messages = QtGui.QWidget()
|
messages = QtGui.QWidget()
|
||||||
self.setup_right_center(messages)
|
self.setup_right_center(messages)
|
||||||
grid.addWidget(messages, 1, 1)
|
grid.addWidget(messages, 1, 1, 2, 1)
|
||||||
info = QtGui.QWidget()
|
info = QtGui.QWidget()
|
||||||
self.setup_right_top(info)
|
self.setup_right_top(info)
|
||||||
grid.addWidget(info, 0, 1)
|
grid.addWidget(info, 0, 1)
|
||||||
message_buttons = QtGui.QWidget()
|
message_buttons = QtGui.QWidget()
|
||||||
self.setup_right_bottom(message_buttons)
|
self.setup_right_bottom(message_buttons)
|
||||||
grid.addWidget(message_buttons, 2, 1, 2, 1)
|
grid.addWidget(message_buttons, 3, 1)
|
||||||
main_list = QtGui.QWidget()
|
main_list = QtGui.QWidget()
|
||||||
self.setup_left_center(main_list)
|
self.setup_left_center(main_list)
|
||||||
grid.addWidget(main_list, 1, 0, 2, 1)
|
grid.addWidget(main_list, 2, 0, 2, 1)
|
||||||
grid.setColumnMinimumWidth(1, 500)
|
grid.setColumnMinimumWidth(1, 500)
|
||||||
grid.setColumnMinimumWidth(0, 270)
|
grid.setColumnMinimumWidth(0, 270)
|
||||||
grid.setRowMinimumHeight(1, 250)
|
grid.setRowMinimumHeight(0, 82)
|
||||||
grid.setRowMinimumHeight(3, 50)
|
grid.setRowMinimumHeight(1, 25)
|
||||||
|
grid.setRowMinimumHeight(2, 410)
|
||||||
|
grid.setRowMinimumHeight(3, 60)
|
||||||
|
grid.setColumnStretch(1, 1)
|
||||||
|
grid.setRowStretch(1, 1)
|
||||||
main.setLayout(grid)
|
main.setLayout(grid)
|
||||||
self.setCentralWidget(main)
|
self.setCentralWidget(main)
|
||||||
self.setup_menu(self)
|
self.setup_menu(self)
|
||||||
|
self.messageEdit.setFocus()
|
||||||
self.user_info = name
|
self.user_info = name
|
||||||
self.friend_info = info
|
self.friend_info = info
|
||||||
self.profile = Profile(tox, self)
|
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
self.profile = Profile(tox, self)
|
||||||
|
|
||||||
def closeEvent(self, *args, **kwargs):
|
def closeEvent(self, *args, **kwargs):
|
||||||
self.profile.save_history()
|
self.profile.save_history()
|
||||||
self.profile.close()
|
self.profile.close()
|
||||||
QtGui.QApplication.closeAllWindows()
|
QtGui.QApplication.closeAllWindows()
|
||||||
|
|
||||||
|
def resizeEvent(self, *args, **kwargs):
|
||||||
|
self.messages.setGeometry(0, 0, self.width() - 300, self.height() - 172)
|
||||||
|
self.friends_list.setGeometry(0, 0, 270, self.height() - 142)
|
||||||
|
self.callButton.setGeometry(QtCore.QRect(self.width() - 370, 20, 50, 50))
|
||||||
|
self.typing.setGeometry(QtCore.QRect(self.width() - 420, 30, 50, 30))
|
||||||
|
|
||||||
|
self.messageEdit.setGeometry(QtCore.QRect(120, 2, self.width() - 490, 55))
|
||||||
|
self.screenshotButton.setGeometry(QtCore.QRect(0, 2, 55, 55))
|
||||||
|
self.fileTransferButton.setGeometry(QtCore.QRect(60, 2, 55, 55))
|
||||||
|
self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 360, 2, 60, 55))
|
||||||
|
|
||||||
|
self.account_name.setGeometry(QtCore.QRect(100, 30, self.width() - 520, 25))
|
||||||
|
self.account_status.setGeometry(QtCore.QRect(100, 50, self.width() - 520, 25))
|
||||||
|
self.profile.update()
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.key() == QtCore.Qt.Key_Escape:
|
||||||
|
self.hide()
|
||||||
|
else:
|
||||||
|
super(MainWindow, self).keyPressEvent(event)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Functions which called when user click in menu
|
# Functions which called when user click in menu
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -302,13 +362,18 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
import util
|
import util
|
||||||
msgBox = QtGui.QMessageBox()
|
msgBox = QtGui.QMessageBox()
|
||||||
msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
|
msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
msgBox.setText(QtGui.QApplication.translate("MainWindow", 'Toxygen is Tox client written on Python 2.7. Version: ', None, QtGui.QApplication.UnicodeUTF8) + util.program_version)
|
text = (QtGui.QApplication.translate("MainWindow", 'Toxygen is Tox client written on Python.\nVersion: ', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgBox.setText(text + util.program_version + '\nGitHub: github.com/xveduk/toxygen/')
|
||||||
msgBox.exec_()
|
msgBox.exec_()
|
||||||
|
|
||||||
def network_settings(self):
|
def network_settings(self):
|
||||||
self.n_s = NetworkSettings(self.reset)
|
self.n_s = NetworkSettings(self.reset)
|
||||||
self.n_s.show()
|
self.n_s.show()
|
||||||
|
|
||||||
|
def plugins_menu(self):
|
||||||
|
self.p_s = PluginsSettings()
|
||||||
|
self.p_s.show()
|
||||||
|
|
||||||
def add_contact(self):
|
def add_contact(self):
|
||||||
self.a_c = AddContact()
|
self.a_c = AddContact()
|
||||||
self.a_c.show()
|
self.a_c.show()
|
||||||
@@ -343,15 +408,18 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def send_file(self):
|
def send_file(self):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
if self.profile.is_active_online(): # active friend exists and online
|
||||||
choose = QtGui.QApplication.translate("MainWindow", "Choose file", None, QtGui.QApplication.UnicodeUTF8)
|
choose_file = QtGui.QApplication.translate("MainWindow", 'Choose file', None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
choose = QtGui.QApplication.translate("MainWindow", choose_file, None, QtGui.QApplication.UnicodeUTF8)
|
||||||
name = QtGui.QFileDialog.getOpenFileName(self, choose)
|
name = QtGui.QFileDialog.getOpenFileName(self, choose)
|
||||||
if name[0]:
|
if name[0]:
|
||||||
self.profile.send_file(name[0])
|
self.profile.send_file(name[0])
|
||||||
|
|
||||||
def send_screenshot(self):
|
def send_screenshot(self, hide=False):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
if self.profile.is_active_online(): # active friend exists and online
|
||||||
self.sw = ScreenShotWindow()
|
self.sw = ScreenShotWindow(self)
|
||||||
self.sw.show()
|
self.sw.show()
|
||||||
|
if hide:
|
||||||
|
self.hide()
|
||||||
|
|
||||||
def call(self):
|
def call(self):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
if self.profile.is_active_online(): # active friend exists and online
|
||||||
@@ -379,7 +447,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
def friend_right_click(self, pos):
|
def friend_right_click(self, pos):
|
||||||
item = self.friends_list.itemAt(pos)
|
item = self.friends_list.itemAt(pos)
|
||||||
num = self.friends_list.indexFromItem(item).row()
|
num = self.friends_list.indexFromItem(item).row()
|
||||||
friend = Profile.get_instance().get_friend_by_number(num)
|
friend = Profile.get_instance().get_friend(num)
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
allowed = friend.tox_id in settings['auto_accept_from_friends']
|
allowed = friend.tox_id in settings['auto_accept_from_friends']
|
||||||
auto = QtGui.QApplication.translate("MainWindow", 'Disallow auto accept', None, QtGui.QApplication.UnicodeUTF8) if allowed else QtGui.QApplication.translate("MainWindow", 'Allow auto accept', None, QtGui.QApplication.UnicodeUTF8)
|
auto = QtGui.QApplication.translate("MainWindow", 'Disallow auto accept', None, QtGui.QApplication.UnicodeUTF8) if allowed else QtGui.QApplication.translate("MainWindow", 'Allow auto accept', None, QtGui.QApplication.UnicodeUTF8)
|
||||||
@@ -390,6 +458,10 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
copy_key_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Copy public key', None, QtGui.QApplication.UnicodeUTF8))
|
copy_key_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Copy public key', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
auto_accept_item = self.listMenu.addAction(auto)
|
auto_accept_item = self.listMenu.addAction(auto)
|
||||||
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
|
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
submenu = plugin_support.PluginLoader.get_instance().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(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(remove_item, QtCore.SIGNAL("triggered()"), lambda: self.remove_friend(num))
|
||||||
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
|
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
|
||||||
@@ -419,8 +491,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
if value:
|
if value:
|
||||||
settings['auto_accept_from_friends'].append(tox_id)
|
settings['auto_accept_from_friends'].append(tox_id)
|
||||||
else:
|
else:
|
||||||
index = settings['auto_accept_from_friends'].index(tox_id)
|
settings['auto_accept_from_friends'].remove(tox_id)
|
||||||
del settings['auto_accept_from_friends'][index]
|
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -440,19 +511,24 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
super(self.__class__, self).mouseReleaseEvent(event)
|
super(self.__class__, self).mouseReleaseEvent(event)
|
||||||
|
|
||||||
def filtering(self):
|
def filtering(self):
|
||||||
self.profile.filtration(self.online_contacts.isChecked(), self.contact_name.text())
|
self.profile.filtration(self.online_contacts.currentIndex() == 1, self.contact_name.text())
|
||||||
|
|
||||||
|
|
||||||
class ScreenShotWindow(QtGui.QWidget):
|
class ScreenShotWindow(QtGui.QWidget):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, parent):
|
||||||
super(ScreenShotWindow, self).__init__()
|
super(ScreenShotWindow, self).__init__()
|
||||||
|
self.parent = parent
|
||||||
self.setMouseTracking(True)
|
self.setMouseTracking(True)
|
||||||
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
||||||
self.showFullScreen()
|
self.showFullScreen()
|
||||||
self.setWindowOpacity(0.5)
|
self.setWindowOpacity(0.5)
|
||||||
self.rubberband = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, None)
|
self.rubberband = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, None)
|
||||||
|
|
||||||
|
def closeEvent(self, *args):
|
||||||
|
if self.parent.isHidden():
|
||||||
|
self.parent.show()
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
self.origin = event.pos()
|
self.origin = event.pos()
|
||||||
self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
|
self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
|
||||||
@@ -473,6 +549,7 @@ class ScreenShotWindow(QtGui.QWidget):
|
|||||||
self.rubberband.hide()
|
self.rubberband.hide()
|
||||||
rect = self.rubberband.geometry()
|
rect = self.rubberband.geometry()
|
||||||
print rect
|
print rect
|
||||||
|
if rect.width() and rect.height():
|
||||||
p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
|
p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
|
||||||
rect.x() + 4,
|
rect.x() + 4,
|
||||||
rect.y() + 4,
|
rect.y() + 4,
|
||||||
|
|||||||
249
src/menu.py
@@ -1,9 +1,14 @@
|
|||||||
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
from settings import *
|
from settings import *
|
||||||
from profile import Profile
|
from profile import Profile
|
||||||
from util import get_style, curr_directory
|
from util import get_style, curr_directory
|
||||||
from widgets import CenteredWidget
|
from widgets import CenteredWidget, DataLabel
|
||||||
import pyaudio
|
import pyaudio
|
||||||
|
import toxencryptsave
|
||||||
|
import plugin_support
|
||||||
|
|
||||||
|
|
||||||
class AddContact(CenteredWidget):
|
class AddContact(CenteredWidget):
|
||||||
@@ -26,28 +31,29 @@ class AddContact(CenteredWidget):
|
|||||||
self.tox_id.setGeometry(QtCore.QRect(50, 40, 471, 27))
|
self.tox_id.setGeometry(QtCore.QRect(50, 40, 471, 27))
|
||||||
self.tox_id.setObjectName("lineEdit")
|
self.tox_id.setObjectName("lineEdit")
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
self.label.setGeometry(QtCore.QRect(30, 10, 80, 20))
|
self.label.setGeometry(QtCore.QRect(60, 10, 80, 20))
|
||||||
self.error_label = QtGui.QLabel(self)
|
self.error_label = DataLabel(self)
|
||||||
self.error_label.setGeometry(QtCore.QRect(120, 10, 400, 20))
|
self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(16)
|
font.setPointSize(10)
|
||||||
font.setWeight(75)
|
font.setWeight(30)
|
||||||
font.setBold(True)
|
|
||||||
self.label.setFont(font)
|
|
||||||
font.setPointSize(12)
|
|
||||||
font.setBold(False)
|
|
||||||
font.setWeight(40)
|
|
||||||
self.error_label.setFont(font)
|
self.error_label.setFont(font)
|
||||||
self.error_label.setStyleSheet("QLabel { color: red; }")
|
self.error_label.setStyleSheet("QLabel { color: red; }")
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
self.message_edit = QtGui.QTextEdit(self)
|
self.message_edit = QtGui.QTextEdit(self)
|
||||||
self.message_edit.setGeometry(QtCore.QRect(50, 110, 471, 151))
|
self.message_edit.setGeometry(QtCore.QRect(50, 110, 471, 151))
|
||||||
self.message_edit.setObjectName("textEdit")
|
self.message_edit.setObjectName("textEdit")
|
||||||
self.label_2 = QtGui.QLabel(self)
|
self.message = QtGui.QLabel(self)
|
||||||
self.label_2.setGeometry(QtCore.QRect(60, 70, 101, 31))
|
self.message.setGeometry(QtCore.QRect(60, 70, 101, 31))
|
||||||
self.label_2.setFont(font)
|
self.message.setFont(font)
|
||||||
self.label_2.setObjectName("label_2")
|
self.message.setObjectName("label_2")
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
self.message_edit.setText('Hello! Add me to your contact list please')
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(12)
|
||||||
|
font.setBold(True)
|
||||||
|
self.label.setFont(font)
|
||||||
|
self.message.setFont(font)
|
||||||
QtCore.QMetaObject.connectSlotsByName(self)
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
def add_friend(self):
|
def add_friend(self):
|
||||||
@@ -57,15 +63,13 @@ class AddContact(CenteredWidget):
|
|||||||
# request was successful
|
# request was successful
|
||||||
self.close()
|
self.close()
|
||||||
else: # print error data
|
else: # print error data
|
||||||
if len(send) > 40:
|
|
||||||
send = send[:40] + '...'
|
|
||||||
self.error_label.setText(send)
|
self.error_label.setText(send)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.setWindowTitle(QtGui.QApplication.translate('AddContact', "Add contact", None, QtGui.QApplication.UnicodeUTF8))
|
self.setWindowTitle(QtGui.QApplication.translate('AddContact', "Add contact", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.sendRequestButton.setText(QtGui.QApplication.translate("Form", "Send request", None, QtGui.QApplication.UnicodeUTF8))
|
self.sendRequestButton.setText(QtGui.QApplication.translate("Form", "Send request", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label.setText(QtGui.QApplication.translate('AddContact', "TOX ID:", None, QtGui.QApplication.UnicodeUTF8))
|
self.label.setText(QtGui.QApplication.translate('AddContact', "TOX ID:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_2.setText(QtGui.QApplication.translate('AddContact', "Message:", None, QtGui.QApplication.UnicodeUTF8))
|
self.message.setText(QtGui.QApplication.translate('AddContact', "Message:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
|
||||||
class ProfileSettings(CenteredWidget):
|
class ProfileSettings(CenteredWidget):
|
||||||
@@ -73,11 +77,12 @@ class ProfileSettings(CenteredWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ProfileSettings, self).__init__()
|
super(ProfileSettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("ProfileSettingsForm")
|
self.setObjectName("ProfileSettingsForm")
|
||||||
self.setMinimumSize(QtCore.QSize(650, 370))
|
self.setMinimumSize(QtCore.QSize(650, 520))
|
||||||
self.setMaximumSize(QtCore.QSize(650, 370))
|
self.setMaximumSize(QtCore.QSize(650, 520))
|
||||||
self.nick = QtGui.QLineEdit(self)
|
self.nick = QtGui.QLineEdit(self)
|
||||||
self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27))
|
self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27))
|
||||||
self.nick.setObjectName("nick")
|
self.nick.setObjectName("nick")
|
||||||
@@ -88,7 +93,7 @@ class ProfileSettings(CenteredWidget):
|
|||||||
self.status.setObjectName("status")
|
self.status.setObjectName("status")
|
||||||
self.status.setText(profile.status_message)
|
self.status.setText(profile.status_message)
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
self.label.setGeometry(QtCore.QRect(50, 30, 91, 21))
|
self.label.setGeometry(QtCore.QRect(40, 30, 91, 25))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(18)
|
font.setPointSize(18)
|
||||||
font.setWeight(75)
|
font.setWeight(75)
|
||||||
@@ -96,11 +101,11 @@ class ProfileSettings(CenteredWidget):
|
|||||||
self.label.setFont(font)
|
self.label.setFont(font)
|
||||||
self.label.setObjectName("label")
|
self.label.setObjectName("label")
|
||||||
self.label_2 = QtGui.QLabel(self)
|
self.label_2 = QtGui.QLabel(self)
|
||||||
self.label_2.setGeometry(QtCore.QRect(40, 100, 100, 21))
|
self.label_2.setGeometry(QtCore.QRect(40, 100, 100, 25))
|
||||||
self.label_2.setFont(font)
|
self.label_2.setFont(font)
|
||||||
self.label_2.setObjectName("label_2")
|
self.label_2.setObjectName("label_2")
|
||||||
self.label_3 = QtGui.QLabel(self)
|
self.label_3 = QtGui.QLabel(self)
|
||||||
self.label_3.setGeometry(QtCore.QRect(40, 170, 100, 21))
|
self.label_3.setGeometry(QtCore.QRect(40, 180, 100, 25))
|
||||||
self.label_3.setFont(font)
|
self.label_3.setFont(font)
|
||||||
self.label_3.setObjectName("label_3")
|
self.label_3.setObjectName("label_3")
|
||||||
self.tox_id = QtGui.QLabel(self)
|
self.tox_id = QtGui.QLabel(self)
|
||||||
@@ -111,19 +116,44 @@ class ProfileSettings(CenteredWidget):
|
|||||||
s = profile.tox_id
|
s = profile.tox_id
|
||||||
self.tox_id.setText(s)
|
self.tox_id.setText(s)
|
||||||
self.copyId = QtGui.QPushButton(self)
|
self.copyId = QtGui.QPushButton(self)
|
||||||
self.copyId.setGeometry(QtCore.QRect(40, 250, 150, 30))
|
self.copyId.setGeometry(QtCore.QRect(40, 250, 160, 30))
|
||||||
self.copyId.setObjectName("copyId")
|
self.copyId.setObjectName("copyId")
|
||||||
self.copyId.clicked.connect(self.copy)
|
self.copyId.clicked.connect(self.copy)
|
||||||
self.export = QtGui.QPushButton(self)
|
self.export = QtGui.QPushButton(self)
|
||||||
self.export.setGeometry(QtCore.QRect(200, 250, 150, 30))
|
self.export.setGeometry(QtCore.QRect(210, 250, 160, 30))
|
||||||
self.export.setObjectName("export")
|
self.export.setObjectName("export")
|
||||||
self.export.clicked.connect(self.export_profile)
|
self.export.clicked.connect(self.export_profile)
|
||||||
|
self.new_nospam = QtGui.QPushButton(self)
|
||||||
|
self.new_nospam.setGeometry(QtCore.QRect(380, 250, 160, 30))
|
||||||
|
self.new_nospam.clicked.connect(self.new_no_spam)
|
||||||
self.new_avatar = QtGui.QPushButton(self)
|
self.new_avatar = QtGui.QPushButton(self)
|
||||||
self.new_avatar.setGeometry(QtCore.QRect(400, 50, 200, 50))
|
self.new_avatar.setGeometry(QtCore.QRect(400, 50, 200, 50))
|
||||||
self.delete_avatar = QtGui.QPushButton(self)
|
self.delete_avatar = QtGui.QPushButton(self)
|
||||||
self.delete_avatar.setGeometry(QtCore.QRect(400, 120, 200, 50))
|
self.delete_avatar.setGeometry(QtCore.QRect(400, 120, 200, 50))
|
||||||
self.delete_avatar.clicked.connect(self.reset_avatar)
|
self.delete_avatar.clicked.connect(self.reset_avatar)
|
||||||
self.new_avatar.clicked.connect(self.set_avatar)
|
self.new_avatar.clicked.connect(self.set_avatar)
|
||||||
|
self.profile_pass = QtGui.QLabel(self)
|
||||||
|
self.profile_pass.setGeometry(QtCore.QRect(40, 300, 300, 50))
|
||||||
|
font.setPointSize(18)
|
||||||
|
self.profile_pass.setFont(font)
|
||||||
|
self.password = QtGui.QLineEdit(self)
|
||||||
|
self.password.setGeometry(QtCore.QRect(30, 350, 300, 30))
|
||||||
|
self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
||||||
|
self.leave_blank = QtGui.QLabel(self)
|
||||||
|
self.leave_blank.setGeometry(QtCore.QRect(340, 350, 300, 30))
|
||||||
|
self.confirm_password = QtGui.QLineEdit(self)
|
||||||
|
self.confirm_password.setGeometry(QtCore.QRect(30, 400, 300, 30))
|
||||||
|
self.confirm_password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
||||||
|
self.set_password = QtGui.QPushButton(self)
|
||||||
|
self.set_password.setGeometry(QtCore.QRect(30, 450, 300, 30))
|
||||||
|
self.set_password.clicked.connect(self.new_password)
|
||||||
|
self.not_match = QtGui.QLabel(self)
|
||||||
|
self.not_match.setGeometry(QtCore.QRect(340, 400, 300, 30))
|
||||||
|
self.not_match.setVisible(False)
|
||||||
|
self.not_match.setStyleSheet('QLabel { color: #F70D1A; }')
|
||||||
|
self.warning = QtGui.QLabel(self)
|
||||||
|
self.warning.setGeometry(QtCore.QRect(30, 490, 500, 30))
|
||||||
|
self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
QtCore.QMetaObject.connectSlotsByName(self)
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
@@ -136,18 +166,49 @@ class ProfileSettings(CenteredWidget):
|
|||||||
self.copyId.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Copy TOX ID", None, QtGui.QApplication.UnicodeUTF8))
|
self.copyId.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Copy TOX ID", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.new_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "New avatar", None, QtGui.QApplication.UnicodeUTF8))
|
self.new_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "New avatar", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.delete_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Reset avatar", None, QtGui.QApplication.UnicodeUTF8))
|
self.delete_avatar.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Reset avatar", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.new_nospam.setText(QtGui.QApplication.translate("ProfileSettingsForm", "New NoSpam", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.profile_pass.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Profile password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.password.setPlaceholderText(QtGui.QApplication.translate("ProfileSettingsForm", "Password (at least 8 symbols)", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.confirm_password.setPlaceholderText(QtGui.QApplication.translate("ProfileSettingsForm", "Confirm password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.set_password.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Set password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.leave_blank.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.warning.setText(QtGui.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
def new_password(self):
|
||||||
|
if self.password.text() == self.confirm_password.text():
|
||||||
|
if not len(self.password.text()) or len(self.password.text()) >= 8:
|
||||||
|
e = toxencryptsave.LibToxEncryptSave.get_instance()
|
||||||
|
e.set_password(self.password.text())
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
self.not_match.setText(
|
||||||
|
QtGui.QApplication.translate("ProfileSettingsForm", "Password must be at least 8 symbols", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.not_match.setVisible(True)
|
||||||
|
else:
|
||||||
|
self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.not_match.setVisible(True)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
clipboard = QtGui.QApplication.clipboard()
|
clipboard = QtGui.QApplication.clipboard()
|
||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
clipboard.setText(profile.tox_id)
|
clipboard.setText(profile.tox_id)
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.copyId.setIcon(icon)
|
||||||
|
self.copyId.setIconSize(QtCore.QSize(10, 10))
|
||||||
|
|
||||||
|
def new_no_spam(self):
|
||||||
|
self.tox_id.setText(Profile.get_instance().new_nospam())
|
||||||
|
|
||||||
def reset_avatar(self):
|
def reset_avatar(self):
|
||||||
Profile.get_instance().reset_avatar()
|
Profile.get_instance().reset_avatar()
|
||||||
|
|
||||||
def set_avatar(self):
|
def set_avatar(self):
|
||||||
name = QtGui.QFileDialog.getOpenFileName(self, 'Open file', None, 'Image Files (*.png)')
|
choose = QtGui.QApplication.translate("ProfileSettingsForm", "Choose avatar", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
print name
|
name = QtGui.QFileDialog.getOpenFileName(self, choose, None, 'Image Files (*.png)')
|
||||||
if name[0]:
|
if name[0]:
|
||||||
with open(name[0], 'rb') as f:
|
with open(name[0], 'rb') as f:
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -156,7 +217,7 @@ class ProfileSettings(CenteredWidget):
|
|||||||
def export_profile(self):
|
def export_profile(self):
|
||||||
directory = QtGui.QFileDialog.getExistingDirectory() + '/'
|
directory = QtGui.QFileDialog.getExistingDirectory() + '/'
|
||||||
if directory != '/':
|
if directory != '/':
|
||||||
ProfileHelper.export_profile(directory)
|
ProfileHelper.get_instance().export_profile(directory)
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
settings.export(directory)
|
settings.export(directory)
|
||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
@@ -173,15 +234,15 @@ class NetworkSettings(CenteredWidget):
|
|||||||
def __init__(self, reset):
|
def __init__(self, reset):
|
||||||
super(NetworkSettings, self).__init__()
|
super(NetworkSettings, self).__init__()
|
||||||
self.reset = reset
|
self.reset = reset
|
||||||
self.reconn = False
|
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("NetworkSettings")
|
self.setObjectName("NetworkSettings")
|
||||||
self.resize(300, 300)
|
self.resize(300, 330)
|
||||||
self.setMinimumSize(QtCore.QSize(300, 300))
|
self.setMinimumSize(QtCore.QSize(300, 330))
|
||||||
self.setMaximumSize(QtCore.QSize(300, 300))
|
self.setMaximumSize(QtCore.QSize(300, 330))
|
||||||
self.setBaseSize(QtCore.QSize(300, 300))
|
self.setBaseSize(QtCore.QSize(300, 330))
|
||||||
self.ipv = QtGui.QCheckBox(self)
|
self.ipv = QtGui.QCheckBox(self)
|
||||||
self.ipv.setGeometry(QtCore.QRect(20, 10, 97, 22))
|
self.ipv.setGeometry(QtCore.QRect(20, 10, 97, 22))
|
||||||
self.ipv.setObjectName("ipv")
|
self.ipv.setObjectName("ipv")
|
||||||
@@ -204,14 +265,18 @@ class NetworkSettings(CenteredWidget):
|
|||||||
self.label_2 = QtGui.QLabel(self)
|
self.label_2 = QtGui.QLabel(self)
|
||||||
self.label_2.setGeometry(QtCore.QRect(40, 165, 66, 17))
|
self.label_2.setGeometry(QtCore.QRect(40, 165, 66, 17))
|
||||||
self.reconnect = QtGui.QPushButton(self)
|
self.reconnect = QtGui.QPushButton(self)
|
||||||
self.reconnect.setGeometry(QtCore.QRect(40, 230, 200, 30))
|
self.reconnect.setGeometry(QtCore.QRect(40, 230, 231, 30))
|
||||||
self.reconnect.clicked.connect(self.restart_core)
|
self.reconnect.clicked.connect(self.restart_core)
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
self.ipv.setChecked(settings['ipv6_enabled'])
|
self.ipv.setChecked(settings['ipv6_enabled'])
|
||||||
self.udp.setChecked(settings['udp_enabled'])
|
self.udp.setChecked(settings['udp_enabled'])
|
||||||
self.proxy.setChecked(settings['proxy_type'] != 0)
|
self.proxy.setChecked(settings['proxy_type'])
|
||||||
self.proxyip.setText(settings['proxy_host'])
|
self.proxyip.setText(settings['proxy_host'])
|
||||||
self.proxyport.setText(unicode(settings['proxy_port']))
|
self.proxyport.setText(unicode(settings['proxy_port']))
|
||||||
|
self.http.setChecked(settings['proxy_type'] == 1)
|
||||||
|
self.warning = QtGui.QLabel(self)
|
||||||
|
self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60))
|
||||||
|
self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.proxy.stateChanged.connect(lambda x: self.activate())
|
self.proxy.stateChanged.connect(lambda x: self.activate())
|
||||||
self.activate()
|
self.activate()
|
||||||
@@ -226,6 +291,8 @@ class NetworkSettings(CenteredWidget):
|
|||||||
self.label_2.setText(QtGui.QApplication.translate("Form", "Port:", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_2.setText(QtGui.QApplication.translate("Form", "Port:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.reconnect.setText(QtGui.QApplication.translate("NetworkSettings", "Restart TOX core", None, QtGui.QApplication.UnicodeUTF8))
|
self.reconnect.setText(QtGui.QApplication.translate("NetworkSettings", "Restart TOX core", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.http.setText(QtGui.QApplication.translate("Form", "HTTP", None, QtGui.QApplication.UnicodeUTF8))
|
self.http.setText(QtGui.QApplication.translate("Form", "HTTP", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.warning.setText(QtGui.QApplication.translate("Form", "WARNING:\nusing proxy with enabled UDP\ncan produce IP leak",
|
||||||
|
None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
def activate(self):
|
def activate(self):
|
||||||
bl = self.proxy.isChecked()
|
bl = self.proxy.isChecked()
|
||||||
@@ -233,27 +300,20 @@ class NetworkSettings(CenteredWidget):
|
|||||||
self.http.setEnabled(bl)
|
self.http.setEnabled(bl)
|
||||||
self.proxyport.setEnabled(bl)
|
self.proxyport.setEnabled(bl)
|
||||||
|
|
||||||
def closeEvent(self, *args, **kwargs):
|
def restart_core(self):
|
||||||
|
try:
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
old_data = str(settings['ipv6_enabled']) + str(settings['udp_enabled']) + str(bool(settings['proxy_type']))
|
|
||||||
new_data = str(self.ipv.isChecked()) + str(self.udp.isChecked()) + str(self.proxy.isChecked())
|
|
||||||
changed = old_data != new_data
|
|
||||||
if self.proxy.isChecked() and (self.proxyip.text() != settings['proxy_host'] or self.proxyport.text() != unicode(settings['proxy_port'])):
|
|
||||||
changed = True
|
|
||||||
if changed:
|
|
||||||
settings['ipv6_enabled'] = self.ipv.isChecked()
|
settings['ipv6_enabled'] = self.ipv.isChecked()
|
||||||
settings['udp_enabled'] = self.udp.isChecked()
|
settings['udp_enabled'] = self.udp.isChecked()
|
||||||
settings['proxy_type'] = 2 - int(self.http.isChecked())
|
settings['proxy_type'] = 2 - int(self.http.isChecked()) if self.proxy.isChecked() else 0
|
||||||
settings['proxy_host'] = self.proxyip.text()
|
settings['proxy_host'] = str(self.proxyip.text())
|
||||||
settings['proxy_port'] = int(self.proxyport.text())
|
settings['proxy_port'] = int(self.proxyport.text())
|
||||||
settings.save()
|
settings.save()
|
||||||
if changed or self.reconnect:
|
|
||||||
# recreate tox instance
|
# recreate tox instance
|
||||||
Profile.get_instance().reset(self.reset)
|
Profile.get_instance().reset(self.reset)
|
||||||
|
|
||||||
def restart_core(self):
|
|
||||||
self.reconn = True
|
|
||||||
self.close()
|
self.close()
|
||||||
|
except Exception as ex:
|
||||||
|
log('Exception in restart: ' + str(ex))
|
||||||
|
|
||||||
|
|
||||||
class PrivacySettings(CenteredWidget):
|
class PrivacySettings(CenteredWidget):
|
||||||
@@ -262,6 +322,7 @@ class PrivacySettings(CenteredWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(PrivacySettings, self).__init__()
|
super(PrivacySettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("privacySettings")
|
self.setObjectName("privacySettings")
|
||||||
@@ -269,21 +330,21 @@ class PrivacySettings(CenteredWidget):
|
|||||||
self.setMinimumSize(QtCore.QSize(350, 550))
|
self.setMinimumSize(QtCore.QSize(350, 550))
|
||||||
self.setMaximumSize(QtCore.QSize(350, 550))
|
self.setMaximumSize(QtCore.QSize(350, 550))
|
||||||
self.saveHistory = QtGui.QCheckBox(self)
|
self.saveHistory = QtGui.QCheckBox(self)
|
||||||
self.saveHistory.setGeometry(QtCore.QRect(40, 20, 291, 22))
|
self.saveHistory.setGeometry(QtCore.QRect(10, 20, 291, 22))
|
||||||
self.saveHistory.setObjectName("saveHistory")
|
self.saveHistory.setObjectName("saveHistory")
|
||||||
self.fileautoaccept = QtGui.QCheckBox(self)
|
self.fileautoaccept = QtGui.QCheckBox(self)
|
||||||
self.fileautoaccept.setGeometry(QtCore.QRect(40, 60, 271, 22))
|
self.fileautoaccept.setGeometry(QtCore.QRect(10, 60, 271, 22))
|
||||||
self.fileautoaccept.setObjectName("fileautoaccept")
|
self.fileautoaccept.setObjectName("fileautoaccept")
|
||||||
self.typingNotifications = QtGui.QCheckBox(self)
|
self.typingNotifications = QtGui.QCheckBox(self)
|
||||||
self.typingNotifications.setGeometry(QtCore.QRect(40, 100, 350, 30))
|
self.typingNotifications.setGeometry(QtCore.QRect(10, 100, 350, 30))
|
||||||
self.typingNotifications.setObjectName("typingNotifications")
|
self.typingNotifications.setObjectName("typingNotifications")
|
||||||
self.inlines = QtGui.QCheckBox(self)
|
self.inlines = QtGui.QCheckBox(self)
|
||||||
self.inlines.setGeometry(QtCore.QRect(40, 140, 350, 30))
|
self.inlines.setGeometry(QtCore.QRect(10, 140, 350, 30))
|
||||||
self.inlines.setObjectName("inlines")
|
self.inlines.setObjectName("inlines")
|
||||||
|
|
||||||
|
|
||||||
self.auto_path = QtGui.QLabel(self)
|
self.auto_path = QtGui.QLabel(self)
|
||||||
self.auto_path.setGeometry(QtCore.QRect(40, 190, 350, 30))
|
self.auto_path.setGeometry(QtCore.QRect(10, 190, 350, 30))
|
||||||
self.path = QtGui.QPlainTextEdit(self)
|
self.path = QtGui.QPlainTextEdit(self)
|
||||||
self.path.setGeometry(QtCore.QRect(10, 225, 330, 45))
|
self.path.setGeometry(QtCore.QRect(10, 225, 330, 45))
|
||||||
self.change_path = QtGui.QPushButton(self)
|
self.change_path = QtGui.QPushButton(self)
|
||||||
@@ -370,6 +431,7 @@ class NotificationsSettings(CenteredWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(NotificationsSettings, self).__init__()
|
super(NotificationsSettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("notificationsForm")
|
self.setObjectName("notificationsForm")
|
||||||
@@ -382,6 +444,12 @@ class NotificationsSettings(CenteredWidget):
|
|||||||
self.callsSound.setGeometry(QtCore.QRect(10, 120, 340, 18))
|
self.callsSound.setGeometry(QtCore.QRect(10, 120, 340, 18))
|
||||||
self.soundNotifications = QtGui.QCheckBox(self)
|
self.soundNotifications = QtGui.QCheckBox(self)
|
||||||
self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18))
|
self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18))
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(12)
|
||||||
|
font.setBold(True)
|
||||||
|
self.callsSound.setFont(font)
|
||||||
|
self.soundNotifications.setFont(font)
|
||||||
|
self.enableNotifications.setFont(font)
|
||||||
s = Settings.get_instance()
|
s = Settings.get_instance()
|
||||||
self.enableNotifications.setChecked(s['notifications'])
|
self.enableNotifications.setChecked(s['notifications'])
|
||||||
self.soundNotifications.setChecked(s['sound_notifications'])
|
self.soundNotifications.setChecked(s['sound_notifications'])
|
||||||
@@ -409,6 +477,7 @@ class InterfaceSettings(CenteredWidget):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(InterfaceSettings, self).__init__()
|
super(InterfaceSettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("interfaceForm")
|
self.setObjectName("interfaceForm")
|
||||||
@@ -476,6 +545,7 @@ class AudioSettings(CenteredWidget):
|
|||||||
super(AudioSettings, self).__init__()
|
super(AudioSettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
self.center()
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("audioSettingsForm")
|
self.setObjectName("audioSettingsForm")
|
||||||
@@ -520,3 +590,76 @@ class AudioSettings(CenteredWidget):
|
|||||||
settings.audio['input'] = self.in_indexes[self.input.currentIndex()]
|
settings.audio['input'] = self.in_indexes[self.input.currentIndex()]
|
||||||
settings.audio['output'] = self.out_indexes[self.output.currentIndex()]
|
settings.audio['output'] = self.out_indexes[self.output.currentIndex()]
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
|
class PluginsSettings(CenteredWidget):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(PluginsSettings, self).__init__()
|
||||||
|
self.initUI()
|
||||||
|
self.center()
|
||||||
|
self.retranslateUi()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
self.resize(400, 210)
|
||||||
|
self.setMinimumSize(QtCore.QSize(400, 210))
|
||||||
|
self.setMaximumSize(QtCore.QSize(400, 210))
|
||||||
|
self.comboBox = QtGui.QComboBox(self)
|
||||||
|
self.comboBox.setGeometry(QtCore.QRect(30, 10, 340, 30))
|
||||||
|
self.label = QtGui.QLabel(self)
|
||||||
|
self.label.setGeometry(QtCore.QRect(30, 40, 340, 90))
|
||||||
|
self.label.setWordWrap(True)
|
||||||
|
self.button = QtGui.QPushButton(self)
|
||||||
|
self.button.setGeometry(QtCore.QRect(30, 130, 340, 30))
|
||||||
|
self.button.clicked.connect(self.button_click)
|
||||||
|
self.open = QtGui.QPushButton(self)
|
||||||
|
self.open.setGeometry(QtCore.QRect(30, 170, 340, 30))
|
||||||
|
self.open.clicked.connect(self.open_plugin)
|
||||||
|
self.pl_loader = plugin_support.PluginLoader.get_instance()
|
||||||
|
self.update_list()
|
||||||
|
self.comboBox.currentIndexChanged.connect(self.show_data)
|
||||||
|
self.show_data()
|
||||||
|
|
||||||
|
def retranslateUi(self):
|
||||||
|
self.setWindowTitle(QtGui.QApplication.translate('PluginsForm', "Plugins", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.open.setText(QtGui.QApplication.translate('PluginsForm', "Open selected plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
def open_plugin(self):
|
||||||
|
ind = self.comboBox.currentIndex()
|
||||||
|
plugin = self.data[ind]
|
||||||
|
window = self.pl_loader.plugin_window(plugin[-1])
|
||||||
|
if window is not None:
|
||||||
|
self.window = window
|
||||||
|
self.window.show()
|
||||||
|
else:
|
||||||
|
msgBox = QtGui.QMessageBox()
|
||||||
|
text = (QtGui.QApplication.translate("PluginsForm", 'No GUI found for this plugin', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgBox.setText(text)
|
||||||
|
msgBox.exec_()
|
||||||
|
|
||||||
|
def update_list(self):
|
||||||
|
self.comboBox.clear()
|
||||||
|
data = self.pl_loader.get_plugins_list()
|
||||||
|
self.comboBox.addItems(map(lambda x: x[0], data))
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def show_data(self):
|
||||||
|
ind = self.comboBox.currentIndex()
|
||||||
|
plugin = self.data[ind]
|
||||||
|
descr = plugin[2] or QtGui.QApplication.translate("PluginsForm", "No description available", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
self.label.setText(descr)
|
||||||
|
if plugin[1]:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
def button_click(self):
|
||||||
|
ind = self.comboBox.currentIndex()
|
||||||
|
plugin = self.data[ind]
|
||||||
|
self.pl_loader.toggle_plugin(plugin[-1])
|
||||||
|
plugin[1] = not plugin[1]
|
||||||
|
if plugin[1]:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ FILE_TRANSFER_MESSAGE_STATUS = {
|
|||||||
'CANCELLED': 1,
|
'CANCELLED': 1,
|
||||||
'OUTGOING': 2,
|
'OUTGOING': 2,
|
||||||
'INCOMING_NOT_STARTED': 3,
|
'INCOMING_NOT_STARTED': 3,
|
||||||
'INCOMING_STARTED': 4
|
'INCOMING_STARTED': 4,
|
||||||
|
'PAUSED_BY_FRIEND': 5,
|
||||||
|
'PAUSED_BY_USER': 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
from PySide import QtGui, QtCore
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
from util import curr_directory
|
from util import curr_directory
|
||||||
import wave
|
import wave
|
||||||
import pyaudio
|
import pyaudio
|
||||||
|
|||||||
76
src/passwordscreen.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from widgets import CenteredWidget
|
||||||
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordArea(QtGui.QLineEdit):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(PasswordArea, self).__init__(parent)
|
||||||
|
self.parent = parent
|
||||||
|
self.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.key() == QtCore.Qt.Key_Return:
|
||||||
|
self.parent.button_click()
|
||||||
|
else:
|
||||||
|
super(PasswordArea, self).keyPressEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordScreen(CenteredWidget):
|
||||||
|
|
||||||
|
def __init__(self, encrypt, data):
|
||||||
|
super(PasswordScreen, self).__init__()
|
||||||
|
self._encrypt = encrypt
|
||||||
|
self._data = data
|
||||||
|
self.initUI()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
self.resize(360, 170)
|
||||||
|
self.setMinimumSize(QtCore.QSize(360, 170))
|
||||||
|
self.setMaximumSize(QtCore.QSize(360, 170))
|
||||||
|
|
||||||
|
self.enter_pass = QtGui.QLabel(self)
|
||||||
|
self.enter_pass.setGeometry(QtCore.QRect(30, 10, 300, 30))
|
||||||
|
|
||||||
|
self.password = PasswordArea(self)
|
||||||
|
self.password.setGeometry(QtCore.QRect(30, 50, 300, 30))
|
||||||
|
|
||||||
|
self.button = QtGui.QPushButton(self)
|
||||||
|
self.button.setGeometry(QtCore.QRect(30, 90, 300, 30))
|
||||||
|
self.button.setText('OK')
|
||||||
|
self.button.clicked.connect(self.button_click)
|
||||||
|
|
||||||
|
self.warning = QtGui.QLabel(self)
|
||||||
|
self.warning.setGeometry(QtCore.QRect(30, 130, 300, 30))
|
||||||
|
self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
|
||||||
|
self.warning.setVisible(False)
|
||||||
|
|
||||||
|
self.retranslateUi()
|
||||||
|
self.center()
|
||||||
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
|
def button_click(self):
|
||||||
|
if self.password.text():
|
||||||
|
try:
|
||||||
|
self._encrypt.set_password(self.password.text())
|
||||||
|
new_data = self._encrypt.pass_decrypt(self._data[0])
|
||||||
|
except Exception:
|
||||||
|
self.warning.setVisible(True)
|
||||||
|
else:
|
||||||
|
self._data[0] = new_data
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.key() == QtCore.Qt.Key_Enter:
|
||||||
|
self.button_click()
|
||||||
|
else:
|
||||||
|
super(PasswordScreen, self).keyPressEvent(event)
|
||||||
|
|
||||||
|
def retranslateUi(self):
|
||||||
|
self.setWindowTitle(QtGui.QApplication.translate("pass", "Enter password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.enter_pass.setText(QtGui.QApplication.translate("pass", "Password:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.warning.setText(QtGui.QApplication.translate("pass", "Incorrect password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
146
src/plugin_support.py
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
import util
|
||||||
|
import profile
|
||||||
|
import os
|
||||||
|
import imp
|
||||||
|
import inspect
|
||||||
|
import plugins.plugin_super_class as pl
|
||||||
|
import toxencryptsave
|
||||||
|
|
||||||
|
|
||||||
|
class PluginLoader(util.Singleton):
|
||||||
|
|
||||||
|
def __init__(self, tox, settings):
|
||||||
|
self._profile = profile.Profile.get_instance()
|
||||||
|
self._settings = settings
|
||||||
|
self._plugins = {} # dict. key - plugin unique short name, value - tuple (plugin instance, is active)
|
||||||
|
self._tox = tox
|
||||||
|
self._encr = toxencryptsave.LibToxEncryptSave.get_instance()
|
||||||
|
|
||||||
|
def set_tox(self, tox):
|
||||||
|
"""
|
||||||
|
New tox instance
|
||||||
|
"""
|
||||||
|
self._tox = tox
|
||||||
|
for value in self._plugins.values():
|
||||||
|
value[0].set_tox(tox)
|
||||||
|
|
||||||
|
def load(self):
|
||||||
|
"""
|
||||||
|
Load all plugins in plugins folder
|
||||||
|
"""
|
||||||
|
path = util.curr_directory() + '/plugins/'
|
||||||
|
files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
|
||||||
|
for fl in files:
|
||||||
|
if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'):
|
||||||
|
continue
|
||||||
|
name = fl[:-3] # module name without .py
|
||||||
|
try:
|
||||||
|
module = imp.load_source('plugins.' + name, path + fl) # import plugin
|
||||||
|
except ImportError:
|
||||||
|
util.log('Import error in module ' + name)
|
||||||
|
continue
|
||||||
|
except Exception as ex:
|
||||||
|
util.log('Exception in module ' + name + ' Exception: ' + str(ex))
|
||||||
|
continue
|
||||||
|
for elem in dir(module):
|
||||||
|
obj = getattr(module, elem)
|
||||||
|
if inspect.isclass(obj) and issubclass(obj, pl.PluginSuperClass): # looking for plugin class in module
|
||||||
|
print 'Plugin', elem
|
||||||
|
try: # create instance of plugin class
|
||||||
|
inst = obj(self._tox, self._profile, self._settings, self._encr)
|
||||||
|
autostart = inst.get_short_name() in self._settings['plugins']
|
||||||
|
if autostart:
|
||||||
|
inst.start()
|
||||||
|
except Exception as ex:
|
||||||
|
util.log('Exception in module ' + name + ' Exception: ' + str(ex))
|
||||||
|
continue
|
||||||
|
self._plugins[inst.get_short_name()] = [inst, autostart] # (inst, is active)
|
||||||
|
break
|
||||||
|
|
||||||
|
def callback_lossless(self, friend_number, data, length):
|
||||||
|
"""
|
||||||
|
New incoming custom lossless packet (callback)
|
||||||
|
"""
|
||||||
|
l = data[0] - pl.LOSSLESS_FIRST_BYTE
|
||||||
|
name = ''.join(chr(x) for x in data[1:l + 1])
|
||||||
|
if name in self._plugins and self._plugins[name][1]:
|
||||||
|
self._plugins[name][0].lossless_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
|
||||||
|
|
||||||
|
def callback_lossy(self, friend_number, data, length):
|
||||||
|
"""
|
||||||
|
New incoming custom lossy packet (callback)
|
||||||
|
"""
|
||||||
|
l = data[0] - pl.LOSSY_FIRST_BYTE
|
||||||
|
name = ''.join(chr(x) for x in data[1:l + 1])
|
||||||
|
if name in self._plugins and self._plugins[name][1]:
|
||||||
|
self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
|
||||||
|
|
||||||
|
def friend_online(self, friend_number):
|
||||||
|
for elem in self._plugins.values():
|
||||||
|
if elem[1]:
|
||||||
|
elem[0].friend_connected(friend_number)
|
||||||
|
|
||||||
|
def get_plugins_list(self):
|
||||||
|
"""
|
||||||
|
Returns list of all plugins
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for data in self._plugins.values():
|
||||||
|
result.append([data[0].get_name(), # plugin full name
|
||||||
|
data[1], # is enabled
|
||||||
|
data[0].get_description(), # plugin description
|
||||||
|
data[0].get_short_name()]) # key - short unique name
|
||||||
|
return result
|
||||||
|
|
||||||
|
def plugin_window(self, key):
|
||||||
|
"""
|
||||||
|
Return window or None for specified plugin
|
||||||
|
"""
|
||||||
|
return self._plugins[key][0].get_window()
|
||||||
|
|
||||||
|
def toggle_plugin(self, key):
|
||||||
|
"""
|
||||||
|
Enable/disable plugin
|
||||||
|
:param key: plugin short name
|
||||||
|
"""
|
||||||
|
plugin = self._plugins[key]
|
||||||
|
if plugin[1]:
|
||||||
|
plugin[0].stop()
|
||||||
|
else:
|
||||||
|
plugin[0].start()
|
||||||
|
plugin[1] = not plugin[1]
|
||||||
|
if plugin[1]:
|
||||||
|
self._settings['plugins'].append(key)
|
||||||
|
else:
|
||||||
|
self._settings['plugins'].remove(key)
|
||||||
|
self._settings.save()
|
||||||
|
|
||||||
|
def command(self, text):
|
||||||
|
"""
|
||||||
|
New command for plugin
|
||||||
|
"""
|
||||||
|
text = text.strip()
|
||||||
|
name = text.split()[0]
|
||||||
|
if name in self._plugins and self._plugins[name][1]:
|
||||||
|
self._plugins[name][0].command(text[len(name) + 1:])
|
||||||
|
|
||||||
|
def get_menu(self, menu, num):
|
||||||
|
"""
|
||||||
|
Return list of items for menu
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for elem in self._plugins.values():
|
||||||
|
if elem[1]:
|
||||||
|
try:
|
||||||
|
result.extend(elem[0].get_menu(menu, num))
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
return result
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
App is closing, stop all plugins
|
||||||
|
"""
|
||||||
|
for key in self._plugins.keys():
|
||||||
|
self._plugins[key][0].close()
|
||||||
|
del self._plugins[key]
|
||||||
0
src/plugins/__init__.py
Normal file
233
src/plugins/plugin_super_class.py
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
import os
|
||||||
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
|
MAX_SHORT_NAME_LENGTH = 5
|
||||||
|
|
||||||
|
LOSSY_FIRST_BYTE = 200
|
||||||
|
|
||||||
|
LOSSLESS_FIRST_BYTE = 160
|
||||||
|
|
||||||
|
|
||||||
|
def path_to_data(name):
|
||||||
|
"""
|
||||||
|
:param name: plugin unique name
|
||||||
|
:return path do plugin's directory
|
||||||
|
"""
|
||||||
|
return os.path.dirname(os.path.realpath(__file__)) + '/' + name + '/'
|
||||||
|
|
||||||
|
|
||||||
|
def log(name, data):
|
||||||
|
"""
|
||||||
|
:param name: plugin unique name
|
||||||
|
:param data: data for saving in log
|
||||||
|
"""
|
||||||
|
with open(path_to_data(name) + 'logs.txt', 'a') as fl:
|
||||||
|
fl.write(str(data) + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSuperClass(object):
|
||||||
|
"""
|
||||||
|
Superclass for all plugins. Plugin is python module with at least one class derived from PluginSuperClass.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, short_name, tox=None, profile=None, settings=None, encrypt_save=None):
|
||||||
|
"""
|
||||||
|
:param name: plugin full name
|
||||||
|
:param short_name: plugin unique short name (length of short name should not exceed MAX_SHORT_NAME_LENGTH)
|
||||||
|
:param tox: tox instance
|
||||||
|
:param profile: profile instance
|
||||||
|
:param settings: profile settings
|
||||||
|
:param encrypt_save: LibToxEncryptSave instance.
|
||||||
|
"""
|
||||||
|
self._settings = settings
|
||||||
|
self._profile = profile
|
||||||
|
self._tox = tox
|
||||||
|
name = name.strip()
|
||||||
|
short_name = short_name.strip()
|
||||||
|
if not name or not short_name:
|
||||||
|
raise NameError('Wrong name')
|
||||||
|
self._name = name
|
||||||
|
self._short_name = short_name[:MAX_SHORT_NAME_LENGTH]
|
||||||
|
self._translator = None # translator for plugin's GUI
|
||||||
|
self._encrypt_save = encrypt_save
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Get methods
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""
|
||||||
|
:return plugin full name
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def get_short_name(self):
|
||||||
|
"""
|
||||||
|
:return plugin unique (short) name
|
||||||
|
"""
|
||||||
|
return self._short_name
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
"""
|
||||||
|
Should return plugin description
|
||||||
|
"""
|
||||||
|
return self.__doc__
|
||||||
|
|
||||||
|
def get_menu(self, menu, row_number):
|
||||||
|
"""
|
||||||
|
This method creates items for menu which called on right click in list of friends
|
||||||
|
:param menu: menu instance
|
||||||
|
:param row_number: number of selected row in list of contacts
|
||||||
|
:return list of QAction's
|
||||||
|
"""
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_window(self):
|
||||||
|
"""
|
||||||
|
This method should return window for plugins with GUI or None
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_tox(self, tox):
|
||||||
|
"""
|
||||||
|
New tox instance
|
||||||
|
"""
|
||||||
|
self._tox = tox
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Plugin was stopped, started or new command received
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
This method called when plugin was started
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
This method called when plugin was stopped
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""
|
||||||
|
App is closing
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def command(self, command):
|
||||||
|
"""
|
||||||
|
New command. On 'help' this method should provide user list of available commands
|
||||||
|
:param command: string with command
|
||||||
|
"""
|
||||||
|
msgbox = QtGui.QMessageBox()
|
||||||
|
title = QtGui.QApplication.translate("PluginWindow", "List of commands for plugin {}", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
msgbox.setWindowTitle(title.format(self._name))
|
||||||
|
msgbox.setText(QtGui.QApplication.translate("PluginWindow", "No commands available", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgbox.exec_()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Translations support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_translator(self):
|
||||||
|
"""
|
||||||
|
This method loads translations for GUI
|
||||||
|
"""
|
||||||
|
app = QtGui.QApplication.instance()
|
||||||
|
langs = self._settings.supported_languages()
|
||||||
|
curr_lang = self._settings['language']
|
||||||
|
if curr_lang in map(lambda x: x[0], langs):
|
||||||
|
if self._translator is not None:
|
||||||
|
app.removeTranslator(self._translator)
|
||||||
|
self._translator = QtCore.QTranslator()
|
||||||
|
lang_path = filter(lambda x: x[0] == curr_lang, langs)[0][1]
|
||||||
|
self._translator.load(path_to_data(self._short_name) + lang_path)
|
||||||
|
app.installTranslator(self._translator)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Settings loading and saving
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_settings(self):
|
||||||
|
"""
|
||||||
|
This method loads settings of plugin and returns raw data
|
||||||
|
"""
|
||||||
|
with open(path_to_data(self._short_name) + 'settings.json') as fl:
|
||||||
|
data = fl.read()
|
||||||
|
return data
|
||||||
|
|
||||||
|
def save_settings(self, data):
|
||||||
|
"""
|
||||||
|
This method saves plugin's settings to file
|
||||||
|
:param data: string with data
|
||||||
|
"""
|
||||||
|
with open(path_to_data(self._short_name) + 'settings.json', 'wb') as fl:
|
||||||
|
fl.write(data)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Callbacks
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def lossless_packet(self, data, friend_number):
|
||||||
|
"""
|
||||||
|
Incoming lossless packet
|
||||||
|
:param data: string with data
|
||||||
|
:param friend_number: number of friend who sent packet
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def lossy_packet(self, data, friend_number):
|
||||||
|
"""
|
||||||
|
Incoming lossy packet
|
||||||
|
:param data: string with data
|
||||||
|
:param friend_number: number of friend who sent packet
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def friend_connected(self, friend_number):
|
||||||
|
"""
|
||||||
|
Friend with specified number is online now
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Custom packets sending
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def send_lossless(self, data, friend_number):
|
||||||
|
"""
|
||||||
|
This method sends lossless packet to friend
|
||||||
|
Wrapper for self._tox.friend_send_lossless_packet
|
||||||
|
Use it instead of direct using self._tox.friend_send_lossless_packet
|
||||||
|
:return True on success
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
data = ''
|
||||||
|
try:
|
||||||
|
return self._tox.friend_send_lossless_packet(friend_number,
|
||||||
|
chr(len(self._short_name) + LOSSLESS_FIRST_BYTE) +
|
||||||
|
self._short_name + str(data))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_lossy(self, data, friend_number):
|
||||||
|
"""
|
||||||
|
This method sends lossy packet to friend
|
||||||
|
Wrapper for self._tox.friend_send_lossy_packet
|
||||||
|
Use it instead of direct using self._tox.friend_send_lossy_packet
|
||||||
|
:return True on success
|
||||||
|
"""
|
||||||
|
if data is None:
|
||||||
|
data = ''
|
||||||
|
try:
|
||||||
|
return self._tox.friend_send_lossy_packet(friend_number,
|
||||||
|
chr(len(self._short_name) + LOSSY_FIRST_BYTE) +
|
||||||
|
self._short_name + str(data))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
147
src/profile.py
@@ -1,5 +1,8 @@
|
|||||||
from list_items import MessageItem, ContactItem, FileTransferItem, InlineImageItem
|
from list_items import MessageItem, ContactItem, FileTransferItem, InlineImageItem
|
||||||
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
from tox import Tox
|
from tox import Tox
|
||||||
import os
|
import os
|
||||||
from messages import *
|
from messages import *
|
||||||
@@ -13,6 +16,7 @@ from file_transfers import *
|
|||||||
import time
|
import time
|
||||||
import calls
|
import calls
|
||||||
import avwidgets
|
import avwidgets
|
||||||
|
import plugin_support
|
||||||
|
|
||||||
|
|
||||||
class Contact(object):
|
class Contact(object):
|
||||||
@@ -207,7 +211,7 @@ class Friend(Contact):
|
|||||||
"""
|
"""
|
||||||
if hasattr(self, '_message_getter'):
|
if hasattr(self, '_message_getter'):
|
||||||
del self._message_getter
|
del self._message_getter
|
||||||
self._corr = filter(lambda x: x.get_type() == 2 and x.get_status() in (2, 4), self._corr)
|
self._corr = filter(lambda x: x.get_type() == 2 and x.get_status() >= 2, self._corr)
|
||||||
self._unsaved_messages = 0
|
self._unsaved_messages = 0
|
||||||
|
|
||||||
def update_transfer_data(self, file_number, status, inline=None):
|
def update_transfer_data(self, file_number, status, inline=None):
|
||||||
@@ -298,7 +302,7 @@ class Profile(Contact, Singleton):
|
|||||||
self._incoming_calls = set()
|
self._incoming_calls = set()
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
self._show_online = settings['show_online_friends']
|
self._show_online = settings['show_online_friends']
|
||||||
screen.online_contacts.setChecked(self._show_online)
|
screen.online_contacts.setCurrentIndex(int(self._show_online))
|
||||||
aliases = settings['friends_aliases']
|
aliases = settings['friends_aliases']
|
||||||
data = tox.self_get_friend_list()
|
data = tox.self_get_friend_list()
|
||||||
self._history = History(tox.self_get_public_key()) # connection to db
|
self._history = History(tox.self_get_public_key()) # connection to db
|
||||||
@@ -329,8 +333,10 @@ class Profile(Contact, Singleton):
|
|||||||
Changes status of user (online, away, busy)
|
Changes status of user (online, away, busy)
|
||||||
"""
|
"""
|
||||||
if self._status is not None:
|
if self._status is not None:
|
||||||
status = (self._status + 1) % 3
|
self.set_status((self._status + 1) % 3)
|
||||||
super(self.__class__, self).set_status(status)
|
|
||||||
|
def set_status(self, status):
|
||||||
|
super(Profile, self).set_status(status)
|
||||||
self._tox.self_set_status(status)
|
self._tox.self_set_status(status)
|
||||||
|
|
||||||
def set_name(self, value):
|
def set_name(self, value):
|
||||||
@@ -341,6 +347,12 @@ class Profile(Contact, Singleton):
|
|||||||
super(self.__class__, self).set_status_message(value)
|
super(self.__class__, self).set_status_message(value)
|
||||||
self._tox.self_set_status_message(self._status_message.encode('utf-8'))
|
self._tox.self_set_status_message(self._status_message.encode('utf-8'))
|
||||||
|
|
||||||
|
def new_nospam(self):
|
||||||
|
import random
|
||||||
|
self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32
|
||||||
|
self._tox_id = self._tox.self_get_address()
|
||||||
|
return self._tox_id
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Filtration
|
# Filtration
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -372,6 +384,9 @@ class Profile(Contact, Singleton):
|
|||||||
def get_friend_by_number(self, num):
|
def get_friend_by_number(self, num):
|
||||||
return filter(lambda x: x.number == num, self._friends)[0]
|
return filter(lambda x: x.number == num, self._friends)[0]
|
||||||
|
|
||||||
|
def get_friend(self, num):
|
||||||
|
return self._friends[num]
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Work with active friend
|
# Work with active friend
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@@ -381,6 +396,7 @@ class Profile(Contact, Singleton):
|
|||||||
|
|
||||||
def set_active(self, value=None):
|
def set_active(self, value=None):
|
||||||
"""
|
"""
|
||||||
|
Change current active friend or update info
|
||||||
:param value: number of new active friend in friend's list or None to update active user's data
|
:param value: number of new active friend in friend's list or None to update active user's data
|
||||||
"""
|
"""
|
||||||
if value is None and self._active_friend == -1: # nothing to update
|
if value is None and self._active_friend == -1: # nothing to update
|
||||||
@@ -413,9 +429,13 @@ class Profile(Contact, Singleton):
|
|||||||
data[3])
|
data[3])
|
||||||
elif message.get_type() == 2:
|
elif message.get_type() == 2:
|
||||||
item = self.create_file_transfer_item(message)
|
item = self.create_file_transfer_item(message)
|
||||||
if message.get_status() in (2, 4): # active file transfer
|
if message.get_status() >= 2: # active file transfer
|
||||||
|
try:
|
||||||
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
||||||
ft.set_state_changed_handler(item.update)
|
ft.set_state_changed_handler(item.update)
|
||||||
|
ft.signal()
|
||||||
|
except:
|
||||||
|
print 'Incoming not started transfer - no info found'
|
||||||
else: # inline
|
else: # inline
|
||||||
self.create_inline_item(message.get_data())
|
self.create_inline_item(message.get_data())
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
@@ -433,6 +453,7 @@ class Profile(Contact, Singleton):
|
|||||||
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
||||||
if not os.path.isfile(avatar_path): # load default image
|
if not os.path.isfile(avatar_path): # load default image
|
||||||
avatar_path = curr_directory() + '/images/avatar.png'
|
avatar_path = curr_directory() + '/images/avatar.png'
|
||||||
|
os.chdir(os.path.dirname(avatar_path))
|
||||||
pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
|
pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
|
||||||
pixmap.load(avatar_path)
|
pixmap.load(avatar_path)
|
||||||
self._screen.account_avatar.setScaledContents(False)
|
self._screen.account_avatar.setScaledContents(False)
|
||||||
@@ -456,17 +477,36 @@ class Profile(Contact, Singleton):
|
|||||||
def is_active_online(self):
|
def is_active_online(self):
|
||||||
return self._active_friend + 1 and self._friends[self._active_friend].status is not None
|
return self._active_friend + 1 and self._friends[self._active_friend].status is not None
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self._active_friend + 1:
|
||||||
|
self.set_active(self._active_friend)
|
||||||
|
|
||||||
|
def friend_exit(self, friend_number):
|
||||||
|
self.get_friend_by_number(friend_number).status = None
|
||||||
|
self.friend_typing(friend_number, False)
|
||||||
|
if friend_number in self._call:
|
||||||
|
self._call.finish_call(friend_number, True)
|
||||||
|
for key in filter(lambda x: x[0] == friend_number, self._file_transfers.keys()):
|
||||||
|
self._file_transfers[key].cancelled()
|
||||||
|
del self._file_transfers[key]
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Typing notifications
|
# Typing notifications
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_typing(self, typing):
|
def send_typing(self, typing):
|
||||||
|
"""
|
||||||
|
Send typing notification to a friend
|
||||||
|
"""
|
||||||
if Settings.get_instance()['typing_notifications']:
|
if Settings.get_instance()['typing_notifications']:
|
||||||
friend = self._friends[self._active_friend]
|
friend = self._friends[self._active_friend]
|
||||||
if friend.status is not None:
|
if friend.status is not None:
|
||||||
self._tox.self_set_typing(friend.number, typing)
|
self._tox.self_set_typing(friend.number, typing)
|
||||||
|
|
||||||
def friend_typing(self, friend_number, typing):
|
def friend_typing(self, friend_number, typing):
|
||||||
|
"""
|
||||||
|
Display incoming typing notification
|
||||||
|
"""
|
||||||
if friend_number == self.get_active_number():
|
if friend_number == self.get_active_number():
|
||||||
self._screen.typing.setVisible(typing)
|
self._screen.typing.setVisible(typing)
|
||||||
|
|
||||||
@@ -491,7 +531,7 @@ class Profile(Contact, Singleton):
|
|||||||
elif '.' in last_part:
|
elif '.' in last_part:
|
||||||
index = last_part.index('.')
|
index = last_part.index('.')
|
||||||
else:
|
else:
|
||||||
index = TOX_MAX_MESSAGE_LENGTH - size
|
index = TOX_MAX_MESSAGE_LENGTH - size - 1
|
||||||
index += size + 1
|
index += size + 1
|
||||||
self._tox.friend_send_message(number, message_type, message[:index])
|
self._tox.friend_send_message(number, message_type, message[:index])
|
||||||
message = message[index:]
|
message = message[index:]
|
||||||
@@ -521,7 +561,10 @@ class Profile(Contact, Singleton):
|
|||||||
Send message to active friend
|
Send message to active friend
|
||||||
:param text: message text
|
:param text: message text
|
||||||
"""
|
"""
|
||||||
if self.is_active_online() and text:
|
if text.startswith('/plugin '):
|
||||||
|
plugin_support.PluginLoader.get_instance().command(text[8:])
|
||||||
|
self._screen.messageEdit.clear()
|
||||||
|
elif self.is_active_online() and text:
|
||||||
if text.startswith('/me '):
|
if text.startswith('/me '):
|
||||||
message_type = TOX_MESSAGE_TYPE['ACTION']
|
message_type = TOX_MESSAGE_TYPE['ACTION']
|
||||||
text = text[4:]
|
text = text[4:]
|
||||||
@@ -549,6 +592,7 @@ class Profile(Contact, Singleton):
|
|||||||
if not self._history.friend_exists_in_db(friend.tox_id):
|
if not self._history.friend_exists_in_db(friend.tox_id):
|
||||||
self._history.add_friend_to_db(friend.tox_id)
|
self._history.add_friend_to_db(friend.tox_id)
|
||||||
self._history.save_messages_to_db(friend.tox_id, messages)
|
self._history.save_messages_to_db(friend.tox_id, messages)
|
||||||
|
self._history.save()
|
||||||
del self._history
|
del self._history
|
||||||
|
|
||||||
def clear_history(self, num=None):
|
def clear_history(self, num=None):
|
||||||
@@ -586,7 +630,7 @@ class Profile(Contact, Singleton):
|
|||||||
False)
|
False)
|
||||||
elif message.get_type() == 2:
|
elif message.get_type() == 2:
|
||||||
item = self.create_file_transfer_item(message, False)
|
item = self.create_file_transfer_item(message, False)
|
||||||
if message.get_status() in (2, 4):
|
if message.get_status() >= 2:
|
||||||
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
||||||
ft.set_state_changed_handler(item.update)
|
ft.set_state_changed_handler(item.update)
|
||||||
|
|
||||||
@@ -622,9 +666,10 @@ class Profile(Contact, Singleton):
|
|||||||
def create_file_transfer_item(self, tm, append=True):
|
def create_file_transfer_item(self, tm, append=True):
|
||||||
data = list(tm.get_data())
|
data = list(tm.get_data())
|
||||||
data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name
|
data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name
|
||||||
|
data.append(self._messages.width())
|
||||||
item = FileTransferItem(*data)
|
item = FileTransferItem(*data)
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
elem.setSizeHint(QtCore.QSize(600, 50))
|
elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
|
||||||
if append:
|
if append:
|
||||||
self._messages.addItem(elem)
|
self._messages.addItem(elem)
|
||||||
else:
|
else:
|
||||||
@@ -633,9 +678,9 @@ class Profile(Contact, Singleton):
|
|||||||
return item
|
return item
|
||||||
|
|
||||||
def create_inline_item(self, data, append=True):
|
def create_inline_item(self, data, append=True):
|
||||||
item = InlineImageItem(data)
|
item = InlineImageItem(data, self._messages.width())
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
elem.setSizeHint(QtCore.QSize(600, item.height()))
|
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
||||||
if append:
|
if append:
|
||||||
self._messages.addItem(elem)
|
self._messages.addItem(elem)
|
||||||
else:
|
else:
|
||||||
@@ -691,6 +736,13 @@ class Profile(Contact, Singleton):
|
|||||||
:param num: number of friend in list
|
:param num: number of friend in list
|
||||||
"""
|
"""
|
||||||
friend = self._friends[num]
|
friend = self._friends[num]
|
||||||
|
try:
|
||||||
|
settings = Settings.get_instance()
|
||||||
|
index = map(lambda x: x[0], settings['friends_aliases']).index(friend.tox_id)
|
||||||
|
del settings['friends_aliases'][index]
|
||||||
|
settings.save()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self.clear_history(num)
|
self.clear_history(num)
|
||||||
if self._history.friend_exists_in_db(friend.tox_id):
|
if self._history.friend_exists_in_db(friend.tox_id):
|
||||||
self._history.delete_friend_from_db(friend.tox_id)
|
self._history.delete_friend_from_db(friend.tox_id)
|
||||||
@@ -702,6 +754,8 @@ class Profile(Contact, Singleton):
|
|||||||
self.set_active(-1)
|
self.set_active(-1)
|
||||||
else:
|
else:
|
||||||
self.set_active(0)
|
self.set_active(0)
|
||||||
|
data = self._tox.get_savedata()
|
||||||
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
|
|
||||||
def add_friend(self, tox_id):
|
def add_friend(self, tox_id):
|
||||||
num = self._tox.friend_add_norequest(tox_id) # num - friend number
|
num = self._tox.friend_add_norequest(tox_id) # num - friend number
|
||||||
@@ -717,6 +771,9 @@ class Profile(Contact, Singleton):
|
|||||||
self._friends.append(friend)
|
self._friends.append(friend)
|
||||||
|
|
||||||
def block_user(self, tox_id):
|
def block_user(self, tox_id):
|
||||||
|
"""
|
||||||
|
Block user with specified tox id (or public key) - delete from friends list and ignore friend requests
|
||||||
|
"""
|
||||||
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
|
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
|
||||||
if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]:
|
if tox_id == self.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]:
|
||||||
return
|
return
|
||||||
@@ -727,15 +784,24 @@ class Profile(Contact, Singleton):
|
|||||||
try:
|
try:
|
||||||
num = self._tox.friend_by_public_key(tox_id)
|
num = self._tox.friend_by_public_key(tox_id)
|
||||||
self.delete_friend(num)
|
self.delete_friend(num)
|
||||||
|
data = self._tox.get_savedata()
|
||||||
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
except: # not in friend list
|
except: # not in friend list
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def unblock_user(self, tox_id, add_to_friend_list):
|
def unblock_user(self, tox_id, add_to_friend_list):
|
||||||
|
"""
|
||||||
|
Unblock user
|
||||||
|
:param tox_id: tox id of contact
|
||||||
|
:param add_to_friend_list: add this contact to friend list or not
|
||||||
|
"""
|
||||||
s = Settings.get_instance()
|
s = Settings.get_instance()
|
||||||
s['blocked'].remove(tox_id)
|
s['blocked'].remove(tox_id)
|
||||||
s.save()
|
s.save()
|
||||||
if add_to_friend_list:
|
if add_to_friend_list:
|
||||||
self.add_friend(tox_id)
|
self.add_friend(tox_id)
|
||||||
|
data = self._tox.get_savedata()
|
||||||
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Friend requests
|
# Friend requests
|
||||||
@@ -749,11 +815,19 @@ class Profile(Contact, Singleton):
|
|||||||
:return: True on success else error string
|
:return: True on success else error string
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
message = message or 'Add me to your contact list'
|
message = message or 'Hello! Add me to your contact list please'
|
||||||
if '@' in tox_id: # value like groupbot@toxme.io
|
if '@' in tox_id: # value like groupbot@toxme.io
|
||||||
tox_id = tox_dns(tox_id)
|
tox_id = tox_dns(tox_id)
|
||||||
if tox_id is None:
|
if tox_id is None:
|
||||||
raise Exception('TOX DNS lookup failed')
|
raise Exception('TOX DNS lookup failed')
|
||||||
|
if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key
|
||||||
|
self.add_friend(tox_id)
|
||||||
|
msgBox = QtGui.QMessageBox()
|
||||||
|
msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Friend added", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
text = (QtGui.QApplication.translate("MainWindow", 'Friend added without sending friend request', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgBox.setText(text)
|
||||||
|
msgBox.exec_()
|
||||||
|
else:
|
||||||
result = self._tox.friend_add(tox_id, message.encode('utf-8'))
|
result = self._tox.friend_add(tox_id, message.encode('utf-8'))
|
||||||
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
|
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
|
||||||
item = self.create_friend_item()
|
item = self.create_friend_item()
|
||||||
@@ -762,6 +836,8 @@ class Profile(Contact, Singleton):
|
|||||||
message_getter = self._history.messages_getter(tox_id)
|
message_getter = self._history.messages_getter(tox_id)
|
||||||
friend = Friend(message_getter, result, tox_id, '', item, tox_id)
|
friend = Friend(message_getter, result, tox_id, '', item, tox_id)
|
||||||
self._friends.append(friend)
|
self._friends.append(friend)
|
||||||
|
data = self._tox.get_savedata()
|
||||||
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
return True
|
return True
|
||||||
except Exception as ex: # wrong data
|
except Exception as ex: # wrong data
|
||||||
log('Friend request failed with ' + str(ex))
|
log('Friend request failed with ' + str(ex))
|
||||||
@@ -780,6 +856,8 @@ class Profile(Contact, Singleton):
|
|||||||
reply = QtGui.QMessageBox.question(None, fr_req, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
|
reply = QtGui.QMessageBox.question(None, fr_req, info, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
|
||||||
if reply == QtGui.QMessageBox.Yes: # accepted
|
if reply == QtGui.QMessageBox.Yes: # accepted
|
||||||
self.add_friend(tox_id)
|
self.add_friend(tox_id)
|
||||||
|
data = self._tox.get_savedata()
|
||||||
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
except Exception as ex: # something is wrong
|
except Exception as ex: # something is wrong
|
||||||
log('Accept friend request failed! ' + str(ex))
|
log('Accept friend request failed! ' + str(ex))
|
||||||
|
|
||||||
@@ -795,6 +873,7 @@ class Profile(Contact, Singleton):
|
|||||||
for key in self._file_transfers.keys():
|
for key in self._file_transfers.keys():
|
||||||
self._file_transfers[key].cancel()
|
self._file_transfers[key].cancel()
|
||||||
del self._file_transfers[key]
|
del self._file_transfers[key]
|
||||||
|
self._call.stop()
|
||||||
del self._tox
|
del self._tox
|
||||||
self._tox = restart()
|
self._tox = restart()
|
||||||
self.status = None
|
self.status = None
|
||||||
@@ -802,6 +881,7 @@ class Profile(Contact, Singleton):
|
|||||||
friend.status = None
|
friend.status = None
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
if hasattr(self, '_stop'):
|
||||||
self._call.stop()
|
self._call.stop()
|
||||||
del self._call
|
del self._call
|
||||||
|
|
||||||
@@ -884,10 +964,34 @@ class Profile(Contact, Singleton):
|
|||||||
tr.cancel()
|
tr.cancel()
|
||||||
else:
|
else:
|
||||||
tr.cancelled()
|
tr.cancelled()
|
||||||
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
|
del tr
|
||||||
del self._file_transfers[(friend_number, file_number)]
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
else:
|
else:
|
||||||
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
||||||
|
|
||||||
|
def pause_transfer(self, friend_number, file_number, by_friend=False):
|
||||||
|
"""
|
||||||
|
Pause transfer with specified data
|
||||||
|
"""
|
||||||
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
|
tr.pause(by_friend)
|
||||||
|
t = FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND'] if by_friend else FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']
|
||||||
|
self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
|
||||||
|
|
||||||
|
def resume_transfer(self, friend_number, file_number, by_friend=False):
|
||||||
|
"""
|
||||||
|
Resume transfer with specified data
|
||||||
|
"""
|
||||||
|
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
|
FILE_TRANSFER_MESSAGE_STATUS['OUTGOING'])
|
||||||
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
|
if by_friend:
|
||||||
|
tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
||||||
|
tr.signal()
|
||||||
|
else: # send seek control?
|
||||||
|
tr.send_control(TOX_FILE_CONTROL['RESUME'])
|
||||||
|
|
||||||
def accept_transfer(self, item, path, friend_number, file_number, size, inline=False):
|
def accept_transfer(self, item, path, friend_number, file_number, size, inline=False):
|
||||||
"""
|
"""
|
||||||
:param item: transfer item.
|
:param item: transfer item.
|
||||||
@@ -918,7 +1022,7 @@ class Profile(Contact, Singleton):
|
|||||||
self._file_transfers[(friend.number, st.get_file_number())] = st
|
self._file_transfers[(friend.number, st.get_file_number())] = st
|
||||||
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['OUTGOING'],
|
FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND'], # OUTGOING NOT STARTED
|
||||||
len(data),
|
len(data),
|
||||||
'toxygen_inline.png',
|
'toxygen_inline.png',
|
||||||
friend.number,
|
friend.number,
|
||||||
@@ -928,17 +1032,20 @@ class Profile(Contact, Singleton):
|
|||||||
st.set_state_changed_handler(item.update)
|
st.set_state_changed_handler(item.update)
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
def send_file(self, path):
|
def send_file(self, path, number=None):
|
||||||
"""
|
"""
|
||||||
Send file to current active friend
|
Send file to current active friend
|
||||||
:param path: file path
|
:param path: file path
|
||||||
|
:param number: friend_number
|
||||||
"""
|
"""
|
||||||
friend_number = self.get_active_number()
|
friend_number = number or self.get_active_number()
|
||||||
|
if self.get_friend_by_number(friend_number).status is None:
|
||||||
|
return
|
||||||
st = SendTransfer(path, self._tox, friend_number)
|
st = SendTransfer(path, self._tox, friend_number)
|
||||||
self._file_transfers[(friend_number, st.get_file_number())] = st
|
self._file_transfers[(friend_number, st.get_file_number())] = st
|
||||||
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['OUTGOING'],
|
FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND'], # OUTGOING NOT STARTED
|
||||||
os.path.getsize(path),
|
os.path.getsize(path),
|
||||||
os.path.basename(path),
|
os.path.basename(path),
|
||||||
friend_number,
|
friend_number,
|
||||||
@@ -952,7 +1059,7 @@ class Profile(Contact, Singleton):
|
|||||||
if (friend_number, file_number) in self._file_transfers:
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
transfer = self._file_transfers[(friend_number, file_number)]
|
transfer = self._file_transfers[(friend_number, file_number)]
|
||||||
transfer.write_chunk(position, data)
|
transfer.write_chunk(position, data)
|
||||||
if transfer.state:
|
if transfer.state in (2, 3): # finished or cancelled
|
||||||
if type(transfer) is ReceiveAvatar:
|
if type(transfer) is ReceiveAvatar:
|
||||||
self.get_friend_by_number(friend_number).load_avatar()
|
self.get_friend_by_number(friend_number).load_avatar()
|
||||||
self.set_active(None)
|
self.set_active(None)
|
||||||
@@ -963,7 +1070,7 @@ class Profile(Contact, Singleton):
|
|||||||
inline)
|
inline)
|
||||||
if friend_number == self.get_active_number():
|
if friend_number == self.get_active_number():
|
||||||
count = self._messages.count()
|
count = self._messages.count()
|
||||||
item = InlineImageItem(transfer.get_data())
|
item = InlineImageItem(transfer.get_data(), self._messages.width())
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
elem.setSizeHint(QtCore.QSize(600, item.height()))
|
elem.setSizeHint(QtCore.QSize(600, item.height()))
|
||||||
self._messages.insertItem(count + i + 1, elem)
|
self._messages.insertItem(count + i + 1, elem)
|
||||||
@@ -977,7 +1084,7 @@ class Profile(Contact, Singleton):
|
|||||||
if (friend_number, file_number) in self._file_transfers:
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
transfer = self._file_transfers[(friend_number, file_number)]
|
transfer = self._file_transfers[(friend_number, file_number)]
|
||||||
transfer.send_chunk(position, size)
|
transfer.send_chunk(position, size)
|
||||||
if transfer.state:
|
if transfer.state in (2, 3): # finished or cancelled
|
||||||
del self._file_transfers[(friend_number, file_number)]
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
if type(transfer) is not SendAvatar:
|
if type(transfer) is not SendAvatar:
|
||||||
if type(transfer) is SendFromBuffer and Settings.get_instance()['allow_inline']: # inline
|
if type(transfer) is SendFromBuffer and Settings.get_instance()['allow_inline']: # inline
|
||||||
@@ -985,7 +1092,7 @@ class Profile(Contact, Singleton):
|
|||||||
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'],
|
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'],
|
||||||
inline)
|
inline)
|
||||||
self.set_active(self._active_friend)
|
self.update()
|
||||||
else:
|
else:
|
||||||
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'])
|
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'])
|
||||||
|
|||||||
109
src/settings.py
@@ -2,11 +2,15 @@ from platform import system
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import locale
|
import locale
|
||||||
from util import Singleton, curr_directory
|
from util import Singleton, curr_directory, log
|
||||||
import pyaudio
|
import pyaudio
|
||||||
|
from toxencryptsave import LibToxEncryptSave
|
||||||
|
|
||||||
|
|
||||||
class Settings(Singleton, dict):
|
class Settings(Singleton, dict):
|
||||||
|
"""
|
||||||
|
Settings of current profile + global app settings
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.path = ProfileHelper.get_path() + str(name) + '.json'
|
self.path = ProfileHelper.get_path() + str(name) + '.json'
|
||||||
@@ -14,7 +18,15 @@ class Settings(Singleton, dict):
|
|||||||
if os.path.isfile(self.path):
|
if os.path.isfile(self.path):
|
||||||
with open(self.path) as fl:
|
with open(self.path) as fl:
|
||||||
data = fl.read()
|
data = fl.read()
|
||||||
super(self.__class__, self).__init__(json.loads(data))
|
inst = LibToxEncryptSave.get_instance()
|
||||||
|
if inst.has_password():
|
||||||
|
data = inst.pass_decrypt(data)
|
||||||
|
try:
|
||||||
|
info = json.loads(data)
|
||||||
|
except Exception as ex:
|
||||||
|
info = Settings.get_default_settings()
|
||||||
|
log('Parsing settings error: ' + str(ex))
|
||||||
|
super(self.__class__, self).__init__(info)
|
||||||
self.upgrade()
|
self.upgrade()
|
||||||
else:
|
else:
|
||||||
super(self.__class__, self).__init__(Settings.get_default_settings())
|
super(self.__class__, self).__init__(Settings.get_default_settings())
|
||||||
@@ -42,6 +54,9 @@ class Settings(Singleton, dict):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_settings():
|
def get_default_settings():
|
||||||
|
"""
|
||||||
|
Default profile settings
|
||||||
|
"""
|
||||||
return {
|
return {
|
||||||
'theme': 'default',
|
'theme': 'default',
|
||||||
'ipv6_enabled': True,
|
'ipv6_enabled': True,
|
||||||
@@ -57,14 +72,15 @@ class Settings(Singleton, dict):
|
|||||||
'language': 'English',
|
'language': 'English',
|
||||||
'save_history': False,
|
'save_history': False,
|
||||||
'allow_inline': True,
|
'allow_inline': True,
|
||||||
'allow_auto_accept': False,
|
'allow_auto_accept': True,
|
||||||
'auto_accept_path': None,
|
'auto_accept_path': None,
|
||||||
'show_online_friends': False,
|
'show_online_friends': False,
|
||||||
'auto_accept_from_friends': [],
|
'auto_accept_from_friends': [],
|
||||||
'friends_aliases': [],
|
'friends_aliases': [],
|
||||||
'typing_notifications': False,
|
'typing_notifications': False,
|
||||||
'calls_sound': True,
|
'calls_sound': True,
|
||||||
'blocked': []
|
'blocked': [],
|
||||||
|
'plugins': []
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -85,6 +101,9 @@ class Settings(Singleton, dict):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
text = json.dumps(self)
|
text = json.dumps(self)
|
||||||
|
inst = LibToxEncryptSave.get_instance()
|
||||||
|
if inst.has_password():
|
||||||
|
text = inst.pass_encrypt(text)
|
||||||
with open(self.path, 'w') as fl:
|
with open(self.path, 'w') as fl:
|
||||||
fl.write(text)
|
fl.write(text)
|
||||||
|
|
||||||
@@ -103,6 +122,9 @@ class Settings(Singleton, dict):
|
|||||||
fl.write(data)
|
fl.write(data)
|
||||||
|
|
||||||
def set_active_profile(self):
|
def set_active_profile(self):
|
||||||
|
"""
|
||||||
|
Mark current profile as active
|
||||||
|
"""
|
||||||
path = Settings.get_default_path() + 'toxygen.json'
|
path = Settings.get_default_path() + 'toxygen.json'
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
with open(path) as fl:
|
with open(path) as fl:
|
||||||
@@ -131,12 +153,51 @@ class Settings(Singleton, dict):
|
|||||||
return os.getenv('APPDATA') + '/Tox/'
|
return os.getenv('APPDATA') + '/Tox/'
|
||||||
|
|
||||||
|
|
||||||
class ProfileHelper(object):
|
class ProfileHelper(Singleton):
|
||||||
"""
|
"""
|
||||||
Class with static methods for search, load and save profiles
|
Class with methods for search, load and save profiles
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, path, name):
|
||||||
|
path = path.decode(locale.getpreferredencoding())
|
||||||
|
self._path = path + name + '.tox'
|
||||||
|
self._directory = path
|
||||||
|
# create /avatars if not exists:
|
||||||
|
directory = path + 'avatars'
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory)
|
||||||
|
|
||||||
|
def open_profile(self):
|
||||||
|
with open(self._path, 'rb') as fl:
|
||||||
|
data = fl.read()
|
||||||
|
if data:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
raise IOError('Save file has zero size!')
|
||||||
|
|
||||||
|
def get_dir(self):
|
||||||
|
return self._directory
|
||||||
|
|
||||||
|
def save_profile(self, data):
|
||||||
|
inst = LibToxEncryptSave.get_instance()
|
||||||
|
if inst.has_password():
|
||||||
|
data = inst.pass_encrypt(data)
|
||||||
|
with open(self._path, 'wb') as fl:
|
||||||
|
fl.write(data)
|
||||||
|
print 'Profile saved successfully'
|
||||||
|
|
||||||
|
def export_profile(self, new_path):
|
||||||
|
new_path += os.path.basename(self._path)
|
||||||
|
with open(self._path, 'rb') as fin:
|
||||||
|
data = fin.read()
|
||||||
|
with open(new_path, 'wb') as fout:
|
||||||
|
fout.write(data)
|
||||||
|
print 'Profile exported successfully'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def find_profiles():
|
def find_profiles():
|
||||||
|
"""
|
||||||
|
Find available tox profiles
|
||||||
|
"""
|
||||||
path = Settings.get_default_path()
|
path = Settings.get_default_path()
|
||||||
result = []
|
result = []
|
||||||
# check default path
|
# check default path
|
||||||
@@ -164,42 +225,8 @@ class ProfileHelper(object):
|
|||||||
data = json.loads(data)
|
data = json.loads(data)
|
||||||
if 'active_profile' in data:
|
if 'active_profile' in data:
|
||||||
return path in data['active_profile']
|
return path in data['active_profile']
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def open_profile(path, name):
|
|
||||||
path = path.decode(locale.getpreferredencoding())
|
|
||||||
ProfileHelper._path = path + name + '.tox'
|
|
||||||
ProfileHelper._directory = path
|
|
||||||
# create /avatars if not exists:
|
|
||||||
directory = path + 'avatars'
|
|
||||||
if not os.path.exists(directory):
|
|
||||||
os.makedirs(directory)
|
|
||||||
with open(ProfileHelper._path, 'rb') as fl:
|
|
||||||
data = fl.read()
|
|
||||||
if data:
|
|
||||||
return data
|
|
||||||
else:
|
|
||||||
raise IOError('Save file has zero size!')
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def save_profile(data, name=None):
|
|
||||||
if name is not None:
|
|
||||||
ProfileHelper._path = Settings.get_default_path() + name + '.tox'
|
|
||||||
ProfileHelper._directory = Settings.get_default_path()
|
|
||||||
with open(ProfileHelper._path, 'wb') as fl:
|
|
||||||
fl.write(data)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def export_profile(new_path):
|
|
||||||
new_path += os.path.basename(ProfileHelper._path)
|
|
||||||
with open(ProfileHelper._path, 'rb') as fin:
|
|
||||||
data = fin.read()
|
|
||||||
with open(new_path, 'wb') as fout:
|
|
||||||
fout.write(data)
|
|
||||||
print 'Data exported to: {}'.format(new_path)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_path():
|
def get_path():
|
||||||
return ProfileHelper._directory
|
return ProfileHelper.get_instance().get_dir()
|
||||||
|
|||||||
0
src/styles/rc/Hmovetoolbar.png
Normal file → Executable file
|
Before Width: | Height: | Size: 220 B After Width: | Height: | Size: 220 B |
0
src/styles/rc/Hsepartoolbar.png
Normal file → Executable file
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 172 B |
0
src/styles/rc/Vmovetoolbar.png
Normal file → Executable file
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 228 B |
0
src/styles/rc/Vsepartoolbar.png
Normal file → Executable file
|
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 187 B |
0
src/styles/rc/branch_closed-on.png
Normal file → Executable file
|
Before Width: | Height: | Size: 147 B After Width: | Height: | Size: 147 B |
0
src/styles/rc/branch_closed.png
Normal file → Executable file
|
Before Width: | Height: | Size: 160 B After Width: | Height: | Size: 160 B |
0
src/styles/rc/branch_open-on.png
Normal file → Executable file
|
Before Width: | Height: | Size: 150 B After Width: | Height: | Size: 150 B |
0
src/styles/rc/branch_open.png
Normal file → Executable file
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
BIN
src/styles/rc/checkbox_checked.png
Normal file → Executable file
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 4.4 KiB |
BIN
src/styles/rc/checkbox_checked_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 4.4 KiB |
BIN
src/styles/rc/checkbox_checked_focus.png
Normal file → Executable file
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 4.4 KiB |
0
src/styles/rc/checkbox_indeterminate.png
Normal file → Executable file
|
Before Width: | Height: | Size: 493 B After Width: | Height: | Size: 493 B |
0
src/styles/rc/checkbox_indeterminate_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 492 B After Width: | Height: | Size: 492 B |
0
src/styles/rc/checkbox_indeterminate_focus.png
Normal file → Executable file
|
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 514 B |
BIN
src/styles/rc/checkbox_unchecked.png
Normal file → Executable file
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 2.8 KiB |
BIN
src/styles/rc/checkbox_unchecked_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 2.8 KiB |
BIN
src/styles/rc/checkbox_unchecked_focus.png
Normal file → Executable file
|
Before Width: | Height: | Size: 483 B After Width: | Height: | Size: 2.8 KiB |
0
src/styles/rc/close-hover.png
Normal file → Executable file
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
0
src/styles/rc/close-pressed.png
Normal file → Executable file
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
0
src/styles/rc/close.png
Normal file → Executable file
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 586 B |
0
src/styles/rc/down_arrow.png
Normal file → Executable file
|
Before Width: | Height: | Size: 165 B After Width: | Height: | Size: 165 B |
0
src/styles/rc/down_arrow_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
0
src/styles/rc/left_arrow.png
Normal file → Executable file
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
0
src/styles/rc/left_arrow_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 166 B After Width: | Height: | Size: 166 B |
0
src/styles/rc/radio_checked.png
Normal file → Executable file
|
Before Width: | Height: | Size: 940 B After Width: | Height: | Size: 940 B |
0
src/styles/rc/radio_checked_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 972 B After Width: | Height: | Size: 972 B |
0
src/styles/rc/radio_checked_focus.png
Normal file → Executable file
|
Before Width: | Height: | Size: 933 B After Width: | Height: | Size: 933 B |
0
src/styles/rc/radio_unchecked.png
Normal file → Executable file
|
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 728 B |
0
src/styles/rc/radio_unchecked_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 760 B |
0
src/styles/rc/radio_unchecked_focus.png
Normal file → Executable file
|
Before Width: | Height: | Size: 724 B After Width: | Height: | Size: 724 B |
0
src/styles/rc/right_arrow.png
Normal file → Executable file
|
Before Width: | Height: | Size: 160 B After Width: | Height: | Size: 160 B |
0
src/styles/rc/right_arrow_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 160 B After Width: | Height: | Size: 160 B |
0
src/styles/rc/sizegrip.png
Normal file → Executable file
|
Before Width: | Height: | Size: 129 B After Width: | Height: | Size: 129 B |
0
src/styles/rc/stylesheet-branch-end.png
Normal file → Executable file
|
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 224 B |
0
src/styles/rc/stylesheet-branch-more.png
Normal file → Executable file
|
Before Width: | Height: | Size: 182 B After Width: | Height: | Size: 182 B |
0
src/styles/rc/stylesheet-vline.png
Normal file → Executable file
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
0
src/styles/rc/transparent.png
Normal file → Executable file
|
Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 195 B |
0
src/styles/rc/undock.png
Normal file → Executable file
|
Before Width: | Height: | Size: 578 B After Width: | Height: | Size: 578 B |
0
src/styles/rc/up_arrow.png
Normal file → Executable file
|
Before Width: | Height: | Size: 158 B After Width: | Height: | Size: 158 B |
0
src/styles/rc/up_arrow_disabled.png
Normal file → Executable file
|
Before Width: | Height: | Size: 159 B After Width: | Height: | Size: 159 B |
@@ -668,7 +668,7 @@ QComboBox QAbstractItemView
|
|||||||
background-color: #201F1F;
|
background-color: #201F1F;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
selection-background-color: #3d8ec9;
|
selection-background-color: #A9A9A9;
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox::drop-down
|
QComboBox::drop-down
|
||||||
@@ -1251,9 +1251,7 @@ QPushButton:hover
|
|||||||
|
|
||||||
#friends_list:item:selected
|
#friends_list:item:selected
|
||||||
{
|
{
|
||||||
border: 2px solid;
|
background-color: #333333;
|
||||||
background-color: transparent;
|
|
||||||
border-color: green;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#toxygen
|
#toxygen
|
||||||
@@ -1269,3 +1267,8 @@ QCheckBox
|
|||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QListWidget > QLabel
|
||||||
|
{
|
||||||
|
color: #A9A9A9;
|
||||||
|
}
|
||||||
142
src/tox.py
@@ -32,6 +32,7 @@ def bin_to_string(raw_id, length):
|
|||||||
|
|
||||||
|
|
||||||
class Tox(object):
|
class Tox(object):
|
||||||
|
|
||||||
libtoxcore = LibToxCore()
|
libtoxcore = LibToxCore()
|
||||||
|
|
||||||
def __init__(self, tox_options=None, tox_pointer=None):
|
def __init__(self, tox_options=None, tox_pointer=None):
|
||||||
@@ -57,9 +58,9 @@ class Tox(object):
|
|||||||
raise MemoryError('The function was unable to allocate enough '
|
raise MemoryError('The function was unable to allocate enough '
|
||||||
'memory to store the internal structures for the Tox object.')
|
'memory to store the internal structures for the Tox object.')
|
||||||
elif tox_err_new == TOX_ERR_NEW['PORT_ALLOC']:
|
elif tox_err_new == TOX_ERR_NEW['PORT_ALLOC']:
|
||||||
raise MemoryError('The function was unable to bind to a port. This may mean that all ports have already'
|
raise RuntimeError('The function was unable to bind to a port. This may mean that all ports have '
|
||||||
' been bound, e.g. by other Tox instances, or it may mean a permission error. You may'
|
'already been bound, e.g. by other Tox instances, or it may mean a permission error.'
|
||||||
' be able to gather more information from errno.')
|
' You may be able to gather more information from errno.')
|
||||||
elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']:
|
elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_TYPE']:
|
||||||
raise ArgumentError('proxy_type was invalid.')
|
raise ArgumentError('proxy_type was invalid.')
|
||||||
elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']:
|
elif tox_err_new == TOX_ERR_NEW['PROXY_BAD_HOST']:
|
||||||
@@ -89,6 +90,8 @@ class Tox(object):
|
|||||||
self.file_chunk_request_cb = None
|
self.file_chunk_request_cb = None
|
||||||
self.file_recv_cb = None
|
self.file_recv_cb = None
|
||||||
self.file_recv_chunk_cb = None
|
self.file_recv_chunk_cb = None
|
||||||
|
self.friend_lossy_packet_cb = None
|
||||||
|
self.friend_lossless_packet_cb = None
|
||||||
|
|
||||||
self.AV = ToxAV(self._tox_pointer)
|
self.AV = ToxAV(self._tox_pointer)
|
||||||
|
|
||||||
@@ -218,7 +221,7 @@ class Tox(object):
|
|||||||
"""
|
"""
|
||||||
tox_err_bootstrap = c_int()
|
tox_err_bootstrap = c_int()
|
||||||
result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port),
|
result = Tox.libtoxcore.tox_add_tcp_relay(self._tox_pointer, c_char_p(address), c_uint16(port),
|
||||||
c_char_p(public_key), byref(tox_err_bootstrap))
|
string_to_bin(public_key), byref(tox_err_bootstrap))
|
||||||
tox_err_bootstrap = tox_err_bootstrap.value
|
tox_err_bootstrap = tox_err_bootstrap.value
|
||||||
if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
|
if tox_err_bootstrap == TOX_ERR_BOOTSTRAP['OK']:
|
||||||
return bool(result)
|
return bool(result)
|
||||||
@@ -674,6 +677,7 @@ class Tox(object):
|
|||||||
|
|
||||||
The data written to `name` is equal to the data received by the last `friend_name` callback.
|
The data written to `name` is equal to the data received by the last `friend_name` callback.
|
||||||
|
|
||||||
|
:param friend_number: number of friend
|
||||||
:param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name.
|
:param name: pointer (c_char_p) to a valid memory region large enough to store the friend's name.
|
||||||
:return: name of the friend
|
:return: name of the friend
|
||||||
"""
|
"""
|
||||||
@@ -949,7 +953,7 @@ class Tox(object):
|
|||||||
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']:
|
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['FRIEND_NOT_CONNECTED']:
|
||||||
raise ArgumentError('This client is currently not connected to the friend.')
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']:
|
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['SENDQ']:
|
||||||
raise ArgumentError('An allocation error occurred while increasing the send queue size.')
|
raise MemoryError('An allocation error occurred while increasing the send queue size.')
|
||||||
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']:
|
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['TOO_LONG']:
|
||||||
raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.')
|
raise ArgumentError('Message length exceeded TOX_MAX_MESSAGE_LENGTH.')
|
||||||
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']:
|
elif tox_err_friend_send_message == TOX_ERR_FRIEND_SEND_MESSAGE['EMPTY']:
|
||||||
@@ -1055,16 +1059,16 @@ class Tox(object):
|
|||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_FOUND']:
|
||||||
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['FRIEND_NOT_CONNECTED']:
|
||||||
raise RuntimeError('This client is currently not connected to the friend.')
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_FOUND']:
|
||||||
raise ArgumentError('No file transfer with the given file number was found for the given friend.')
|
raise ArgumentError('No file transfer with the given file number was found for the given friend.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['NOT_PAUSED']:
|
||||||
raise ArgumentError('A RESUME control was sent, but the file transfer is running normally.')
|
raise RuntimeError('A RESUME control was sent, but the file transfer is running normally.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['DENIED']:
|
||||||
raise ArgumentError('A RESUME control was sent, but the file transfer was paused by the other party. Only '
|
raise RuntimeError('A RESUME control was sent, but the file transfer was paused by the other party. Only '
|
||||||
'the party that paused the transfer can resume it.')
|
'the party that paused the transfer can resume it.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['ALREADY_PAUSED']:
|
||||||
raise ArgumentError('A PAUSE control was sent, but the file transfer was already paused.')
|
raise RuntimeError('A PAUSE control was sent, but the file transfer was already paused.')
|
||||||
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']:
|
elif tox_err_file_control == TOX_ERR_FILE_CONTROL['SENDQ']:
|
||||||
raise RuntimeError('Packet queue is full.')
|
raise RuntimeError('Packet queue is full.')
|
||||||
|
|
||||||
@@ -1110,15 +1114,15 @@ class Tox(object):
|
|||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_FOUND']:
|
||||||
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['FRIEND_NOT_CONNECTED']:
|
||||||
raise RuntimeError('This client is currently not connected to the friend.')
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['NOT_FOUND']:
|
||||||
raise ArgumentError('No file transfer with the given file number was found for the given friend.')
|
raise ArgumentError('No file transfer with the given file number was found for the given friend.')
|
||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SEEK_DENIED']:
|
||||||
raise ArgumentError('File was not in a state where it could be seeked.')
|
raise IOError('File was not in a state where it could be seeked.')
|
||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['INVALID_POSITION']:
|
||||||
raise ArgumentError('Seek position was invalid')
|
raise ArgumentError('Seek position was invalid')
|
||||||
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']:
|
elif tox_err_file_seek == TOX_ERR_FILE_SEEK['SENDQ']:
|
||||||
raise ArgumentError('Packet queue is full.')
|
raise RuntimeError('Packet queue is full.')
|
||||||
|
|
||||||
def file_get_file_id(self, friend_number, file_number, file_id=None):
|
def file_get_file_id(self, friend_number, file_number, file_id=None):
|
||||||
"""
|
"""
|
||||||
@@ -1206,7 +1210,7 @@ class Tox(object):
|
|||||||
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']:
|
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_FOUND']:
|
||||||
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
raise ArgumentError('The friend_number passed did not designate a valid friend.')
|
||||||
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']:
|
elif tox_err_file_send == TOX_ERR_FILE_SEND['FRIEND_NOT_CONNECTED']:
|
||||||
raise RuntimeError('This client is currently not connected to the friend.')
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']:
|
elif tox_err_file_send == TOX_ERR_FILE_SEND['NAME_TOO_LONG']:
|
||||||
raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.')
|
raise ArgumentError('Filename length exceeded TOX_MAX_FILENAME_LENGTH bytes.')
|
||||||
elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']:
|
elif tox_err_file_send == TOX_ERR_FILE_SEND['TOO_MANY']:
|
||||||
@@ -1252,7 +1256,7 @@ class Tox(object):
|
|||||||
'adjusted according to maximum transmission unit and the expected end of the file. '
|
'adjusted according to maximum transmission unit and the expected end of the file. '
|
||||||
'Trying to send less or more than requested will return this error.')
|
'Trying to send less or more than requested will return this error.')
|
||||||
elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']:
|
elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['SENDQ']:
|
||||||
raise ArgumentError('Packet queue is full.')
|
raise RuntimeError('Packet queue is full.')
|
||||||
elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']:
|
elif tox_err_file_send_chunk == TOX_ERR_FILE_SEND_CHUNK['WRONG_POSITION']:
|
||||||
raise ArgumentError('Position parameter was wrong.')
|
raise ArgumentError('Position parameter was wrong.')
|
||||||
|
|
||||||
@@ -1346,9 +1350,117 @@ class Tox(object):
|
|||||||
self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data)
|
self.libtoxcore.tox_callback_file_recv_chunk(self._tox_pointer, self.file_recv_chunk_cb, user_data)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# TODO Low-level custom packet sending and receiving
|
# Low-level custom packet sending and receiving
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def friend_send_lossy_packet(self, friend_number, data):
|
||||||
|
"""
|
||||||
|
Send a custom lossy packet to a friend.
|
||||||
|
The first byte of data must be in the range 200-254. Maximum length of a
|
||||||
|
custom packet is TOX_MAX_CUSTOM_PACKET_SIZE.
|
||||||
|
|
||||||
|
Lossy packets behave like UDP packets, meaning they might never reach the
|
||||||
|
other side or might arrive more than once (if someone is messing with the
|
||||||
|
connection) or might arrive in the wrong order.
|
||||||
|
|
||||||
|
Unless latency is an issue, it is recommended that you use lossless custom packets instead.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend this lossy packet
|
||||||
|
:param data: python string containing the packet data
|
||||||
|
:return: True on success.
|
||||||
|
"""
|
||||||
|
tox_err_friend_custom_packet = c_int()
|
||||||
|
result = self.libtoxcore.tox_friend_send_lossy_packet(self._tox_pointer, c_uint32(friend_number),
|
||||||
|
c_char_p(data), c_size_t(len(data)),
|
||||||
|
byref(tox_err_friend_custom_packet))
|
||||||
|
tox_err_friend_custom_packet = tox_err_friend_custom_packet.value
|
||||||
|
if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']:
|
||||||
|
raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend number did not designate a valid friend.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']:
|
||||||
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']:
|
||||||
|
raise ArgumentError('The first byte of data was not in the specified range for the packet type.'
|
||||||
|
'This range is 200-254 for lossy, and 160-191 for lossless packets.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']:
|
||||||
|
raise ArgumentError('Attempted to send an empty packet.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']:
|
||||||
|
raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']:
|
||||||
|
raise RuntimeError('Packet queue is full.')
|
||||||
|
|
||||||
|
def friend_send_lossless_packet(self, friend_number, data):
|
||||||
|
"""
|
||||||
|
Send a custom lossless packet to a friend.
|
||||||
|
The first byte of data must be in the range 160-191. Maximum length of a
|
||||||
|
custom packet is TOX_MAX_CUSTOM_PACKET_SIZE.
|
||||||
|
|
||||||
|
Lossless packet behaviour is comparable to TCP (reliability, arrive in order)
|
||||||
|
but with packets instead of a stream.
|
||||||
|
|
||||||
|
:param friend_number: The friend number of the friend this lossless packet
|
||||||
|
:param data: python string containing the packet data
|
||||||
|
:return: True on success.
|
||||||
|
"""
|
||||||
|
tox_err_friend_custom_packet = c_int()
|
||||||
|
result = self.libtoxcore.tox_friend_send_lossless_packet(self._tox_pointer, c_uint32(friend_number),
|
||||||
|
c_char_p(data), c_size_t(len(data)),
|
||||||
|
byref(tox_err_friend_custom_packet))
|
||||||
|
tox_err_friend_custom_packet = tox_err_friend_custom_packet.value
|
||||||
|
if tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['OK']:
|
||||||
|
return bool(result)
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['NULL']:
|
||||||
|
raise ArgumentError('One of the arguments to the function was NULL when it was not expected.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_FOUND']:
|
||||||
|
raise ArgumentError('The friend number did not designate a valid friend.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['FRIEND_NOT_CONNECTED']:
|
||||||
|
raise ArgumentError('This client is currently not connected to the friend.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['INVALID']:
|
||||||
|
raise ArgumentError('The first byte of data was not in the specified range for the packet type.'
|
||||||
|
'This range is 200-254 for lossy, and 160-191 for lossless packets.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['EMPTY']:
|
||||||
|
raise ArgumentError('Attempted to send an empty packet.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['TOO_LONG']:
|
||||||
|
raise ArgumentError('Packet data length exceeded TOX_MAX_CUSTOM_PACKET_SIZE.')
|
||||||
|
elif tox_err_friend_custom_packet == TOX_ERR_FRIEND_CUSTOM_PACKET['SENDQ']:
|
||||||
|
raise RuntimeError('Packet queue is full.')
|
||||||
|
|
||||||
|
def callback_friend_lossy_packet(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `friend_lossy_packet` event. Pass NULL to unset.
|
||||||
|
|
||||||
|
:param callback: Python function.
|
||||||
|
Should take pointer (c_void_p) to Tox object,
|
||||||
|
friend_number (c_uint32) - The friend number of the friend who sent a lossy packet,
|
||||||
|
A byte array (c_uint8 array) containing the received packet data,
|
||||||
|
length (c_size_t) - The length of the packet data byte array,
|
||||||
|
pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
|
||||||
|
self.friend_lossy_packet_cb = c_callback(callback)
|
||||||
|
self.libtoxcore.tox_callback_friend_lossy_packet(self._tox_pointer, self.friend_lossy_packet_cb, user_data)
|
||||||
|
|
||||||
|
def callback_friend_lossless_packet(self, callback, user_data):
|
||||||
|
"""
|
||||||
|
Set the callback for the `friend_lossless_packet` event. Pass NULL to unset.
|
||||||
|
|
||||||
|
:param callback: Python function.
|
||||||
|
Should take pointer (c_void_p) to Tox object,
|
||||||
|
friend_number (c_uint32) - The friend number of the friend who sent a lossless packet,
|
||||||
|
A byte array (c_uint8 array) containing the received packet data,
|
||||||
|
length (c_size_t) - The length of the packet data byte array,
|
||||||
|
pointer (c_void_p) to user_data
|
||||||
|
:param user_data: pointer (c_void_p) to user data
|
||||||
|
"""
|
||||||
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, POINTER(c_uint8), c_size_t, c_void_p)
|
||||||
|
self.friend_lossless_packet_cb = c_callback(callback)
|
||||||
|
self.libtoxcore.tox_callback_friend_lossless_packet(self._tox_pointer, self.friend_lossless_packet_cb,
|
||||||
|
user_data)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Low-level network information
|
# Low-level network information
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ class ToxAV(object):
|
|||||||
height Height (c_uint16) of the frame in pixels.
|
height Height (c_uint16) of the frame in pixels.
|
||||||
y
|
y
|
||||||
u
|
u
|
||||||
v Plane data (c_char_p).
|
v Plane data (POINTER(c_uint8)).
|
||||||
The size of plane data is derived from width and height where
|
The size of plane data is derived from width and height where
|
||||||
Y = MAX(width, abs(ystride)) * height,
|
Y = MAX(width, abs(ystride)) * height,
|
||||||
U = MAX(width/2, abs(ustride)) * (height/2) and
|
U = MAX(width/2, abs(ustride)) * (height/2) and
|
||||||
@@ -357,7 +357,7 @@ class ToxAV(object):
|
|||||||
user_data pointer (c_void_p) to user_data
|
user_data pointer (c_void_p) to user_data
|
||||||
:param user_data: pointer (c_void_p) to user data
|
:param user_data: pointer (c_void_p) to user data
|
||||||
"""
|
"""
|
||||||
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, c_char_p, c_char_p, c_char_p, c_int32,
|
c_callback = CFUNCTYPE(None, c_void_p, c_uint32, c_uint16, c_uint16, POINTER(c_uint8), POINTER(c_uint8),
|
||||||
c_int32, c_int32, c_void_p)
|
POINTER(c_uint8), c_int32, c_int32, c_int32, c_void_p)
|
||||||
self.video_receive_frame_cb = c_callback(callback)
|
self.video_receive_frame_cb = c_callback(callback)
|
||||||
ToxAV.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data)
|
ToxAV.libtoxav.toxav_callback_video_receive_frame(self._toxav_pointer, self.video_receive_frame_cb, user_data)
|
||||||
|
|||||||
110
src/toxencryptsave.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import libtox
|
||||||
|
import util
|
||||||
|
from ctypes import c_size_t, create_string_buffer, byref, c_int, ArgumentError, c_char_p, c_bool
|
||||||
|
|
||||||
|
|
||||||
|
TOX_ERR_ENCRYPTION = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Some input data, or maybe the output pointer, was null.
|
||||||
|
'NULL': 1,
|
||||||
|
# The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
|
||||||
|
# functions accepting keys do not produce this error.
|
||||||
|
'KEY_DERIVATION_FAILED': 2,
|
||||||
|
# The encryption itself failed.
|
||||||
|
'FAILED': 3
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_DECRYPTION = {
|
||||||
|
# The function returned successfully.
|
||||||
|
'OK': 0,
|
||||||
|
# Some input data, or maybe the output pointer, was null.
|
||||||
|
'NULL': 1,
|
||||||
|
# The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes
|
||||||
|
'INVALID_LENGTH': 2,
|
||||||
|
# The input data is missing the magic number (i.e. wasn't created by this module, or is corrupted)
|
||||||
|
'BAD_FORMAT': 3,
|
||||||
|
# The crypto lib was unable to derive a key from the given passphrase, which is usually a lack of memory issue. The
|
||||||
|
# functions accepting keys do not produce this error.
|
||||||
|
'KEY_DERIVATION_FAILED': 4,
|
||||||
|
# The encrypted byte array could not be decrypted. Either the data was corrupt or the password/key was incorrect.
|
||||||
|
'FAILED': 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_PASS_ENCRYPTION_EXTRA_LENGTH = 80
|
||||||
|
|
||||||
|
|
||||||
|
class LibToxEncryptSave(util.Singleton):
|
||||||
|
|
||||||
|
libtoxencryptsave = libtox.LibToxEncryptSave()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._passphrase = None
|
||||||
|
|
||||||
|
def set_password(self, passphrase):
|
||||||
|
self._passphrase = passphrase
|
||||||
|
|
||||||
|
def has_password(self):
|
||||||
|
return bool(self._passphrase)
|
||||||
|
|
||||||
|
def is_data_encrypted(self, data):
|
||||||
|
func = self.libtoxencryptsave.tox_is_data_encrypted
|
||||||
|
func.restype = c_bool
|
||||||
|
result = func(c_char_p(data))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def pass_encrypt(self, data):
|
||||||
|
"""
|
||||||
|
Encrypts the given data with the given passphrase.
|
||||||
|
|
||||||
|
:return: output array
|
||||||
|
"""
|
||||||
|
out = create_string_buffer(len(data) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||||
|
tox_err_encryption = c_int()
|
||||||
|
self.libtoxencryptsave.tox_pass_encrypt(c_char_p(data),
|
||||||
|
c_size_t(len(data)),
|
||||||
|
c_char_p(self._passphrase),
|
||||||
|
c_size_t(len(self._passphrase)),
|
||||||
|
out,
|
||||||
|
byref(tox_err_encryption))
|
||||||
|
tox_err_encryption = tox_err_encryption.value
|
||||||
|
if tox_err_encryption == TOX_ERR_ENCRYPTION['OK']:
|
||||||
|
return out[:]
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['NULL']:
|
||||||
|
raise ArgumentError('Some input data, or maybe the output pointer, was null.')
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['KEY_DERIVATION_FAILED']:
|
||||||
|
raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
|
||||||
|
' lack of memory issue. The functions accepting keys do not produce this error.')
|
||||||
|
elif tox_err_encryption == TOX_ERR_ENCRYPTION['FAILED']:
|
||||||
|
raise RuntimeError('The encryption itself failed.')
|
||||||
|
|
||||||
|
def pass_decrypt(self, data):
|
||||||
|
"""
|
||||||
|
Decrypts the given data with the given passphrase.
|
||||||
|
|
||||||
|
:return: output array
|
||||||
|
"""
|
||||||
|
out = create_string_buffer(len(data) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH)
|
||||||
|
tox_err_decryption = c_int()
|
||||||
|
self.libtoxencryptsave.tox_pass_decrypt(c_char_p(data),
|
||||||
|
c_size_t(len(data)),
|
||||||
|
c_char_p(self._passphrase),
|
||||||
|
c_size_t(len(self._passphrase)),
|
||||||
|
out,
|
||||||
|
byref(tox_err_decryption))
|
||||||
|
tox_err_decryption = tox_err_decryption.value
|
||||||
|
if tox_err_decryption == TOX_ERR_DECRYPTION['OK']:
|
||||||
|
return out[:]
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['NULL']:
|
||||||
|
raise ArgumentError('Some input data, or maybe the output pointer, was null.')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['INVALID_LENGTH']:
|
||||||
|
raise ArgumentError('The input data was shorter than TOX_PASS_ENCRYPTION_EXTRA_LENGTH bytes')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['BAD_FORMAT']:
|
||||||
|
raise ArgumentError('The input data is missing the magic number (i.e. wasn\'t created by this module, or is'
|
||||||
|
' corrupted)')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['KEY_DERIVATION_FAILED']:
|
||||||
|
raise RuntimeError('The crypto lib was unable to derive a key from the given passphrase, which is usually a'
|
||||||
|
' lack of memory issue. The functions accepting keys do not produce this error.')
|
||||||
|
elif tox_err_decryption == TOX_ERR_DECRYPTION['FAILED']:
|
||||||
|
raise RuntimeError('The encrypted byte array could not be decrypted. Either the data was corrupt or the '
|
||||||
|
'password/key was incorrect.')
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
SOURCES = main.py profile.py menu.py list_items.py loginscreen.py mainscreen.py
|
SOURCES = main.py profile.py menu.py list_items.py loginscreen.py mainscreen.py plugins/plugin_super_class.py
|
||||||
TRANSLATIONS = translations/en_GB.ts translations/ru_RU.ts translations/fr_FR.ts
|
TRANSLATIONS = translations/en_GB.ts translations/ru_RU.ts translations/fr_FR.ts
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS><TS version="1.1" language="en">
|
||||||
<TS version="2.0" language="en">
|
|
||||||
<context>
|
<context>
|
||||||
<name>AddContact</name>
|
<name>AddContact</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="65"/>
|
<location filename="menu.py" line="69"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation>Add contact</translation>
|
<translation>Add contact</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="67"/>
|
<location filename="menu.py" line="71"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation>TOX ID:</translation>
|
<translation>TOX ID:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="68"/>
|
<location filename="menu.py" line="72"/>
|
||||||
<source>Message:</source>
|
<source>Message:</source>
|
||||||
<translation>Message:</translation>
|
<translation>Message:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -22,220 +21,395 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>Form</name>
|
<name>Form</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="66"/>
|
<location filename="menu.py" line="70"/>
|
||||||
<source>Send request</source>
|
<source>Send request</source>
|
||||||
<translation>Send request</translation>
|
<translation>Send request</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="220"/>
|
<location filename="menu.py" line="287"/>
|
||||||
<source>IPv6</source>
|
<source>IPv6</source>
|
||||||
<translation>IPv6</translation>
|
<translation>IPv6</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="221"/>
|
<location filename="menu.py" line="288"/>
|
||||||
<source>UDP</source>
|
<source>UDP</source>
|
||||||
<translation>UDP</translation>
|
<translation>UDP</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="222"/>
|
<location filename="menu.py" line="289"/>
|
||||||
<source>Proxy</source>
|
<source>Proxy</source>
|
||||||
<translation>Proxy</translation>
|
<translation>Proxy</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="223"/>
|
<location filename="menu.py" line="290"/>
|
||||||
<source>IP:</source>
|
<source>IP:</source>
|
||||||
<translation>IP:</translation>
|
<translation>IP:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="224"/>
|
<location filename="menu.py" line="291"/>
|
||||||
<source>Port:</source>
|
<source>Port:</source>
|
||||||
<translation>Port:</translation>
|
<translation>Port:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="90"/>
|
<location filename="mainscreen.py" line="99"/>
|
||||||
<source>Online contacts</source>
|
<source>Online contacts</source>
|
||||||
<translation>Online contacts</translation>
|
<translation type="obsolete">Online contacts</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="293"/>
|
||||||
|
<source>HTTP</source>
|
||||||
|
<translation>HTTP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="295"/>
|
||||||
|
<source>WARNING:
|
||||||
|
using proxy with enabled UDP
|
||||||
|
can produce IP leak</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="95"/>
|
<location filename="mainscreen.py" line="115"/>
|
||||||
<source>Profile</source>
|
<source>Profile</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="101"/>
|
<location filename="mainscreen.py" line="121"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="286"/>
|
<location filename="mainscreen.py" line="363"/>
|
||||||
<source>About</source>
|
<source>About</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="94"/>
|
<location filename="mainscreen.py" line="114"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="96"/>
|
<location filename="mainscreen.py" line="116"/>
|
||||||
<source>Privacy</source>
|
<source>Privacy</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="97"/>
|
<location filename="mainscreen.py" line="117"/>
|
||||||
<source>Interface</source>
|
<source>Interface</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="98"/>
|
<location filename="mainscreen.py" line="118"/>
|
||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="99"/>
|
<location filename="mainscreen.py" line="119"/>
|
||||||
<source>Network</source>
|
<source>Network</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="100"/>
|
<location filename="mainscreen.py" line="120"/>
|
||||||
<source>About program</source>
|
<source>About program</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="762"/>
|
<location filename="profile.py" line="854"/>
|
||||||
<source>User {} wants to add you to contact list. Message:
|
<source>User {} wants to add you to contact list. Message:
|
||||||
{}</source>
|
{}</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="764"/>
|
<location filename="profile.py" line="856"/>
|
||||||
<source>Friend request</source>
|
<source>Friend request</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="287"/>
|
<location filename="mainscreen.py" line="411"/>
|
||||||
<source>Toxygen is Tox client written on Python 2.7. Version: </source>
|
|
||||||
<translation></translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="mainscreen.py" line="329"/>
|
|
||||||
<source>Choose file</source>
|
<source>Choose file</source>
|
||||||
<translation></translation>
|
<translation>Choose file</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="367"/>
|
<location filename="mainscreen.py" line="452"/>
|
||||||
<source>Disallow auto accept</source>
|
<source>Disallow auto accept</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="368"/>
|
<location filename="mainscreen.py" line="453"/>
|
||||||
<source>Allow auto accept</source>
|
<source>Allow auto accept</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="370"/>
|
<location filename="mainscreen.py" line="455"/>
|
||||||
<source>Set alias</source>
|
<source>Set alias</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="371"/>
|
<location filename="mainscreen.py" line="456"/>
|
||||||
<source>Clear history</source>
|
<source>Clear history</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="372"/>
|
<location filename="mainscreen.py" line="457"/>
|
||||||
<source>Copy public key</source>
|
<source>Copy public key</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="374"/>
|
<location filename="mainscreen.py" line="459"/>
|
||||||
<source>Remove friend</source>
|
<source>Remove friend</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="639"/>
|
<location filename="profile.py" line="700"/>
|
||||||
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
||||||
<translation>Enter new alias for friend {} or leave empty to use friend's name:</translation>
|
<translation>Enter new alias for friend {} or leave empty to use friend's name:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="102"/>
|
<location filename="mainscreen.py" line="122"/>
|
||||||
<source>Audio</source>
|
<source>Audio</source>
|
||||||
<translation>Audio</translation>
|
<translation>Audio</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="112"/>
|
||||||
|
<source>Find contact</source>
|
||||||
|
<translation type="obsolete">Find contact</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="profile.py" line="826"/>
|
||||||
|
<source>Friend added</source>
|
||||||
|
<translation>Friend added</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="364"/>
|
||||||
|
<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="827"/>
|
||||||
|
<source>Friend added without sending friend request</source>
|
||||||
|
<translation>Friend added without sending friend request</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="list_items.py" line="234"/>
|
||||||
|
<source>Choose folder</source>
|
||||||
|
<translation>Choose folder</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="124"/>
|
||||||
|
<source>Send screenshot</source>
|
||||||
|
<translation>Send screenshot</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="125"/>
|
||||||
|
<source>Send file</source>
|
||||||
|
<translation>Send file</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="126"/>
|
||||||
|
<source>Send message</source>
|
||||||
|
<translation>Send message</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="127"/>
|
||||||
|
<source>Start audio call with friend</source>
|
||||||
|
<translation>Start audio call with friend</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="462"/>
|
||||||
|
<source>Plugins</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="110"/>
|
||||||
|
<source>List of plugins</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="123"/>
|
||||||
|
<source>Search</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="129"/>
|
||||||
|
<source>All</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="130"/>
|
||||||
|
<source>Online</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NetworkSettings</name>
|
<name>NetworkSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="219"/>
|
<location filename="menu.py" line="286"/>
|
||||||
<source>Network settings</source>
|
<source>Network settings</source>
|
||||||
<translation>Network settings</translation>
|
<translation>Network settings</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="225"/>
|
<location filename="menu.py" line="292"/>
|
||||||
<source>Restart TOX core</source>
|
<source>Restart TOX core</source>
|
||||||
<translation>Restart Tox core</translation>
|
<translation>Restart Tox core</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="121"/>
|
||||||
|
<source>List of commands for plugin {}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="122"/>
|
||||||
|
<source>No commands available</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginsForm</name>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="624"/>
|
||||||
|
<source>Plugins</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="625"/>
|
||||||
|
<source>Open selected plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="637"/>
|
||||||
|
<source>No GUI found for this plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="651"/>
|
||||||
|
<source>No description available</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="663"/>
|
||||||
|
<source>Disable plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="665"/>
|
||||||
|
<source>Enable plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ProfileSettingsForm</name>
|
<name>ProfileSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="131"/>
|
<location filename="menu.py" line="161"/>
|
||||||
<source>Export profile</source>
|
<source>Export profile</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="132"/>
|
<location filename="menu.py" line="162"/>
|
||||||
<source>Profile settings</source>
|
<source>Profile settings</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="133"/>
|
<location filename="menu.py" line="163"/>
|
||||||
<source>Name:</source>
|
<source>Name:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="134"/>
|
<location filename="menu.py" line="164"/>
|
||||||
<source>Status:</source>
|
<source>Status:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="135"/>
|
<location filename="menu.py" line="165"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="136"/>
|
<location filename="menu.py" line="166"/>
|
||||||
<source>Copy TOX ID</source>
|
<source>Copy TOX ID</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="137"/>
|
<location filename="menu.py" line="167"/>
|
||||||
<source>New avatar</source>
|
<source>New avatar</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="138"/>
|
<location filename="menu.py" line="168"/>
|
||||||
<source>Reset avatar</source>
|
<source>Reset avatar</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="169"/>
|
||||||
|
<source>New NoSpam</source>
|
||||||
|
<translation>New NoSpam</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="170"/>
|
||||||
|
<source>Profile password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="171"/>
|
||||||
|
<source>Password (at least 8 symbols)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="172"/>
|
||||||
|
<source>Confirm password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="173"/>
|
||||||
|
<source>Set password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="191"/>
|
||||||
|
<source>Passwords do not match</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="175"/>
|
||||||
|
<source>Leaving blank will reset current password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="176"/>
|
||||||
|
<source>There is no way to recover lost passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="187"/>
|
||||||
|
<source>Password must be at least 8 symbols</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="211"/>
|
||||||
|
<source>Choose avatar</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>audioSettingsForm</name>
|
<name>audioSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="504"/>
|
<location filename="menu.py" line="584"/>
|
||||||
<source>Audio settings</source>
|
<source>Audio settings</source>
|
||||||
<translation>Audio settings</translation>
|
<translation>Audio settings</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="505"/>
|
<location filename="menu.py" line="585"/>
|
||||||
<source>Input device:</source>
|
<source>Input device:</source>
|
||||||
<translation>Input device:</translation>
|
<translation>Input device:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="506"/>
|
<location filename="menu.py" line="586"/>
|
||||||
<source>Output device:</source>
|
<source>Output device:</source>
|
||||||
<translation>Output device:</translation>
|
<translation>Output device:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -243,12 +417,12 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>incoming_call</name>
|
<name>incoming_call</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1041"/>
|
<location filename="profile.py" line="1168"/>
|
||||||
<source>Incoming video call</source>
|
<source>Incoming video call</source>
|
||||||
<translation>Incoming video call</translation>
|
<translation>Incoming video call</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1043"/>
|
<location filename="profile.py" line="1170"/>
|
||||||
<source>Incoming audio call</source>
|
<source>Incoming audio call</source>
|
||||||
<translation>Incoming audio call</translation>
|
<translation>Incoming audio call</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -256,17 +430,17 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>interfaceForm</name>
|
<name>interfaceForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="442"/>
|
<location filename="menu.py" line="521"/>
|
||||||
<source>Interface settings</source>
|
<source>Interface settings</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="443"/>
|
<location filename="menu.py" line="522"/>
|
||||||
<source>Theme:</source>
|
<source>Theme:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="444"/>
|
<location filename="menu.py" line="523"/>
|
||||||
<source>Language:</source>
|
<source>Language:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -274,47 +448,47 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>login</name>
|
<name>login</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="57"/>
|
<location filename="loginscreen.py" line="74"/>
|
||||||
<source>Log in</source>
|
<source>Log in</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="58"/>
|
<location filename="loginscreen.py" line="75"/>
|
||||||
<source>Create</source>
|
<source>Create</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="59"/>
|
<location filename="loginscreen.py" line="76"/>
|
||||||
<source>Profile name:</source>
|
<source>Profile name:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="60"/>
|
<location filename="loginscreen.py" line="77"/>
|
||||||
<source>Load profile</source>
|
<source>Load profile</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="61"/>
|
<location filename="loginscreen.py" line="78"/>
|
||||||
<source>Use as default</source>
|
<source>Use as default</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="62"/>
|
<location filename="loginscreen.py" line="79"/>
|
||||||
<source>Load existing profile</source>
|
<source>Load existing profile</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="63"/>
|
<location filename="loginscreen.py" line="80"/>
|
||||||
<source>Create new profile</source>
|
<source>Create new profile</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="64"/>
|
<location filename="loginscreen.py" line="81"/>
|
||||||
<source>toxygen</source>
|
<source>toxygen</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="80"/>
|
<location filename="main.py" line="118"/>
|
||||||
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -322,22 +496,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>notificationsForm</name>
|
<name>notificationsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="383"/>
|
<location filename="menu.py" line="461"/>
|
||||||
<source>Notification settings</source>
|
<source>Notification settings</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="384"/>
|
<location filename="menu.py" line="462"/>
|
||||||
<source>Enable notifications</source>
|
<source>Enable notifications</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="385"/>
|
<location filename="menu.py" line="463"/>
|
||||||
<source>Enable call's sound</source>
|
<source>Enable call's sound</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="386"/>
|
<location filename="menu.py" line="464"/>
|
||||||
<source>Enable sound notifications</source>
|
<source>Enable sound notifications</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -345,77 +519,77 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>privacySettings</name>
|
<name>privacySettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="307"/>
|
<location filename="menu.py" line="378"/>
|
||||||
<source>Privacy settings</source>
|
<source>Privacy settings</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="308"/>
|
<location filename="menu.py" line="379"/>
|
||||||
<source>Save chat history</source>
|
<source>Save chat history</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="309"/>
|
<location filename="menu.py" line="380"/>
|
||||||
<source>Allow file auto accept</source>
|
<source>Allow file auto accept</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="310"/>
|
<location filename="menu.py" line="381"/>
|
||||||
<source>Send typing notifications</source>
|
<source>Send typing notifications</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="311"/>
|
<location filename="menu.py" line="382"/>
|
||||||
<source>Auto accept default path:</source>
|
<source>Auto accept default path:</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="312"/>
|
<location filename="menu.py" line="383"/>
|
||||||
<source>Change</source>
|
<source>Change</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="313"/>
|
<location filename="menu.py" line="384"/>
|
||||||
<source>Allow inlines</source>
|
<source>Allow inlines</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="336"/>
|
<location filename="menu.py" line="407"/>
|
||||||
<source>Chat history</source>
|
<source>Chat history</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="339"/>
|
<location filename="menu.py" line="410"/>
|
||||||
<source>History will be cleaned! Continue?</source>
|
<source>History will be cleaned! Continue?</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="315"/>
|
<location filename="menu.py" line="386"/>
|
||||||
<source>Blocked users:</source>
|
<source>Blocked users:</source>
|
||||||
<translation>Blocked users:</translation>
|
<translation>Blocked users:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="316"/>
|
<location filename="menu.py" line="387"/>
|
||||||
<source>Unblock</source>
|
<source>Unblock</source>
|
||||||
<translation>Unblock</translation>
|
<translation>Unblock</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="317"/>
|
<location filename="menu.py" line="388"/>
|
||||||
<source>Block user</source>
|
<source>Block user</source>
|
||||||
<translation>Block user</translation>
|
<translation>Block user</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="323"/>
|
<location filename="menu.py" line="394"/>
|
||||||
<source>Add to friend list</source>
|
<source>Add to friend list</source>
|
||||||
<translation>Add to friend list</translation>
|
<translation>Add to friend list</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="324"/>
|
<location filename="menu.py" line="395"/>
|
||||||
<source>Do you want to add this user to friend list?</source>
|
<source>Do you want to add this user to friend list?</source>
|
||||||
<translation>Do you want to add this user to friend list?</translation>
|
<translation>Do you want to add this user to friend list?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="314"/>
|
<location filename="menu.py" line="385"/>
|
||||||
<source>Block by TOX ID:</source>
|
<source>Block by TOX ID:</source>
|
||||||
<translation>Block by TOX ID:</translation>
|
<translation>Block by TOX ID:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -423,14 +597,34 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>tray</name>
|
<name>tray</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="106"/>
|
<location filename="main.py" line="166"/>
|
||||||
<source>Open Toxygen</source>
|
<source>Open Toxygen</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="107"/>
|
<location filename="main.py" line="175"/>
|
||||||
<source>Exit</source>
|
<source>Exit</source>
|
||||||
<translation></translation>
|
<translation></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="167"/>
|
||||||
|
<source>Set status</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="168"/>
|
||||||
|
<source>Online</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="169"/>
|
||||||
|
<source>Away</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="170"/>
|
||||||
|
<source>Busy</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
@@ -3,17 +3,17 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>AddContact</name>
|
<name>AddContact</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="65"/>
|
<location filename="menu.py" line="69"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation>Rajouter un contact</translation>
|
<translation>Rajouter un contact</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="67"/>
|
<location filename="menu.py" line="71"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation>ID TOX :</translation>
|
<translation>ID TOX :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="68"/>
|
<location filename="menu.py" line="72"/>
|
||||||
<source>Message:</source>
|
<source>Message:</source>
|
||||||
<translation>Message :</translation>
|
<translation>Message :</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -21,251 +21,431 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>Form</name>
|
<name>Form</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="66"/>
|
<location filename="menu.py" line="70"/>
|
||||||
<source>Send request</source>
|
<source>Send request</source>
|
||||||
<translation>Envoyer une demande</translation>
|
<translation>Envoyer une demande</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="220"/>
|
<location filename="menu.py" line="287"/>
|
||||||
<source>IPv6</source>
|
<source>IPv6</source>
|
||||||
<translation>IPv6</translation>
|
<translation>IPv6</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="221"/>
|
<location filename="menu.py" line="288"/>
|
||||||
<source>UDP</source>
|
<source>UDP</source>
|
||||||
<translation>UDP</translation>
|
<translation>UDP</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="222"/>
|
<location filename="menu.py" line="289"/>
|
||||||
<source>Proxy</source>
|
<source>Proxy</source>
|
||||||
<translation>Proxy</translation>
|
<translation>Proxy</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="223"/>
|
<location filename="menu.py" line="290"/>
|
||||||
<source>IP:</source>
|
<source>IP:</source>
|
||||||
<translation>IP :</translation>
|
<translation>IP :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="224"/>
|
<location filename="menu.py" line="291"/>
|
||||||
<source>Port:</source>
|
<source>Port:</source>
|
||||||
<translation>Port :</translation>
|
<translation>Port :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="90"/>
|
<location filename="mainscreen.py" line="99"/>
|
||||||
<source>Online contacts</source>
|
<source>Online contacts</source>
|
||||||
<translation>Contacts connectés</translation>
|
<translation type="obsolete">Contacts connectés</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="293"/>
|
||||||
|
<source>HTTP</source>
|
||||||
|
<translation>HTTP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="295"/>
|
||||||
|
<source>WARNING:
|
||||||
|
using proxy with enabled UDP
|
||||||
|
can produce IP leak</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="95"/>
|
<location filename="mainscreen.py" line="115"/>
|
||||||
<source>Profile</source>
|
<source>Profile</source>
|
||||||
<translation>Profile</translation>
|
<translation>Profile</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="101"/>
|
<location filename="mainscreen.py" line="121"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Paramêtres</translation>
|
<translation>Paramêtres</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="286"/>
|
<location filename="mainscreen.py" line="363"/>
|
||||||
<source>About</source>
|
<source>About</source>
|
||||||
<translation>À Propos</translation>
|
<translation>À Propos</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="94"/>
|
<location filename="mainscreen.py" line="114"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation>Rajouter un contact</translation>
|
<translation>Rajouter un contact</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="96"/>
|
<location filename="mainscreen.py" line="116"/>
|
||||||
<source>Privacy</source>
|
<source>Privacy</source>
|
||||||
<translation>Confidentialité</translation>
|
<translation>Confidentialité</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="97"/>
|
<location filename="mainscreen.py" line="117"/>
|
||||||
<source>Interface</source>
|
<source>Interface</source>
|
||||||
<translation>Interface</translation>
|
<translation>Interface</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="98"/>
|
<location filename="mainscreen.py" line="118"/>
|
||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<translation>Notifications</translation>
|
<translation>Notifications</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="99"/>
|
<location filename="mainscreen.py" line="119"/>
|
||||||
<source>Network</source>
|
<source>Network</source>
|
||||||
<translation>Réseau</translation>
|
<translation>Réseau</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="100"/>
|
<location filename="mainscreen.py" line="120"/>
|
||||||
<source>About program</source>
|
<source>About program</source>
|
||||||
<translation>À propos du programme</translation>
|
<translation>À propos du programme</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="762"/>
|
<location filename="profile.py" line="854"/>
|
||||||
<source>User {} wants to add you to contact list. Message:
|
<source>User {} wants to add you to contact list. Message:
|
||||||
{}</source>
|
{}</source>
|
||||||
<translation>L'Utilisateur {} veut vout rajouter à sa liste de contacts. Message : {}</translation>
|
<translation>L'Utilisateur {} veut vout rajouter à sa liste de contacts. Message : {}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="764"/>
|
<location filename="profile.py" line="856"/>
|
||||||
<source>Friend request</source>
|
<source>Friend request</source>
|
||||||
<translation>Demande d'amis</translation>
|
<translation>Demande d'amis</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="287"/>
|
<location filename="mainscreen.py" line="319"/>
|
||||||
<source>Toxygen is Tox client written on Python 2.7. Version: </source>
|
<source>Toxygen is Tox client written on Python 2.7. Version: </source>
|
||||||
<translation>Toxygen est un client Tox écris en Python 2.7. Version : </translation>
|
<translation type="obsolete">Toxygen est un client Tox écris en Python 2.7. Version : </translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="329"/>
|
<location filename="mainscreen.py" line="411"/>
|
||||||
<source>Choose file</source>
|
<source>Choose file</source>
|
||||||
<translation>Choisir un fichier</translation>
|
<translation>Choisir un fichier</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="367"/>
|
<location filename="mainscreen.py" line="452"/>
|
||||||
<source>Disallow auto accept</source>
|
<source>Disallow auto accept</source>
|
||||||
<translation>Désactiver l'auto-réception</translation>
|
<translation>Désactiver l'auto-réception</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="368"/>
|
<location filename="mainscreen.py" line="453"/>
|
||||||
<source>Allow auto accept</source>
|
<source>Allow auto accept</source>
|
||||||
<translation>Activer l'auto-réception</translation>
|
<translation>Activer l'auto-réception</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="370"/>
|
<location filename="mainscreen.py" line="455"/>
|
||||||
<source>Set alias</source>
|
<source>Set alias</source>
|
||||||
<translation>Définir un alias</translation>
|
<translation>Définir un alias</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="371"/>
|
<location filename="mainscreen.py" line="456"/>
|
||||||
<source>Clear history</source>
|
<source>Clear history</source>
|
||||||
<translation>Vider l'historique</translation>
|
<translation>Vider l'historique</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="372"/>
|
<location filename="mainscreen.py" line="457"/>
|
||||||
<source>Copy public key</source>
|
<source>Copy public key</source>
|
||||||
<translation>Copier la clé publique</translation>
|
<translation>Copier la clé publique</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="374"/>
|
<location filename="mainscreen.py" line="459"/>
|
||||||
<source>Remove friend</source>
|
<source>Remove friend</source>
|
||||||
<translation>Retirer un ami</translation>
|
<translation>Retirer un ami</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="639"/>
|
<location filename="profile.py" line="700"/>
|
||||||
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
||||||
|
<translation>Entrez un nouvel alias pour l'ami {} ou laissez vide pour garder son nom de base :</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="122"/>
|
||||||
|
<source>Audio</source>
|
||||||
|
<translation>Audio</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="112"/>
|
||||||
|
<source>Find contact</source>
|
||||||
|
<translation type="obsolete">Trouver le contact</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="profile.py" line="826"/>
|
||||||
|
<source>Friend added</source>
|
||||||
|
<translation>Ami rajouté</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="364"/>
|
||||||
|
<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="827"/>
|
||||||
|
<source>Friend added without sending friend request</source>
|
||||||
|
<translation>Ami rajouté sans avoir envoyé de demande</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="list_items.py" line="234"/>
|
||||||
|
<source>Choose folder</source>
|
||||||
|
<translation>Choisir le dossier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="124"/>
|
||||||
|
<source>Send screenshot</source>
|
||||||
|
<translation>Envoyer une capture d'écran</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="125"/>
|
||||||
|
<source>Send file</source>
|
||||||
|
<translation>Envoyer le fichier</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="126"/>
|
||||||
|
<source>Send message</source>
|
||||||
|
<translation>Envoyer le message</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="127"/>
|
||||||
|
<source>Start audio call with friend</source>
|
||||||
|
<translation>Lancer un appel audio avec un ami</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="462"/>
|
||||||
|
<source>Plugins</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="102"/>
|
<location filename="mainscreen.py" line="110"/>
|
||||||
<source>Audio</source>
|
<source>List of plugins</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="123"/>
|
||||||
|
<source>Search</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="129"/>
|
||||||
|
<source>All</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="130"/>
|
||||||
|
<source>Online</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NetworkSettings</name>
|
<name>NetworkSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="219"/>
|
<location filename="menu.py" line="286"/>
|
||||||
<source>Network settings</source>
|
<source>Network settings</source>
|
||||||
<translation>Paramêtres réseaux</translation>
|
<translation>Paramètres réseaux</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="225"/>
|
<location filename="menu.py" line="292"/>
|
||||||
<source>Restart TOX core</source>
|
<source>Restart TOX core</source>
|
||||||
|
<translation>Relancer le noyau TOX</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="121"/>
|
||||||
|
<source>List of commands for plugin {}</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="122"/>
|
||||||
|
<source>No commands available</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginsForm</name>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="624"/>
|
||||||
|
<source>Plugins</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="625"/>
|
||||||
|
<source>Open selected plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="637"/>
|
||||||
|
<source>No GUI found for this plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="651"/>
|
||||||
|
<source>No description available</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="663"/>
|
||||||
|
<source>Disable plugin</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="665"/>
|
||||||
|
<source>Enable plugin</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ProfileSettingsForm</name>
|
<name>ProfileSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="131"/>
|
<location filename="menu.py" line="161"/>
|
||||||
<source>Export profile</source>
|
<source>Export profile</source>
|
||||||
<translation>Exporter le profile</translation>
|
<translation>Exporter le profile</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="132"/>
|
<location filename="menu.py" line="162"/>
|
||||||
<source>Profile settings</source>
|
<source>Profile settings</source>
|
||||||
<translation>Paramêtres du profil</translation>
|
<translation>Paramêtres du profil</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="133"/>
|
<location filename="menu.py" line="163"/>
|
||||||
<source>Name:</source>
|
<source>Name:</source>
|
||||||
<translation>Nom :</translation>
|
<translation>Nom :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="134"/>
|
<location filename="menu.py" line="164"/>
|
||||||
<source>Status:</source>
|
<source>Status:</source>
|
||||||
<translation>Status :</translation>
|
<translation>Status :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="135"/>
|
<location filename="menu.py" line="165"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation>ID TOX :</translation>
|
<translation>ID TOX :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="136"/>
|
<location filename="menu.py" line="166"/>
|
||||||
<source>Copy TOX ID</source>
|
<source>Copy TOX ID</source>
|
||||||
<translation>Copier l'ID TOX</translation>
|
<translation>Copier l'ID TOX</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="137"/>
|
<location filename="menu.py" line="167"/>
|
||||||
<source>New avatar</source>
|
<source>New avatar</source>
|
||||||
<translation>Nouvel avatar</translation>
|
<translation>Nouvel avatar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="138"/>
|
<location filename="menu.py" line="168"/>
|
||||||
<source>Reset avatar</source>
|
<source>Reset avatar</source>
|
||||||
<translation>Réinitialiser l'avatar</translation>
|
<translation>Réinitialiser l'avatar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="169"/>
|
||||||
|
<source>New NoSpam</source>
|
||||||
|
<translation>Nouveau NoSpam</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="170"/>
|
||||||
|
<source>Profile password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="171"/>
|
||||||
|
<source>Password (at least 8 symbols)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="172"/>
|
||||||
|
<source>Confirm password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="173"/>
|
||||||
|
<source>Set password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="191"/>
|
||||||
|
<source>Passwords do not match</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="175"/>
|
||||||
|
<source>Leaving blank will reset current password</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="176"/>
|
||||||
|
<source>There is no way to recover lost passwords</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="187"/>
|
||||||
|
<source>Password must be at least 8 symbols</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="211"/>
|
||||||
|
<source>Choose avatar</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>audioSettingsForm</name>
|
<name>audioSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="504"/>
|
<location filename="menu.py" line="584"/>
|
||||||
<source>Audio settings</source>
|
<source>Audio settings</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Paramètres audio</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="505"/>
|
<location filename="menu.py" line="585"/>
|
||||||
<source>Input device:</source>
|
<source>Input device:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Péripherique d'entrée :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="506"/>
|
<location filename="menu.py" line="586"/>
|
||||||
<source>Output device:</source>
|
<source>Output device:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Péripherique de sortie :</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>incoming_call</name>
|
<name>incoming_call</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1041"/>
|
<location filename="profile.py" line="1168"/>
|
||||||
<source>Incoming video call</source>
|
<source>Incoming video call</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Appel vidéo entrant</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1043"/>
|
<location filename="profile.py" line="1170"/>
|
||||||
<source>Incoming audio call</source>
|
<source>Incoming audio call</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Appel audio entrant</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>interfaceForm</name>
|
<name>interfaceForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="442"/>
|
<location filename="menu.py" line="521"/>
|
||||||
<source>Interface settings</source>
|
<source>Interface settings</source>
|
||||||
<translation>Paramêtres de l'interface</translation>
|
<translation>Paramêtres de l'interface</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="443"/>
|
<location filename="menu.py" line="522"/>
|
||||||
<source>Theme:</source>
|
<source>Theme:</source>
|
||||||
<translation>Thème :</translation>
|
<translation>Thème :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="444"/>
|
<location filename="menu.py" line="523"/>
|
||||||
<source>Language:</source>
|
<source>Language:</source>
|
||||||
<translation>Langue :</translation>
|
<translation>Langue :</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -273,47 +453,47 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>login</name>
|
<name>login</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="57"/>
|
<location filename="loginscreen.py" line="74"/>
|
||||||
<source>Log in</source>
|
<source>Log in</source>
|
||||||
<translation>Se connecter</translation>
|
<translation>Se connecter</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="58"/>
|
<location filename="loginscreen.py" line="75"/>
|
||||||
<source>Create</source>
|
<source>Create</source>
|
||||||
<translation>Créer</translation>
|
<translation>Créer</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="59"/>
|
<location filename="loginscreen.py" line="76"/>
|
||||||
<source>Profile name:</source>
|
<source>Profile name:</source>
|
||||||
<translation>Nom du profil :</translation>
|
<translation>Nom du profil :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="60"/>
|
<location filename="loginscreen.py" line="77"/>
|
||||||
<source>Load profile</source>
|
<source>Load profile</source>
|
||||||
<translation>Charger le profil</translation>
|
<translation>Charger le profil</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="61"/>
|
<location filename="loginscreen.py" line="78"/>
|
||||||
<source>Use as default</source>
|
<source>Use as default</source>
|
||||||
<translation>Utiliser par défaut</translation>
|
<translation>Utiliser par défaut</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="62"/>
|
<location filename="loginscreen.py" line="79"/>
|
||||||
<source>Load existing profile</source>
|
<source>Load existing profile</source>
|
||||||
<translation>Charger un profil existant</translation>
|
<translation>Charger un profil existant</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="63"/>
|
<location filename="loginscreen.py" line="80"/>
|
||||||
<source>Create new profile</source>
|
<source>Create new profile</source>
|
||||||
<translation>Créer un nouveau profil</translation>
|
<translation>Créer un nouveau profil</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="64"/>
|
<location filename="loginscreen.py" line="81"/>
|
||||||
<source>toxygen</source>
|
<source>toxygen</source>
|
||||||
<translation>toxygen</translation>
|
<translation>toxygen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="80"/>
|
<location filename="main.py" line="118"/>
|
||||||
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
||||||
<translation>Il semble qu'une autre instance de Toxygen utilise ce profil ! Continuer ?</translation>
|
<translation>Il semble qu'une autre instance de Toxygen utilise ce profil ! Continuer ?</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -321,22 +501,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>notificationsForm</name>
|
<name>notificationsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="383"/>
|
<location filename="menu.py" line="461"/>
|
||||||
<source>Notification settings</source>
|
<source>Notification settings</source>
|
||||||
<translation>Paramêtres de notification</translation>
|
<translation>Paramêtres de notification</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="384"/>
|
<location filename="menu.py" line="462"/>
|
||||||
<source>Enable notifications</source>
|
<source>Enable notifications</source>
|
||||||
<translation>Activer les notifications</translation>
|
<translation>Activer les notifications</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="385"/>
|
<location filename="menu.py" line="463"/>
|
||||||
<source>Enable call's sound</source>
|
<source>Enable call's sound</source>
|
||||||
<translation>Activer les sons d'appel</translation>
|
<translation>Activer les sons d'appel</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="386"/>
|
<location filename="menu.py" line="464"/>
|
||||||
<source>Enable sound notifications</source>
|
<source>Enable sound notifications</source>
|
||||||
<translation>Activer les sons de notifications</translation>
|
<translation>Activer les sons de notifications</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -344,92 +524,112 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>privacySettings</name>
|
<name>privacySettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="307"/>
|
<location filename="menu.py" line="378"/>
|
||||||
<source>Privacy settings</source>
|
<source>Privacy settings</source>
|
||||||
<translation>Paramêtres de confidentialité</translation>
|
<translation>Paramêtres de confidentialité</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="308"/>
|
<location filename="menu.py" line="379"/>
|
||||||
<source>Save chat history</source>
|
<source>Save chat history</source>
|
||||||
<translation>Sauvegarder l'historique du chat</translation>
|
<translation>Sauvegarder l'historique de chat</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="309"/>
|
<location filename="menu.py" line="380"/>
|
||||||
<source>Allow file auto accept</source>
|
<source>Allow file auto accept</source>
|
||||||
<translation>Autoriser les fichier automatiquement</translation>
|
<translation>Autoriser les fichier automatiquement</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="310"/>
|
<location filename="menu.py" line="381"/>
|
||||||
<source>Send typing notifications</source>
|
<source>Send typing notifications</source>
|
||||||
<translation>Notifier la frappe</translation>
|
<translation>Notifier la frappe</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="311"/>
|
<location filename="menu.py" line="382"/>
|
||||||
<source>Auto accept default path:</source>
|
<source>Auto accept default path:</source>
|
||||||
<translation>Chemin d'accès des fichiers acceptés automatiquement :</translation>
|
<translation>Chemin d'accès des fichiers acceptés automatiquement :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="312"/>
|
<location filename="menu.py" line="383"/>
|
||||||
<source>Change</source>
|
<source>Change</source>
|
||||||
<translation>Modifier</translation>
|
<translation>Modifier</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="313"/>
|
<location filename="menu.py" line="384"/>
|
||||||
<source>Allow inlines</source>
|
<source>Allow inlines</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Activer l'auto-réception</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="336"/>
|
<location filename="menu.py" line="407"/>
|
||||||
<source>Chat history</source>
|
<source>Chat history</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Historique de chat</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="339"/>
|
<location filename="menu.py" line="410"/>
|
||||||
<source>History will be cleaned! Continue?</source>
|
<source>History will be cleaned! Continue?</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>L'Historique va être nettoyé ! Confirmer ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="315"/>
|
<location filename="menu.py" line="386"/>
|
||||||
<source>Blocked users:</source>
|
<source>Blocked users:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Utilisateurs bloqués :</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="316"/>
|
<location filename="menu.py" line="387"/>
|
||||||
<source>Unblock</source>
|
<source>Unblock</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Débloquer</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="317"/>
|
<location filename="menu.py" line="388"/>
|
||||||
<source>Block user</source>
|
<source>Block user</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Bloquer l'utilisateur</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="323"/>
|
<location filename="menu.py" line="394"/>
|
||||||
<source>Add to friend list</source>
|
<source>Add to friend list</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Ajouter à la liste des amis</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="324"/>
|
<location filename="menu.py" line="395"/>
|
||||||
<source>Do you want to add this user to friend list?</source>
|
<source>Do you want to add this user to friend list?</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Voulez vous rajouter cet utilisateur à votre liste d'amis ?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="314"/>
|
<location filename="menu.py" line="385"/>
|
||||||
<source>Block by TOX ID:</source>
|
<source>Block by TOX ID:</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation>Bloquer l'ID TOX :</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>tray</name>
|
<name>tray</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="106"/>
|
<location filename="main.py" line="166"/>
|
||||||
<source>Open Toxygen</source>
|
<source>Open Toxygen</source>
|
||||||
<translation>Ouvrir Toxygen</translation>
|
<translation>Ouvrir Toxygen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="107"/>
|
<location filename="main.py" line="175"/>
|
||||||
<source>Exit</source>
|
<source>Exit</source>
|
||||||
<translation>Quitter</translation>
|
<translation>Quitter</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="167"/>
|
||||||
|
<source>Set status</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="168"/>
|
||||||
|
<source>Online</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="169"/>
|
||||||
|
<source>Away</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="170"/>
|
||||||
|
<source>Busy</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>AddContact</name>
|
<name>AddContact</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="65"/>
|
<location filename="menu.py" line="69"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation>Добавить контакт</translation>
|
<translation>Добавить контакт</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="67"/>
|
<location filename="menu.py" line="71"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation>TOX ID:</translation>
|
<translation>TOX ID:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="68"/>
|
<location filename="menu.py" line="72"/>
|
||||||
<source>Message:</source>
|
<source>Message:</source>
|
||||||
<translation>Сообщение:</translation>
|
<translation>Сообщение:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -22,193 +22,319 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>Form</name>
|
<name>Form</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="66"/>
|
<location filename="menu.py" line="70"/>
|
||||||
<source>Send request</source>
|
<source>Send request</source>
|
||||||
<translation>Отправить запрос</translation>
|
<translation>Отправить запрос</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="220"/>
|
<location filename="menu.py" line="287"/>
|
||||||
<source>IPv6</source>
|
<source>IPv6</source>
|
||||||
<translation>IPv6</translation>
|
<translation>IPv6</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="221"/>
|
<location filename="menu.py" line="288"/>
|
||||||
<source>UDP</source>
|
<source>UDP</source>
|
||||||
<translation>UDP</translation>
|
<translation>UDP</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="222"/>
|
<location filename="menu.py" line="289"/>
|
||||||
<source>Proxy</source>
|
<source>Proxy</source>
|
||||||
<translation>Прокси</translation>
|
<translation>Прокси</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="223"/>
|
<location filename="menu.py" line="290"/>
|
||||||
<source>IP:</source>
|
<source>IP:</source>
|
||||||
<translation>IP:</translation>
|
<translation>IP:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="224"/>
|
<location filename="menu.py" line="291"/>
|
||||||
<source>Port:</source>
|
<source>Port:</source>
|
||||||
<translation>Порт:</translation>
|
<translation>Порт:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="90"/>
|
<location filename="mainscreen.py" line="99"/>
|
||||||
<source>Online contacts</source>
|
<source>Online contacts</source>
|
||||||
<translation>Контакты в сети</translation>
|
<translation type="obsolete">Контакты в сети</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="293"/>
|
||||||
|
<source>HTTP</source>
|
||||||
|
<translation>HTTP</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="295"/>
|
||||||
|
<source>WARNING:
|
||||||
|
using proxy with enabled UDP
|
||||||
|
can produce IP leak</source>
|
||||||
|
<translation type="unfinished">Предупреждение:
|
||||||
|
использование прокси с UDP
|
||||||
|
может привести к утечке IP</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MainWindow</name>
|
<name>MainWindow</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="95"/>
|
<location filename="mainscreen.py" line="115"/>
|
||||||
<source>Profile</source>
|
<source>Profile</source>
|
||||||
<translation>Профиль</translation>
|
<translation>Профиль</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="101"/>
|
<location filename="mainscreen.py" line="121"/>
|
||||||
<source>Settings</source>
|
<source>Settings</source>
|
||||||
<translation>Настройки</translation>
|
<translation>Настройки</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="286"/>
|
<location filename="mainscreen.py" line="363"/>
|
||||||
<source>About</source>
|
<source>About</source>
|
||||||
<translation>О программе</translation>
|
<translation>О программе</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="94"/>
|
<location filename="mainscreen.py" line="114"/>
|
||||||
<source>Add contact</source>
|
<source>Add contact</source>
|
||||||
<translation>Добавить контакт</translation>
|
<translation>Добавить контакт</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="96"/>
|
<location filename="mainscreen.py" line="116"/>
|
||||||
<source>Privacy</source>
|
<source>Privacy</source>
|
||||||
<translation>Приватность</translation>
|
<translation>Приватность</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="97"/>
|
<location filename="mainscreen.py" line="117"/>
|
||||||
<source>Interface</source>
|
<source>Interface</source>
|
||||||
<translation>Интерфейс</translation>
|
<translation>Интерфейс</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="98"/>
|
<location filename="mainscreen.py" line="118"/>
|
||||||
<source>Notifications</source>
|
<source>Notifications</source>
|
||||||
<translation>Уведомления</translation>
|
<translation>Уведомления</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="99"/>
|
<location filename="mainscreen.py" line="119"/>
|
||||||
<source>Network</source>
|
<source>Network</source>
|
||||||
<translation>Сеть</translation>
|
<translation>Сеть</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="100"/>
|
<location filename="mainscreen.py" line="120"/>
|
||||||
<source>About program</source>
|
<source>About program</source>
|
||||||
<translation>О программе</translation>
|
<translation>О программе</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="762"/>
|
<location filename="profile.py" line="854"/>
|
||||||
<source>User {} wants to add you to contact list. Message:
|
<source>User {} wants to add you to contact list. Message:
|
||||||
{}</source>
|
{}</source>
|
||||||
<translation>Пользователь {} хочет добавить Вас в список контактов. Сообщение:
|
<translation>Пользователь {} хочет добавить Вас в список контактов. Сообщение:
|
||||||
{}</translation>
|
{}</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="764"/>
|
<location filename="profile.py" line="856"/>
|
||||||
<source>Friend request</source>
|
<source>Friend request</source>
|
||||||
<translation>Запрос на добавление в друзья</translation>
|
<translation>Запрос на добавление в друзья</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="287"/>
|
<location filename="mainscreen.py" line="411"/>
|
||||||
<source>Toxygen is Tox client written on Python 2.7. Version: </source>
|
|
||||||
<translation>Toxygen - клиент для мессенджера Tox, написанный на Python 2.7. Версия: </translation>
|
|
||||||
</message>
|
|
||||||
<message>
|
|
||||||
<location filename="mainscreen.py" line="329"/>
|
|
||||||
<source>Choose file</source>
|
<source>Choose file</source>
|
||||||
<translation>Выберите файл</translation>
|
<translation>Выберите файл</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="367"/>
|
<location filename="mainscreen.py" line="452"/>
|
||||||
<source>Disallow auto accept</source>
|
<source>Disallow auto accept</source>
|
||||||
<translation>Запретить автоматическое получение файлов</translation>
|
<translation>Запретить автоматическое получение файлов</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="368"/>
|
<location filename="mainscreen.py" line="453"/>
|
||||||
<source>Allow auto accept</source>
|
<source>Allow auto accept</source>
|
||||||
<translation>Разрешить автоматическое получение файлов</translation>
|
<translation>Разрешить автоматическое получение файлов</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="370"/>
|
<location filename="mainscreen.py" line="455"/>
|
||||||
<source>Set alias</source>
|
<source>Set alias</source>
|
||||||
<translation>Изменить псевдоним</translation>
|
<translation>Изменить псевдоним</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="371"/>
|
<location filename="mainscreen.py" line="456"/>
|
||||||
<source>Clear history</source>
|
<source>Clear history</source>
|
||||||
<translation>Очистить историю</translation>
|
<translation>Очистить историю</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="372"/>
|
<location filename="mainscreen.py" line="457"/>
|
||||||
<source>Copy public key</source>
|
<source>Copy public key</source>
|
||||||
<translation>Копировать публичный ключ</translation>
|
<translation>Копировать публичный ключ</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="374"/>
|
<location filename="mainscreen.py" line="459"/>
|
||||||
<source>Remove friend</source>
|
<source>Remove friend</source>
|
||||||
<translation>Удалить друга</translation>
|
<translation>Удалить друга</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="639"/>
|
<location filename="profile.py" line="700"/>
|
||||||
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
<source>Enter new alias for friend {} or leave empty to use friend's name:</source>
|
||||||
<translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation>
|
<translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="mainscreen.py" line="102"/>
|
<location filename="mainscreen.py" line="122"/>
|
||||||
<source>Audio</source>
|
<source>Audio</source>
|
||||||
<translation>Аудио</translation>
|
<translation>Аудио</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="112"/>
|
||||||
|
<source>Find contact</source>
|
||||||
|
<translation type="obsolete">Найти контакт</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="profile.py" line="826"/>
|
||||||
|
<source>Friend added</source>
|
||||||
|
<translation>Друг добавлен</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="364"/>
|
||||||
|
<source>Toxygen is Tox client written on Python.
|
||||||
|
Version: </source>
|
||||||
|
<translation>Toxygen - клиент для мессенджера Tox, написанный на Python. Версия: </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="profile.py" line="827"/>
|
||||||
|
<source>Friend added without sending friend request</source>
|
||||||
|
<translation>Друг добавлен без отправки запроса на добавление в друзья</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="list_items.py" line="234"/>
|
||||||
|
<source>Choose folder</source>
|
||||||
|
<translation>Выбрать папку</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="124"/>
|
||||||
|
<source>Send screenshot</source>
|
||||||
|
<translation>Отправить снимок экрана</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="125"/>
|
||||||
|
<source>Send file</source>
|
||||||
|
<translation>Отправить файл</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="126"/>
|
||||||
|
<source>Send message</source>
|
||||||
|
<translation>Отправить сообщение</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="127"/>
|
||||||
|
<source>Start audio call with friend</source>
|
||||||
|
<translation>Начать аудиозвонок с другом</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="462"/>
|
||||||
|
<source>Plugins</source>
|
||||||
|
<translation>Плагины</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="110"/>
|
||||||
|
<source>List of plugins</source>
|
||||||
|
<translation>Список плагинов</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="123"/>
|
||||||
|
<source>Search</source>
|
||||||
|
<translation>Поиск</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="129"/>
|
||||||
|
<source>All</source>
|
||||||
|
<translation>Все</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainscreen.py" line="130"/>
|
||||||
|
<source>Online</source>
|
||||||
|
<translation>Онлайн</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>NetworkSettings</name>
|
<name>NetworkSettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="219"/>
|
<location filename="menu.py" line="286"/>
|
||||||
<source>Network settings</source>
|
<source>Network settings</source>
|
||||||
<translation>Настройки сети</translation>
|
<translation>Настройки сети</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="225"/>
|
<location filename="menu.py" line="292"/>
|
||||||
<source>Restart TOX core</source>
|
<source>Restart TOX core</source>
|
||||||
<translation>Перезапустить ядро TOX</translation>
|
<translation>Перезапустить ядро TOX</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="121"/>
|
||||||
|
<source>List of commands for plugin {}</source>
|
||||||
|
<translation>Список команд для плагина {}</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="plugins/plugin_super_class.py" line="122"/>
|
||||||
|
<source>No commands available</source>
|
||||||
|
<translation>Команды не найдены</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PluginsForm</name>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="624"/>
|
||||||
|
<source>Plugins</source>
|
||||||
|
<translation>Плагины</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="625"/>
|
||||||
|
<source>Open selected plugin</source>
|
||||||
|
<translation>Открыть выбранный плагин</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="637"/>
|
||||||
|
<source>No GUI found for this plugin</source>
|
||||||
|
<translation>GUI для данного плагина не найден</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="651"/>
|
||||||
|
<source>No description available</source>
|
||||||
|
<translation>Описание недоступно</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="663"/>
|
||||||
|
<source>Disable plugin</source>
|
||||||
|
<translation>Отключить плагин</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="665"/>
|
||||||
|
<source>Enable plugin</source>
|
||||||
|
<translation>Включить плагин</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>ProfileSettingsForm</name>
|
<name>ProfileSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="131"/>
|
<location filename="menu.py" line="161"/>
|
||||||
<source>Export profile</source>
|
<source>Export profile</source>
|
||||||
<translation>Экспорт профиля</translation>
|
<translation>Экспорт профиля</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="132"/>
|
<location filename="menu.py" line="162"/>
|
||||||
<source>Profile settings</source>
|
<source>Profile settings</source>
|
||||||
<translation>Настройки профиля</translation>
|
<translation>Настройки профиля</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="133"/>
|
<location filename="menu.py" line="163"/>
|
||||||
<source>Name:</source>
|
<source>Name:</source>
|
||||||
<translation>Имя:</translation>
|
<translation>Имя:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="134"/>
|
<location filename="menu.py" line="164"/>
|
||||||
<source>Status:</source>
|
<source>Status:</source>
|
||||||
<translation>Статус:</translation>
|
<translation>Статус:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="135"/>
|
<location filename="menu.py" line="165"/>
|
||||||
<source>TOX ID:</source>
|
<source>TOX ID:</source>
|
||||||
<translation>TOX ID:</translation>
|
<translation>TOX ID:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="136"/>
|
<location filename="menu.py" line="166"/>
|
||||||
<source>Copy TOX ID</source>
|
<source>Copy TOX ID</source>
|
||||||
<translation>Копировать TOX ID</translation>
|
<translation>Копировать TOX ID</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -218,30 +344,80 @@
|
|||||||
<translation type="obsolete">Язык:</translation>
|
<translation type="obsolete">Язык:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="137"/>
|
<location filename="menu.py" line="167"/>
|
||||||
<source>New avatar</source>
|
<source>New avatar</source>
|
||||||
<translation>Новый аватар</translation>
|
<translation>Новый аватар</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="138"/>
|
<location filename="menu.py" line="168"/>
|
||||||
<source>Reset avatar</source>
|
<source>Reset avatar</source>
|
||||||
<translation>Сбросить аватар</translation>
|
<translation>Сбросить аватар</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="169"/>
|
||||||
|
<source>New NoSpam</source>
|
||||||
|
<translation>Новый NoSpam</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="170"/>
|
||||||
|
<source>Profile password</source>
|
||||||
|
<translation>Пароль профиля</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="171"/>
|
||||||
|
<source>Password (at least 8 symbols)</source>
|
||||||
|
<translation>Пароль (минимум 8 символов)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="172"/>
|
||||||
|
<source>Confirm password</source>
|
||||||
|
<translation>Подтверждение пароля</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="173"/>
|
||||||
|
<source>Set password</source>
|
||||||
|
<translation>Изменить пароль</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="191"/>
|
||||||
|
<source>Passwords do not match</source>
|
||||||
|
<translation>Пароли не совпадают</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="175"/>
|
||||||
|
<source>Leaving blank will reset current password</source>
|
||||||
|
<translation>Пустое поле сбросит текущий пароль</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="176"/>
|
||||||
|
<source>There is no way to recover lost passwords</source>
|
||||||
|
<translation>Восстановление забытых паролей не поддерживается</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="187"/>
|
||||||
|
<source>Password must be at least 8 symbols</source>
|
||||||
|
<translation>Пароль должен быть длиной не менее 8 символов</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="menu.py" line="211"/>
|
||||||
|
<source>Choose avatar</source>
|
||||||
|
<translation>Выбрать аватар</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>audioSettingsForm</name>
|
<name>audioSettingsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="504"/>
|
<location filename="menu.py" line="584"/>
|
||||||
<source>Audio settings</source>
|
<source>Audio settings</source>
|
||||||
<translation>Настройки аудио</translation>
|
<translation>Настройки аудио</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="505"/>
|
<location filename="menu.py" line="585"/>
|
||||||
<source>Input device:</source>
|
<source>Input device:</source>
|
||||||
<translation>Устройство ввода:</translation>
|
<translation>Устройство ввода:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="506"/>
|
<location filename="menu.py" line="586"/>
|
||||||
<source>Output device:</source>
|
<source>Output device:</source>
|
||||||
<translation>Устройство вывода:</translation>
|
<translation>Устройство вывода:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -249,12 +425,12 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>incoming_call</name>
|
<name>incoming_call</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1041"/>
|
<location filename="profile.py" line="1168"/>
|
||||||
<source>Incoming video call</source>
|
<source>Incoming video call</source>
|
||||||
<translation>Входящий видеозвонок</translation>
|
<translation>Входящий видеозвонок</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="profile.py" line="1043"/>
|
<location filename="profile.py" line="1170"/>
|
||||||
<source>Incoming audio call</source>
|
<source>Incoming audio call</source>
|
||||||
<translation>Входящий аудиозвонок</translation>
|
<translation>Входящий аудиозвонок</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -262,17 +438,17 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>interfaceForm</name>
|
<name>interfaceForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="442"/>
|
<location filename="menu.py" line="521"/>
|
||||||
<source>Interface settings</source>
|
<source>Interface settings</source>
|
||||||
<translation>Настройки интерфейса</translation>
|
<translation>Настройки интерфейса</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="443"/>
|
<location filename="menu.py" line="522"/>
|
||||||
<source>Theme:</source>
|
<source>Theme:</source>
|
||||||
<translation>Тема:</translation>
|
<translation>Тема:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="444"/>
|
<location filename="menu.py" line="523"/>
|
||||||
<source>Language:</source>
|
<source>Language:</source>
|
||||||
<translation>Язык:</translation>
|
<translation>Язык:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -280,47 +456,47 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>login</name>
|
<name>login</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="57"/>
|
<location filename="loginscreen.py" line="74"/>
|
||||||
<source>Log in</source>
|
<source>Log in</source>
|
||||||
<translation>Вход</translation>
|
<translation>Вход</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="58"/>
|
<location filename="loginscreen.py" line="75"/>
|
||||||
<source>Create</source>
|
<source>Create</source>
|
||||||
<translation>Создать</translation>
|
<translation>Создать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="59"/>
|
<location filename="loginscreen.py" line="76"/>
|
||||||
<source>Profile name:</source>
|
<source>Profile name:</source>
|
||||||
<translation>Имя профиля:</translation>
|
<translation>Имя профиля:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="60"/>
|
<location filename="loginscreen.py" line="77"/>
|
||||||
<source>Load profile</source>
|
<source>Load profile</source>
|
||||||
<translation>Загрузить профиль</translation>
|
<translation>Загрузить профиль</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="61"/>
|
<location filename="loginscreen.py" line="78"/>
|
||||||
<source>Use as default</source>
|
<source>Use as default</source>
|
||||||
<translation>По умолчанию</translation>
|
<translation>По умолчанию</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="62"/>
|
<location filename="loginscreen.py" line="79"/>
|
||||||
<source>Load existing profile</source>
|
<source>Load existing profile</source>
|
||||||
<translation>Загрузить профиль</translation>
|
<translation>Загрузить профиль</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="63"/>
|
<location filename="loginscreen.py" line="80"/>
|
||||||
<source>Create new profile</source>
|
<source>Create new profile</source>
|
||||||
<translation>Создать новый профиль</translation>
|
<translation>Создать новый профиль</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="loginscreen.py" line="64"/>
|
<location filename="loginscreen.py" line="81"/>
|
||||||
<source>toxygen</source>
|
<source>toxygen</source>
|
||||||
<translation>toxygen</translation>
|
<translation>toxygen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="80"/>
|
<location filename="main.py" line="118"/>
|
||||||
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
<source>Looks like other instance of Toxygen uses this profile! Continue?</source>
|
||||||
<translation>Похоже, что этот профиль используется другим экземпляром Toxygen! Продолжить?</translation>
|
<translation>Похоже, что этот профиль используется другим экземпляром Toxygen! Продолжить?</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -328,22 +504,22 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>notificationsForm</name>
|
<name>notificationsForm</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="383"/>
|
<location filename="menu.py" line="461"/>
|
||||||
<source>Notification settings</source>
|
<source>Notification settings</source>
|
||||||
<translation>Настройки уведомлений</translation>
|
<translation>Настройки уведомлений</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="384"/>
|
<location filename="menu.py" line="462"/>
|
||||||
<source>Enable notifications</source>
|
<source>Enable notifications</source>
|
||||||
<translation>Включить уведомления</translation>
|
<translation>Включить уведомления</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="385"/>
|
<location filename="menu.py" line="463"/>
|
||||||
<source>Enable call's sound</source>
|
<source>Enable call's sound</source>
|
||||||
<translation>Включить звук звонка</translation>
|
<translation>Включить звук звонка</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="386"/>
|
<location filename="menu.py" line="464"/>
|
||||||
<source>Enable sound notifications</source>
|
<source>Enable sound notifications</source>
|
||||||
<translation>Включить звуковые уведомления
|
<translation>Включить звуковые уведомления
|
||||||
</translation>
|
</translation>
|
||||||
@@ -352,77 +528,77 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>privacySettings</name>
|
<name>privacySettings</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="307"/>
|
<location filename="menu.py" line="378"/>
|
||||||
<source>Privacy settings</source>
|
<source>Privacy settings</source>
|
||||||
<translation>Настройки приватности</translation>
|
<translation>Настройки приватности</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="308"/>
|
<location filename="menu.py" line="379"/>
|
||||||
<source>Save chat history</source>
|
<source>Save chat history</source>
|
||||||
<translation>Сохранять историю переписки</translation>
|
<translation>Сохранять историю переписки</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="309"/>
|
<location filename="menu.py" line="380"/>
|
||||||
<source>Allow file auto accept</source>
|
<source>Allow file auto accept</source>
|
||||||
<translation>Разрешить автополучение файлов</translation>
|
<translation>Разрешить автополучение файлов</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="310"/>
|
<location filename="menu.py" line="381"/>
|
||||||
<source>Send typing notifications</source>
|
<source>Send typing notifications</source>
|
||||||
<translation>Посылать уведомления о наборе текста</translation>
|
<translation>Посылать уведомления о наборе текста</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="311"/>
|
<location filename="menu.py" line="382"/>
|
||||||
<source>Auto accept default path:</source>
|
<source>Auto accept default path:</source>
|
||||||
<translation>Путь автоприема файлов:</translation>
|
<translation>Путь автоприема файлов:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="312"/>
|
<location filename="menu.py" line="383"/>
|
||||||
<source>Change</source>
|
<source>Change</source>
|
||||||
<translation>Изменить</translation>
|
<translation>Изменить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="313"/>
|
<location filename="menu.py" line="384"/>
|
||||||
<source>Allow inlines</source>
|
<source>Allow inlines</source>
|
||||||
<translation>Разрешать инлайны</translation>
|
<translation>Разрешать инлайны</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="336"/>
|
<location filename="menu.py" line="407"/>
|
||||||
<source>Chat history</source>
|
<source>Chat history</source>
|
||||||
<translation>История чата</translation>
|
<translation>История чата</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="339"/>
|
<location filename="menu.py" line="410"/>
|
||||||
<source>History will be cleaned! Continue?</source>
|
<source>History will be cleaned! Continue?</source>
|
||||||
<translation>История переписки будет очищена! Продолжить?</translation>
|
<translation>История переписки будет очищена! Продолжить?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="315"/>
|
<location filename="menu.py" line="386"/>
|
||||||
<source>Blocked users:</source>
|
<source>Blocked users:</source>
|
||||||
<translation>Заблокированные пользователи:</translation>
|
<translation>Заблокированные пользователи:</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="316"/>
|
<location filename="menu.py" line="387"/>
|
||||||
<source>Unblock</source>
|
<source>Unblock</source>
|
||||||
<translation>Разблокировать</translation>
|
<translation>Разблокировать</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="317"/>
|
<location filename="menu.py" line="388"/>
|
||||||
<source>Block user</source>
|
<source>Block user</source>
|
||||||
<translation>Заблокировать пользователя</translation>
|
<translation>Заблокировать пользователя</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="323"/>
|
<location filename="menu.py" line="394"/>
|
||||||
<source>Add to friend list</source>
|
<source>Add to friend list</source>
|
||||||
<translation>Добавить в список друзей</translation>
|
<translation>Добавить в список друзей</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="324"/>
|
<location filename="menu.py" line="395"/>
|
||||||
<source>Do you want to add this user to friend list?</source>
|
<source>Do you want to add this user to friend list?</source>
|
||||||
<translation>Добавить этого пользователя в список друзей?</translation>
|
<translation>Добавить этого пользователя в список друзей?</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="menu.py" line="314"/>
|
<location filename="menu.py" line="385"/>
|
||||||
<source>Block by TOX ID:</source>
|
<source>Block by TOX ID:</source>
|
||||||
<translation>Блокировать по TOX ID:</translation>
|
<translation>Блокировать по TOX ID:</translation>
|
||||||
</message>
|
</message>
|
||||||
@@ -430,14 +606,34 @@
|
|||||||
<context>
|
<context>
|
||||||
<name>tray</name>
|
<name>tray</name>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="106"/>
|
<location filename="main.py" line="166"/>
|
||||||
<source>Open Toxygen</source>
|
<source>Open Toxygen</source>
|
||||||
<translation>Открыть Toxygen</translation>
|
<translation>Открыть Toxygen</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="main.py" line="107"/>
|
<location filename="main.py" line="175"/>
|
||||||
<source>Exit</source>
|
<source>Exit</source>
|
||||||
<translation>Выход</translation>
|
<translation>Выход</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="167"/>
|
||||||
|
<source>Set status</source>
|
||||||
|
<translation>Изменить статус</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="168"/>
|
||||||
|
<source>Online</source>
|
||||||
|
<translation>Онлайн</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="169"/>
|
||||||
|
<source>Away</source>
|
||||||
|
<translation>Нет на месте</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="main.py" line="170"/>
|
||||||
|
<source>Busy</source>
|
||||||
|
<translation>Занят</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
</TS>
|
</TS>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import time
|
|||||||
from platform import system
|
from platform import system
|
||||||
|
|
||||||
|
|
||||||
program_version = '0.1.1'
|
program_version = '0.1.3'
|
||||||
|
|
||||||
|
|
||||||
def log(data):
|
def log(data):
|
||||||
@@ -27,14 +27,15 @@ def convert_time(t):
|
|||||||
return '%02d:%02d' % (h, m)
|
return '%02d:%02d' % (h, m)
|
||||||
|
|
||||||
|
|
||||||
|
# obsolete
|
||||||
def get_style(style):
|
def get_style(style):
|
||||||
if style != 'default':
|
if style != 'default':
|
||||||
return style
|
return style
|
||||||
else:
|
else:
|
||||||
if system() == 'Linux':
|
if system() == 'Windows':
|
||||||
return 'gtk'
|
|
||||||
elif system() == 'Windows':
|
|
||||||
return 'windows'
|
return 'windows'
|
||||||
|
else:
|
||||||
|
return 'gtk'
|
||||||
|
|
||||||
|
|
||||||
class Singleton(object):
|
class Singleton(object):
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
from PySide import QtGui, QtCore
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
class DataLabel(QtGui.QLabel):
|
class DataLabel(QtGui.QLabel):
|
||||||
@@ -21,3 +24,14 @@ class CenteredWidget(QtGui.QWidget):
|
|||||||
cp = QtGui.QDesktopWidget().availableGeometry().center()
|
cp = QtGui.QDesktopWidget().availableGeometry().center()
|
||||||
qr.moveCenter(cp)
|
qr.moveCenter(cp)
|
||||||
self.move(qr.topLeft())
|
self.move(qr.topLeft())
|
||||||
|
|
||||||
|
|
||||||
|
class QRightClickButton(QtGui.QPushButton):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(QRightClickButton, self).__init__(parent)
|
||||||
|
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
if event.button() == QtCore.Qt.RightButton:
|
||||||
|
self.emit(QtCore.SIGNAL("rightClicked()"))
|
||||||
|
else:
|
||||||
|
super(QRightClickButton, self).mousePressEvent(event)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from src.bootstrap import node_generator
|
|||||||
from src.profile import *
|
from src.profile import *
|
||||||
from src.settings import ProfileHelper
|
from src.settings import ProfileHelper
|
||||||
from src.tox_dns import tox_dns
|
from src.tox_dns import tox_dns
|
||||||
|
from src.toxencryptsave import LibToxEncryptSave
|
||||||
|
|
||||||
|
|
||||||
class TestProfile():
|
class TestProfile():
|
||||||
@@ -12,13 +13,13 @@ class TestProfile():
|
|||||||
assert len(arr) >= 2
|
assert len(arr) >= 2
|
||||||
|
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
data = ProfileHelper.open_profile(Settings.get_default_path(), 'alice')
|
data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
|
||||||
assert data
|
assert data
|
||||||
|
|
||||||
def test_open_save(self):
|
def test_open_save(self):
|
||||||
data = ProfileHelper.open_profile(Settings.get_default_path(), 'alice')
|
data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
|
||||||
ProfileHelper.save_profile(data)
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
new_data = ProfileHelper.open_profile(Settings.get_default_path(), 'alice')
|
new_data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
|
||||||
assert new_data == data
|
assert new_data == data
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class TestNodeGen():
|
|||||||
class TestTox():
|
class TestTox():
|
||||||
|
|
||||||
def test_loading(self):
|
def test_loading(self):
|
||||||
data = ProfileHelper.open_profile(Settings.get_default_path(), 'alice')
|
data = ProfileHelper(Settings.get_default_path(), 'alice').open_profile()
|
||||||
settings = Settings.get_default_settings()
|
settings = Settings.get_default_settings()
|
||||||
tox = tox_factory(data, settings)
|
tox = tox_factory(data, settings)
|
||||||
for data in node_generator():
|
for data in node_generator():
|
||||||
@@ -56,13 +57,13 @@ class TestTox():
|
|||||||
assert tox.self_get_status_message() == status_message
|
assert tox.self_get_status_message() == status_message
|
||||||
|
|
||||||
def test_friend_list(self):
|
def test_friend_list(self):
|
||||||
data = ProfileHelper.open_profile(Settings.get_default_path(), 'bob')
|
data = ProfileHelper(Settings.get_default_path(), 'bob').open_profile()
|
||||||
settings = Settings.get_default_settings()
|
settings = Settings.get_default_settings()
|
||||||
tox = tox_factory(data, settings)
|
tox = tox_factory(data, settings)
|
||||||
s = tox.self_get_friend_list()
|
s = tox.self_get_friend_list()
|
||||||
size = tox.self_get_friend_list_size()
|
size = tox.self_get_friend_list_size()
|
||||||
assert size == 2
|
assert size <= 2
|
||||||
assert len(s) == 2
|
assert len(s) <= 2
|
||||||
del tox
|
del tox
|
||||||
|
|
||||||
|
|
||||||
@@ -72,3 +73,15 @@ class TestDNS():
|
|||||||
bot_id = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5'
|
bot_id = '56A1ADE4B65B86BCD51CC73E2CD4E542179F47959FE3E0E21B4B0ACDADE51855D34D34D37CB5'
|
||||||
tox_id = tox_dns('groupbot@toxme.io')
|
tox_id = tox_dns('groupbot@toxme.io')
|
||||||
assert tox_id == bot_id
|
assert tox_id == bot_id
|
||||||
|
|
||||||
|
|
||||||
|
class TestEncryption():
|
||||||
|
|
||||||
|
def test_encr_decr(self):
|
||||||
|
with open(settings.Settings.get_default_path() + '/alice.tox') as fl:
|
||||||
|
data = fl.read()
|
||||||
|
lib = LibToxEncryptSave('easypassword')
|
||||||
|
copy_data = data[:]
|
||||||
|
data = lib.pass_encrypt(data)
|
||||||
|
data = lib.pass_decrypt(data)
|
||||||
|
assert copy_data == data
|
||||||
|
|||||||