38 Commits

Author SHA1 Message Date
ingvar1995
f445a79e11 version 0.1.2 2016-05-13 12:08:45 +03:00
ingvar1995
29617c2168 fr translation - .qm 2016-05-12 23:05:20 +03:00
Ingvar
e441b8ffd2 Merge pull request #3 from limalayla/develop
Fixed missing french translations
2016-05-12 13:02:52 -07:00
Ingvar
b588e19789 Update README.md 2016-05-12 22:52:47 +03:00
limalayla
168d259ebd Fixed missing french translations 2016-05-12 21:50:52 +02:00
ingvar1995
0db5195689 new screenshots 2016-05-12 21:25:23 +03:00
ingvar1995
91b558775a os.chdir 2016-05-12 20:45:01 +03:00
Integrator
4d1addfa63 Merge pull request #2 from limalayla/develop
Updated french translation
2016-05-11 23:29:58 +03:00
limalayla
0239b4f8ef Updated french translation 2016-05-11 22:25:36 +02:00
ingvar1995
60df9f7b1d incoming inline fix 2016-05-11 21:55:36 +03:00
ingvar1995
2ed4e00d3e translations 2016-05-11 19:51:35 +03:00
ingvar1995
e6a0988c6d tooltips 2016-05-11 19:35:54 +03:00
ingvar1995
57b4e2af67 sending pause control fix 2016-05-11 12:01:29 +03:00
ingvar1995
6192688e0c wrapper fixes and file transfer fix (pause) 2016-05-10 22:57:52 +03:00
ingvar1995
177e180e37 file transfers bug fix, settings load fix 2016-05-10 13:08:44 +03:00
ingvar1995
8d3db9f897 menu ui fixes and translations update 2016-05-09 23:09:07 +03:00
ingvar1995
2903ed1877 file transfer item fixes 2016-05-09 22:37:11 +03:00
ingvar1995
a1fbd87719 pause and resume images 2016-05-09 18:42:40 +03:00
ingvar1995
be6acff18d menu fixes 2016-05-09 18:38:42 +03:00
ingvar1995
81660a9ad4 file resuming support 2016-05-09 18:32:29 +03:00
ingvar1995
0e103749a2 ui checkbox update and friend addition via public key 2016-05-08 23:13:06 +03:00
ingvar1995
160e9dc72f file resuming by friend support. file transfers improvements 2016-05-08 22:29:50 +03:00
ingvar1995
c503cf4a72 ui fixes 2016-05-08 14:46:18 +03:00
ingvar1995
575d626e03 resizing update 2016-05-08 14:40:27 +03:00
ingvar1995
c6d832ae92 UI improvements. screenshots sending update 2016-05-08 13:51:56 +03:00
ingvar1995
78c376a0b0 bug with clearing history fixed, close to tray on esc 2016-05-08 00:47:10 +03:00
ingvar1995
8df2e3ebce exceptions and typo fix in tox.py 2016-05-07 16:53:23 +03:00
ingvar1995
845af6d37e custom packets support in wrapper 2016-05-07 16:41:53 +03:00
ingvar1995
5e1dfe6d36 resizing update. alias deleting 2016-05-06 16:13:33 +03:00
ingvar1995
2b8da1deb4 command line argument - path to profile - added. windows qcombobox color fix 2016-05-06 13:31:18 +03:00
ingvar1995
89be47dc72 stop call and file transfers on friend exit. 2016-05-06 02:00:10 +03:00
ingvar1995
f5782c2dcb better profile saving 2016-05-04 19:38:24 +03:00
ingvar1995
7a5b1fc05e changing no spam 2016-05-03 22:02:56 +03:00
ingvar1995
61b124dad4 menu bug fixes 2016-05-02 23:20:02 +03:00
ingvar1995
ac1800703f avatar reset bug fixed 2016-05-02 21:54:32 +03:00
ingvar1995
89c4b2cdf6 translations short update 2016-05-02 19:46:42 +03:00
ingvar1995
ce40acc4a0 message splitting fix and window resizing bug fix 2016-05-02 18:51:47 +03:00
ingvar1995
7a02ae3ef4 window resizing 2016-05-02 18:27:46 +03:00
70 changed files with 1060 additions and 533 deletions

View File

@@ -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) [![Release](https://img.shields.io/github/release/xveduk/toxygen.svg?style=flat)](https://github.com/xveduk/toxygen/releases/latest)
[![Open issues](https://img.shields.io/github/issues/xveduk/toxygen.svg?style=flat)](https://github.com/xveduk/toxygen/issues)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://raw.githubusercontent.com/xveduk/toxygen/master/LICENSE.md)
# Supported OS: ### [Install](/docs/install.md) - [Contribute](/docs/contributing.md)
### Supported OS:
- Windows - Windows
- Linux - Linux
@@ -25,10 +28,10 @@ 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
- [ ] Video - [ ] Video
- [ ] Save file encryption - [ ] Save file encryption
- [ ] File resuming
- [ ] Changing nospam
- [ ] Plugins support - [ ] Plugins support
- [ ] Group chats - [ ] Group chats
- [ ] Read receipts - [ ] Read receipts
@@ -43,7 +46,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*
![Ubuntu](/docs/ubuntu.png) ![Ubuntu](/docs/ubuntu.png)
![Windows](/docs/windows.png) ![Windows](/docs/windows.png)

View File

@@ -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``

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 317 KiB

View File

@@ -76,7 +76,7 @@ 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'])
@@ -183,11 +183,14 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us
""" """
Incoming chunk Incoming chunk
""" """
invoke_in_main_thread(Profile.get_instance().incoming_chunk, if not length:
friend_number, invoke_in_main_thread(Profile.get_instance().incoming_chunk,
file_number, friend_number,
position, file_number,
chunk[:length] if length else None) position,
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 +213,10 @@ 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)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------

View File

@@ -12,11 +12,12 @@ 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) signal = QtCore.Signal(int, float) # state and progress
class FileTransfer(QtCore.QObject): class FileTransfer(QtCore.QObject):
@@ -42,6 +43,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 +62,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,9 +108,10 @@ 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:
self._file.close() if hasattr(self, '_file'):
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 +221,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):
@@ -226,7 +239,6 @@ class ReceiveAvatar(ReceiveTransfer):
elif exists(path): elif exists(path):
if 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()
remove(path) remove(path)
remove(path + '.tmp') remove(path + '.tmp')
@@ -237,7 +249,6 @@ class ReceiveAvatar(ReceiveTransfer):
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'])

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/images/pause.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

BIN
src/images/resume.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -136,17 +136,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 +158,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 +213,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']:
if directory: directory = QtGui.QFileDialog.getExistingDirectory(self,
pr = profile.Profile.get_instance() QtGui.QApplication.translate("MainWindow", 'Choose folder', None, QtGui.QApplication.UnicodeUTF8),
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size) curr_directory(),
self.accept.setVisible(False) QtGui.QFileDialog.ShowDirsOnly)
if directory:
pr = profile.Profile.get_instance()
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
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:
self.cancel.setVisible(False) if state == FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
self.accept.setVisible(False) self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
self.pb.setVisible(False) self.cancel.setVisible(False)
elif state == TOX_FILE_TRANSFER_STATE['FINISHED']: self.accept_or_pause.setVisible(False)
self.pb.setVisible(False) self.pb.setVisible(False)
self.cancel.setVisible(False) self.state = state
elif state == FILE_TRANSFER_MESSAGE_STATUS['FINISHED']:
self.accept_or_pause.setVisible(False)
self.pb.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()))

View File

@@ -13,9 +13,10 @@ import locale
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 main(self): def main(self):
""" """
@@ -28,51 +29,57 @@ class Toxygen(object):
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)
if self.path is not None:
auto_profile = Settings.get_auto_profile() path = os.path.dirname(self.path.encode(locale.getpreferredencoding())) + '/'
if not auto_profile: name = os.path.basename(self.path.encode(locale.getpreferredencoding()))[:-4]
# show login screen if default profile not found
current_locale = QtCore.QLocale()
curr_lang = current_locale.languageToString(current_locale.language())
langs = Settings.supported_languages()
if curr_lang in map(lambda x: x[0], langs):
lang_path = filter(lambda x: x[0] == curr_lang, langs)[0][1]
translator = QtCore.QTranslator()
translator.load(curr_directory() + '/translations/' + lang_path)
app.installTranslator(translator)
app.translator = translator
ls = LoginScreen()
ls.setWindowIconText("Toxygen")
profiles = ProfileHelper.find_profiles()
ls.update_select(map(lambda x: x[1], profiles))
_login = self.Login(profiles)
ls.update_on_close(_login.login_screen_close)
ls.show()
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
app.exec_()
if not _login.t:
return
elif _login.t == 1: # create new profile
name = _login.name if _login.name else 'toxygen_user'
self.tox = tox_factory()
self.tox.self_set_name(_login.name if _login.name else 'Toxygen User')
self.tox.self_set_status_message('Toxing on Toxygen')
ProfileHelper.save_profile(self.tox.get_savedata(), name)
path = Settings.get_default_path()
settings = Settings(name)
else: # load existing profile
path, name = _login.get_data()
if _login.default:
Settings.set_auto_profile(path, name)
data = ProfileHelper.open_profile(path, name)
settings = Settings(name)
self.tox = tox_factory(data, settings)
else:
path, name = auto_profile
path = path.encode(locale.getpreferredencoding())
data = ProfileHelper.open_profile(path, name) data = ProfileHelper.open_profile(path, name)
settings = Settings(name) settings = Settings(name)
self.tox = tox_factory(data, settings) self.tox = tox_factory(data, settings)
else:
auto_profile = Settings.get_auto_profile()
if not auto_profile:
# show login screen if default profile not found
current_locale = QtCore.QLocale()
curr_lang = current_locale.languageToString(current_locale.language())
langs = Settings.supported_languages()
if curr_lang in map(lambda x: x[0], langs):
lang_path = filter(lambda x: x[0] == curr_lang, langs)[0][1]
translator = QtCore.QTranslator()
translator.load(curr_directory() + '/translations/' + lang_path)
app.installTranslator(translator)
app.translator = translator
ls = LoginScreen()
ls.setWindowIconText("Toxygen")
profiles = ProfileHelper.find_profiles()
ls.update_select(map(lambda x: x[1], profiles))
_login = self.Login(profiles)
ls.update_on_close(_login.login_screen_close)
ls.show()
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
app.exec_()
if not _login.t:
return
elif _login.t == 1: # create new profile
name = _login.name if _login.name else 'toxygen_user'
self.tox = tox_factory()
self.tox.self_set_name(_login.name if _login.name else 'Toxygen User')
self.tox.self_set_status_message('Toxing on Toxygen')
ProfileHelper.save_profile(self.tox.get_savedata(), name)
path = Settings.get_default_path()
settings = Settings(name)
else: # load existing profile
path, name = _login.get_data()
if _login.default:
Settings.set_auto_profile(path, name)
data = ProfileHelper.open_profile(path, name)
settings = Settings(name)
self.tox = tox_factory(data, settings)
else:
path, name = auto_profile
path = path.encode(locale.getpreferredencoding())
data = ProfileHelper.open_profile(path, name)
settings = Settings(name)
self.tox = tox_factory(data, settings)
if ProfileHelper.is_active_profile(path, name): # profile is in use if ProfileHelper.is_active_profile(path, name): # profile is in use
reply = QtGui.QMessageBox.question(None, reply = QtGui.QMessageBox.question(None,
@@ -110,6 +117,8 @@ class Toxygen(object):
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()
if self.ms.isHidden():
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())
@@ -252,6 +261,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()

View File

@@ -19,10 +19,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:
@@ -109,50 +109,70 @@ 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", "Find contact", 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))
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, 75)
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, 5, 450, 70))
self.messageEdit.setObjectName("messageEdit") self.messageEdit.setObjectName("messageEdit")
self.screenshotButton = QtGui.QPushButton(Form)
self.screenshotButton.setGeometry(QtCore.QRect(460, 10, 90, 55)) 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)
self.screenshotButton = QRightClickButton(Form)
self.screenshotButton.setGeometry(QtCore.QRect(455, 5, 55, 70))
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, 5, 55, 70))
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, 5, 55, 70))
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(30, 45))
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_bottom(self, Form):
Form.setObjectName("left_bottom") Form.setObjectName("left_bottom")
Form.resize(500, 60) Form.resize(270, 80)
self.online_contacts = QtGui.QCheckBox(Form) self.online_contacts = QtGui.QCheckBox(Form)
self.online_contacts.setGeometry(QtCore.QRect(0, 0, 250, 20)) self.online_contacts.setGeometry(QtCore.QRect(0, 0, 250, 20))
self.online_contacts.setObjectName("online_contacts") self.online_contacts.setObjectName("online_contacts")
self.online_contacts.clicked.connect(self.filtering) self.online_contacts.clicked.connect(self.filtering)
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(0, 23, 270, 26))
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)
QtCore.QMetaObject.connectSlotsByName(Form) QtCore.QMetaObject.connectSlotsByName(Form)
@@ -240,10 +260,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,9 +275,9 @@ 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()
@@ -279,8 +300,11 @@ class MainWindow(QtGui.QMainWindow):
grid.addWidget(main_list, 1, 0, 2, 1) grid.addWidget(main_list, 1, 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(1, 400)
grid.setRowMinimumHeight(2, 20)
grid.setRowMinimumHeight(3, 50) grid.setRowMinimumHeight(3, 50)
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)
@@ -294,6 +318,25 @@ class MainWindow(QtGui.QMainWindow):
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() - 205)
self.friends_list.setGeometry(0, 0, 270, self.height() - 180)
self.callButton.setGeometry(QtCore.QRect(self.width() - 370, 30, 50, 50))
self.typing.setGeometry(QtCore.QRect(self.width() - 420, 40, 50, 30))
self.messageEdit.setGeometry(QtCore.QRect(0, 5, self.width() - 470, 70))
self.screenshotButton.setGeometry(QtCore.QRect(self.width() - 465, 5, 55, 70))
self.fileTransferButton.setGeometry(QtCore.QRect(self.width() - 410, 5, 55, 70))
self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 355, 5, 55, 70))
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,7 +345,8 @@ 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):
@@ -343,15 +387,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
@@ -419,8 +466,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()
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@@ -445,14 +491,19 @@ class MainWindow(QtGui.QMainWindow):
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,16 +524,17 @@ class ScreenShotWindow(QtGui.QWidget):
self.rubberband.hide() self.rubberband.hide()
rect = self.rubberband.geometry() rect = self.rubberband.geometry()
print rect print rect
p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(), if rect.width() and rect.height():
rect.x() + 4, p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
rect.y() + 4, rect.x() + 4,
rect.width() - 8, rect.y() + 4,
rect.height() - 8) rect.width() - 8,
byte_array = QtCore.QByteArray() rect.height() - 8)
buffer = QtCore.QBuffer(byte_array) byte_array = QtCore.QByteArray()
buffer.open(QtCore.QIODevice.WriteOnly) buffer = QtCore.QBuffer(byte_array)
p.save(buffer, 'PNG') buffer.open(QtCore.QIODevice.WriteOnly)
Profile.get_instance().send_screenshot(str(byte_array.data())) p.save(buffer, 'PNG')
Profile.get_instance().send_screenshot(str(byte_array.data()))
self.close() self.close()
def keyPressEvent(self, event): def keyPressEvent(self, event):

View File

@@ -2,7 +2,7 @@ from PySide 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
@@ -26,28 +26,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 +58,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):
@@ -76,8 +75,8 @@ class ProfileSettings(CenteredWidget):
def initUI(self): def initUI(self):
self.setObjectName("ProfileSettingsForm") self.setObjectName("ProfileSettingsForm")
self.setMinimumSize(QtCore.QSize(650, 370)) self.setMinimumSize(QtCore.QSize(650, 320))
self.setMaximumSize(QtCore.QSize(650, 370)) self.setMaximumSize(QtCore.QSize(650, 320))
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 +87,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 +95,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,13 +110,16 @@ 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)
@@ -136,18 +138,25 @@ 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))
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)') name = QtGui.QFileDialog.getOpenFileName(self, 'Open file', None, 'Image Files (*.png)')
print name
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()
@@ -173,7 +182,6 @@ 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()
def initUI(self): def initUI(self):
@@ -204,14 +212,15 @@ 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.retranslateUi() self.retranslateUi()
self.proxy.stateChanged.connect(lambda x: self.activate()) self.proxy.stateChanged.connect(lambda x: self.activate())
self.activate() self.activate()
@@ -233,27 +242,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):
settings = Settings.get_instance() try:
old_data = str(settings['ipv6_enabled']) + str(settings['udp_enabled']) + str(bool(settings['proxy_type'])) settings = Settings.get_instance()
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)
self.close()
def restart_core(self): except:
self.reconn = True pass
self.close()
class PrivacySettings(CenteredWidget): class PrivacySettings(CenteredWidget):
@@ -269,21 +271,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)
@@ -382,6 +384,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'])

View File

@@ -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
} }

View File

@@ -207,7 +207,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):
@@ -341,6 +341,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
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@@ -381,6 +387,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 +420,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
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] try:
ft.set_state_changed_handler(item.update) ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
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 +444,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 +468,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 +522,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:]
@@ -586,7 +617,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 +653,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 +665,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 +723,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 +741,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.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 +758,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 +771,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.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.save_profile(data)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Friend requests # Friend requests
@@ -749,19 +802,29 @@ 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')
result = self._tox.friend_add(tox_id, message.encode('utf-8')) if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] self.add_friend(tox_id)
item = self.create_friend_item() msgBox = QtGui.QMessageBox()
if not self._history.friend_exists_in_db(tox_id): msgBox.setWindowTitle(QtGui.QApplication.translate("MainWindow", "Friend added", None, QtGui.QApplication.UnicodeUTF8))
self._history.add_friend_to_db(tox_id) text = (QtGui.QApplication.translate("MainWindow", 'Friend added without sending friend request', None, QtGui.QApplication.UnicodeUTF8))
message_getter = self._history.messages_getter(tox_id) msgBox.setText(text)
friend = Friend(message_getter, result, tox_id, '', item, tox_id) msgBox.exec_()
self._friends.append(friend) else:
result = self._tox.friend_add(tox_id, message.encode('utf-8'))
tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2]
item = self.create_friend_item()
if not self._history.friend_exists_in_db(tox_id):
self._history.add_friend_to_db(tox_id)
message_getter = self._history.messages_getter(tox_id)
friend = Friend(message_getter, result, tox_id, '', item, tox_id)
self._friends.append(friend)
data = self._tox.get_savedata()
ProfileHelper.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 +843,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.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 +860,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
@@ -884,10 +950,34 @@ class Profile(Contact, Singleton):
tr.cancel() tr.cancel()
else: else:
tr.cancelled() tr.cancelled()
del self._file_transfers[(friend_number, file_number)] if (friend_number, file_number) in self._file_transfers:
del tr
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 +1008,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,
@@ -938,7 +1028,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
os.path.getsize(path), os.path.getsize(path),
os.path.basename(path), os.path.basename(path),
friend_number, friend_number,
@@ -952,7 +1042,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 +1053,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 +1067,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 +1075,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'])

View File

@@ -2,7 +2,7 @@ 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
@@ -14,7 +14,12 @@ 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)) 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())
@@ -57,7 +62,7 @@ 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': [],

0
src/styles/rc/Hmovetoolbar.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 220 B

After

Width:  |  Height:  |  Size: 220 B

0
src/styles/rc/Hsepartoolbar.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 172 B

After

Width:  |  Height:  |  Size: 172 B

0
src/styles/rc/Vmovetoolbar.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 228 B

After

Width:  |  Height:  |  Size: 228 B

0
src/styles/rc/Vsepartoolbar.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 147 B

After

Width:  |  Height:  |  Size: 147 B

0
src/styles/rc/branch_closed.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 150 B

After

Width:  |  Height:  |  Size: 150 B

0
src/styles/rc/branch_open.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 166 B

BIN
src/styles/rc/checkbox_checked.png Normal file → Executable file

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 4.4 KiB

0
src/styles/rc/checkbox_indeterminate.png Normal file → Executable file
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 514 B

BIN
src/styles/rc/checkbox_unchecked.png Normal file → Executable file

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 2.8 KiB

0
src/styles/rc/close-hover.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

0
src/styles/rc/close-pressed.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 598 B

After

Width:  |  Height:  |  Size: 598 B

0
src/styles/rc/close.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 586 B

0
src/styles/rc/down_arrow.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 166 B

0
src/styles/rc/left_arrow.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 166 B

0
src/styles/rc/radio_checked.png Normal file → Executable file
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 933 B

After

Width:  |  Height:  |  Size: 933 B

0
src/styles/rc/radio_unchecked.png Normal file → Executable file
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 724 B

After

Width:  |  Height:  |  Size: 724 B

0
src/styles/rc/right_arrow.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 160 B

0
src/styles/rc/sizegrip.png Normal file → Executable file
View 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
View 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
View File

Before

Width:  |  Height:  |  Size: 182 B

After

Width:  |  Height:  |  Size: 182 B

0
src/styles/rc/stylesheet-vline.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 239 B

After

Width:  |  Height:  |  Size: 239 B

0
src/styles/rc/transparent.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 195 B

After

Width:  |  Height:  |  Size: 195 B

0
src/styles/rc/undock.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 578 B

After

Width:  |  Height:  |  Size: 578 B

0
src/styles/rc/up_arrow.png Normal file → Executable file
View 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
View File

Before

Width:  |  Height:  |  Size: 159 B

After

Width:  |  Height:  |  Size: 159 B

File diff suppressed because one or more lines are too long

View File

@@ -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
@@ -1268,4 +1268,9 @@ QCheckBox
color: #bbb; color: #bbb;
margin-bottom: 2px; margin-bottom: 2px;
text-align: center; text-align: center;
}
QListWidget > QLabel
{
color: #A9A9A9;
} }

View File

@@ -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
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------

View File

@@ -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)

Binary file not shown.

View File

@@ -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="64"/>
<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="66"/>
<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="67"/>
<source>Message:</source> <source>Message:</source>
<translation>Message:</translation> <translation>Message:</translation>
</message> </message>
@@ -22,159 +22,206 @@
<context> <context>
<name>Form</name> <name>Form</name>
<message> <message>
<location filename="menu.py" line="66"/> <location filename="menu.py" line="65"/>
<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="231"/>
<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="232"/>
<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="233"/>
<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="234"/>
<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="235"/>
<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>Online contacts</translation>
</message> </message>
<message>
<location filename="menu.py" line="237"/>
<source>HTTP</source>
<translation>HTTP</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="mainscreen.py" line="95"/> <location filename="mainscreen.py" line="104"/>
<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="110"/>
<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="346"/>
<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="103"/>
<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="105"/>
<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="106"/>
<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="107"/>
<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="108"/>
<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="109"/>
<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="840"/>
<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="842"/>
<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="390"/>
<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="431"/>
<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="432"/>
<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="434"/>
<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="435"/>
<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="436"/>
<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="438"/>
<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="686"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source> <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation>Enter new alias for friend {} or leave empty to use friend&apos;s name:</translation> <translation>Enter new alias for friend {} or leave empty to use friend&apos;s name:</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="102"/> <location filename="mainscreen.py" line="111"/>
<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>Find contact</translation>
</message>
<message>
<location filename="profile.py" line="812"/>
<source>Friend added</source>
<translation>Friend added</translation>
</message>
<message>
<location filename="mainscreen.py" line="347"/>
<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="813"/>
<source>Friend added without sending friend request</source>
<translation>Friend added without sending friend request</translation>
</message>
<message>
<location filename="list_items.py" line="231"/>
<source>Choose folder</source>
<translation>Choose folder</translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<source>Send screenshot</source>
<translation>Send screenshot</translation>
</message>
<message>
<location filename="mainscreen.py" line="114"/>
<source>Send file</source>
<translation>Send file</translation>
</message>
<message>
<location filename="mainscreen.py" line="115"/>
<source>Send message</source>
<translation>Send message</translation>
</message>
<message>
<location filename="mainscreen.py" line="116"/>
<source>Start audio call with friend</source>
<translation>Start audio call with friend</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="230"/>
<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="236"/>
<source>Restart TOX core</source> <source>Restart TOX core</source>
<translation>Restart Tox core</translation> <translation>Restart Tox core</translation>
</message> </message>
@@ -182,60 +229,65 @@
<context> <context>
<name>ProfileSettingsForm</name> <name>ProfileSettingsForm</name>
<message> <message>
<location filename="menu.py" line="131"/> <location filename="menu.py" line="133"/>
<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="134"/>
<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="135"/>
<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="136"/>
<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="137"/>
<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="138"/>
<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="139"/>
<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="140"/>
<source>Reset avatar</source> <source>Reset avatar</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<location filename="menu.py" line="141"/>
<source>New NoSpam</source>
<translation>New NoSpam</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="522"/>
<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="523"/>
<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="524"/>
<source>Output device:</source> <source>Output device:</source>
<translation>Output device:</translation> <translation>Output device:</translation>
</message> </message>
@@ -243,12 +295,12 @@
<context> <context>
<name>incoming_call</name> <name>incoming_call</name>
<message> <message>
<location filename="profile.py" line="1041"/> <location filename="profile.py" line="1151"/>
<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="1153"/>
<source>Incoming audio call</source> <source>Incoming audio call</source>
<translation>Incoming audio call</translation> <translation>Incoming audio call</translation>
</message> </message>
@@ -256,17 +308,17 @@
<context> <context>
<name>interfaceForm</name> <name>interfaceForm</name>
<message> <message>
<location filename="menu.py" line="442"/> <location filename="menu.py" line="460"/>
<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="461"/>
<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="462"/>
<source>Language:</source> <source>Language:</source>
<translation></translation> <translation></translation>
</message> </message>
@@ -274,47 +326,47 @@
<context> <context>
<name>login</name> <name>login</name>
<message> <message>
<location filename="loginscreen.py" line="57"/> <location filename="loginscreen.py" line="70"/>
<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="71"/>
<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="72"/>
<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="73"/>
<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="74"/>
<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="75"/>
<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="76"/>
<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="77"/>
<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="87"/>
<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 +374,22 @@
<context> <context>
<name>notificationsForm</name> <name>notificationsForm</name>
<message> <message>
<location filename="menu.py" line="383"/> <location filename="menu.py" line="401"/>
<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="402"/>
<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="403"/>
<source>Enable call&apos;s sound</source> <source>Enable call&apos;s sound</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="386"/> <location filename="menu.py" line="404"/>
<source>Enable sound notifications</source> <source>Enable sound notifications</source>
<translation></translation> <translation></translation>
</message> </message>
@@ -345,77 +397,77 @@
<context> <context>
<name>privacySettings</name> <name>privacySettings</name>
<message> <message>
<location filename="menu.py" line="307"/> <location filename="menu.py" line="319"/>
<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="320"/>
<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="321"/>
<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="322"/>
<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="323"/>
<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="324"/>
<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="325"/>
<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="348"/>
<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="351"/>
<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="327"/>
<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="328"/>
<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="329"/>
<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="335"/>
<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="336"/>
<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="326"/>
<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,12 +475,12 @@
<context> <context>
<name>tray</name> <name>tray</name>
<message> <message>
<location filename="main.py" line="106"/> <location filename="main.py" line="113"/>
<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="114"/>
<source>Exit</source> <source>Exit</source>
<translation></translation> <translation></translation>
</message> </message>

Binary file not shown.

View File

@@ -1,19 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="fr"> <!DOCTYPE TS>
<TS version="2.1" language="fr">
<context> <context>
<name>AddContact</name> <name>AddContact</name>
<message> <message>
<location filename="menu.py" line="65"/> <location filename="menu.py" line="64"/>
<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="66"/>
<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="67"/>
<source>Message:</source> <source>Message:</source>
<translation>Message :</translation> <translation>Message :</translation>
</message> </message>
@@ -21,251 +22,308 @@
<context> <context>
<name>Form</name> <name>Form</name>
<message> <message>
<location filename="menu.py" line="66"/> <location filename="menu.py" line="65"/>
<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="231"/>
<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="232"/>
<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="233"/>
<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="234"/>
<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="235"/>
<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>Contacts connectés</translation>
</message> </message>
<message>
<location filename="menu.py" line="237"/>
<source>HTTP</source>
<translation>HTTP</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="mainscreen.py" line="95"/> <location filename="mainscreen.py" line="104"/>
<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="110"/>
<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="346"/>
<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="103"/>
<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="105"/>
<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="106"/>
<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="107"/>
<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="108"/>
<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="109"/>
<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="840"/>
<source>User {} wants to add you to contact list. Message: <source>User {} wants to add you to contact list. Message:
{}</source> {}</source>
<translation>L&apos;Utilisateur {} veut vout rajouter à sa liste de contacts. Message : {}</translation> <translation>L&apos;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="842"/>
<source>Friend request</source> <source>Friend request</source>
<translation>Demande d&apos;amis</translation> <translation>Demande d&apos;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="390"/>
<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="431"/>
<source>Disallow auto accept</source> <source>Disallow auto accept</source>
<translation>Désactiver l&apos;auto-réception</translation> <translation>Désactiver l&apos;auto-réception</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="368"/> <location filename="mainscreen.py" line="432"/>
<source>Allow auto accept</source> <source>Allow auto accept</source>
<translation>Activer l&apos;auto-réception</translation> <translation>Activer l&apos;auto-réception</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="370"/> <location filename="mainscreen.py" line="434"/>
<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="435"/>
<source>Clear history</source> <source>Clear history</source>
<translation>Vider l&apos;historique</translation> <translation>Vider l&apos;historique</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="372"/> <location filename="mainscreen.py" line="436"/>
<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="438"/>
<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="686"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source> <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation type="unfinished"></translation> <translation>Entrez un nouvel alias pour l&apos;ami {} ou laissez vide pour garder son nom de base :</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="102"/> <location filename="mainscreen.py" line="111"/>
<source>Audio</source> <source>Audio</source>
<translation type="unfinished"></translation> <translation>Audio</translation>
</message>
<message>
<location filename="mainscreen.py" line="112"/>
<source>Find contact</source>
<translation>Trouver le contact</translation>
</message>
<message>
<location filename="profile.py" line="812"/>
<source>Friend added</source>
<translation>Ami rajouté</translation>
</message>
<message>
<location filename="mainscreen.py" line="347"/>
<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="813"/>
<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="231"/>
<source>Choose folder</source>
<translation>Choisir le dossier</translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<source>Send screenshot</source>
<translation>Envoyer une capture d&apos;écran</translation>
</message>
<message>
<location filename="mainscreen.py" line="114"/>
<source>Send file</source>
<translation>Envoyer le fichier</translation>
</message>
<message>
<location filename="mainscreen.py" line="115"/>
<source>Send message</source>
<translation>Envoyer le message</translation>
</message>
<message>
<location filename="mainscreen.py" line="116"/>
<source>Start audio call with friend</source>
<translation>Lancer un appel audio avec un ami</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="230"/>
<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="236"/>
<source>Restart TOX core</source> <source>Restart TOX core</source>
<translation type="unfinished"></translation> <translation>Relancer le noyau TOX</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="133"/>
<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="134"/>
<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="135"/>
<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="136"/>
<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="137"/>
<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="138"/>
<source>Copy TOX ID</source> <source>Copy TOX ID</source>
<translation>Copier l&apos;ID TOX</translation> <translation>Copier l&apos;ID TOX</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="137"/> <location filename="menu.py" line="139"/>
<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="140"/>
<source>Reset avatar</source> <source>Reset avatar</source>
<translation>Réinitialiser l&apos;avatar</translation> <translation>Réinitialiser l&apos;avatar</translation>
</message> </message>
<message>
<location filename="menu.py" line="141"/>
<source>New NoSpam</source>
<translation>Nouveau NoSpam</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="522"/>
<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="523"/>
<source>Input device:</source> <source>Input device:</source>
<translation type="unfinished"></translation> <translation>Péripherique d&apos;entrée :</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="506"/> <location filename="menu.py" line="524"/>
<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="1151"/>
<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="1153"/>
<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="460"/>
<source>Interface settings</source> <source>Interface settings</source>
<translation>Paramêtres de l&apos;interface</translation> <translation>Paramêtres de l&apos;interface</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="443"/> <location filename="menu.py" line="461"/>
<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="462"/>
<source>Language:</source> <source>Language:</source>
<translation>Langue :</translation> <translation>Langue :</translation>
</message> </message>
@@ -273,47 +331,47 @@
<context> <context>
<name>login</name> <name>login</name>
<message> <message>
<location filename="loginscreen.py" line="57"/> <location filename="loginscreen.py" line="70"/>
<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="71"/>
<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="72"/>
<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="73"/>
<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="74"/>
<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="75"/>
<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="76"/>
<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="77"/>
<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="87"/>
<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&apos;une autre instance de Toxygen utilise ce profil ! Continuer ?</translation> <translation>Il semble qu&apos;une autre instance de Toxygen utilise ce profil ! Continuer ?</translation>
</message> </message>
@@ -321,22 +379,22 @@
<context> <context>
<name>notificationsForm</name> <name>notificationsForm</name>
<message> <message>
<location filename="menu.py" line="383"/> <location filename="menu.py" line="401"/>
<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="402"/>
<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="403"/>
<source>Enable call&apos;s sound</source> <source>Enable call&apos;s sound</source>
<translation>Activer les sons d&apos;appel</translation> <translation>Activer les sons d&apos;appel</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="386"/> <location filename="menu.py" line="404"/>
<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,90 +402,90 @@
<context> <context>
<name>privacySettings</name> <name>privacySettings</name>
<message> <message>
<location filename="menu.py" line="307"/> <location filename="menu.py" line="319"/>
<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="320"/>
<source>Save chat history</source> <source>Save chat history</source>
<translation>Sauvegarder l&apos;historique du chat</translation> <translation>Sauvegarder l&apos;historique de chat</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="309"/> <location filename="menu.py" line="321"/>
<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="322"/>
<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="323"/>
<source>Auto accept default path:</source> <source>Auto accept default path:</source>
<translation>Chemin d&apos;accès des fichiers acceptés automatiquement :</translation> <translation>Chemin d&apos;accès des fichiers acceptés automatiquement :</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="312"/> <location filename="menu.py" line="324"/>
<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="325"/>
<source>Allow inlines</source> <source>Allow inlines</source>
<translation type="unfinished"></translation> <translation>Activer l&apos;auto-réception</translation>
</message>
<message>
<location filename="menu.py" line="348"/>
<source>Chat history</source>
<translation>Historique de chat</translation>
</message>
<message>
<location filename="menu.py" line="351"/>
<source>History will be cleaned! Continue?</source>
<translation>L&apos;Historique va être nettoyé ! Confirmer ?</translation>
</message>
<message>
<location filename="menu.py" line="327"/>
<source>Blocked users:</source>
<translation>Utilisateurs bloqués :</translation>
</message>
<message>
<location filename="menu.py" line="328"/>
<source>Unblock</source>
<translation>Débloquer</translation>
</message>
<message>
<location filename="menu.py" line="329"/>
<source>Block user</source>
<translation>Bloquer l&apos;utilisateur</translation>
</message>
<message>
<location filename="menu.py" line="335"/>
<source>Add to friend list</source>
<translation>Ajouter à la liste des amis</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="336"/> <location filename="menu.py" line="336"/>
<source>Chat history</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="339"/>
<source>History will be cleaned! Continue?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="315"/>
<source>Blocked users:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="316"/>
<source>Unblock</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="317"/>
<source>Block user</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="323"/>
<source>Add to friend list</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="menu.py" line="324"/>
<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&apos;amis ?</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="314"/> <location filename="menu.py" line="326"/>
<source>Block by TOX ID:</source> <source>Block by TOX ID:</source>
<translation type="unfinished"></translation> <translation>Bloquer l&apos;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="113"/>
<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="114"/>
<source>Exit</source> <source>Exit</source>
<translation>Quitter</translation> <translation>Quitter</translation>
</message> </message>

Binary file not shown.

View File

@@ -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="64"/>
<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="66"/>
<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="67"/>
<source>Message:</source> <source>Message:</source>
<translation>Сообщение:</translation> <translation>Сообщение:</translation>
</message> </message>
@@ -22,160 +22,206 @@
<context> <context>
<name>Form</name> <name>Form</name>
<message> <message>
<location filename="menu.py" line="66"/> <location filename="menu.py" line="65"/>
<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="231"/>
<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="232"/>
<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="233"/>
<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="234"/>
<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="235"/>
<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>Контакты в сети</translation>
</message> </message>
<message>
<location filename="menu.py" line="237"/>
<source>HTTP</source>
<translation>HTTP</translation>
</message>
</context> </context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="mainscreen.py" line="95"/> <location filename="mainscreen.py" line="104"/>
<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="110"/>
<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="346"/>
<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="103"/>
<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="105"/>
<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="106"/>
<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="107"/>
<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="108"/>
<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="109"/>
<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="840"/>
<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="842"/>
<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="390"/>
<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="431"/>
<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="432"/>
<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="434"/>
<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="435"/>
<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="436"/>
<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="438"/>
<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="686"/>
<source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source> <source>Enter new alias for friend {} or leave empty to use friend&apos;s name:</source>
<translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation> <translation>Введите новый псевдоним для друга {} или оставьте пустым для использования его имени:</translation>
</message> </message>
<message> <message>
<location filename="mainscreen.py" line="102"/> <location filename="mainscreen.py" line="111"/>
<source>Audio</source> <source>Audio</source>
<translation>Аудио</translation> <translation>Аудио</translation>
</message> </message>
<message>
<location filename="mainscreen.py" line="112"/>
<source>Find contact</source>
<translation>Найти контакт</translation>
</message>
<message>
<location filename="profile.py" line="812"/>
<source>Friend added</source>
<translation>Друг добавлен</translation>
</message>
<message>
<location filename="mainscreen.py" line="347"/>
<source>Toxygen is Tox client written on Python.
Version: </source>
<translation>Toxygen - клиент для мессенджера Tox, написанный на Python. Версия: </translation>
</message>
<message>
<location filename="profile.py" line="813"/>
<source>Friend added without sending friend request</source>
<translation>Друг добавлен без отправки запроса на добавление в друзья</translation>
</message>
<message>
<location filename="list_items.py" line="231"/>
<source>Choose folder</source>
<translation>Выбрать папку</translation>
</message>
<message>
<location filename="mainscreen.py" line="113"/>
<source>Send screenshot</source>
<translation>Отправить снимок экрана</translation>
</message>
<message>
<location filename="mainscreen.py" line="114"/>
<source>Send file</source>
<translation>Отправить файл</translation>
</message>
<message>
<location filename="mainscreen.py" line="115"/>
<source>Send message</source>
<translation>Отправить сообщение</translation>
</message>
<message>
<location filename="mainscreen.py" line="116"/>
<source>Start audio call with friend</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="230"/>
<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="236"/>
<source>Restart TOX core</source> <source>Restart TOX core</source>
<translation>Перезапустить ядро TOX</translation> <translation>Перезапустить ядро TOX</translation>
</message> </message>
@@ -183,32 +229,32 @@
<context> <context>
<name>ProfileSettingsForm</name> <name>ProfileSettingsForm</name>
<message> <message>
<location filename="menu.py" line="131"/> <location filename="menu.py" line="133"/>
<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="134"/>
<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="135"/>
<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="136"/>
<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="137"/>
<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="138"/>
<source>Copy TOX ID</source> <source>Copy TOX ID</source>
<translation>Копировать TOX ID</translation> <translation>Копировать TOX ID</translation>
</message> </message>
@@ -218,30 +264,35 @@
<translation type="obsolete">Язык:</translation> <translation type="obsolete">Язык:</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="137"/> <location filename="menu.py" line="139"/>
<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="140"/>
<source>Reset avatar</source> <source>Reset avatar</source>
<translation>Сбросить аватар</translation> <translation>Сбросить аватар</translation>
</message> </message>
<message>
<location filename="menu.py" line="141"/>
<source>New NoSpam</source>
<translation>Новый NoSpam</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="522"/>
<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="523"/>
<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="524"/>
<source>Output device:</source> <source>Output device:</source>
<translation>Устройство вывода:</translation> <translation>Устройство вывода:</translation>
</message> </message>
@@ -249,12 +300,12 @@
<context> <context>
<name>incoming_call</name> <name>incoming_call</name>
<message> <message>
<location filename="profile.py" line="1041"/> <location filename="profile.py" line="1151"/>
<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="1153"/>
<source>Incoming audio call</source> <source>Incoming audio call</source>
<translation>Входящий аудиозвонок</translation> <translation>Входящий аудиозвонок</translation>
</message> </message>
@@ -262,17 +313,17 @@
<context> <context>
<name>interfaceForm</name> <name>interfaceForm</name>
<message> <message>
<location filename="menu.py" line="442"/> <location filename="menu.py" line="460"/>
<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="461"/>
<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="462"/>
<source>Language:</source> <source>Language:</source>
<translation>Язык:</translation> <translation>Язык:</translation>
</message> </message>
@@ -280,47 +331,47 @@
<context> <context>
<name>login</name> <name>login</name>
<message> <message>
<location filename="loginscreen.py" line="57"/> <location filename="loginscreen.py" line="70"/>
<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="71"/>
<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="72"/>
<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="73"/>
<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="74"/>
<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="75"/>
<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="76"/>
<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="77"/>
<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="87"/>
<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 +379,22 @@
<context> <context>
<name>notificationsForm</name> <name>notificationsForm</name>
<message> <message>
<location filename="menu.py" line="383"/> <location filename="menu.py" line="401"/>
<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="402"/>
<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="403"/>
<source>Enable call&apos;s sound</source> <source>Enable call&apos;s sound</source>
<translation>Включить звук звонка</translation> <translation>Включить звук звонка</translation>
</message> </message>
<message> <message>
<location filename="menu.py" line="386"/> <location filename="menu.py" line="404"/>
<source>Enable sound notifications</source> <source>Enable sound notifications</source>
<translation>Включить звуковые уведомления <translation>Включить звуковые уведомления
</translation> </translation>
@@ -352,77 +403,77 @@
<context> <context>
<name>privacySettings</name> <name>privacySettings</name>
<message> <message>
<location filename="menu.py" line="307"/> <location filename="menu.py" line="319"/>
<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="320"/>
<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="321"/>
<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="322"/>
<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="323"/>
<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="324"/>
<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="325"/>
<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="348"/>
<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="351"/>
<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="327"/>
<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="328"/>
<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="329"/>
<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="335"/>
<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="336"/>
<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="326"/>
<source>Block by TOX ID:</source> <source>Block by TOX ID:</source>
<translation>Блокировать по TOX ID:</translation> <translation>Блокировать по TOX ID:</translation>
</message> </message>
@@ -430,12 +481,12 @@
<context> <context>
<name>tray</name> <name>tray</name>
<message> <message>
<location filename="main.py" line="106"/> <location filename="main.py" line="113"/>
<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="114"/>
<source>Exit</source> <source>Exit</source>
<translation>Выход</translation> <translation>Выход</translation>
</message> </message>

View File

@@ -3,7 +3,7 @@ import time
from platform import system from platform import system
program_version = '0.1.1' program_version = '0.1.2'
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):