Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
02aa2981b5 | |||
bde1160562 | |||
3936f652e9 | |||
fa5b77fd2d | |||
cbeb25e0be | |||
3cb961957c | |||
65930716f9 | |||
03e34cc43f | |||
48360ea54c | |||
def6e10b93 | |||
24d27f9f47 | |||
7bdc506ff2 | |||
23b045070a | |||
6a7a38ef02 | |||
0e86fd66df | |||
e375dca9cc | |||
fd9bfa52f3 | |||
5fd5a9bd85 | |||
e75190f767 | |||
96705ed1cc | |||
5c4bfceee7 | |||
85061454a6 | |||
fe4af18196 | |||
183dfadc77 | |||
d3c8c99048 | |||
63774ba5df | |||
53a381222f | |||
7e63d9634a | |||
f6affc14ef | |||
9bf072e122 | |||
3ed4e8f511 | |||
94eb292b16 | |||
a0b23b0faa | |||
04f9852050 | |||
01854ff2d0 | |||
298dba4d6c | |||
bc248a9e30 | |||
7901aad3e7 | |||
6a244b30c1 | |||
17a8d81486 | |||
c114e9b6c3 | |||
672dafc701 | |||
d02ee92508 | |||
42617c8816 | |||
453b5e25be | |||
8fb263dadc | |||
4b4075606e | |||
b02e063302 | |||
cc8c5f7e99 | |||
51ec1af369 | |||
74d7c67d8c | |||
b40c0a868b | |||
b6d13166bc |
@ -33,24 +33,25 @@ Toxygen is cross-platform [Tox](https://tox.chat/) client written on Python
|
|||||||
- [x] File resuming
|
- [x] File resuming
|
||||||
- [x] Save file encryption
|
- [x] Save file encryption
|
||||||
- [x] Plugins support
|
- [x] Plugins support
|
||||||
|
- [x] Read receipts
|
||||||
|
- [x] Faux offline messaging
|
||||||
- [ ] Video
|
- [ ] Video
|
||||||
- [ ] Group chats
|
- [ ] Group chats
|
||||||
- [ ] Read receipts
|
|
||||||
- [ ] Faux offline messaging
|
|
||||||
- [ ] Desktop sharing
|
- [ ] Desktop sharing
|
||||||
|
|
||||||
###Downloads
|
###Downloads
|
||||||
|
[Releases](https://github.com/xveduk/toxygen/releases)
|
||||||
|
|
||||||
[Download last stable version](https://github.com/xveduk/toxygen/archive/master.zip)
|
[Download last stable version](https://github.com/xveduk/toxygen/archive/master.zip)
|
||||||
|
|
||||||
[Download develop version](https://github.com/xveduk/toxygen/archive/develop.zip)
|
[Download develop version](https://github.com/xveduk/toxygen/archive/develop.zip)
|
||||||
|
|
||||||
[Releases](https://github.com/xveduk/toxygen/releases)
|
|
||||||
|
|
||||||
###Screenshots
|
###Screenshots
|
||||||
*Toxygen on Ubuntu and Windows*
|
*Toxygen on Ubuntu and Windows*
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
###Docs
|
###Docs
|
||||||
[Check /docs/ for more info](/docs/)
|
[Check /docs/ for more info](/docs/)
|
||||||
|
|
||||||
|
@ -9,13 +9,13 @@ Help us find all bugs in Toxygen! Please provide following info:
|
|||||||
- Toxygen executable info - .py or precompiled binary
|
- Toxygen executable info - .py or precompiled binary
|
||||||
- Steps to reproduce the bug
|
- Steps to reproduce the bug
|
||||||
|
|
||||||
Want to see new feature in Toxygen? [Open issue!](https://github.com/xveduk/toxygen/issues)
|
Want to see new feature in Toxygen? [Ask for it!](https://github.com/xveduk/toxygen/issues)
|
||||||
|
|
||||||
#Pull requests
|
#Pull requests
|
||||||
|
|
||||||
Developer? Feel free to open pull request. Our dev team is small so we glad to get help.
|
Developer? Feel free to open pull request. Our dev team is small so we glad to get help.
|
||||||
Don't know what to do? Impove UI, fix [issues](https://github.com/xveduk/toxygen/issues) or implement features from our TODO list.
|
Don't know what to do? Improve UI, fix [issues](https://github.com/xveduk/toxygen/issues) or implement features from our TODO list.
|
||||||
You can find our TODO's in code and [here](/README.md)
|
You can find our TODO's in code and [here](/README.md). Also you can implement plugins for Toxygen.
|
||||||
|
|
||||||
#Translations
|
#Translations
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
# How to install Toxygen
|
# How to install Toxygen
|
||||||
|
|
||||||
|
## Use precompiled binary:
|
||||||
|
[Check our releases page](https://github.com/xveduk/toxygen/releases)
|
||||||
|
|
||||||
## From source code (recommended for developers)
|
## From source code (recommended for developers)
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
1. [Download and install latest Python 2.7](https://www.python.org/downloads/windows/)
|
1. [Download and install latest Python 2.7](https://www.python.org/downloads/windows/)
|
||||||
2. [Install PySide](https://pypi.python.org/pypi/PySide/1.2.4) *(PyQt4 support will be added later)*
|
2. [Install PySide](https://pypi.python.org/pypi/PySide/1.2.4) (recommended) or [PyQt4](https://riverbankcomputing.com/software/pyqt/download)
|
||||||
3. Install PyAudio: ``python -m pip install pyaudio``
|
3. Install PyAudio: ``python -m pip install pyaudio``
|
||||||
4. [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
|
4. [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
|
||||||
5. Unpack archive
|
5. Unpack archive
|
||||||
@ -23,22 +26,23 @@
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
- Install Python2.7:
|
Dependencies:
|
||||||
|
|
||||||
|
1. Install Python2.7:
|
||||||
``sudo apt-get install python2.7``
|
``sudo apt-get install python2.7``
|
||||||
- [Install PySide](https://wiki.qt.io/PySide_Binaries_Linux) *(PyQt4 support will be added later)*
|
2. [Install PySide](https://wiki.qt.io/PySide_Binaries_Linux) (recommended) or [PyQt4](https://riverbankcomputing.com/software/pyqt/download)
|
||||||
- Install PyAudio:
|
3. Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) with toxav support in your system (install in /usr/lib/)
|
||||||
|
4. Install PyAudio:
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install portaudio19-dev
|
sudo apt-get install portaudio19-dev
|
||||||
sudo pip install pyaudio
|
sudo pip install pyaudio
|
||||||
```
|
```
|
||||||
- [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
|
Toxygen:
|
||||||
- Unpack archive
|
|
||||||
- Install [toxcore](https://github.com/irungentoo/toxcore/blob/master/INSTALL.md) in your system (install in /usr/lib/)
|
|
||||||
- Run app:
|
|
||||||
``python main.py``
|
|
||||||
|
|
||||||
## Use precompiled binary:
|
1. [Download toxygen](https://github.com/xveduk/toxygen/archive/master.zip)
|
||||||
[Check our releases page](https://github.com/xveduk/toxygen/releases)
|
2. Unpack archive
|
||||||
|
3. Run app:
|
||||||
|
``python main.py``
|
||||||
|
|
||||||
## Compile Toxygen
|
## Compile Toxygen
|
||||||
Check [compile.md](/docs/compile.md) for more info
|
Check [compile.md](/docs/compile.md) for more info
|
||||||
|
@ -49,5 +49,5 @@ Plugin's methods should not raise exceptions.
|
|||||||
|
|
||||||
#Examples
|
#Examples
|
||||||
|
|
||||||
You can find example of a plugin in [official repo](https://github.com/ingvar1995/toxygen_plugins)
|
You can find examples in [official repo](https://github.com/ingvar1995/toxygen_plugins)
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#Plugins
|
#Plugins
|
||||||
|
|
||||||
Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python module and directory with plugin's data which provide some additional functionality.
|
Toxygen is the first [Tox](https://tox.chat/) client with plugins support. Plugin is Python module (.py file) and directory with plugin's data which provide some additional functionality.
|
||||||
|
|
||||||
#How to write plugin
|
#How to write plugin
|
||||||
|
|
||||||
|
11
docs/smileys_and_stickers.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#Smileys
|
||||||
|
|
||||||
|
Toxygen support smileys. Smiley is small picture which replaces some combination of symbols. If you want to create your own smiley pack, create directory in src/smileys/. This directory must contain images with smileys and config.json. Example of config.json:
|
||||||
|
|
||||||
|
{":)": "one.png", ":(": "two.png"}
|
||||||
|
|
||||||
|
Animated smileys (.gif) are supported too.
|
||||||
|
|
||||||
|
#Stickers
|
||||||
|
|
||||||
|
Sticker is inline image. If you want to create your own smiley pack, create directory in src/stickers/ and place your stickers there.
|
BIN
docs/ubuntu.png
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 109 KiB |
BIN
docs/windows.png
Before Width: | Height: | Size: 317 KiB After Width: | Height: | Size: 81 KiB |
@ -70,20 +70,22 @@ class IncomingCallWidget(widgets.CenteredWidget):
|
|||||||
|
|
||||||
def __init__(self, fl):
|
def __init__(self, fl):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.wf = wave.open(fl, 'rb')
|
self.fl = fl
|
||||||
|
self.wf = wave.open(self.fl, 'rb')
|
||||||
self.p = pyaudio.PyAudio()
|
self.p = pyaudio.PyAudio()
|
||||||
self.stream = self.p.open(
|
self.stream = self.p.open(
|
||||||
format=self.p.get_format_from_width(self.wf.getsampwidth()),
|
format=self.p.get_format_from_width(self.wf.getsampwidth()),
|
||||||
channels=self.wf.getnchannels(),
|
channels=self.wf.getnchannels(),
|
||||||
rate=self.wf.getframerate(),
|
rate=self.wf.getframerate(),
|
||||||
output=True
|
output=True)
|
||||||
)
|
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
data = self.wf.readframes(self.chunk)
|
while not self.stop:
|
||||||
while data and not self.stop:
|
|
||||||
self.stream.write(data)
|
|
||||||
data = self.wf.readframes(self.chunk)
|
data = self.wf.readframes(self.chunk)
|
||||||
|
while data and not self.stop:
|
||||||
|
self.stream.write(data)
|
||||||
|
data = self.wf.readframes(self.chunk)
|
||||||
|
self.wf = wave.open(self.fl, 'rb')
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
@ -107,3 +109,31 @@ class IncomingCallWidget(widgets.CenteredWidget):
|
|||||||
|
|
||||||
def set_pixmap(self, pixmap):
|
def set_pixmap(self, pixmap):
|
||||||
self.avatar_label.setPixmap(pixmap)
|
self.avatar_label.setPixmap(pixmap)
|
||||||
|
|
||||||
|
|
||||||
|
class AudioMessageRecorder(widgets.CenteredWidget):
|
||||||
|
|
||||||
|
def __init__(self, friend_number, name):
|
||||||
|
super(AudioMessageRecorder, self).__init__()
|
||||||
|
self.label = QtGui.QLabel(self)
|
||||||
|
self.label.setGeometry(QtCore.QRect(10, 20, 250, 20))
|
||||||
|
text = QtGui.QApplication.translate("MenuWindow", "Send audio message to friend {}", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
self.label.setText(text.format(name))
|
||||||
|
self.record = QtGui.QPushButton(self)
|
||||||
|
self.record.setGeometry(QtCore.QRect(20, 100, 150, 150))
|
||||||
|
|
||||||
|
self.record.setText(QtGui.QApplication.translate("MenuWindow", "Start recording", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.record.clicked.connect(self.start_or_stop_recording)
|
||||||
|
self.recording = False
|
||||||
|
self.friend_num = friend_number
|
||||||
|
|
||||||
|
def start_or_stop_recording(self):
|
||||||
|
if not self.recording:
|
||||||
|
self.recording = True
|
||||||
|
self.record.setText(QtGui.QApplication.translate("MenuWindow", "Stop recording", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ def friend_status(tox, friend_num, new_status, user_data):
|
|||||||
if friend.status is None and Settings.get_instance()['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
if friend.status is None and 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'])
|
||||||
invoke_in_main_thread(friend.set_status, new_status)
|
invoke_in_main_thread(friend.set_status, new_status)
|
||||||
|
invoke_in_main_thread(profile.send_files, friend_num)
|
||||||
invoke_in_main_thread(profile.update_filtration)
|
invoke_in_main_thread(profile.update_filtration)
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +76,7 @@ def friend_connection_status(tox, friend_num, new_status, user_data):
|
|||||||
"""
|
"""
|
||||||
Check friend's connection status (offline, udp, tcp)
|
Check friend's connection status (offline, udp, tcp)
|
||||||
"""
|
"""
|
||||||
print "Friend #{} connected! Friend's status: {}".format(friend_num, new_status)
|
print "Friend #{} connection status: {}".format(friend_num, new_status)
|
||||||
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']:
|
||||||
@ -85,7 +86,7 @@ def friend_connection_status(tox, friend_num, new_status, user_data):
|
|||||||
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
sound_notification(SOUND_NOTIFICATION['FRIEND_CONNECTION_STATUS'])
|
||||||
elif friend.status is None:
|
elif friend.status is None:
|
||||||
invoke_in_main_thread(profile.send_avatar, friend_num)
|
invoke_in_main_thread(profile.send_avatar, friend_num)
|
||||||
PluginLoader.get_instance().friend_online(friend_num)
|
invoke_in_main_thread(PluginLoader.get_instance().friend_online, friend_num)
|
||||||
|
|
||||||
|
|
||||||
def friend_name(tox, friend_num, name, size, user_data):
|
def friend_name(tox, friend_num, name, size, user_data):
|
||||||
@ -93,11 +94,8 @@ def friend_name(tox, friend_num, name, size, user_data):
|
|||||||
Friend changed his name
|
Friend changed his name
|
||||||
"""
|
"""
|
||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
friend = profile.get_friend_by_number(friend_num)
|
print 'New name: ', friend_num, name
|
||||||
print 'New name: ', str(friend_num), str(name)
|
invoke_in_main_thread(profile.new_name, friend_num, name)
|
||||||
invoke_in_main_thread(friend.set_name, name)
|
|
||||||
if profile.get_active_number() == friend_num:
|
|
||||||
invoke_in_main_thread(profile.set_active)
|
|
||||||
|
|
||||||
|
|
||||||
def friend_status_message(tox, friend_num, status_message, size, user_data):
|
def friend_status_message(tox, friend_num, status_message, size, user_data):
|
||||||
@ -109,6 +107,7 @@ def friend_status_message(tox, friend_num, status_message, size, user_data):
|
|||||||
friend = profile.get_friend_by_number(friend_num)
|
friend = profile.get_friend_by_number(friend_num)
|
||||||
invoke_in_main_thread(friend.set_status_message, status_message)
|
invoke_in_main_thread(friend.set_status_message, status_message)
|
||||||
print 'User #{} has new status: {}'.format(friend_num, status_message)
|
print 'User #{} has new status: {}'.format(friend_num, status_message)
|
||||||
|
invoke_in_main_thread(profile.send_messages, friend_num)
|
||||||
if profile.get_active_number() == friend_num:
|
if profile.get_active_number() == friend_num:
|
||||||
invoke_in_main_thread(profile.set_active)
|
invoke_in_main_thread(profile.set_active)
|
||||||
|
|
||||||
@ -127,6 +126,7 @@ def friend_message(window, tray):
|
|||||||
invoke_in_main_thread(tray_notification, friend.name, message.decode('utf8'), tray, window)
|
invoke_in_main_thread(tray_notification, friend.name, message.decode('utf8'), tray, window)
|
||||||
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
||||||
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
|
sound_notification(SOUND_NOTIFICATION['MESSAGE'])
|
||||||
|
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
@ -145,6 +145,13 @@ def friend_request(tox, public_key, message, message_size, user_data):
|
|||||||
def friend_typing(tox, friend_number, typing, user_data):
|
def friend_typing(tox, friend_number, typing, user_data):
|
||||||
invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing)
|
invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing)
|
||||||
|
|
||||||
|
|
||||||
|
def friend_read_receipt(tox, friend_number, message_id, user_data):
|
||||||
|
profile = Profile.get_instance()
|
||||||
|
profile.get_friend_by_number(friend_number).dec_receipt()
|
||||||
|
if friend_number == profile.get_active_number():
|
||||||
|
invoke_in_main_thread(profile.receipt)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - file transfers
|
# Callbacks - file transfers
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -171,9 +178,11 @@ def tox_file_recv(window, tray):
|
|||||||
if not window.isActiveWindow():
|
if not window.isActiveWindow():
|
||||||
friend = profile.get_friend_by_number(friend_number)
|
friend = profile.get_friend_by_number(friend_number)
|
||||||
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
if settings['notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
||||||
invoke_in_main_thread(tray_notification, 'File from ' + friend.name, file_name, tray, window)
|
file_from = QtGui.QApplication.translate("Callback", "File from", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
invoke_in_main_thread(tray_notification, file_from + ' ' + friend.name, file_name, tray, window)
|
||||||
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
if settings['sound_notifications'] and profile.status != TOX_USER_STATUS['BUSY']:
|
||||||
sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
|
sound_notification(SOUND_NOTIFICATION['FILE_TRANSFER'])
|
||||||
|
invoke_in_main_thread(tray.setIcon, QtGui.QIcon(curr_directory() + '/images/icon_new_messages.png'))
|
||||||
else: # AVATAR
|
else: # AVATAR
|
||||||
print 'Avatar'
|
print 'Avatar'
|
||||||
invoke_in_main_thread(profile.incoming_avatar,
|
invoke_in_main_thread(profile.incoming_avatar,
|
||||||
@ -194,7 +203,7 @@ def file_recv_chunk(tox, friend_number, file_number, position, chunk, length, us
|
|||||||
position,
|
position,
|
||||||
None)
|
None)
|
||||||
else:
|
else:
|
||||||
Profile.get_instance().incoming_chunk(friend_number, file_number, position,chunk[:length])
|
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):
|
||||||
@ -216,11 +225,11 @@ def file_recv_control(tox, friend_number, file_number, file_control, user_data):
|
|||||||
Friend cancelled, paused or resumed file transfer
|
Friend cancelled, paused or resumed file transfer
|
||||||
"""
|
"""
|
||||||
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
if file_control == TOX_FILE_CONTROL['CANCEL']:
|
||||||
Profile.get_instance().cancel_transfer(friend_number, file_number, True)
|
invoke_in_main_thread(Profile.get_instance().cancel_transfer, friend_number, file_number, True)
|
||||||
elif file_control == TOX_FILE_CONTROL['PAUSE']:
|
elif file_control == TOX_FILE_CONTROL['PAUSE']:
|
||||||
Profile.get_instance().pause_transfer(friend_number, file_number, True)
|
invoke_in_main_thread(Profile.get_instance().pause_transfer, friend_number, file_number, True)
|
||||||
elif file_control == TOX_FILE_CONTROL['RESUME']:
|
elif file_control == TOX_FILE_CONTROL['RESUME']:
|
||||||
Profile.get_instance().resume_transfer(friend_number, file_number, True)
|
invoke_in_main_thread(Profile.get_instance().resume_transfer, friend_number, file_number, True)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Callbacks - custom packets
|
# Callbacks - custom packets
|
||||||
@ -270,7 +279,7 @@ def callback_audio(toxav, friend_number, samples, audio_samples_per_channel, aud
|
|||||||
"""
|
"""
|
||||||
New audio chunk
|
New audio chunk
|
||||||
"""
|
"""
|
||||||
print audio_samples_per_channel, audio_channels_count, rate
|
# print audio_samples_per_channel, audio_channels_count, rate
|
||||||
Profile.get_instance().call.chunk(
|
Profile.get_instance().call.chunk(
|
||||||
''.join(chr(x) for x in samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
''.join(chr(x) for x in samples[:audio_samples_per_channel * 2 * audio_channels_count]),
|
||||||
audio_channels_count,
|
audio_channels_count,
|
||||||
@ -298,6 +307,7 @@ def init_callbacks(tox, window, tray):
|
|||||||
tox.callback_friend_status_message(friend_status_message, 0)
|
tox.callback_friend_status_message(friend_status_message, 0)
|
||||||
tox.callback_friend_request(friend_request, 0)
|
tox.callback_friend_request(friend_request, 0)
|
||||||
tox.callback_friend_typing(friend_typing, 0)
|
tox.callback_friend_typing(friend_typing, 0)
|
||||||
|
tox.callback_friend_read_receipt(friend_read_receipt, 0)
|
||||||
|
|
||||||
tox.callback_file_recv(tox_file_recv(window, tray), 0)
|
tox.callback_file_recv(tox_file_recv(window, tray), 0)
|
||||||
tox.callback_file_recv_chunk(file_recv_chunk, 0)
|
tox.callback_file_recv_chunk(file_recv_chunk, 0)
|
||||||
|
@ -4,6 +4,7 @@ import threading
|
|||||||
import settings
|
import settings
|
||||||
from toxav_enums import *
|
from toxav_enums import *
|
||||||
# TODO: play sound until outgoing call will be started or cancelled and add timeout
|
# TODO: play sound until outgoing call will be started or cancelled and add timeout
|
||||||
|
# TODO: add widget for call
|
||||||
|
|
||||||
CALL_TYPE = {
|
CALL_TYPE = {
|
||||||
'NONE': 0,
|
'NONE': 0,
|
||||||
|
113
src/contact.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import os
|
||||||
|
from settings import *
|
||||||
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
from toxcore_enums_and_consts import TOX_PUBLIC_KEY_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
class Contact(object):
|
||||||
|
"""
|
||||||
|
Class encapsulating TOX contact
|
||||||
|
Properties: name (alias of contact or name), status_message, status (connection status)
|
||||||
|
widget - widget for update
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, status_message, widget, tox_id):
|
||||||
|
"""
|
||||||
|
:param name: name, example: 'Toxygen user'
|
||||||
|
:param status_message: status message, example: 'Toxing on Toxygen'
|
||||||
|
:param widget: ContactItem instance
|
||||||
|
:param tox_id: tox id of contact
|
||||||
|
"""
|
||||||
|
self._name, self._status_message = name, status_message
|
||||||
|
self._status, self._widget = None, widget
|
||||||
|
self._widget.name.setText(name)
|
||||||
|
self._widget.status_message.setText(status_message)
|
||||||
|
self._tox_id = tox_id
|
||||||
|
self.load_avatar()
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# name - current name or alias of user
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def set_name(self, value):
|
||||||
|
self._name = value.decode('utf-8')
|
||||||
|
self._widget.name.setText(self._name)
|
||||||
|
self._widget.name.repaint()
|
||||||
|
|
||||||
|
name = property(get_name, set_name)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Status message
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_status_message(self):
|
||||||
|
return self._status_message
|
||||||
|
|
||||||
|
def set_status_message(self, value):
|
||||||
|
self._status_message = value.decode('utf-8')
|
||||||
|
self._widget.status_message.setText(self._status_message)
|
||||||
|
self._widget.status_message.repaint()
|
||||||
|
|
||||||
|
status_message = property(get_status_message, set_status_message)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Status
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
return self._status
|
||||||
|
|
||||||
|
def set_status(self, value):
|
||||||
|
self._status = value
|
||||||
|
self._widget.connection_status.update(value)
|
||||||
|
|
||||||
|
status = property(get_status, set_status)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# TOX ID. WARNING: for friend it will return public key, for profile - full address
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_tox_id(self):
|
||||||
|
return self._tox_id
|
||||||
|
|
||||||
|
tox_id = property(get_tox_id)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Avatars
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_avatar(self):
|
||||||
|
"""
|
||||||
|
Tries to load avatar of contact or uses default avatar
|
||||||
|
"""
|
||||||
|
avatar_path = '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
||||||
|
os.chdir(ProfileHelper.get_path() + 'avatars/')
|
||||||
|
if not os.path.isfile(avatar_path): # load default image
|
||||||
|
avatar_path = 'avatar.png'
|
||||||
|
os.chdir(curr_directory() + '/images/')
|
||||||
|
pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
|
||||||
|
pixmap.load(avatar_path)
|
||||||
|
self._widget.avatar_label.setScaledContents(False)
|
||||||
|
self._widget.avatar_label.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio))
|
||||||
|
self._widget.avatar_label.repaint()
|
||||||
|
|
||||||
|
def reset_avatar(self):
|
||||||
|
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
||||||
|
if os.path.isfile(avatar_path):
|
||||||
|
os.remove(avatar_path)
|
||||||
|
self.load_avatar()
|
||||||
|
|
||||||
|
def set_avatar(self, avatar):
|
||||||
|
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
||||||
|
with open(avatar_path, 'wb') as f:
|
||||||
|
f.write(avatar)
|
||||||
|
self.load_avatar()
|
||||||
|
|
||||||
|
def get_pixmap(self):
|
||||||
|
return self._widget.avatar_label.pixmap()
|
@ -1,6 +1,6 @@
|
|||||||
from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
|
from toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL
|
||||||
from os.path import basename, getsize, exists
|
from os.path import basename, getsize, exists, dirname
|
||||||
from os import remove, rename
|
from os import remove, rename, chdir
|
||||||
from time import time, sleep
|
from time import time, sleep
|
||||||
from tox import Tox
|
from tox import Tox
|
||||||
import settings
|
import settings
|
||||||
@ -9,21 +9,35 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
|
# TODO: threads!
|
||||||
|
|
||||||
|
|
||||||
TOX_FILE_TRANSFER_STATE = {
|
TOX_FILE_TRANSFER_STATE = {
|
||||||
'RUNNING': 0,
|
'RUNNING': 0,
|
||||||
'PAUSED': 1,
|
'PAUSED_BY_USER': 1,
|
||||||
'CANCELED': 2,
|
'CANCELLED': 2,
|
||||||
'FINISHED': 3,
|
'FINISHED': 3,
|
||||||
'PAUSED_BY_FRIEND': 4
|
'PAUSED_BY_FRIEND': 4,
|
||||||
|
'INCOMING_NOT_STARTED': 5,
|
||||||
|
'OUTGOING_NOT_STARTED': 6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACTIVE_FILE_TRANSFERS = (0, 1, 4, 5, 6)
|
||||||
|
|
||||||
|
PAUSED_FILE_TRANSFERS = (1, 4, 5, 6)
|
||||||
|
|
||||||
|
DO_NOT_SHOW_ACCEPT_BUTTON = (2, 3, 4, 6)
|
||||||
|
|
||||||
|
SHOW_PROGRESS_BAR = (0, 1, 4)
|
||||||
|
|
||||||
|
ALLOWED_FILES = ('toxygen_inline.png', 'utox-inline.png', 'sticker.png')
|
||||||
|
|
||||||
|
|
||||||
class StateSignal(QtCore.QObject):
|
class StateSignal(QtCore.QObject):
|
||||||
try:
|
try:
|
||||||
signal = QtCore.Signal(int, float) # state and progress
|
signal = QtCore.Signal(int, float, int) # state and progress
|
||||||
except:
|
except:
|
||||||
signal = QtCore.pyqtSignal(int, float) # state and progress - pyqt4
|
signal = QtCore.pyqtSignal(int, float, int) # state and progress - pyqt4
|
||||||
|
|
||||||
|
|
||||||
class FileTransfer(QtCore.QObject):
|
class FileTransfer(QtCore.QObject):
|
||||||
@ -38,7 +52,7 @@ class FileTransfer(QtCore.QObject):
|
|||||||
self._friend_number = friend_number
|
self._friend_number = friend_number
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
||||||
self._file_number = file_number
|
self._file_number = file_number
|
||||||
self._creation_time = time()
|
self._creation_time = None
|
||||||
self._size = float(size)
|
self._size = float(size)
|
||||||
self._done = 0
|
self._done = 0
|
||||||
self._state_changed = StateSignal()
|
self._state_changed = StateSignal()
|
||||||
@ -50,7 +64,12 @@ class FileTransfer(QtCore.QObject):
|
|||||||
self._state_changed.signal.connect(handler)
|
self._state_changed.signal.connect(handler)
|
||||||
|
|
||||||
def signal(self):
|
def signal(self):
|
||||||
self._state_changed.signal.emit(self.state, self._done / self._size if self._size else 0)
|
percentage = self._done / self._size if self._size else 0
|
||||||
|
if self._creation_time is None or not percentage:
|
||||||
|
t = -1
|
||||||
|
else:
|
||||||
|
t = ((time() - self._creation_time) / percentage) * (1 - percentage)
|
||||||
|
self._state_changed.signal.emit(self.state, percentage, int(t))
|
||||||
|
|
||||||
def get_file_number(self):
|
def get_file_number(self):
|
||||||
return self._file_number
|
return self._file_number
|
||||||
@ -62,14 +81,14 @@ class FileTransfer(QtCore.QObject):
|
|||||||
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
if hasattr(self, '_file'):
|
if hasattr(self, '_file'):
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
|
|
||||||
def cancelled(self):
|
def cancelled(self):
|
||||||
if hasattr(self, '_file'):
|
if hasattr(self, '_file'):
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['CANCELED']
|
self.state = TOX_FILE_TRANSFER_STATE['CANCELLED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
|
|
||||||
def pause(self, by_friend):
|
def pause(self, by_friend):
|
||||||
if not by_friend:
|
if not by_friend:
|
||||||
@ -100,6 +119,7 @@ class SendTransfer(FileTransfer):
|
|||||||
else:
|
else:
|
||||||
size = 0
|
size = 0
|
||||||
super(SendTransfer, self).__init__(path, tox, friend_number, size)
|
super(SendTransfer, self).__init__(path, tox, friend_number, size)
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
|
||||||
self._file_number = tox.file_send(friend_number, kind, size, file_id,
|
self._file_number = tox.file_send(friend_number, kind, size, file_id,
|
||||||
basename(path).encode('utf-8') if path else '')
|
basename(path).encode('utf-8') if path else '')
|
||||||
|
|
||||||
@ -109,6 +129,8 @@ class SendTransfer(FileTransfer):
|
|||||||
:param position: start position in file
|
:param position: start position in file
|
||||||
:param size: chunk max size
|
:param size: chunk max size
|
||||||
"""
|
"""
|
||||||
|
if self._creation_time is None:
|
||||||
|
self._creation_time = time()
|
||||||
if size:
|
if size:
|
||||||
self._file.seek(position)
|
self._file.seek(position)
|
||||||
data = self._file.read(size)
|
data = self._file.read(size)
|
||||||
@ -119,7 +141,7 @@ class SendTransfer(FileTransfer):
|
|||||||
if hasattr(self, '_file'):
|
if hasattr(self, '_file'):
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
|
|
||||||
|
|
||||||
class SendAvatar(SendTransfer):
|
class SendAvatar(SendTransfer):
|
||||||
@ -143,6 +165,7 @@ class SendFromBuffer(FileTransfer):
|
|||||||
|
|
||||||
def __init__(self, tox, friend_number, data, file_name):
|
def __init__(self, tox, friend_number, data, file_name):
|
||||||
super(SendFromBuffer, self).__init__(None, tox, friend_number, len(data))
|
super(SendFromBuffer, self).__init__(None, tox, friend_number, len(data))
|
||||||
|
self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
|
||||||
self._data = data
|
self._data = data
|
||||||
self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], len(data), None, file_name)
|
self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'], len(data), None, file_name)
|
||||||
|
|
||||||
@ -150,14 +173,28 @@ class SendFromBuffer(FileTransfer):
|
|||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
def send_chunk(self, position, size):
|
def send_chunk(self, position, size):
|
||||||
|
if self._creation_time is None:
|
||||||
|
self._creation_time = time()
|
||||||
if size:
|
if size:
|
||||||
data = self._data[position:position + size]
|
data = self._data[position:position + 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.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
|
|
||||||
|
|
||||||
|
class SendFromFileBuffer(SendTransfer):
|
||||||
|
|
||||||
|
def __init__(self, *args):
|
||||||
|
super(SendFromFileBuffer, self).__init__(*args)
|
||||||
|
|
||||||
|
def send_chunk(self, position, size):
|
||||||
|
super(SendFromFileBuffer, self).send_chunk(position, size)
|
||||||
|
if not size:
|
||||||
|
chdir(dirname(self._path))
|
||||||
|
remove(self._path)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Receive file
|
# Receive file
|
||||||
@ -182,23 +219,24 @@ class ReceiveTransfer(FileTransfer):
|
|||||||
:param position: position in file to save data
|
:param position: position in file to save data
|
||||||
:param data: raw data (string)
|
:param data: raw data (string)
|
||||||
"""
|
"""
|
||||||
|
if self._creation_time is None:
|
||||||
|
self._creation_time = time()
|
||||||
if data is None:
|
if data is None:
|
||||||
self._file.close()
|
self._file.close()
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
else:
|
else:
|
||||||
data = ''.join(chr(x) for x in data)
|
data = bytearray(data)
|
||||||
if self._file_size < position:
|
if self._file_size < position:
|
||||||
self._file.seek(0, 2)
|
self._file.seek(0, 2)
|
||||||
self._file.write('\0' * (position - self._file_size))
|
self._file.write('\0' * (position - self._file_size))
|
||||||
self._file.seek(position)
|
self._file.seek(position)
|
||||||
self._file.write(data)
|
self._file.write(data)
|
||||||
self._file.flush()
|
|
||||||
l = len(data)
|
l = len(data)
|
||||||
if position + l > self._file_size:
|
if position + l > self._file_size:
|
||||||
self._file_size = position + l
|
self._file_size = position + l
|
||||||
self._done += l
|
self._done += l
|
||||||
self._state_changed.signal.emit(self.state, self._done / self._size)
|
self.signal()
|
||||||
|
|
||||||
|
|
||||||
class ReceiveToBuffer(FileTransfer):
|
class ReceiveToBuffer(FileTransfer):
|
||||||
@ -215,9 +253,11 @@ class ReceiveToBuffer(FileTransfer):
|
|||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
def write_chunk(self, position, data):
|
def write_chunk(self, position, data):
|
||||||
|
if self._creation_time is None:
|
||||||
|
self._creation_time = time()
|
||||||
if data is None:
|
if data is None:
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
self.state = TOX_FILE_TRANSFER_STATE['FINISHED']
|
||||||
self._state_changed.signal.emit(self.state, 1)
|
self.signal()
|
||||||
else:
|
else:
|
||||||
data = ''.join(chr(x) for x in data)
|
data = ''.join(chr(x) for x in data)
|
||||||
l = len(data)
|
l = len(data)
|
||||||
@ -266,6 +306,7 @@ class ReceiveAvatar(ReceiveTransfer):
|
|||||||
if self.state:
|
if self.state:
|
||||||
avatar_path = self._path[:-4]
|
avatar_path = self._path[:-4]
|
||||||
if exists(avatar_path):
|
if exists(avatar_path):
|
||||||
|
chdir(dirname(avatar_path))
|
||||||
remove(avatar_path)
|
remove(avatar_path)
|
||||||
rename(self._path, avatar_path)
|
rename(self._path, avatar_path)
|
||||||
|
|
||||||
|
223
src/friend.py
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
import contact
|
||||||
|
from messages import *
|
||||||
|
from history import *
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class Friend(contact.Contact):
|
||||||
|
"""
|
||||||
|
Friend in list of friends. Can be hidden, properties 'has unread messages' and 'has alias' added
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, message_getter, number, *args):
|
||||||
|
"""
|
||||||
|
:param message_getter: gets messages from db
|
||||||
|
:param number: number of friend.
|
||||||
|
"""
|
||||||
|
super(Friend, self).__init__(*args)
|
||||||
|
self._number = number
|
||||||
|
self._new_messages = False
|
||||||
|
self._visible = True
|
||||||
|
self._alias = False
|
||||||
|
self._message_getter = message_getter
|
||||||
|
self._corr = []
|
||||||
|
self._unsaved_messages = 0
|
||||||
|
self._history_loaded = self._new_actions = False
|
||||||
|
self._receipts = 0
|
||||||
|
self._curr_text = ''
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.set_visibility(False)
|
||||||
|
del self._widget
|
||||||
|
if hasattr(self, '_message_getter'):
|
||||||
|
del self._message_getter
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# History support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_receipts(self):
|
||||||
|
return self._receipts
|
||||||
|
|
||||||
|
receipts = property(get_receipts) # read receipts
|
||||||
|
|
||||||
|
def inc_receipts(self):
|
||||||
|
self._receipts += 1
|
||||||
|
|
||||||
|
def dec_receipt(self):
|
||||||
|
if self._receipts:
|
||||||
|
self._receipts -= 1
|
||||||
|
self.mark_as_sent()
|
||||||
|
|
||||||
|
def load_corr(self, first_time=True):
|
||||||
|
"""
|
||||||
|
:param first_time: friend became active, load first part of messages
|
||||||
|
"""
|
||||||
|
if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')):
|
||||||
|
return
|
||||||
|
data = self._message_getter.get(PAGE_SIZE)
|
||||||
|
if data is not None and len(data):
|
||||||
|
data.reverse()
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
data = map(lambda tupl: TextMessage(*tupl), data)
|
||||||
|
self._corr = data + self._corr
|
||||||
|
self._history_loaded = True
|
||||||
|
|
||||||
|
def get_corr_for_saving(self):
|
||||||
|
"""
|
||||||
|
Get data to save in db
|
||||||
|
:return: list of unsaved messages or []
|
||||||
|
"""
|
||||||
|
if hasattr(self, '_message_getter'):
|
||||||
|
del self._message_getter
|
||||||
|
messages = filter(lambda x: x.get_type() <= 1, self._corr)
|
||||||
|
return map(lambda x: x.get_data(), messages[-self._unsaved_messages:]) if self._unsaved_messages else []
|
||||||
|
|
||||||
|
def get_corr(self):
|
||||||
|
return self._corr[:]
|
||||||
|
|
||||||
|
def append_message(self, message):
|
||||||
|
"""
|
||||||
|
:param message: text or file transfer message
|
||||||
|
"""
|
||||||
|
self._corr.append(message)
|
||||||
|
if message.get_type() <= 1:
|
||||||
|
self._unsaved_messages += 1
|
||||||
|
|
||||||
|
def get_last_message_text(self):
|
||||||
|
messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_OWNER['FRIEND'], self._corr)
|
||||||
|
if messages:
|
||||||
|
return messages[-1].get_data()[0]
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def unsent_messages(self):
|
||||||
|
"""
|
||||||
|
:return list of unsent messages
|
||||||
|
"""
|
||||||
|
messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr)
|
||||||
|
return messages
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
try:
|
||||||
|
message = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr)[0]
|
||||||
|
message.mark_as_sent()
|
||||||
|
except Exception as ex:
|
||||||
|
util.log('Mark as sent ex: ' + str(ex))
|
||||||
|
|
||||||
|
def clear_corr(self):
|
||||||
|
"""
|
||||||
|
Clear messages list
|
||||||
|
"""
|
||||||
|
if hasattr(self, '_message_getter'):
|
||||||
|
del self._message_getter
|
||||||
|
# don't delete data about active file transfer
|
||||||
|
self._corr = filter(lambda x: x.get_type() in (2, 3) and x.get_status() >= 2, self._corr)
|
||||||
|
self._unsaved_messages = 0
|
||||||
|
|
||||||
|
def get_curr_text(self):
|
||||||
|
return self._curr_text
|
||||||
|
|
||||||
|
def set_curr_text(self, value):
|
||||||
|
self._curr_text = value
|
||||||
|
|
||||||
|
curr_text = property(get_curr_text, set_curr_text)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# File transfers support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def update_transfer_data(self, file_number, status, inline=None):
|
||||||
|
"""
|
||||||
|
Update status of active transfer and load inline if needed
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
tr = filter(lambda x: x.get_type() == MESSAGE_TYPE['FILE_TRANSFER'] and x.is_active(file_number),
|
||||||
|
self._corr)[0]
|
||||||
|
tr.set_status(status)
|
||||||
|
i = self._corr.index(tr)
|
||||||
|
if inline: # inline was loaded
|
||||||
|
self._corr.insert(i, inline)
|
||||||
|
return i - len(self._corr)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_unsent_files(self):
|
||||||
|
messages = filter(lambda x: type(x) is UnsentFile, self._corr)
|
||||||
|
return messages
|
||||||
|
|
||||||
|
def clear_unsent_files(self):
|
||||||
|
self._corr = filter(lambda x: type(x) is not UnsentFile, self._corr)
|
||||||
|
|
||||||
|
def delete_one_unsent_file(self, time):
|
||||||
|
self._corr = filter(lambda x: not (type(x) is UnsentFile and x.get_data()[2] == time), self._corr)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Alias support
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def set_name(self, value):
|
||||||
|
"""
|
||||||
|
Set new name or ignore if alias exists
|
||||||
|
:param value: new name
|
||||||
|
"""
|
||||||
|
if not self._alias:
|
||||||
|
super(Friend, self).set_name(value)
|
||||||
|
|
||||||
|
def set_alias(self, alias):
|
||||||
|
self._alias = bool(alias)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Visibility in friends' list
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_visibility(self):
|
||||||
|
return self._visible
|
||||||
|
|
||||||
|
def set_visibility(self, value):
|
||||||
|
self._visible = value
|
||||||
|
|
||||||
|
visibility = property(get_visibility, set_visibility)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Unread messages from friend
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_actions(self):
|
||||||
|
return self._new_actions
|
||||||
|
|
||||||
|
def set_actions(self, value):
|
||||||
|
self._new_actions = value
|
||||||
|
self._widget.connection_status.update(self.status, value)
|
||||||
|
|
||||||
|
actions = property(get_actions, set_actions) # unread messages, incoming files, av calls
|
||||||
|
|
||||||
|
def get_messages(self):
|
||||||
|
return self._new_messages
|
||||||
|
|
||||||
|
def inc_messages(self):
|
||||||
|
self._new_messages += 1
|
||||||
|
self._new_actions = True
|
||||||
|
self._widget.connection_status.update(self.status, True)
|
||||||
|
self._widget.messages.update(self._new_messages)
|
||||||
|
|
||||||
|
def reset_messages(self):
|
||||||
|
self._new_actions = False
|
||||||
|
self._new_messages = 0
|
||||||
|
self._widget.messages.update(self._new_messages)
|
||||||
|
self._widget.connection_status.update(self.status, False)
|
||||||
|
|
||||||
|
messages = property(get_messages)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Friend's number (can be used in toxcore)
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def get_number(self):
|
||||||
|
return self._number
|
||||||
|
|
||||||
|
def set_number(self, value):
|
||||||
|
self._number = value
|
||||||
|
|
||||||
|
number = property(get_number, set_number)
|
@ -10,7 +10,8 @@ PAGE_SIZE = 42
|
|||||||
|
|
||||||
MESSAGE_OWNER = {
|
MESSAGE_OWNER = {
|
||||||
'ME': 0,
|
'ME': 0,
|
||||||
'FRIEND': 1
|
'FRIEND': 1,
|
||||||
|
'NOT_SENT': 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -22,12 +23,15 @@ class History(object):
|
|||||||
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
path = settings.ProfileHelper.get_path() + self._name + '.hstr'
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
decr = LibToxEncryptSave.get_instance()
|
decr = LibToxEncryptSave.get_instance()
|
||||||
with open(path, 'rb') as fin:
|
try:
|
||||||
data = fin.read()
|
with open(path, 'rb') as fin:
|
||||||
if decr.is_data_encrypted(data):
|
data = fin.read()
|
||||||
data = decr.pass_decrypt(data)
|
if decr.is_data_encrypted(data):
|
||||||
with open(path, 'wb') as fout:
|
data = decr.pass_decrypt(data)
|
||||||
fout.write(data)
|
with open(path, 'wb') as fout:
|
||||||
|
fout.write(data)
|
||||||
|
except:
|
||||||
|
os.remove(path)
|
||||||
db = connect(name + '.hstr')
|
db = connect(name + '.hstr')
|
||||||
cursor = db.cursor()
|
cursor = db.cursor()
|
||||||
cursor.execute('CREATE TABLE IF NOT EXISTS friends('
|
cursor.execute('CREATE TABLE IF NOT EXISTS friends('
|
||||||
@ -50,6 +54,9 @@ class History(object):
|
|||||||
new_path = directory + self._name + '.hstr'
|
new_path = directory + self._name + '.hstr'
|
||||||
with open(path, 'rb') as fin:
|
with open(path, 'rb') as fin:
|
||||||
data = fin.read()
|
data = fin.read()
|
||||||
|
encr = LibToxEncryptSave.get_instance()
|
||||||
|
if encr.has_password():
|
||||||
|
data = encr.pass_encrypt(data)
|
||||||
with open(new_path, 'wb') as fout:
|
with open(new_path, 'wb') as fout:
|
||||||
fout.write(data)
|
fout.write(data)
|
||||||
|
|
||||||
@ -110,6 +117,21 @@ class History(object):
|
|||||||
finally:
|
finally:
|
||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
def update_messages(self, tox_id, unsent_time):
|
||||||
|
chdir(settings.ProfileHelper.get_path())
|
||||||
|
db = connect(self._name + '.hstr')
|
||||||
|
try:
|
||||||
|
cursor = db.cursor()
|
||||||
|
cursor.execute('UPDATE id' + tox_id + ' SET owner = 0 '
|
||||||
|
'WHERE unix_time < ' + str(unsent_time) + ' AND owner = 2;')
|
||||||
|
db.commit()
|
||||||
|
except:
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
pass
|
||||||
|
|
||||||
def delete_messages(self, tox_id):
|
def delete_messages(self, tox_id):
|
||||||
chdir(settings.ProfileHelper.get_path())
|
chdir(settings.ProfileHelper.get_path())
|
||||||
db = connect(self._name + '.hstr')
|
db = connect(self._name + '.hstr')
|
||||||
|
BIN
src/images/audio_message.png
Executable file
After Width: | Height: | Size: 4.2 KiB |
BIN
src/images/busy.png
Normal file
After Width: | Height: | Size: 329 B |
BIN
src/images/busy_notification.png
Normal file
After Width: | Height: | Size: 609 B |
BIN
src/images/icon_new_messages.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/images/idle.png
Normal file
After Width: | Height: | Size: 231 B |
BIN
src/images/idle_notification.png
Normal file
After Width: | Height: | Size: 405 B |
BIN
src/images/menu.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/images/offline.png
Normal file
After Width: | Height: | Size: 159 B |
BIN
src/images/offline_notification.png
Normal file
After Width: | Height: | Size: 445 B |
BIN
src/images/online.png
Normal file
After Width: | Height: | Size: 201 B |
BIN
src/images/online_notification.png
Normal file
After Width: | Height: | Size: 351 B |
BIN
src/images/search.png
Executable file → Normal file
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.7 KiB |
BIN
src/images/smiley.png
Executable file
After Width: | Height: | Size: 5.6 KiB |
BIN
src/images/spinner.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/images/sticker.png
Executable file
After Width: | Height: | Size: 94 KiB |
BIN
src/images/video_message.png
Executable file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/images/videocall.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
@ -4,75 +4,153 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
import profile
|
import profile
|
||||||
from file_transfers import TOX_FILE_TRANSFER_STATE
|
from file_transfers import TOX_FILE_TRANSFER_STATE, PAUSED_FILE_TRANSFERS, DO_NOT_SHOW_ACCEPT_BUTTON, ACTIVE_FILE_TRANSFERS, SHOW_PROGRESS_BAR
|
||||||
from util import curr_directory, convert_time
|
from util import curr_directory, convert_time, curr_time
|
||||||
from messages import FILE_TRANSFER_MESSAGE_STATUS
|
from widgets import DataLabel, create_menu
|
||||||
from widgets import DataLabel
|
import cgi
|
||||||
|
import smileys
|
||||||
|
import settings
|
||||||
|
|
||||||
|
|
||||||
class MessageEdit(QtGui.QTextEdit):
|
class MessageEdit(QtGui.QTextBrowser):
|
||||||
|
|
||||||
def __init__(self, text, width, parent=None):
|
def __init__(self, text, width, message_type, parent=None):
|
||||||
super(MessageEdit, self).__init__(parent)
|
super(MessageEdit, self).__init__(parent)
|
||||||
|
self.urls = {}
|
||||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
|
self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
|
||||||
self.document().setTextWidth(width)
|
self.document().setTextWidth(width)
|
||||||
self.setPlainText(text)
|
self.setOpenExternalLinks(True)
|
||||||
|
self.setAcceptRichText(True)
|
||||||
|
self.setOpenLinks(False)
|
||||||
|
self.setSearchPaths([smileys.SmileyLoader.get_instance().get_smileys_path()])
|
||||||
|
self.document().setDefaultStyleSheet('a { color: #306EFF; }')
|
||||||
|
text = self.decoratedText(text)
|
||||||
|
if message_type != TOX_MESSAGE_TYPE['NORMAL']:
|
||||||
|
self.setHtml('<p style="color: #5CB3FF; font: italic; font-size: 20px;" >' + text + '</p>')
|
||||||
|
else:
|
||||||
|
self.setHtml(text)
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
font.setPixelSize(14)
|
font.setPixelSize(settings.Settings.get_instance()['message_font_size'])
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
self.setFont(font)
|
self.setFont(font)
|
||||||
self.setFixedHeight(self.document().size().height())
|
self.resize(width, self.document().size().height())
|
||||||
self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
|
self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
|
||||||
|
self.anchorClicked.connect(self.on_anchor_clicked)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
menu = create_menu(self.createStandardContextMenu(event.pos()))
|
||||||
|
menu.popup(event.globalPos())
|
||||||
|
menu.exec_(event.globalPos())
|
||||||
|
del menu
|
||||||
|
|
||||||
|
def on_anchor_clicked(self, url):
|
||||||
|
text = str(url.toString())
|
||||||
|
if text.startswith('tox:'):
|
||||||
|
import menu
|
||||||
|
self.add_contact = menu.AddContact(text[4:])
|
||||||
|
self.add_contact.show()
|
||||||
|
else:
|
||||||
|
QtGui.QDesktopServices.openUrl(url)
|
||||||
|
self.clearFocus()
|
||||||
|
|
||||||
|
def addAnimation(self, url, fileName):
|
||||||
|
movie = QtGui.QMovie(self)
|
||||||
|
movie.setFileName(fileName)
|
||||||
|
self.urls[movie] = url
|
||||||
|
movie.frameChanged[int].connect(lambda x: self.animate(movie))
|
||||||
|
movie.start()
|
||||||
|
|
||||||
|
def animate(self, movie):
|
||||||
|
self.document().addResource(QtGui.QTextDocument.ImageResource,
|
||||||
|
self.urls[movie],
|
||||||
|
movie.currentPixmap())
|
||||||
|
self.setLineWrapColumnOrWidth(self.lineWrapColumnOrWidth())
|
||||||
|
|
||||||
|
def decoratedText(self, text):
|
||||||
|
text = cgi.escape(text) # replace < and >
|
||||||
|
exp = QtCore.QRegExp(
|
||||||
|
'('
|
||||||
|
'(?:\\b)((www\\.)|(http[s]?|ftp)://)'
|
||||||
|
'\\w+\\S+)'
|
||||||
|
'|(?:\\b)(file:///)([\\S| ]*)'
|
||||||
|
'|(?:\\b)(tox:[a-zA-Z\\d]{76}$)'
|
||||||
|
'|(?:\\b)(mailto:\\S+@\\S+\\.\\S+)'
|
||||||
|
'|(?:\\b)(tox:\\S+@\\S+)')
|
||||||
|
offset = exp.indexIn(text, 0)
|
||||||
|
while offset != -1: # add links
|
||||||
|
url = exp.cap()
|
||||||
|
if exp.cap(2) == 'www.':
|
||||||
|
html = '<a href="http://{0}">{0}</a>'.format(url)
|
||||||
|
else:
|
||||||
|
html = '<a href="{0}">{0}</a>'.format(url)
|
||||||
|
text = text[:offset] + html + text[offset + len(exp.cap()):]
|
||||||
|
offset += len(html)
|
||||||
|
offset = exp.indexIn(text, offset)
|
||||||
|
arr = text.split('\n')
|
||||||
|
for i in range(len(arr)): # quotes
|
||||||
|
if arr[i].startswith('>'):
|
||||||
|
arr[i] = '<font color="green"><b>' + arr[i][4:] + '</b></font>'
|
||||||
|
text = '<br>'.join(arr)
|
||||||
|
text = smileys.SmileyLoader.get_instance().add_smileys_to_text(text, self) # smileys
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
class MessageItem(QtGui.QWidget):
|
class MessageItem(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
Message in messages list
|
Message in messages list
|
||||||
"""
|
"""
|
||||||
def __init__(self, text, time, user='', message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None):
|
def __init__(self, text, time, user='', sent=True, message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None):
|
||||||
QtGui.QWidget.__init__(self, parent)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.name = DataLabel(self)
|
self.name = DataLabel(self)
|
||||||
self.name.setGeometry(QtCore.QRect(0, 2, 95, 20))
|
self.name.setGeometry(QtCore.QRect(2, 2, 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")
|
||||||
font.setPointSize(11)
|
font.setPointSize(11)
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.name.setFont(font)
|
self.name.setFont(font)
|
||||||
self.name.setObjectName("name")
|
|
||||||
self.name.setText(user)
|
self.name.setText(user)
|
||||||
|
|
||||||
self.time = QtGui.QLabel(self)
|
self.time = QtGui.QLabel(self)
|
||||||
self.time.setGeometry(QtCore.QRect(parent.width() - 50, 0, 50, 25))
|
self.time.setGeometry(QtCore.QRect(parent.width() - 50, 0, 50, 20))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
self.time.setFont(font)
|
self.time.setFont(font)
|
||||||
self.time.setObjectName("time")
|
|
||||||
self.time.setText(time)
|
|
||||||
|
|
||||||
self.message = MessageEdit(text, parent.width() - 150, self)
|
if not sent:
|
||||||
|
movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif')
|
||||||
|
self.time.setMovie(movie)
|
||||||
|
movie.start()
|
||||||
|
self.t = time
|
||||||
|
else:
|
||||||
|
self.time.setText(time)
|
||||||
|
|
||||||
|
self.message = MessageEdit(text, parent.width() - 150, message_type, self)
|
||||||
|
if message_type != TOX_MESSAGE_TYPE['NORMAL']:
|
||||||
|
self.name.setStyleSheet("QLabel { color: #5CB3FF; }")
|
||||||
|
self.message.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.time.setStyleSheet("QLabel { color: #5CB3FF; }")
|
||||||
self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 150, self.message.height()))
|
self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 150, self.message.height()))
|
||||||
self.setFixedHeight(self.message.height())
|
self.setFixedHeight(self.message.height())
|
||||||
|
|
||||||
if message_type == TOX_MESSAGE_TYPE['ACTION']:
|
def mark_as_sent(self):
|
||||||
self.name.setStyleSheet("QLabel { color: #4169E1; }")
|
if hasattr(self, 't'):
|
||||||
self.message.setStyleSheet("QTextEdit { color: #4169E1; }")
|
self.time.setText(self.t)
|
||||||
else:
|
del self.t
|
||||||
if text[0] == '>':
|
return True
|
||||||
self.message.setStyleSheet("QTextEdit { color: green; }")
|
return False
|
||||||
if text[-1] == '<':
|
|
||||||
self.message.setStyleSheet("QTextEdit { color: red; }")
|
|
||||||
|
|
||||||
|
|
||||||
class ContactItem(QtGui.QWidget):
|
class ContactItem(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
Contact in friends list
|
Contact in friends list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QtGui.QWidget.__init__(self, parent)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.setBaseSize(QtCore.QSize(250, 70))
|
self.setBaseSize(QtCore.QSize(250, 70))
|
||||||
@ -80,22 +158,21 @@ class ContactItem(QtGui.QWidget):
|
|||||||
self.avatar_label.setGeometry(QtCore.QRect(3, 3, 64, 64))
|
self.avatar_label.setGeometry(QtCore.QRect(3, 3, 64, 64))
|
||||||
self.avatar_label.setScaledContents(True)
|
self.avatar_label.setScaledContents(True)
|
||||||
self.name = DataLabel(self)
|
self.name = DataLabel(self)
|
||||||
self.name.setGeometry(QtCore.QRect(70, 10, 160, 25))
|
self.name.setGeometry(QtCore.QRect(75, 10, 150, 25))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.name.setFont(font)
|
self.name.setFont(font)
|
||||||
self.name.setObjectName("name")
|
|
||||||
self.status_message = DataLabel(self)
|
self.status_message = DataLabel(self)
|
||||||
self.status_message.setGeometry(QtCore.QRect(70, 30, 180, 20))
|
self.status_message.setGeometry(QtCore.QRect(75, 30, 170, 20))
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
self.status_message.setFont(font)
|
self.status_message.setFont(font)
|
||||||
self.status_message.setObjectName("status_message")
|
|
||||||
self.connection_status = StatusCircle(self)
|
self.connection_status = StatusCircle(self)
|
||||||
self.connection_status.setGeometry(QtCore.QRect(220, 5, 32, 32))
|
self.connection_status.setGeometry(QtCore.QRect(230, 5, 32, 32))
|
||||||
self.connection_status.setObjectName("connection_status")
|
self.messages = UnreadMessagesCount(self)
|
||||||
|
self.messages.setGeometry(QtCore.QRect(52, 50, 30, 20))
|
||||||
|
|
||||||
|
|
||||||
class StatusCircle(QtGui.QWidget):
|
class StatusCircle(QtGui.QWidget):
|
||||||
@ -105,36 +182,57 @@ class StatusCircle(QtGui.QWidget):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
QtGui.QWidget.__init__(self, parent)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.setGeometry(0, 0, 32, 32)
|
self.setGeometry(0, 0, 32, 32)
|
||||||
self.data = None
|
self.label = QtGui.QLabel(self)
|
||||||
self.messages = False
|
self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
|
||||||
|
self.unread = False
|
||||||
|
|
||||||
def paintEvent(self, event):
|
def update(self, status, unread_messages=None):
|
||||||
paint = QtGui.QPainter()
|
if unread_messages is None:
|
||||||
paint.begin(self)
|
unread_messages = self.unread
|
||||||
paint.setRenderHint(QtGui.QPainter.Antialiasing)
|
|
||||||
k = 16
|
|
||||||
rad_x = rad_y = 5
|
|
||||||
if self.data is None:
|
|
||||||
color = QtCore.Qt.transparent
|
|
||||||
else:
|
else:
|
||||||
if self.data == TOX_USER_STATUS['NONE']:
|
self.unread = unread_messages
|
||||||
color = QtGui.QColor(50, 205, 50)
|
if status == TOX_USER_STATUS['NONE']:
|
||||||
elif self.data == TOX_USER_STATUS['AWAY']:
|
name = 'online'
|
||||||
color = QtGui.QColor(255, 200, 50)
|
elif status == TOX_USER_STATUS['AWAY']:
|
||||||
else: # self.data == TOX_USER_STATUS['BUSY']:
|
name = 'idle'
|
||||||
color = QtGui.QColor(255, 50, 0)
|
elif status == TOX_USER_STATUS['BUSY']:
|
||||||
|
name = 'busy'
|
||||||
|
else:
|
||||||
|
name = 'offline'
|
||||||
|
if unread_messages:
|
||||||
|
name += '_notification'
|
||||||
|
self.label.setGeometry(QtCore.QRect(0, 0, 32, 32))
|
||||||
|
else:
|
||||||
|
self.label.setGeometry(QtCore.QRect(2, 0, 32, 32))
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(name))
|
||||||
|
self.label.setPixmap(pixmap)
|
||||||
|
|
||||||
paint.setPen(color)
|
|
||||||
center = QtCore.QPoint(k, k)
|
class UnreadMessagesCount(QtGui.QWidget):
|
||||||
paint.setBrush(color)
|
|
||||||
paint.drawEllipse(center, rad_x, rad_y)
|
def __init__(self, parent=None):
|
||||||
if self.messages:
|
super(UnreadMessagesCount, self).__init__(parent)
|
||||||
if color == QtCore.Qt.transparent:
|
self.resize(30, 20)
|
||||||
color = QtCore.Qt.darkRed
|
self.label = QtGui.QLabel(self)
|
||||||
paint.setBrush(QtCore.Qt.transparent)
|
self.label.setGeometry(QtCore.QRect(0, 0, 30, 20))
|
||||||
paint.setPen(color)
|
self.label.setVisible(False)
|
||||||
paint.drawEllipse(center, rad_x + 3, rad_y + 3)
|
font = QtGui.QFont()
|
||||||
paint.end()
|
font.setFamily("Times New Roman")
|
||||||
|
font.setPointSize(12)
|
||||||
|
font.setBold(True)
|
||||||
|
self.label.setFont(font)
|
||||||
|
self.label.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignCenter)
|
||||||
|
color = settings.Settings.get_instance()['unread_color']
|
||||||
|
self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
|
||||||
|
|
||||||
|
def update(self, messages_count):
|
||||||
|
color = settings.Settings.get_instance()['unread_color']
|
||||||
|
self.label.setStyleSheet('QLabel { color: white; background-color: ' + color + '; border-radius: 10; }')
|
||||||
|
if messages_count:
|
||||||
|
self.label.setVisible(True)
|
||||||
|
self.label.setText(str(messages_count))
|
||||||
|
else:
|
||||||
|
self.label.setVisible(False)
|
||||||
|
|
||||||
|
|
||||||
class FileTransferItem(QtGui.QListWidget):
|
class FileTransferItem(QtGui.QListWidget):
|
||||||
@ -143,13 +241,12 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
|
|
||||||
QtGui.QListWidget.__init__(self, parent)
|
QtGui.QListWidget.__init__(self, parent)
|
||||||
self.resize(QtCore.QSize(width, 34))
|
self.resize(QtCore.QSize(width, 34))
|
||||||
if state == FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
|
if state == TOX_FILE_TRANSFER_STATE['CANCELLED']:
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
||||||
elif state in (FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED'], FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND']):
|
elif state in PAUSED_FILE_TRANSFERS:
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
||||||
else:
|
else:
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
|
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
self.name = DataLabel(self)
|
self.name = DataLabel(self)
|
||||||
@ -175,18 +272,18 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
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 in ACTIVE_FILE_TRANSFERS)
|
||||||
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; background-color: none;}')
|
self.cancel.setStyleSheet('QPushButton:hover { border: 1px solid #3A3939; background-color: none;}')
|
||||||
|
|
||||||
self.accept_or_pause = QtGui.QPushButton(self)
|
self.accept_or_pause = QtGui.QPushButton(self)
|
||||||
self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30))
|
self.accept_or_pause.setGeometry(QtCore.QRect(width - 170, 2, 30, 30))
|
||||||
if state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED']:
|
if state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
|
||||||
self.accept_or_pause.setVisible(True)
|
self.accept_or_pause.setVisible(True)
|
||||||
self.button_update('accept')
|
self.button_update('accept')
|
||||||
elif state in (0, 1, 5):
|
elif state in DO_NOT_SHOW_ACCEPT_BUTTON:
|
||||||
self.accept_or_pause.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']: # setup for continue
|
elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # setup for continue
|
||||||
self.accept_or_pause.setVisible(True)
|
self.accept_or_pause.setVisible(True)
|
||||||
self.button_update('resume')
|
self.button_update('resume')
|
||||||
else: # pause
|
else: # pause
|
||||||
@ -200,11 +297,10 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
self.pb.setGeometry(QtCore.QRect(100, 7, 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:
|
self.pb.setVisible(state in SHOW_PROGRESS_BAR)
|
||||||
self.pb.setVisible(False)
|
|
||||||
|
|
||||||
self.file_name = DataLabel(self)
|
self.file_name = DataLabel(self)
|
||||||
self.file_name.setGeometry(QtCore.QRect(210, 7, width - 400, 20))
|
self.file_name.setGeometry(QtCore.QRect(210, 7, width - 420, 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
|
||||||
@ -216,7 +312,13 @@ 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.setToolTip(file_name)
|
||||||
self.saved_name = file_name
|
self.saved_name = file_name
|
||||||
|
self.time_left = QtGui.QLabel(self)
|
||||||
|
self.time_left.setGeometry(QtCore.QRect(width - 87, 7, 30, 20))
|
||||||
|
font.setPointSize(10)
|
||||||
|
self.time_left.setFont(font)
|
||||||
|
self.time_left.setVisible(state == TOX_FILE_TRANSFER_STATE['RUNNING'])
|
||||||
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
@ -229,23 +331,24 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
|
|
||||||
def accept_or_pause_transfer(self, friend_number, file_number, size):
|
def accept_or_pause_transfer(self, friend_number, file_number, size):
|
||||||
if self.state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED']:
|
if self.state == TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED']:
|
||||||
directory = QtGui.QFileDialog.getExistingDirectory(self,
|
directory = QtGui.QFileDialog.getExistingDirectory(self,
|
||||||
QtGui.QApplication.translate("MainWindow", 'Choose folder', None, QtGui.QApplication.UnicodeUTF8),
|
QtGui.QApplication.translate("MainWindow", 'Choose folder', None, QtGui.QApplication.UnicodeUTF8),
|
||||||
curr_directory(),
|
curr_directory(),
|
||||||
QtGui.QFileDialog.ShowDirsOnly)
|
QtGui.QFileDialog.ShowDirsOnly)
|
||||||
|
self.pb.setVisible(True)
|
||||||
if directory:
|
if directory:
|
||||||
pr = profile.Profile.get_instance()
|
pr = profile.Profile.get_instance()
|
||||||
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
|
pr.accept_transfer(self, directory + '/' + self.saved_name, friend_number, file_number, size)
|
||||||
self.button_update('pause')
|
self.button_update('pause')
|
||||||
elif self.state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']: # resume
|
elif self.state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume
|
||||||
self.paused = False
|
self.paused = False
|
||||||
profile.Profile.get_instance().resume_transfer(friend_number, file_number)
|
profile.Profile.get_instance().resume_transfer(friend_number, file_number)
|
||||||
self.button_update('pause')
|
self.button_update('pause')
|
||||||
self.state = FILE_TRANSFER_MESSAGE_STATUS['OUTGOING']
|
self.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
||||||
else: # pause
|
else: # pause
|
||||||
self.paused = True
|
self.paused = True
|
||||||
self.state = FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']
|
self.state = TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
|
||||||
profile.Profile.get_instance().pause_transfer(friend_number, file_number)
|
profile.Profile.get_instance().pause_transfer(friend_number, file_number)
|
||||||
self.button_update('resume')
|
self.button_update('resume')
|
||||||
self.accept_or_pause.clearFocus()
|
self.accept_or_pause.clearFocus()
|
||||||
@ -256,63 +359,125 @@ class FileTransferItem(QtGui.QListWidget):
|
|||||||
self.accept_or_pause.setIcon(icon)
|
self.accept_or_pause.setIcon(icon)
|
||||||
self.accept_or_pause.setIconSize(QtCore.QSize(30, 30))
|
self.accept_or_pause.setIconSize(QtCore.QSize(30, 30))
|
||||||
|
|
||||||
def convert(self, state):
|
@QtCore.Slot(int, float, int)
|
||||||
# convert TOX_FILE_TRANSFER_STATE to FILE_TRANSFER_MESSAGE_STATUS
|
def update(self, state, progress, time):
|
||||||
d = {0: 2, 1: 6, 2: 1, 3: 0, 4: 5}
|
|
||||||
return d[state]
|
|
||||||
|
|
||||||
@QtCore.Slot(int, float)
|
|
||||||
def update(self, state, progress):
|
|
||||||
self.pb.setValue(int(progress * 100))
|
self.pb.setValue(int(progress * 100))
|
||||||
state = self.convert(state)
|
if time + 1:
|
||||||
|
m, s = divmod(time, 60)
|
||||||
|
self.time_left.setText('{0:02d}:{1:02d}'.format(m, s))
|
||||||
if self.state != state:
|
if self.state != state:
|
||||||
if state == FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']:
|
if state == TOX_FILE_TRANSFER_STATE['CANCELLED']:
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #B40404; }')
|
||||||
self.cancel.setVisible(False)
|
self.cancel.setVisible(False)
|
||||||
self.accept_or_pause.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
self.state = state
|
self.state = state
|
||||||
elif state == FILE_TRANSFER_MESSAGE_STATUS['FINISHED']:
|
self.time_left.setVisible(False)
|
||||||
|
elif state == TOX_FILE_TRANSFER_STATE['FINISHED']:
|
||||||
self.accept_or_pause.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
self.pb.setVisible(False)
|
self.pb.setVisible(False)
|
||||||
self.cancel.setVisible(False)
|
self.cancel.setVisible(False)
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
self.state = state
|
self.state = state
|
||||||
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND']:
|
self.time_left.setVisible(False)
|
||||||
|
elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND']:
|
||||||
self.accept_or_pause.setVisible(False)
|
self.accept_or_pause.setVisible(False)
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
||||||
self.state = state
|
self.state = state
|
||||||
elif state == FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']:
|
self.time_left.setVisible(False)
|
||||||
|
elif state == TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']:
|
||||||
self.button_update('resume') # setup button continue
|
self.button_update('resume') # setup button continue
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.time_left.setVisible(False)
|
||||||
|
elif state == TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']:
|
||||||
|
self.setStyleSheet('QListWidget { border: 1px solid #FF8000; }')
|
||||||
|
self.accept_or_pause.setVisible(False)
|
||||||
|
self.time_left.setVisible(False)
|
||||||
|
self.pb.setVisible(False)
|
||||||
elif not self.paused: # active
|
elif not self.paused: # active
|
||||||
|
self.pb.setVisible(True)
|
||||||
self.accept_or_pause.setVisible(True) # setup to pause
|
self.accept_or_pause.setVisible(True) # setup to pause
|
||||||
self.button_update('pause')
|
self.button_update('pause')
|
||||||
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
self.setStyleSheet('QListWidget { border: 1px solid green; }')
|
||||||
self.state = state
|
self.state = state
|
||||||
|
self.time_left.setVisible(True)
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class InlineImageItem(QtGui.QWidget):
|
class UnsentFileItem(FileTransferItem):
|
||||||
|
|
||||||
def __init__(self, data, width, parent=None):
|
def __init__(self, file_name, size, user, time, width, parent=None):
|
||||||
|
super(UnsentFileItem, self).__init__(file_name, size, time, user, -1, -1,
|
||||||
|
TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'], width, parent)
|
||||||
|
self._time = time
|
||||||
|
self.pb.setVisible(False)
|
||||||
|
movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif')
|
||||||
|
self.time.setMovie(movie)
|
||||||
|
movie.start()
|
||||||
|
|
||||||
QtGui.QWidget.__init__(self, parent)
|
def cancel_transfer(self, *args):
|
||||||
self.resize(QtCore.QSize(width, 500))
|
pr = profile.Profile.get_instance()
|
||||||
|
pr.cancel_not_started_transfer(self._time)
|
||||||
|
|
||||||
|
|
||||||
|
class InlineImageItem(QtGui.QScrollArea):
|
||||||
|
|
||||||
|
def __init__(self, data, width, elem):
|
||||||
|
|
||||||
|
QtGui.QScrollArea.__init__(self)
|
||||||
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
|
self._elem = elem
|
||||||
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.setWidget(self._image_label)
|
||||||
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")
|
||||||
max_size = width - 40
|
self._max_size = width - 30
|
||||||
if self.pixmap.width() <= max_size:
|
self._resize_needed = not (self._pixmap.width() <= self._max_size)
|
||||||
self._image_label.setPixmap(self.pixmap)
|
self._full_size = not self._resize_needed
|
||||||
self.resize(QtCore.QSize(max_size, self.pixmap.height()))
|
if not self._resize_needed:
|
||||||
|
self._image_label.setPixmap(self._pixmap)
|
||||||
|
self.resize(QtCore.QSize(self._max_size + 5, self._pixmap.height() + 5))
|
||||||
|
self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
|
||||||
else:
|
else:
|
||||||
pixmap = self.pixmap.scaled(max_size, max_size, QtCore.Qt.KeepAspectRatio)
|
pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
|
||||||
self._image_label.setPixmap(pixmap)
|
self._image_label.setPixmap(pixmap)
|
||||||
self.resize(QtCore.QSize(max_size, pixmap.height()))
|
self.resize(QtCore.QSize(self._max_size + 5, pixmap.height()))
|
||||||
|
self._image_label.setGeometry(5, 0, self._max_size + 5, pixmap.height())
|
||||||
|
self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
if event.button() == QtCore.Qt.LeftButton and self._resize_needed: # scale inline
|
||||||
|
if self._full_size:
|
||||||
|
pixmap = self._pixmap.scaled(self._max_size, self._max_size, QtCore.Qt.KeepAspectRatio)
|
||||||
|
self._image_label.setPixmap(pixmap)
|
||||||
|
self.resize(QtCore.QSize(self._max_size, pixmap.height()))
|
||||||
|
self._image_label.setGeometry(5, 0, pixmap.width(), pixmap.height())
|
||||||
|
else:
|
||||||
|
self._image_label.setPixmap(self._pixmap)
|
||||||
|
self.resize(QtCore.QSize(self._max_size, self._pixmap.height() + 17))
|
||||||
|
self._image_label.setGeometry(5, 0, self._pixmap.width(), self._pixmap.height())
|
||||||
|
self._full_size = not self._full_size
|
||||||
|
self._elem.setSizeHint(QtCore.QSize(self.width(), self.height()))
|
||||||
|
elif event.button() == QtCore.Qt.RightButton: # save inline
|
||||||
|
directory = QtGui.QFileDialog.getExistingDirectory(self,
|
||||||
|
QtGui.QApplication.translate("MainWindow",
|
||||||
|
'Choose folder', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8),
|
||||||
|
curr_directory(),
|
||||||
|
QtGui.QFileDialog.ShowDirsOnly)
|
||||||
|
if directory:
|
||||||
|
fl = QtCore.QFile(directory + '/toxygen_inline_' + curr_time().replace(':', '_') + '.png')
|
||||||
|
self._pixmap.save(fl, 'PNG')
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ except ImportError:
|
|||||||
from widgets import *
|
from widgets import *
|
||||||
|
|
||||||
|
|
||||||
class NickEdit(QtGui.QPlainTextEdit):
|
class NickEdit(LineEdit):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(NickEdit, self).__init__(parent)
|
super(NickEdit, self).__init__(parent)
|
||||||
@ -71,6 +71,7 @@ class LoginScreen(CenteredWidget):
|
|||||||
QtCore.QMetaObject.connectSlotsByName(self)
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
|
self.new_name.setPlaceholderText(QtGui.QApplication.translate("login", "Profile name", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.setWindowTitle(QtGui.QApplication.translate("login", "Log in", None, QtGui.QApplication.UnicodeUTF8))
|
self.setWindowTitle(QtGui.QApplication.translate("login", "Log in", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.new_profile.setText(QtGui.QApplication.translate("login", "Create", None, QtGui.QApplication.UnicodeUTF8))
|
self.new_profile.setText(QtGui.QApplication.translate("login", "Create", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label.setText(QtGui.QApplication.translate("login", "Profile name:", None, QtGui.QApplication.UnicodeUTF8))
|
self.label.setText(QtGui.QApplication.translate("login", "Profile name:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
36
src/main.py
@ -9,7 +9,7 @@ from bootstrap import node_generator
|
|||||||
from mainscreen import MainWindow
|
from mainscreen import MainWindow
|
||||||
from profile import tox_factory
|
from profile import tox_factory
|
||||||
from callbacks import init_callbacks
|
from callbacks import init_callbacks
|
||||||
from util import curr_directory, get_style
|
from util import curr_directory
|
||||||
import styles.style
|
import styles.style
|
||||||
import locale
|
import locale
|
||||||
import toxencryptsave
|
import toxencryptsave
|
||||||
@ -20,10 +20,17 @@ from plugin_support import PluginLoader
|
|||||||
|
|
||||||
class Toxygen(object):
|
class Toxygen(object):
|
||||||
|
|
||||||
def __init__(self, path=None):
|
def __init__(self, path_or_uri=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
|
if path_or_uri is None:
|
||||||
|
self.uri = self.path = None
|
||||||
|
elif path_or_uri.startswith('tox:'):
|
||||||
|
self.path = None
|
||||||
|
self.uri = path_or_uri[4:]
|
||||||
|
else:
|
||||||
|
self.path = path_or_uri
|
||||||
|
self.uri = None
|
||||||
|
|
||||||
def enter_pass(self, data):
|
def enter_pass(self, data):
|
||||||
"""
|
"""
|
||||||
@ -64,13 +71,13 @@ class Toxygen(object):
|
|||||||
self.tox = tox_factory(data, settings)
|
self.tox = tox_factory(data, settings)
|
||||||
else:
|
else:
|
||||||
auto_profile = Settings.get_auto_profile()
|
auto_profile = Settings.get_auto_profile()
|
||||||
if not auto_profile:
|
if not auto_profile[0]:
|
||||||
# show login screen if default profile not found
|
# show login screen if default profile not found
|
||||||
current_locale = QtCore.QLocale()
|
current_locale = QtCore.QLocale()
|
||||||
curr_lang = current_locale.languageToString(current_locale.language())
|
curr_lang = current_locale.languageToString(current_locale.language())
|
||||||
langs = Settings.supported_languages()
|
langs = Settings.supported_languages()
|
||||||
if curr_lang in map(lambda x: x[0], langs):
|
if curr_lang in langs:
|
||||||
lang_path = filter(lambda x: x[0] == curr_lang, langs)[0][1]
|
lang_path = langs[curr_lang]
|
||||||
translator = QtCore.QTranslator()
|
translator = QtCore.QTranslator()
|
||||||
translator.load(curr_directory() + '/translations/' + lang_path)
|
translator.load(curr_directory() + '/translations/' + lang_path)
|
||||||
app.installTranslator(translator)
|
app.installTranslator(translator)
|
||||||
@ -112,7 +119,7 @@ class Toxygen(object):
|
|||||||
settings = Settings(name)
|
settings = Settings(name)
|
||||||
self.tox = tox_factory(data, settings)
|
self.tox = tox_factory(data, settings)
|
||||||
|
|
||||||
if ProfileHelper.is_active_profile(path, name): # profile is in use
|
if Settings.is_active_profile(path, name): # profile is in use
|
||||||
reply = QtGui.QMessageBox.question(None,
|
reply = QtGui.QMessageBox.question(None,
|
||||||
'Profile {}'.format(name),
|
'Profile {}'.format(name),
|
||||||
QtGui.QApplication.translate("login", 'Looks like other instance of Toxygen uses this profile! Continue?', None, QtGui.QApplication.UnicodeUTF8),
|
QtGui.QApplication.translate("login", 'Looks like other instance of Toxygen uses this profile! Continue?', None, QtGui.QApplication.UnicodeUTF8),
|
||||||
@ -123,18 +130,18 @@ class Toxygen(object):
|
|||||||
else:
|
else:
|
||||||
settings.set_active_profile()
|
settings.set_active_profile()
|
||||||
|
|
||||||
lang = filter(lambda x: x[0] == settings['language'], Settings.supported_languages())[0]
|
lang = Settings.supported_languages()[settings['language']]
|
||||||
translator = QtCore.QTranslator()
|
translator = QtCore.QTranslator()
|
||||||
translator.load(curr_directory() + '/translations/' + lang[1])
|
translator.load(curr_directory() + '/translations/' + lang)
|
||||||
app.installTranslator(translator)
|
app.installTranslator(translator)
|
||||||
app.translator = translator
|
app.translator = translator
|
||||||
|
|
||||||
self.ms = MainWindow(self.tox, self.reset)
|
|
||||||
|
|
||||||
# tray icon
|
# tray icon
|
||||||
self.tray = QtGui.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
self.tray = QtGui.QSystemTrayIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
||||||
self.tray.setObjectName('tray')
|
self.tray.setObjectName('tray')
|
||||||
|
|
||||||
|
self.ms = MainWindow(self.tox, self.reset, self.tray)
|
||||||
|
|
||||||
class Menu(QtGui.QMenu):
|
class Menu(QtGui.QMenu):
|
||||||
|
|
||||||
def newStatus(self, status):
|
def newStatus(self, status):
|
||||||
@ -186,11 +193,11 @@ class Toxygen(object):
|
|||||||
sub.connect(onl, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(0))
|
sub.connect(onl, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(0))
|
||||||
sub.connect(away, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(1))
|
sub.connect(away, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(1))
|
||||||
sub.connect(busy, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(2))
|
sub.connect(busy, QtCore.SIGNAL("triggered()"), lambda: m.newStatus(2))
|
||||||
|
|
||||||
self.tray.setContextMenu(m)
|
self.tray.setContextMenu(m)
|
||||||
self.tray.show()
|
self.tray.show()
|
||||||
|
|
||||||
self.ms.show()
|
self.ms.show()
|
||||||
QtGui.QApplication.setStyle(get_style(settings['theme'])) # set application style
|
|
||||||
|
|
||||||
plugin_helper = PluginLoader(self.tox, settings) # plugin support
|
plugin_helper = PluginLoader(self.tox, settings) # plugin support
|
||||||
plugin_helper.load()
|
plugin_helper.load()
|
||||||
@ -205,6 +212,9 @@ class Toxygen(object):
|
|||||||
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
self.avloop = self.ToxAVIterateThread(self.tox.AV)
|
||||||
self.avloop.start()
|
self.avloop.start()
|
||||||
|
|
||||||
|
if self.uri is not None:
|
||||||
|
self.ms.add_contact(self.uri)
|
||||||
|
|
||||||
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
|
app.connect(app, QtCore.SIGNAL("lastWindowClosed()"), app, QtCore.SLOT("quit()"))
|
||||||
app.exec_()
|
app.exec_()
|
||||||
self.init.stop = True
|
self.init.stop = True
|
||||||
@ -336,6 +346,6 @@ class Toxygen(object):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
toxygen = Toxygen()
|
toxygen = Toxygen()
|
||||||
else: # path to profile
|
else: # path to profile or tox: uri
|
||||||
toxygen = Toxygen(sys.argv[1])
|
toxygen = Toxygen(sys.argv[1])
|
||||||
toxygen.main()
|
toxygen.main()
|
||||||
|
@ -3,43 +3,18 @@
|
|||||||
from menu import *
|
from menu import *
|
||||||
from profile import *
|
from profile import *
|
||||||
from list_items import *
|
from list_items import *
|
||||||
from widgets import QRightClickButton
|
from widgets import MultilineEdit, LineEdit
|
||||||
import plugin_support
|
import plugin_support
|
||||||
|
from mainscreen_widgets import *
|
||||||
|
|
||||||
class MessageArea(QtGui.QPlainTextEdit):
|
|
||||||
|
|
||||||
def __init__(self, parent, form):
|
|
||||||
super(MessageArea, self).__init__(parent)
|
|
||||||
self.parent = form
|
|
||||||
self.timer = QtCore.QTimer(self)
|
|
||||||
self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False))
|
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
|
||||||
if event.key() == QtCore.Qt.Key_Return:
|
|
||||||
modifiers = event.modifiers()
|
|
||||||
if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
|
|
||||||
self.appendPlainText('')
|
|
||||||
else:
|
|
||||||
if self.timer.isActive():
|
|
||||||
self.timer.stop()
|
|
||||||
self.parent.profile.send_typing(False)
|
|
||||||
self.parent.send_message()
|
|
||||||
elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
|
|
||||||
self.appendPlainText(Profile.get_instance().get_last_message())
|
|
||||||
else:
|
|
||||||
self.parent.profile.send_typing(True)
|
|
||||||
if self.timer.isActive():
|
|
||||||
self.timer.stop()
|
|
||||||
self.timer.start(5000)
|
|
||||||
super(MessageArea, self).keyPressEvent(event)
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtGui.QMainWindow):
|
class MainWindow(QtGui.QMainWindow):
|
||||||
|
|
||||||
def __init__(self, tox, reset):
|
def __init__(self, tox, reset, tray):
|
||||||
super(MainWindow, self).__init__()
|
super(MainWindow, self).__init__()
|
||||||
self.reset = reset
|
self.reset = reset
|
||||||
|
self.tray = tray
|
||||||
|
self.setAcceptDrops(True)
|
||||||
self.initUI(tox)
|
self.initUI(tox)
|
||||||
|
|
||||||
def setup_menu(self, MainWindow):
|
def setup_menu(self, MainWindow):
|
||||||
@ -105,6 +80,11 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
def languageChange(self, *args, **kwargs):
|
def languageChange(self, *args, **kwargs):
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
|
||||||
|
def event(self, event):
|
||||||
|
if event.type() == QtCore.QEvent.WindowActivate:
|
||||||
|
self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
||||||
|
return super(MainWindow, self).event(event)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.menuPlugins.setTitle(QtGui.QApplication.translate("MainWindow", "Plugins", None, QtGui.QApplication.UnicodeUTF8))
|
self.menuPlugins.setTitle(QtGui.QApplication.translate("MainWindow", "Plugins", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.pluginData.setText(QtGui.QApplication.translate("MainWindow", "List of plugins", None, QtGui.QApplication.UnicodeUTF8))
|
self.pluginData.setText(QtGui.QApplication.translate("MainWindow", "List of plugins", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
@ -121,8 +101,6 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
self.actionSettings.setText(QtGui.QApplication.translate("MainWindow", "Settings", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.audioSettings.setText(QtGui.QApplication.translate("MainWindow", "Audio", None, QtGui.QApplication.UnicodeUTF8))
|
self.audioSettings.setText(QtGui.QApplication.translate("MainWindow", "Audio", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.contact_name.setPlaceholderText(QtGui.QApplication.translate("MainWindow", "Search", None, QtGui.QApplication.UnicodeUTF8))
|
self.contact_name.setPlaceholderText(QtGui.QApplication.translate("MainWindow", "Search", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.screenshotButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send screenshot", None, QtGui.QApplication.UnicodeUTF8))
|
|
||||||
self.fileTransferButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send file", None, QtGui.QApplication.UnicodeUTF8))
|
|
||||||
self.sendMessageButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send message", None, QtGui.QApplication.UnicodeUTF8))
|
self.sendMessageButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Send message", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.callButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Start audio call with friend", None, QtGui.QApplication.UnicodeUTF8))
|
self.callButton.setToolTip(QtGui.QApplication.translate("MainWindow", "Start audio call with friend", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.online_contacts.clear()
|
self.online_contacts.clear()
|
||||||
@ -131,7 +109,6 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.online_contacts.setCurrentIndex(int(Settings.get_instance()['show_online_friends']))
|
self.online_contacts.setCurrentIndex(int(Settings.get_instance()['show_online_friends']))
|
||||||
|
|
||||||
def setup_right_bottom(self, Form):
|
def setup_right_bottom(self, Form):
|
||||||
Form.setObjectName("right_bottom")
|
|
||||||
Form.resize(650, 60)
|
Form.resize(650, 60)
|
||||||
self.messageEdit = MessageArea(Form, self)
|
self.messageEdit = MessageArea(Form, self)
|
||||||
self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55))
|
self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55))
|
||||||
@ -140,68 +117,58 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
self.messageEdit.setFont(font)
|
self.messageEdit.setFont(font)
|
||||||
|
|
||||||
self.screenshotButton = QRightClickButton(Form)
|
|
||||||
self.screenshotButton.setGeometry(QtCore.QRect(455, 3, 55, 55))
|
|
||||||
self.screenshotButton.setObjectName("screenshotButton")
|
|
||||||
|
|
||||||
self.fileTransferButton = QtGui.QPushButton(Form)
|
|
||||||
self.fileTransferButton.setGeometry(QtCore.QRect(510, 3, 55, 55))
|
|
||||||
self.fileTransferButton.setObjectName("fileTransferButton")
|
|
||||||
|
|
||||||
self.sendMessageButton = QtGui.QPushButton(Form)
|
self.sendMessageButton = QtGui.QPushButton(Form)
|
||||||
self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55))
|
self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55))
|
||||||
self.sendMessageButton.setObjectName("sendMessageButton")
|
self.sendMessageButton.setObjectName("sendMessageButton")
|
||||||
|
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/send.png')
|
self.menuButton = MenuButton(Form, self.show_menu)
|
||||||
|
self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55)))
|
||||||
|
|
||||||
|
pixmap = QtGui.QPixmap('send.png')
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.sendMessageButton.setIcon(icon)
|
self.sendMessageButton.setIcon(icon)
|
||||||
self.sendMessageButton.setIconSize(QtCore.QSize(45, 60))
|
self.sendMessageButton.setIconSize(QtCore.QSize(45, 60))
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/file.png')
|
|
||||||
icon = QtGui.QIcon(pixmap)
|
|
||||||
self.fileTransferButton.setIcon(icon)
|
|
||||||
self.fileTransferButton.setIconSize(QtCore.QSize(40, 40))
|
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/screenshot.png')
|
|
||||||
icon = QtGui.QIcon(pixmap)
|
|
||||||
self.screenshotButton.setIcon(icon)
|
|
||||||
self.screenshotButton.setIconSize(QtCore.QSize(40, 60))
|
|
||||||
|
|
||||||
self.fileTransferButton.clicked.connect(self.send_file)
|
pixmap = QtGui.QPixmap('menu.png')
|
||||||
self.screenshotButton.clicked.connect(self.send_screenshot)
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.menuButton.setIcon(icon)
|
||||||
|
self.menuButton.setIconSize(QtCore.QSize(40, 40))
|
||||||
|
|
||||||
self.sendMessageButton.clicked.connect(self.send_message)
|
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_center_menu(self, Form):
|
def setup_left_center_menu(self, Form):
|
||||||
Form.resize(270, 25)
|
Form.resize(270, 25)
|
||||||
self.search_label = QtGui.QLabel(Form)
|
self.search_label = QtGui.QLabel(Form)
|
||||||
self.search_label.setGeometry(QtCore.QRect(3, 0, 25, 25))
|
self.search_label.setGeometry(QtCore.QRect(3, 2, 20, 20))
|
||||||
pixmap = QtGui.QPixmap(QtCore.QSize(25, 25))
|
pixmap = QtGui.QPixmap()
|
||||||
pixmap.load(curr_directory() + '/images/search.png')
|
pixmap.load(curr_directory() + '/images/search.png')
|
||||||
self.search_label.setScaledContents(False)
|
self.search_label.setScaledContents(False)
|
||||||
self.search_label.setPixmap(pixmap.scaled(25, 25, QtCore.Qt.KeepAspectRatio))
|
self.search_label.setPixmap(pixmap)
|
||||||
|
|
||||||
self.contact_name = QtGui.QLineEdit(Form)
|
self.contact_name = LineEdit(Form)
|
||||||
self.contact_name.setGeometry(QtCore.QRect(30, 0, 120, 25))
|
self.contact_name.setGeometry(QtCore.QRect(0, 0, 150, 25))
|
||||||
self.contact_name.setObjectName("contact_name")
|
self.contact_name.setObjectName("contact_name")
|
||||||
self.contact_name.textChanged.connect(self.filtering)
|
self.contact_name.textChanged.connect(self.filtering)
|
||||||
|
|
||||||
self.online_contacts = QtGui.QComboBox(Form)
|
self.online_contacts = QtGui.QComboBox(Form)
|
||||||
self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25))
|
self.online_contacts.setGeometry(QtCore.QRect(150, 0, 120, 25))
|
||||||
self.online_contacts.activated[int].connect(lambda x: self.filtering())
|
self.online_contacts.activated[int].connect(lambda x: self.filtering())
|
||||||
|
self.search_label.raise_()
|
||||||
|
|
||||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||||
|
|
||||||
def setup_left_top(self, Form):
|
def setup_left_top(self, Form):
|
||||||
Form.setObjectName("left_top")
|
|
||||||
Form.setCursor(QtCore.Qt.PointingHandCursor)
|
Form.setCursor(QtCore.Qt.PointingHandCursor)
|
||||||
Form.setMinimumSize(QtCore.QSize(250, 80))
|
Form.setMinimumSize(QtCore.QSize(270, 100))
|
||||||
Form.setMaximumSize(QtCore.QSize(250, 80))
|
Form.setMaximumSize(QtCore.QSize(270, 100))
|
||||||
Form.setBaseSize(QtCore.QSize(250, 80))
|
Form.setBaseSize(QtCore.QSize(270, 100))
|
||||||
self.avatar_label = Form.avatar_label = QtGui.QLabel(Form)
|
self.avatar_label = Form.avatar_label = QtGui.QLabel(Form)
|
||||||
self.avatar_label.setGeometry(QtCore.QRect(5, 15, 64, 64))
|
self.avatar_label.setGeometry(QtCore.QRect(5, 30, 64, 64))
|
||||||
self.avatar_label.setScaledContents(True)
|
self.avatar_label.setScaledContents(True)
|
||||||
self.name = Form.name = DataLabel(Form)
|
self.name = Form.name = DataLabel(Form)
|
||||||
Form.name.setGeometry(QtCore.QRect(80, 25, 150, 25))
|
Form.name.setGeometry(QtCore.QRect(75, 40, 150, 25))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setFamily("Times New Roman")
|
font.setFamily("Times New Roman")
|
||||||
font.setPointSize(14)
|
font.setPointSize(14)
|
||||||
@ -209,16 +176,13 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
Form.name.setFont(font)
|
Form.name.setFont(font)
|
||||||
Form.name.setObjectName("name")
|
Form.name.setObjectName("name")
|
||||||
self.status_message = Form.status_message = DataLabel(Form)
|
self.status_message = Form.status_message = DataLabel(Form)
|
||||||
Form.status_message.setGeometry(QtCore.QRect(80, 55, 170, 20))
|
Form.status_message.setGeometry(QtCore.QRect(75, 60, 170, 25))
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
font.setBold(False)
|
font.setBold(False)
|
||||||
Form.status_message.setFont(font)
|
Form.status_message.setFont(font)
|
||||||
Form.status_message.setObjectName("status_message")
|
Form.status_message.setObjectName("status_message")
|
||||||
self.connection_status = Form.connection_status = StatusCircle(self)
|
self.connection_status = Form.connection_status = StatusCircle(Form)
|
||||||
Form.connection_status.setGeometry(QtCore.QRect(230, 29, 64, 64))
|
Form.connection_status.setGeometry(QtCore.QRect(230, 35, 32, 32))
|
||||||
Form.connection_status.setMinimumSize(QtCore.QSize(32, 32))
|
|
||||||
Form.connection_status.setMaximumSize(QtCore.QSize(32, 32))
|
|
||||||
Form.connection_status.setBaseSize(QtCore.QSize(32, 32))
|
|
||||||
self.avatar_label.mouseReleaseEvent = self.profile_settings
|
self.avatar_label.mouseReleaseEvent = self.profile_settings
|
||||||
self.status_message.mouseReleaseEvent = self.profile_settings
|
self.status_message.mouseReleaseEvent = self.profile_settings
|
||||||
self.name.mouseReleaseEvent = self.profile_settings
|
self.name.mouseReleaseEvent = self.profile_settings
|
||||||
@ -226,10 +190,9 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
Form.connection_status.setObjectName("connection_status")
|
Form.connection_status.setObjectName("connection_status")
|
||||||
|
|
||||||
def setup_right_top(self, Form):
|
def setup_right_top(self, Form):
|
||||||
Form.setObjectName("Form")
|
Form.resize(650, 100)
|
||||||
Form.resize(650, 80)
|
|
||||||
self.account_avatar = QtGui.QLabel(Form)
|
self.account_avatar = QtGui.QLabel(Form)
|
||||||
self.account_avatar.setGeometry(QtCore.QRect(10, 17, 64, 64))
|
self.account_avatar.setGeometry(QtCore.QRect(10, 30, 64, 64))
|
||||||
self.account_avatar.setScaledContents(True)
|
self.account_avatar.setScaledContents(True)
|
||||||
self.account_name = DataLabel(Form)
|
self.account_name = DataLabel(Form)
|
||||||
self.account_name.setGeometry(QtCore.QRect(100, 25, 400, 25))
|
self.account_name.setGeometry(QtCore.QRect(100, 25, 400, 25))
|
||||||
@ -250,10 +213,14 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.callButton = QtGui.QPushButton(Form)
|
self.callButton = QtGui.QPushButton(Form)
|
||||||
self.callButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
|
self.callButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
|
||||||
self.callButton.setObjectName("callButton")
|
self.callButton.setObjectName("callButton")
|
||||||
self.callButton.clicked.connect(self.call)
|
self.callButton.clicked.connect(lambda: self.profile.call_click(True))
|
||||||
|
self.videocallButton = QtGui.QPushButton(Form)
|
||||||
|
self.videocallButton.setGeometry(QtCore.QRect(550, 30, 50, 50))
|
||||||
|
self.videocallButton.setObjectName("videocallButton")
|
||||||
|
self.videocallButton.clicked.connect(lambda: self.profile.call_click(True, True))
|
||||||
self.update_call_state('call')
|
self.update_call_state('call')
|
||||||
self.typing = QtGui.QLabel(Form)
|
self.typing = QtGui.QLabel(Form)
|
||||||
self.typing.setGeometry(QtCore.QRect(500, 40, 50, 30))
|
self.typing.setGeometry(QtCore.QRect(500, 50, 50, 30))
|
||||||
pixmap = QtGui.QPixmap(QtCore.QSize(50, 30))
|
pixmap = QtGui.QPixmap(QtCore.QSize(50, 30))
|
||||||
pixmap.load(curr_directory() + '/images/typing.png')
|
pixmap.load(curr_directory() + '/images/typing.png')
|
||||||
self.typing.setScaledContents(False)
|
self.typing.setScaledContents(False)
|
||||||
@ -275,6 +242,7 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.messages = QtGui.QListWidget(widget)
|
self.messages = QtGui.QListWidget(widget)
|
||||||
self.messages.setGeometry(0, 0, 620, 310)
|
self.messages.setGeometry(0, 0, 620, 310)
|
||||||
self.messages.setObjectName("messages")
|
self.messages.setObjectName("messages")
|
||||||
|
self.messages.setSpacing(1)
|
||||||
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)
|
self.messages.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||||
@ -288,37 +256,50 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
|
|
||||||
def initUI(self, tox):
|
def initUI(self, tox):
|
||||||
self.setMinimumSize(920, 500)
|
self.setMinimumSize(920, 500)
|
||||||
self.setGeometry(400, 400, 920, 500)
|
s = Settings.get_instance()
|
||||||
|
self.setGeometry(s['x'], s['y'], s['width'], s['height'])
|
||||||
self.setWindowTitle('Toxygen')
|
self.setWindowTitle('Toxygen')
|
||||||
os.chdir(curr_directory() + '/images/')
|
os.chdir(curr_directory() + '/images/')
|
||||||
main = QtGui.QWidget()
|
main = QtGui.QWidget()
|
||||||
grid = QtGui.QGridLayout()
|
grid = QtGui.QGridLayout()
|
||||||
search = QtGui.QWidget()
|
search = QtGui.QWidget()
|
||||||
self.setup_left_center_menu(search)
|
|
||||||
grid.addWidget(search, 1, 0)
|
|
||||||
name = QtGui.QWidget()
|
name = QtGui.QWidget()
|
||||||
self.setup_left_top(name)
|
|
||||||
grid.addWidget(name, 0, 0)
|
|
||||||
messages = QtGui.QWidget()
|
|
||||||
self.setup_right_center(messages)
|
|
||||||
grid.addWidget(messages, 1, 1, 2, 1)
|
|
||||||
info = QtGui.QWidget()
|
info = QtGui.QWidget()
|
||||||
self.setup_right_top(info)
|
|
||||||
grid.addWidget(info, 0, 1)
|
|
||||||
message_buttons = QtGui.QWidget()
|
|
||||||
self.setup_right_bottom(message_buttons)
|
|
||||||
grid.addWidget(message_buttons, 3, 1)
|
|
||||||
main_list = QtGui.QWidget()
|
main_list = QtGui.QWidget()
|
||||||
|
messages = QtGui.QWidget()
|
||||||
|
message_buttons = QtGui.QWidget()
|
||||||
|
self.setup_left_center_menu(search)
|
||||||
|
self.setup_left_top(name)
|
||||||
|
self.setup_right_center(messages)
|
||||||
|
self.setup_right_top(info)
|
||||||
|
self.setup_right_bottom(message_buttons)
|
||||||
self.setup_left_center(main_list)
|
self.setup_left_center(main_list)
|
||||||
grid.addWidget(main_list, 2, 0, 2, 1)
|
if not Settings.get_instance()['mirror_mode']:
|
||||||
grid.setColumnMinimumWidth(1, 500)
|
grid.addWidget(search, 1, 0)
|
||||||
grid.setColumnMinimumWidth(0, 270)
|
grid.addWidget(name, 0, 0)
|
||||||
grid.setRowMinimumHeight(0, 82)
|
grid.addWidget(messages, 1, 1, 2, 1)
|
||||||
|
grid.addWidget(info, 0, 1)
|
||||||
|
grid.addWidget(message_buttons, 3, 1)
|
||||||
|
grid.addWidget(main_list, 2, 0, 2, 1)
|
||||||
|
grid.setColumnMinimumWidth(1, 500)
|
||||||
|
grid.setColumnMinimumWidth(0, 270)
|
||||||
|
else:
|
||||||
|
grid.addWidget(search, 1, 1)
|
||||||
|
grid.addWidget(name, 0, 1)
|
||||||
|
grid.addWidget(messages, 1, 0, 2, 1)
|
||||||
|
grid.addWidget(info, 0, 0)
|
||||||
|
grid.addWidget(message_buttons, 3, 0)
|
||||||
|
grid.addWidget(main_list, 2, 1, 2, 1)
|
||||||
|
grid.setColumnMinimumWidth(0, 500)
|
||||||
|
grid.setColumnMinimumWidth(1, 270)
|
||||||
|
grid.setSpacing(0)
|
||||||
|
grid.setContentsMargins(0, 0, 0, 0)
|
||||||
|
grid.setRowMinimumHeight(0, 100)
|
||||||
grid.setRowMinimumHeight(1, 25)
|
grid.setRowMinimumHeight(1, 25)
|
||||||
grid.setRowMinimumHeight(2, 410)
|
grid.setRowMinimumHeight(2, 320)
|
||||||
grid.setRowMinimumHeight(3, 60)
|
grid.setRowMinimumHeight(3, 55)
|
||||||
grid.setColumnStretch(1, 1)
|
grid.setColumnStretch(1, 1)
|
||||||
grid.setRowStretch(1, 1)
|
grid.setRowStretch(2, 1)
|
||||||
main.setLayout(grid)
|
main.setLayout(grid)
|
||||||
self.setCentralWidget(main)
|
self.setCentralWidget(main)
|
||||||
self.setup_menu(self)
|
self.setup_menu(self)
|
||||||
@ -331,21 +312,29 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
def closeEvent(self, *args, **kwargs):
|
def closeEvent(self, *args, **kwargs):
|
||||||
self.profile.save_history()
|
self.profile.save_history()
|
||||||
self.profile.close()
|
self.profile.close()
|
||||||
|
s = Settings.get_instance()
|
||||||
|
s['x'] = self.pos().x()
|
||||||
|
s['y'] = self.pos().y()
|
||||||
|
s['width'] = self.width()
|
||||||
|
s['height'] = self.height()
|
||||||
|
s.save()
|
||||||
QtGui.QApplication.closeAllWindows()
|
QtGui.QApplication.closeAllWindows()
|
||||||
|
|
||||||
def resizeEvent(self, *args, **kwargs):
|
def resizeEvent(self, *args, **kwargs):
|
||||||
self.messages.setGeometry(0, 0, self.width() - 300, self.height() - 172)
|
self.messages.setGeometry(0, 0, self.width() - 270, self.height() - 155)
|
||||||
self.friends_list.setGeometry(0, 0, 270, self.height() - 142)
|
self.friends_list.setGeometry(0, 0, 270, self.height() - 125)
|
||||||
self.callButton.setGeometry(QtCore.QRect(self.width() - 370, 20, 50, 50))
|
|
||||||
self.typing.setGeometry(QtCore.QRect(self.width() - 420, 30, 50, 30))
|
|
||||||
|
|
||||||
self.messageEdit.setGeometry(QtCore.QRect(120, 2, self.width() - 490, 55))
|
self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 40, 50, 50))
|
||||||
self.screenshotButton.setGeometry(QtCore.QRect(0, 2, 55, 55))
|
self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 40, 50, 50))
|
||||||
self.fileTransferButton.setGeometry(QtCore.QRect(60, 2, 55, 55))
|
self.typing.setGeometry(QtCore.QRect(self.width() - 450, 50, 50, 30))
|
||||||
self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 360, 2, 60, 55))
|
|
||||||
|
|
||||||
self.account_name.setGeometry(QtCore.QRect(100, 30, self.width() - 520, 25))
|
self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55))
|
||||||
self.account_status.setGeometry(QtCore.QRect(100, 50, self.width() - 520, 25))
|
self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55))
|
||||||
|
self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55))
|
||||||
|
|
||||||
|
self.account_name.setGeometry(QtCore.QRect(100, 40, self.width() - 560, 25))
|
||||||
|
self.account_status.setGeometry(QtCore.QRect(100, 60, self.width() - 560, 25))
|
||||||
|
self.messageEdit.setFocus()
|
||||||
self.profile.update()
|
self.profile.update()
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
def keyPressEvent(self, event):
|
||||||
@ -374,8 +363,8 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.p_s = PluginsSettings()
|
self.p_s = PluginsSettings()
|
||||||
self.p_s.show()
|
self.p_s.show()
|
||||||
|
|
||||||
def add_contact(self):
|
def add_contact(self, link=''):
|
||||||
self.a_c = AddContact()
|
self.a_c = AddContact(link)
|
||||||
self.a_c.show()
|
self.a_c.show()
|
||||||
|
|
||||||
def profile_settings(self, *args):
|
def profile_settings(self, *args):
|
||||||
@ -398,6 +387,15 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.audio_s = AudioSettings()
|
self.audio_s = AudioSettings()
|
||||||
self.audio_s.show()
|
self.audio_s.show()
|
||||||
|
|
||||||
|
def show_menu(self):
|
||||||
|
if not hasattr(self, 'menu'):
|
||||||
|
self.menu = DropdownMenu(self)
|
||||||
|
self.menu.setGeometry(QtCore.QRect(0 if Settings.get_instance()['mirror_mode'] else 270,
|
||||||
|
self.height() - 100,
|
||||||
|
150,
|
||||||
|
100))
|
||||||
|
self.menu.show()
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Messages, calls and file transfers
|
# Messages, calls and file transfers
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -407,23 +405,40 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.profile.send_message(text)
|
self.profile.send_message(text)
|
||||||
|
|
||||||
def send_file(self):
|
def send_file(self):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
self.menu.hide()
|
||||||
choose_file = QtGui.QApplication.translate("MainWindow", 'Choose file', None, QtGui.QApplication.UnicodeUTF8)
|
if self.profile.active_friend + 1:
|
||||||
choose = 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, hide=False):
|
def send_screenshot(self, hide=False):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
self.menu.hide()
|
||||||
|
if self.profile.active_friend + 1:
|
||||||
self.sw = ScreenShotWindow(self)
|
self.sw = ScreenShotWindow(self)
|
||||||
self.sw.show()
|
self.sw.show()
|
||||||
if hide:
|
if hide:
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
def call(self):
|
def send_smiley(self):
|
||||||
if self.profile.is_active_online(): # active friend exists and online
|
self.menu.hide()
|
||||||
self.profile.call_click(True)
|
if self.profile.active_friend + 1:
|
||||||
|
self.smiley = SmileyWindow(self)
|
||||||
|
self.smiley.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(),
|
||||||
|
self.y() + self.height() - 200,
|
||||||
|
self.smiley.width(),
|
||||||
|
self.smiley.height()))
|
||||||
|
self.smiley.show()
|
||||||
|
|
||||||
|
def send_sticker(self):
|
||||||
|
self.menu.hide()
|
||||||
|
if self.profile.active_friend + 1:
|
||||||
|
self.sticker = StickerWindow(self)
|
||||||
|
self.sticker.setGeometry(QtCore.QRect(self.x() if Settings.get_instance()['mirror_mode'] else 270 + self.x(),
|
||||||
|
self.y() + self.height() - 200,
|
||||||
|
self.sticker.width(),
|
||||||
|
self.sticker.height()))
|
||||||
|
self.sticker.show()
|
||||||
|
|
||||||
def active_call(self):
|
def active_call(self):
|
||||||
self.update_call_state('finish_call')
|
self.update_call_state('finish_call')
|
||||||
@ -435,10 +450,16 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.update_call_state('call')
|
self.update_call_state('call')
|
||||||
|
|
||||||
def update_call_state(self, fl):
|
def update_call_state(self, fl):
|
||||||
|
# TODO: do smth with video call button
|
||||||
|
os.chdir(curr_directory() + '/images/')
|
||||||
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(fl))
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/{}.png'.format(fl))
|
||||||
icon = QtGui.QIcon(pixmap)
|
icon = QtGui.QIcon(pixmap)
|
||||||
self.callButton.setIcon(icon)
|
self.callButton.setIcon(icon)
|
||||||
self.callButton.setIconSize(QtCore.QSize(50, 50))
|
self.callButton.setIconSize(QtCore.QSize(50, 50))
|
||||||
|
pixmap = QtGui.QPixmap(curr_directory() + '/images/videocall.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.videocallButton.setIcon(icon)
|
||||||
|
self.videocallButton.setIconSize(QtCore.QSize(35, 35))
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Functions which called when user open context menu in friends list
|
# Functions which called when user open context menu in friends list
|
||||||
@ -458,6 +479,8 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
copy_key_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Copy public key', None, QtGui.QApplication.UnicodeUTF8))
|
copy_key_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Copy public key', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
auto_accept_item = self.listMenu.addAction(auto)
|
auto_accept_item = self.listMenu.addAction(auto)
|
||||||
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
|
remove_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Remove friend', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
notes_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Notes', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
|
submenu = plugin_support.PluginLoader.get_instance().get_menu(self.listMenu, num)
|
||||||
if len(submenu):
|
if len(submenu):
|
||||||
plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
|
plug = self.listMenu.addMenu(QtGui.QApplication.translate("MainWindow", 'Plugins', None, QtGui.QApplication.UnicodeUTF8))
|
||||||
@ -467,10 +490,26 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
|
self.connect(copy_key_item, QtCore.SIGNAL("triggered()"), lambda: self.copy_friend_key(num))
|
||||||
self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
|
self.connect(clear_history_item, QtCore.SIGNAL("triggered()"), lambda: self.clear_history(num))
|
||||||
self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
|
self.connect(auto_accept_item, QtCore.SIGNAL("triggered()"), lambda: self.auto_accept(num, not allowed))
|
||||||
|
self.connect(notes_item, QtCore.SIGNAL("triggered()"), lambda: self.show_note(friend))
|
||||||
parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
|
parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
|
||||||
self.listMenu.move(parent_position + pos)
|
self.listMenu.move(parent_position + pos)
|
||||||
self.listMenu.show()
|
self.listMenu.show()
|
||||||
|
|
||||||
|
def show_note(self, friend):
|
||||||
|
s = Settings.get_instance()
|
||||||
|
note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else ''
|
||||||
|
user = QtGui.QApplication.translate("MainWindow", 'Notes about user', None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
user = u'{} {}'.format(user, friend.name)
|
||||||
|
|
||||||
|
def save_note(text):
|
||||||
|
if friend.tox_id in s['notes']:
|
||||||
|
del s['notes'][friend.tox_id]
|
||||||
|
if text:
|
||||||
|
s['notes'][friend.tox_id] = text
|
||||||
|
s.save()
|
||||||
|
self.note = MultilineEdit(user, note, save_note)
|
||||||
|
self.note.show()
|
||||||
|
|
||||||
def set_alias(self, num):
|
def set_alias(self, num):
|
||||||
self.profile.set_alias(num)
|
self.profile.set_alias(num)
|
||||||
|
|
||||||
@ -503,71 +542,13 @@ class MainWindow(QtGui.QMainWindow):
|
|||||||
self.profile.set_active(num)
|
self.profile.set_active(num)
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
def mouseReleaseEvent(self, event):
|
||||||
x, y = event.x(), event.y()
|
|
||||||
pos = self.connection_status.pos()
|
pos = self.connection_status.pos()
|
||||||
if (pos.x() < x < pos.x() + 32) and (pos.y() < y < pos.y() + 32):
|
x, y = pos.x() + self.user_info.pos().x(), pos.y() + self.user_info.pos().y()
|
||||||
|
if (x < event.x() < x + 32) and (y < event.y() < y + 32):
|
||||||
self.profile.change_status()
|
self.profile.change_status()
|
||||||
else:
|
else:
|
||||||
super(self.__class__, self).mouseReleaseEvent(event)
|
super(MainWindow, self).mouseReleaseEvent(event)
|
||||||
|
|
||||||
def filtering(self):
|
def filtering(self):
|
||||||
self.profile.filtration(self.online_contacts.currentIndex() == 1, self.contact_name.text())
|
self.profile.filtration(self.online_contacts.currentIndex() == 1, self.contact_name.text())
|
||||||
|
|
||||||
|
|
||||||
class ScreenShotWindow(QtGui.QWidget):
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
super(ScreenShotWindow, self).__init__()
|
|
||||||
self.parent = parent
|
|
||||||
self.setMouseTracking(True)
|
|
||||||
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
|
||||||
self.showFullScreen()
|
|
||||||
self.setWindowOpacity(0.5)
|
|
||||||
self.rubberband = QtGui.QRubberBand(QtGui.QRubberBand.Rectangle, None)
|
|
||||||
|
|
||||||
def closeEvent(self, *args):
|
|
||||||
if self.parent.isHidden():
|
|
||||||
self.parent.show()
|
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
|
||||||
self.origin = event.pos()
|
|
||||||
self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
|
|
||||||
self.rubberband.show()
|
|
||||||
QtGui.QWidget.mousePressEvent(self, event)
|
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
|
||||||
if self.rubberband.isVisible():
|
|
||||||
self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized())
|
|
||||||
left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height()))
|
|
||||||
right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height()))
|
|
||||||
top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y())
|
|
||||||
bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height())
|
|
||||||
self.setMask(left + right + top + bottom)
|
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
|
||||||
if self.rubberband.isVisible():
|
|
||||||
self.rubberband.hide()
|
|
||||||
rect = self.rubberband.geometry()
|
|
||||||
print rect
|
|
||||||
if rect.width() and rect.height():
|
|
||||||
p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
|
|
||||||
rect.x() + 4,
|
|
||||||
rect.y() + 4,
|
|
||||||
rect.width() - 8,
|
|
||||||
rect.height() - 8)
|
|
||||||
byte_array = QtCore.QByteArray()
|
|
||||||
buffer = QtCore.QBuffer(byte_array)
|
|
||||||
buffer.open(QtCore.QIODevice.WriteOnly)
|
|
||||||
p.save(buffer, 'PNG')
|
|
||||||
Profile.get_instance().send_screenshot(str(byte_array.data()))
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
|
||||||
if event.key() == QtCore.Qt.Key_Escape:
|
|
||||||
self.rubberband.setHidden(True)
|
|
||||||
self.close()
|
|
||||||
else:
|
|
||||||
super(ScreenShotWindow, self).keyPressEvent(event)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
313
src/mainscreen_widgets.py
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
try:
|
||||||
|
from PySide import QtCore, QtGui
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
from widgets import RubberBand, create_menu, QRightClickButton
|
||||||
|
from profile import Profile
|
||||||
|
import smileys
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class MessageArea(QtGui.QPlainTextEdit):
|
||||||
|
"""User types messages here"""
|
||||||
|
|
||||||
|
def __init__(self, parent, form):
|
||||||
|
super(MessageArea, self).__init__(parent)
|
||||||
|
self.parent = form
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
self.timer = QtCore.QTimer(self)
|
||||||
|
self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False))
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.matches(QtGui.QKeySequence.Paste):
|
||||||
|
self.pasteEvent()
|
||||||
|
elif event.key() in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
|
||||||
|
modifiers = event.modifiers()
|
||||||
|
if modifiers & QtCore.Qt.ControlModifier or modifiers & QtCore.Qt.ShiftModifier:
|
||||||
|
self.insertPlainText('\n')
|
||||||
|
else:
|
||||||
|
if self.timer.isActive():
|
||||||
|
self.timer.stop()
|
||||||
|
self.parent.profile.send_typing(False)
|
||||||
|
self.parent.send_message()
|
||||||
|
elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText():
|
||||||
|
self.appendPlainText(Profile.get_instance().get_last_message())
|
||||||
|
else:
|
||||||
|
self.parent.profile.send_typing(True)
|
||||||
|
if self.timer.isActive():
|
||||||
|
self.timer.stop()
|
||||||
|
self.timer.start(5000)
|
||||||
|
super(MessageArea, self).keyPressEvent(event)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
menu = create_menu(self.createStandardContextMenu())
|
||||||
|
menu.exec_(event.globalPos())
|
||||||
|
del menu
|
||||||
|
|
||||||
|
def dragEnterEvent(self, e):
|
||||||
|
e.accept()
|
||||||
|
|
||||||
|
def dragMoveEvent(self, e):
|
||||||
|
e.accept()
|
||||||
|
|
||||||
|
def dropEvent(self, e):
|
||||||
|
if e.mimeData().hasFormat('text/plain'):
|
||||||
|
e.accept()
|
||||||
|
self.pasteEvent(e.mimeData().text())
|
||||||
|
else:
|
||||||
|
e.ignore()
|
||||||
|
|
||||||
|
def pasteEvent(self, text=None):
|
||||||
|
text = text or QtGui.QApplication.clipboard().text()
|
||||||
|
if text.startswith('file://'):
|
||||||
|
self.parent.profile.send_file(text[7:])
|
||||||
|
else:
|
||||||
|
self.insertPlainText(text)
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenShotWindow(QtGui.QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(ScreenShotWindow, self).__init__()
|
||||||
|
self.parent = parent
|
||||||
|
self.setMouseTracking(True)
|
||||||
|
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
||||||
|
self.showFullScreen()
|
||||||
|
self.setWindowOpacity(0.5)
|
||||||
|
self.rubberband = RubberBand()
|
||||||
|
|
||||||
|
def closeEvent(self, *args):
|
||||||
|
if self.parent.isHidden():
|
||||||
|
self.parent.show()
|
||||||
|
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
self.origin = event.pos()
|
||||||
|
self.rubberband.setGeometry(QtCore.QRect(self.origin, QtCore.QSize()))
|
||||||
|
self.rubberband.show()
|
||||||
|
QtGui.QWidget.mousePressEvent(self, event)
|
||||||
|
|
||||||
|
def mouseMoveEvent(self, event):
|
||||||
|
if self.rubberband.isVisible():
|
||||||
|
self.rubberband.setGeometry(QtCore.QRect(self.origin, event.pos()).normalized())
|
||||||
|
left = QtGui.QRegion(QtCore.QRect(0, 0, self.rubberband.x(), self.height()))
|
||||||
|
right = QtGui.QRegion(QtCore.QRect(self.rubberband.x() + self.rubberband.width(), 0, self.width(), self.height()))
|
||||||
|
top = QtGui.QRegion(0, 0, self.width(), self.rubberband.y())
|
||||||
|
bottom = QtGui.QRegion(0, self.rubberband.y() + self.rubberband.height(), self.width(), self.height())
|
||||||
|
self.setMask(left + right + top + bottom)
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
if self.rubberband.isVisible():
|
||||||
|
self.rubberband.hide()
|
||||||
|
rect = self.rubberband.geometry()
|
||||||
|
print rect
|
||||||
|
if rect.width() and rect.height():
|
||||||
|
p = QtGui.QPixmap.grabWindow(QtGui.QApplication.desktop().winId(),
|
||||||
|
rect.x() + 4,
|
||||||
|
rect.y() + 4,
|
||||||
|
rect.width() - 8,
|
||||||
|
rect.height() - 8)
|
||||||
|
byte_array = QtCore.QByteArray()
|
||||||
|
buffer = QtCore.QBuffer(byte_array)
|
||||||
|
buffer.open(QtCore.QIODevice.WriteOnly)
|
||||||
|
p.save(buffer, 'PNG')
|
||||||
|
Profile.get_instance().send_screenshot(str(byte_array.data()))
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.key() == QtCore.Qt.Key_Escape:
|
||||||
|
self.rubberband.setHidden(True)
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
super(ScreenShotWindow, self).keyPressEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class SmileyWindow(QtGui.QWidget):
|
||||||
|
"""
|
||||||
|
Smiley selection window
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(SmileyWindow, self).__init__()
|
||||||
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
|
||||||
|
inst = smileys.SmileyLoader.get_instance()
|
||||||
|
self.data = inst.get_smileys()
|
||||||
|
count = len(self.data)
|
||||||
|
self.page_size = int(pow(count / 8, 0.5) + 1) * 8 # smileys per page
|
||||||
|
if count % self.page_size == 0:
|
||||||
|
self.page_count = count / self.page_size
|
||||||
|
else:
|
||||||
|
self.page_count = int(count / float(self.page_size) + 0.5)
|
||||||
|
self.page = 0
|
||||||
|
self.radio = []
|
||||||
|
self.parent = parent
|
||||||
|
for i in range(self.page_count): # buttons with smileys
|
||||||
|
elem = QtGui.QRadioButton(self)
|
||||||
|
elem.setGeometry(QtCore.QRect(i * 20 + 5, 180, 20, 20))
|
||||||
|
elem.clicked.connect(lambda i=i: self.checked(i))
|
||||||
|
self.radio.append(elem)
|
||||||
|
width = max(self.page_count * 20 + 30, (self.page_size + 5) * 8 / 10)
|
||||||
|
self.setMaximumSize(width, 200)
|
||||||
|
self.setMinimumSize(width, 200)
|
||||||
|
self.buttons = []
|
||||||
|
for i in range(self.page_size): # pages - radio buttons
|
||||||
|
b = QtGui.QPushButton(self)
|
||||||
|
b.setGeometry(QtCore.QRect((i / 8) * 20 + 5, (i % 8) * 20, 20, 20))
|
||||||
|
b.clicked.connect(lambda i=i: self.clicked(i))
|
||||||
|
self.buttons.append(b)
|
||||||
|
self.checked(0)
|
||||||
|
|
||||||
|
def checked(self, pos): # new page opened
|
||||||
|
self.radio[self.page].setChecked(False)
|
||||||
|
self.radio[pos].setChecked(True)
|
||||||
|
self.page = pos
|
||||||
|
start = self.page * self.page_size
|
||||||
|
for i in range(self.page_size):
|
||||||
|
try:
|
||||||
|
self.buttons[i].setVisible(True)
|
||||||
|
pixmap = QtGui.QPixmap(self.data[start + i][1])
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.buttons[i].setIcon(icon)
|
||||||
|
except:
|
||||||
|
self.buttons[i].setVisible(False)
|
||||||
|
|
||||||
|
def clicked(self, pos): # smiley selected
|
||||||
|
pos += self.page * self.page_size
|
||||||
|
smiley = self.data[pos][0]
|
||||||
|
self.parent.messageEdit.insertPlainText(smiley)
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def leaveEvent(self, event):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
|
class MenuButton(QtGui.QPushButton):
|
||||||
|
|
||||||
|
def __init__(self, parent, enter):
|
||||||
|
super(MenuButton, self).__init__(parent)
|
||||||
|
self.enter = enter
|
||||||
|
|
||||||
|
def enterEvent(self, event):
|
||||||
|
self.enter()
|
||||||
|
super(MenuButton, self).enterEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class DropdownMenu(QtGui.QWidget):
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(DropdownMenu, self).__init__(parent)
|
||||||
|
self.installEventFilter(self)
|
||||||
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
|
||||||
|
self.setMaximumSize(150, 100)
|
||||||
|
self.setMinimumSize(150, 100)
|
||||||
|
self.screenshotButton = QRightClickButton(self)
|
||||||
|
self.screenshotButton.setGeometry(QtCore.QRect(0, 50, 50, 50))
|
||||||
|
self.screenshotButton.setObjectName("screenshotButton")
|
||||||
|
|
||||||
|
self.fileTransferButton = QtGui.QPushButton(self)
|
||||||
|
self.fileTransferButton.setGeometry(QtCore.QRect(50, 50, 50, 50))
|
||||||
|
self.fileTransferButton.setObjectName("fileTransferButton")
|
||||||
|
|
||||||
|
self.audioMessageButton = QtGui.QPushButton(self)
|
||||||
|
self.audioMessageButton.setGeometry(QtCore.QRect(100, 50, 50, 50))
|
||||||
|
|
||||||
|
self.smileyButton = QtGui.QPushButton(self)
|
||||||
|
self.smileyButton.setGeometry(QtCore.QRect(0, 0, 50, 50))
|
||||||
|
|
||||||
|
self.videoMessageButton = QtGui.QPushButton(self)
|
||||||
|
self.videoMessageButton.setGeometry(QtCore.QRect(100, 0, 50, 50))
|
||||||
|
|
||||||
|
self.stickerButton = QtGui.QPushButton(self)
|
||||||
|
self.stickerButton.setGeometry(QtCore.QRect(50, 0, 50, 50))
|
||||||
|
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/file.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.fileTransferButton.setIcon(icon)
|
||||||
|
self.fileTransferButton.setIconSize(QtCore.QSize(40, 40))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/screenshot.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.screenshotButton.setIcon(icon)
|
||||||
|
self.screenshotButton.setIconSize(QtCore.QSize(40, 50))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/audio_message.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.audioMessageButton.setIcon(icon)
|
||||||
|
self.audioMessageButton.setIconSize(QtCore.QSize(40, 40))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/smiley.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.smileyButton.setIcon(icon)
|
||||||
|
self.smileyButton.setIconSize(QtCore.QSize(40, 40))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/video_message.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.videoMessageButton.setIcon(icon)
|
||||||
|
self.videoMessageButton.setIconSize(QtCore.QSize(45, 45))
|
||||||
|
pixmap = QtGui.QPixmap(util.curr_directory() + '/images/sticker.png')
|
||||||
|
icon = QtGui.QIcon(pixmap)
|
||||||
|
self.stickerButton.setIcon(icon)
|
||||||
|
self.stickerButton.setIconSize(QtCore.QSize(45, 45))
|
||||||
|
|
||||||
|
self.screenshotButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send screenshot", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.fileTransferButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send file", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.audioMessageButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send audio message", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.videoMessageButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send video message", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.smileyButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Add smiley", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.stickerButton.setToolTip(QtGui.QApplication.translate("MenuWindow", "Send sticker", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
self.fileTransferButton.clicked.connect(parent.send_file)
|
||||||
|
self.screenshotButton.clicked.connect(parent.send_screenshot)
|
||||||
|
self.connect(self.screenshotButton, QtCore.SIGNAL("rightClicked()"), lambda: parent.send_screenshot(True))
|
||||||
|
self.smileyButton.clicked.connect(parent.send_smiley)
|
||||||
|
self.stickerButton.clicked.connect(parent.send_sticker)
|
||||||
|
|
||||||
|
def leaveEvent(self, event):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def eventFilter(self, object, event):
|
||||||
|
if event.type() == QtCore.QEvent.WindowDeactivate:
|
||||||
|
self.close()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class StickerItem(QtGui.QWidget):
|
||||||
|
|
||||||
|
def __init__(self, fl):
|
||||||
|
super(StickerItem, self).__init__()
|
||||||
|
self._image_label = QtGui.QLabel(self)
|
||||||
|
self.path = fl
|
||||||
|
self.pixmap = QtGui.QPixmap()
|
||||||
|
self.pixmap.load(fl)
|
||||||
|
if self.pixmap.width() > 150:
|
||||||
|
self.pixmap = self.pixmap.scaled(150, 200, QtCore.Qt.KeepAspectRatio)
|
||||||
|
self.setFixedSize(150, self.pixmap.height())
|
||||||
|
self._image_label.setPixmap(self.pixmap)
|
||||||
|
|
||||||
|
|
||||||
|
class StickerWindow(QtGui.QWidget):
|
||||||
|
"""Sticker selection window"""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
super(StickerWindow, self).__init__()
|
||||||
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
|
||||||
|
self.setMaximumSize(250, 200)
|
||||||
|
self.setMinimumSize(250, 200)
|
||||||
|
self.list = QtGui.QListWidget(self)
|
||||||
|
self.list.setGeometry(QtCore.QRect(0, 0, 250, 200))
|
||||||
|
self.arr = smileys.sticker_loader()
|
||||||
|
for sticker in self.arr:
|
||||||
|
item = StickerItem(sticker)
|
||||||
|
elem = QtGui.QListWidgetItem()
|
||||||
|
elem.setSizeHint(QtCore.QSize(250, item.height()))
|
||||||
|
self.list.addItem(elem)
|
||||||
|
self.list.setItemWidget(elem, item)
|
||||||
|
self.list.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
|
||||||
|
self.list.setSpacing(3)
|
||||||
|
self.list.clicked.connect(self.click)
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
def click(self, index):
|
||||||
|
num = index.row()
|
||||||
|
self.parent.profile.send_sticker(self.arr[num])
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def leaveEvent(self, event):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
278
src/menu.py
@ -4,8 +4,8 @@ except ImportError:
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
from settings import *
|
from settings import *
|
||||||
from profile import Profile
|
from profile import Profile
|
||||||
from util import get_style, curr_directory
|
from util import curr_directory
|
||||||
from widgets import CenteredWidget, DataLabel
|
from widgets import CenteredWidget, DataLabel, LineEdit
|
||||||
import pyaudio
|
import pyaudio
|
||||||
import toxencryptsave
|
import toxencryptsave
|
||||||
import plugin_support
|
import plugin_support
|
||||||
@ -14,11 +14,11 @@ import plugin_support
|
|||||||
class AddContact(CenteredWidget):
|
class AddContact(CenteredWidget):
|
||||||
"""Add contact form"""
|
"""Add contact form"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, tox_id=''):
|
||||||
super(AddContact, self).__init__()
|
super(AddContact, self).__init__()
|
||||||
self.initUI()
|
self.initUI(tox_id)
|
||||||
|
|
||||||
def initUI(self):
|
def initUI(self, tox_id):
|
||||||
self.setObjectName('AddContact')
|
self.setObjectName('AddContact')
|
||||||
self.resize(568, 306)
|
self.resize(568, 306)
|
||||||
self.sendRequestButton = QtGui.QPushButton(self)
|
self.sendRequestButton = QtGui.QPushButton(self)
|
||||||
@ -27,24 +27,25 @@ class AddContact(CenteredWidget):
|
|||||||
self.sendRequestButton.setBaseSize(QtCore.QSize(0, 0))
|
self.sendRequestButton.setBaseSize(QtCore.QSize(0, 0))
|
||||||
self.sendRequestButton.setObjectName("sendRequestButton")
|
self.sendRequestButton.setObjectName("sendRequestButton")
|
||||||
self.sendRequestButton.clicked.connect(self.add_friend)
|
self.sendRequestButton.clicked.connect(self.add_friend)
|
||||||
self.tox_id = QtGui.QLineEdit(self)
|
self.tox_id = LineEdit(self)
|
||||||
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.tox_id.setText(tox_id)
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
self.label.setGeometry(QtCore.QRect(60, 10, 80, 20))
|
self.label.setGeometry(QtCore.QRect(50, 10, 80, 20))
|
||||||
self.error_label = DataLabel(self)
|
self.error_label = DataLabel(self)
|
||||||
self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20))
|
self.error_label.setGeometry(QtCore.QRect(120, 10, 420, 20))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
font.setWeight(30)
|
font.setWeight(30)
|
||||||
self.error_label.setFont(font)
|
self.error_label.setFont(font)
|
||||||
self.error_label.setStyleSheet("QLabel { color: red; }")
|
self.error_label.setStyleSheet("QLabel { color: #BC1C1C; }")
|
||||||
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.message = QtGui.QLabel(self)
|
self.message = QtGui.QLabel(self)
|
||||||
self.message.setGeometry(QtCore.QRect(60, 70, 101, 31))
|
self.message.setGeometry(QtCore.QRect(50, 70, 101, 31))
|
||||||
self.message.setFont(font)
|
self.message.setFont(font)
|
||||||
self.message.setObjectName("label_2")
|
self.message.setObjectName("label_2")
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
@ -70,6 +71,7 @@ class AddContact(CenteredWidget):
|
|||||||
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.message.setText(QtGui.QApplication.translate('AddContact', "Message:", None, QtGui.QApplication.UnicodeUTF8))
|
self.message.setText(QtGui.QApplication.translate('AddContact', "Message:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.tox_id.setPlaceholderText(QtGui.QApplication.translate('AddContact', "TOX ID or public key of contact", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
|
||||||
class ProfileSettings(CenteredWidget):
|
class ProfileSettings(CenteredWidget):
|
||||||
@ -81,17 +83,17 @@ class ProfileSettings(CenteredWidget):
|
|||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("ProfileSettingsForm")
|
self.setObjectName("ProfileSettingsForm")
|
||||||
self.setMinimumSize(QtCore.QSize(650, 520))
|
self.setMinimumSize(QtCore.QSize(700, 600))
|
||||||
self.setMaximumSize(QtCore.QSize(650, 520))
|
self.setMaximumSize(QtCore.QSize(700, 600))
|
||||||
self.nick = QtGui.QLineEdit(self)
|
self.nick = LineEdit(self)
|
||||||
self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27))
|
self.nick.setGeometry(QtCore.QRect(30, 60, 350, 27))
|
||||||
self.nick.setObjectName("nick")
|
|
||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
self.nick.setText(profile.name)
|
self.nick.setText(profile.name)
|
||||||
self.status = QtGui.QLineEdit(self)
|
self.status = QtGui.QComboBox(self)
|
||||||
self.status.setGeometry(QtCore.QRect(30, 130, 350, 27))
|
self.status.setGeometry(QtCore.QRect(400, 60, 200, 27))
|
||||||
self.status.setObjectName("status")
|
self.status_message = LineEdit(self)
|
||||||
self.status.setText(profile.status_message)
|
self.status_message.setGeometry(QtCore.QRect(30, 130, 350, 27))
|
||||||
|
self.status_message.setText(profile.status_message)
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
self.label.setGeometry(QtCore.QRect(40, 30, 91, 25))
|
self.label.setGeometry(QtCore.QRect(40, 30, 91, 25))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
@ -99,62 +101,66 @@ class ProfileSettings(CenteredWidget):
|
|||||||
font.setWeight(75)
|
font.setWeight(75)
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.label.setFont(font)
|
self.label.setFont(font)
|
||||||
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, 25))
|
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_3 = QtGui.QLabel(self)
|
self.label_3 = QtGui.QLabel(self)
|
||||||
self.label_3.setGeometry(QtCore.QRect(40, 180, 100, 25))
|
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.tox_id = QtGui.QLabel(self)
|
self.tox_id = QtGui.QLabel(self)
|
||||||
self.tox_id.setGeometry(QtCore.QRect(10, 210, self.width(), 21))
|
self.tox_id.setGeometry(QtCore.QRect(15, 210, 685, 21))
|
||||||
font.setPointSize(10)
|
font.setPointSize(10)
|
||||||
self.tox_id.setFont(font)
|
self.tox_id.setFont(font)
|
||||||
self.tox_id.setObjectName("tox_id")
|
|
||||||
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, 160, 30))
|
self.copyId.setGeometry(QtCore.QRect(40, 250, 180, 30))
|
||||||
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(210, 250, 160, 30))
|
self.export.setGeometry(QtCore.QRect(230, 250, 180, 30))
|
||||||
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 = QtGui.QPushButton(self)
|
||||||
self.new_nospam.setGeometry(QtCore.QRect(380, 250, 160, 30))
|
self.new_nospam.setGeometry(QtCore.QRect(420, 250, 180, 30))
|
||||||
self.new_nospam.clicked.connect(self.new_no_spam)
|
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(40, 300, 180, 30))
|
||||||
self.delete_avatar = QtGui.QPushButton(self)
|
self.delete_avatar = QtGui.QPushButton(self)
|
||||||
self.delete_avatar.setGeometry(QtCore.QRect(400, 120, 200, 50))
|
self.delete_avatar.setGeometry(QtCore.QRect(230, 300, 180, 30))
|
||||||
self.delete_avatar.clicked.connect(self.reset_avatar)
|
self.delete_avatar.clicked.connect(self.reset_avatar)
|
||||||
self.new_avatar.clicked.connect(self.set_avatar)
|
self.new_avatar.clicked.connect(self.set_avatar)
|
||||||
self.profile_pass = QtGui.QLabel(self)
|
self.profile_pass = QtGui.QLabel(self)
|
||||||
self.profile_pass.setGeometry(QtCore.QRect(40, 300, 300, 50))
|
self.profile_pass.setGeometry(QtCore.QRect(40, 340, 300, 30))
|
||||||
font.setPointSize(18)
|
font.setPointSize(18)
|
||||||
self.profile_pass.setFont(font)
|
self.profile_pass.setFont(font)
|
||||||
self.password = QtGui.QLineEdit(self)
|
self.password = LineEdit(self)
|
||||||
self.password.setGeometry(QtCore.QRect(30, 350, 300, 30))
|
self.password.setGeometry(QtCore.QRect(40, 380, 300, 30))
|
||||||
self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
self.password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
||||||
self.leave_blank = QtGui.QLabel(self)
|
self.leave_blank = QtGui.QLabel(self)
|
||||||
self.leave_blank.setGeometry(QtCore.QRect(340, 350, 300, 30))
|
self.leave_blank.setGeometry(QtCore.QRect(350, 380, 300, 30))
|
||||||
self.confirm_password = QtGui.QLineEdit(self)
|
self.confirm_password = LineEdit(self)
|
||||||
self.confirm_password.setGeometry(QtCore.QRect(30, 400, 300, 30))
|
self.confirm_password.setGeometry(QtCore.QRect(40, 420, 300, 30))
|
||||||
self.confirm_password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
self.confirm_password.setEchoMode(QtGui.QLineEdit.EchoMode.Password)
|
||||||
self.set_password = QtGui.QPushButton(self)
|
self.set_password = QtGui.QPushButton(self)
|
||||||
self.set_password.setGeometry(QtCore.QRect(30, 450, 300, 30))
|
self.set_password.setGeometry(QtCore.QRect(40, 470, 300, 30))
|
||||||
self.set_password.clicked.connect(self.new_password)
|
self.set_password.clicked.connect(self.new_password)
|
||||||
self.not_match = QtGui.QLabel(self)
|
self.not_match = QtGui.QLabel(self)
|
||||||
self.not_match.setGeometry(QtCore.QRect(340, 400, 300, 30))
|
self.not_match.setGeometry(QtCore.QRect(350, 420, 300, 30))
|
||||||
self.not_match.setVisible(False)
|
self.not_match.setVisible(False)
|
||||||
self.not_match.setStyleSheet('QLabel { color: #F70D1A; }')
|
self.not_match.setStyleSheet('QLabel { color: #BC1C1C; }')
|
||||||
self.warning = QtGui.QLabel(self)
|
self.warning = QtGui.QLabel(self)
|
||||||
self.warning.setGeometry(QtCore.QRect(30, 490, 500, 30))
|
self.warning.setGeometry(QtCore.QRect(40, 510, 500, 30))
|
||||||
self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
|
self.warning.setStyleSheet('QLabel { color: #BC1C1C; }')
|
||||||
|
self.default = QtGui.QPushButton(self)
|
||||||
|
self.default.setGeometry(QtCore.QRect(40, 550, 620, 30))
|
||||||
|
path, name = Settings.get_auto_profile()
|
||||||
|
self.auto = path + name == ProfileHelper.get_path() + Settings.get_instance().name
|
||||||
|
self.default.clicked.connect(self.auto_profile)
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
if profile.status is not None:
|
||||||
|
self.status.setCurrentIndex(profile.status)
|
||||||
|
else:
|
||||||
|
self.status.setVisible(False)
|
||||||
QtCore.QMetaObject.connectSlotsByName(self)
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
@ -174,6 +180,27 @@ class ProfileSettings(CenteredWidget):
|
|||||||
self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None, QtGui.QApplication.UnicodeUTF8))
|
self.not_match.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Passwords do not match", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.leave_blank.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password", None, QtGui.QApplication.UnicodeUTF8))
|
self.leave_blank.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Leaving blank will reset current password", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.warning.setText(QtGui.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords", None, QtGui.QApplication.UnicodeUTF8))
|
self.warning.setText(QtGui.QApplication.translate("ProfileSettingsForm", "There is no way to recover lost passwords", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Online", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Away", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.status.addItem(QtGui.QApplication.translate("ProfileSettingsForm", "Busy", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
if self.auto:
|
||||||
|
self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as not default profile", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as default profile", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
def auto_profile(self):
|
||||||
|
if self.auto:
|
||||||
|
Settings.reset_auto_profile()
|
||||||
|
else:
|
||||||
|
Settings.set_auto_profile(ProfileHelper.get_path(), Settings.get_instance().name)
|
||||||
|
self.auto = not self.auto
|
||||||
|
if self.auto:
|
||||||
|
self.default.setText(QtGui.QApplication.translate("ProfileSettingsForm", "Mark as not default profile", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.default.setText(
|
||||||
|
QtGui.QApplication.translate("ProfileSettingsForm", "Mark as default profile", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
def new_password(self):
|
def new_password(self):
|
||||||
if self.password.text() == self.confirm_password.text():
|
if self.password.text() == self.confirm_password.text():
|
||||||
@ -208,11 +235,17 @@ class ProfileSettings(CenteredWidget):
|
|||||||
|
|
||||||
def set_avatar(self):
|
def set_avatar(self):
|
||||||
choose = QtGui.QApplication.translate("ProfileSettingsForm", "Choose avatar", None, QtGui.QApplication.UnicodeUTF8)
|
choose = QtGui.QApplication.translate("ProfileSettingsForm", "Choose avatar", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
name = QtGui.QFileDialog.getOpenFileName(self, choose, None, 'Image Files (*.png)')
|
name = QtGui.QFileDialog.getOpenFileName(self, choose, None, 'Images (*.png)')
|
||||||
if name[0]:
|
if name[0]:
|
||||||
with open(name[0], 'rb') as f:
|
bitmap = QtGui.QPixmap(name[0])
|
||||||
data = f.read()
|
bitmap.scaled(QtCore.QSize(128, 128), aspectMode=QtCore.Qt.KeepAspectRatio,
|
||||||
Profile.get_instance().set_avatar(data)
|
mode=QtCore.Qt.SmoothTransformation)
|
||||||
|
|
||||||
|
byte_array = QtCore.QByteArray()
|
||||||
|
buffer = QtCore.QBuffer(byte_array)
|
||||||
|
buffer.open(QtCore.QIODevice.WriteOnly)
|
||||||
|
bitmap.save(buffer, 'PNG')
|
||||||
|
Profile.get_instance().set_avatar(str(byte_array.data()))
|
||||||
|
|
||||||
def export_profile(self):
|
def export_profile(self):
|
||||||
directory = QtGui.QFileDialog.getExistingDirectory() + '/'
|
directory = QtGui.QFileDialog.getExistingDirectory() + '/'
|
||||||
@ -226,7 +259,8 @@ class ProfileSettings(CenteredWidget):
|
|||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
profile = Profile.get_instance()
|
profile = Profile.get_instance()
|
||||||
profile.set_name(self.nick.text().encode('utf-8'))
|
profile.set_name(self.nick.text().encode('utf-8'))
|
||||||
profile.set_status_message(self.status.text().encode('utf-8'))
|
profile.set_status_message(self.status_message.text().encode('utf-8'))
|
||||||
|
profile.set_status(self.status.currentIndex())
|
||||||
|
|
||||||
|
|
||||||
class NetworkSettings(CenteredWidget):
|
class NetworkSettings(CenteredWidget):
|
||||||
@ -254,10 +288,10 @@ class NetworkSettings(CenteredWidget):
|
|||||||
self.http = QtGui.QCheckBox(self)
|
self.http = QtGui.QCheckBox(self)
|
||||||
self.http.setGeometry(QtCore.QRect(20, 70, 97, 22))
|
self.http.setGeometry(QtCore.QRect(20, 70, 97, 22))
|
||||||
self.proxy.setObjectName("proxy")
|
self.proxy.setObjectName("proxy")
|
||||||
self.proxyip = QtGui.QLineEdit(self)
|
self.proxyip = LineEdit(self)
|
||||||
self.proxyip.setGeometry(QtCore.QRect(40, 130, 231, 27))
|
self.proxyip.setGeometry(QtCore.QRect(40, 130, 231, 27))
|
||||||
self.proxyip.setObjectName("proxyip")
|
self.proxyip.setObjectName("proxyip")
|
||||||
self.proxyport = QtGui.QLineEdit(self)
|
self.proxyport = LineEdit(self)
|
||||||
self.proxyport.setGeometry(QtCore.QRect(40, 190, 231, 27))
|
self.proxyport.setGeometry(QtCore.QRect(40, 190, 231, 27))
|
||||||
self.proxyport.setObjectName("proxyport")
|
self.proxyport.setObjectName("proxyport")
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
@ -276,7 +310,7 @@ class NetworkSettings(CenteredWidget):
|
|||||||
self.http.setChecked(settings['proxy_type'] == 1)
|
self.http.setChecked(settings['proxy_type'] == 1)
|
||||||
self.warning = QtGui.QLabel(self)
|
self.warning = QtGui.QLabel(self)
|
||||||
self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60))
|
self.warning.setGeometry(QtCore.QRect(5, 270, 290, 60))
|
||||||
self.warning.setStyleSheet('QLabel { color: #F70D1A; }')
|
self.warning.setStyleSheet('QLabel { color: #BC1C1C; }')
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.proxy.stateChanged.connect(lambda x: self.activate())
|
self.proxy.stateChanged.connect(lambda x: self.activate())
|
||||||
self.activate()
|
self.activate()
|
||||||
@ -331,18 +365,13 @@ class PrivacySettings(CenteredWidget):
|
|||||||
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(10, 20, 291, 22))
|
self.saveHistory.setGeometry(QtCore.QRect(10, 20, 291, 22))
|
||||||
self.saveHistory.setObjectName("saveHistory")
|
|
||||||
self.fileautoaccept = QtGui.QCheckBox(self)
|
self.fileautoaccept = QtGui.QCheckBox(self)
|
||||||
self.fileautoaccept.setGeometry(QtCore.QRect(10, 60, 271, 22))
|
self.fileautoaccept.setGeometry(QtCore.QRect(10, 60, 271, 22))
|
||||||
self.fileautoaccept.setObjectName("fileautoaccept")
|
|
||||||
self.typingNotifications = QtGui.QCheckBox(self)
|
self.typingNotifications = QtGui.QCheckBox(self)
|
||||||
self.typingNotifications.setGeometry(QtCore.QRect(10, 100, 350, 30))
|
self.typingNotifications.setGeometry(QtCore.QRect(10, 100, 350, 30))
|
||||||
self.typingNotifications.setObjectName("typingNotifications")
|
|
||||||
self.inlines = QtGui.QCheckBox(self)
|
self.inlines = QtGui.QCheckBox(self)
|
||||||
self.inlines.setGeometry(QtCore.QRect(10, 140, 350, 30))
|
self.inlines.setGeometry(QtCore.QRect(10, 140, 350, 30))
|
||||||
self.inlines.setObjectName("inlines")
|
|
||||||
|
|
||||||
|
|
||||||
self.auto_path = QtGui.QLabel(self)
|
self.auto_path = QtGui.QLabel(self)
|
||||||
self.auto_path.setGeometry(QtCore.QRect(10, 190, 350, 30))
|
self.auto_path.setGeometry(QtCore.QRect(10, 190, 350, 30))
|
||||||
self.path = QtGui.QPlainTextEdit(self)
|
self.path = QtGui.QPlainTextEdit(self)
|
||||||
@ -382,7 +411,7 @@ class PrivacySettings(CenteredWidget):
|
|||||||
self.auto_path.setText(QtGui.QApplication.translate("privacySettings", "Auto accept default path:", None, QtGui.QApplication.UnicodeUTF8))
|
self.auto_path.setText(QtGui.QApplication.translate("privacySettings", "Auto accept default path:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.change_path.setText(QtGui.QApplication.translate("privacySettings", "Change", None, QtGui.QApplication.UnicodeUTF8))
|
self.change_path.setText(QtGui.QApplication.translate("privacySettings", "Change", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.inlines.setText(QtGui.QApplication.translate("privacySettings", "Allow inlines", None, QtGui.QApplication.UnicodeUTF8))
|
self.inlines.setText(QtGui.QApplication.translate("privacySettings", "Allow inlines", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.block_user_label.setText(QtGui.QApplication.translate("privacySettings", "Block by TOX ID:", None, QtGui.QApplication.UnicodeUTF8))
|
self.block_user_label.setText(QtGui.QApplication.translate("privacySettings", "Block by public key:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.blocked_users_label.setText(QtGui.QApplication.translate("privacySettings", "Blocked users:", None, QtGui.QApplication.UnicodeUTF8))
|
self.blocked_users_label.setText(QtGui.QApplication.translate("privacySettings", "Blocked users:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.unblock.setText(QtGui.QApplication.translate("privacySettings", "Unblock", None, QtGui.QApplication.UnicodeUTF8))
|
self.unblock.setText(QtGui.QApplication.translate("privacySettings", "Unblock", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.block.setText(QtGui.QApplication.translate("privacySettings", "Block user", None, QtGui.QApplication.UnicodeUTF8))
|
self.block.setText(QtGui.QApplication.translate("privacySettings", "Block user", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
@ -435,9 +464,9 @@ class NotificationsSettings(CenteredWidget):
|
|||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("notificationsForm")
|
self.setObjectName("notificationsForm")
|
||||||
self.resize(350, 200)
|
self.resize(350, 180)
|
||||||
self.setMinimumSize(QtCore.QSize(350, 200))
|
self.setMinimumSize(QtCore.QSize(350, 180))
|
||||||
self.setMaximumSize(QtCore.QSize(350, 200))
|
self.setMaximumSize(QtCore.QSize(350, 180))
|
||||||
self.enableNotifications = QtGui.QCheckBox(self)
|
self.enableNotifications = QtGui.QCheckBox(self)
|
||||||
self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18))
|
self.enableNotifications.setGeometry(QtCore.QRect(10, 20, 340, 18))
|
||||||
self.callsSound = QtGui.QCheckBox(self)
|
self.callsSound = QtGui.QCheckBox(self)
|
||||||
@ -446,7 +475,6 @@ class NotificationsSettings(CenteredWidget):
|
|||||||
self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18))
|
self.soundNotifications.setGeometry(QtCore.QRect(10, 70, 340, 18))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(12)
|
font.setPointSize(12)
|
||||||
font.setBold(True)
|
|
||||||
self.callsSound.setFont(font)
|
self.callsSound.setFont(font)
|
||||||
self.soundNotifications.setFont(font)
|
self.soundNotifications.setFont(font)
|
||||||
self.enableNotifications.setFont(font)
|
self.enableNotifications.setFont(font)
|
||||||
@ -473,7 +501,6 @@ class NotificationsSettings(CenteredWidget):
|
|||||||
|
|
||||||
class InterfaceSettings(CenteredWidget):
|
class InterfaceSettings(CenteredWidget):
|
||||||
"""Interface settings form"""
|
"""Interface settings form"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(InterfaceSettings, self).__init__()
|
super(InterfaceSettings, self).__init__()
|
||||||
self.initUI()
|
self.initUI()
|
||||||
@ -481,39 +508,65 @@ class InterfaceSettings(CenteredWidget):
|
|||||||
|
|
||||||
def initUI(self):
|
def initUI(self):
|
||||||
self.setObjectName("interfaceForm")
|
self.setObjectName("interfaceForm")
|
||||||
self.resize(300, 300)
|
self.setMinimumSize(QtCore.QSize(400, 450))
|
||||||
self.setMinimumSize(QtCore.QSize(300, 300))
|
self.setMaximumSize(QtCore.QSize(400, 450))
|
||||||
self.setMaximumSize(QtCore.QSize(300, 300))
|
|
||||||
self.setBaseSize(QtCore.QSize(300, 300))
|
|
||||||
self.label = QtGui.QLabel(self)
|
self.label = QtGui.QLabel(self)
|
||||||
self.label.setGeometry(QtCore.QRect(30, 20, 91, 21))
|
self.label.setGeometry(QtCore.QRect(30, 10, 370, 20))
|
||||||
font = QtGui.QFont()
|
font = QtGui.QFont()
|
||||||
font.setPointSize(16)
|
font.setPointSize(14)
|
||||||
font.setWeight(75)
|
|
||||||
font.setBold(True)
|
font.setBold(True)
|
||||||
self.label.setFont(font)
|
self.label.setFont(font)
|
||||||
self.label.setObjectName("label")
|
|
||||||
self.themeSelect = QtGui.QComboBox(self)
|
self.themeSelect = QtGui.QComboBox(self)
|
||||||
self.themeSelect.setGeometry(QtCore.QRect(30, 60, 160, 30))
|
self.themeSelect.setGeometry(QtCore.QRect(30, 40, 120, 30))
|
||||||
self.themeSelect.setObjectName("themeSelect")
|
list_of_themes = ['dark']
|
||||||
list_of_themes = ['default', 'windows', 'gtk', 'cde', 'plastique', 'motif']
|
|
||||||
self.themeSelect.addItems(list_of_themes)
|
self.themeSelect.addItems(list_of_themes)
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
theme = settings['theme']
|
theme = settings['theme']
|
||||||
index = list_of_themes.index(theme)
|
if theme in list_of_themes:
|
||||||
|
index = list_of_themes.index(theme)
|
||||||
|
else:
|
||||||
|
index = 0
|
||||||
self.themeSelect.setCurrentIndex(index)
|
self.themeSelect.setCurrentIndex(index)
|
||||||
self.lang_choose = QtGui.QComboBox(self)
|
self.lang_choose = QtGui.QComboBox(self)
|
||||||
self.lang_choose.setGeometry(QtCore.QRect(30, 150, 160, 30))
|
self.lang_choose.setGeometry(QtCore.QRect(30, 110, 120, 30))
|
||||||
self.lang_choose.setObjectName("comboBox")
|
|
||||||
supported = Settings.supported_languages()
|
supported = Settings.supported_languages()
|
||||||
for elem in supported:
|
for key in supported:
|
||||||
self.lang_choose.addItem(elem[0])
|
self.lang_choose.insertItem(0, key)
|
||||||
lang = settings['language']
|
if settings['language'] == key:
|
||||||
index = map(lambda x: x[0], supported).index(lang)
|
self.lang_choose.setCurrentIndex(0)
|
||||||
self.lang_choose.setCurrentIndex(index)
|
|
||||||
self.lang = QtGui.QLabel(self)
|
self.lang = QtGui.QLabel(self)
|
||||||
self.lang.setGeometry(QtCore.QRect(30, 110, 121, 31))
|
self.lang.setGeometry(QtCore.QRect(30, 80, 370, 20))
|
||||||
self.lang.setFont(font)
|
self.lang.setFont(font)
|
||||||
|
self.mirror_mode = QtGui.QCheckBox(self)
|
||||||
|
self.mirror_mode.setGeometry(QtCore.QRect(30, 160, 370, 20))
|
||||||
|
self.mirror_mode.setChecked(settings['mirror_mode'])
|
||||||
|
self.smileys = QtGui.QCheckBox(self)
|
||||||
|
self.smileys.setGeometry(QtCore.QRect(30, 190, 120, 20))
|
||||||
|
self.smileys.setChecked(settings['smileys'])
|
||||||
|
self.smiley_pack_label = QtGui.QLabel(self)
|
||||||
|
self.smiley_pack_label.setGeometry(QtCore.QRect(30, 230, 370, 20))
|
||||||
|
self.smiley_pack_label.setFont(font)
|
||||||
|
self.smiley_pack = QtGui.QComboBox(self)
|
||||||
|
self.smiley_pack.setGeometry(QtCore.QRect(30, 260, 160, 30))
|
||||||
|
sm = smileys.SmileyLoader.get_instance()
|
||||||
|
self.smiley_pack.addItems(sm.get_packs_list())
|
||||||
|
try:
|
||||||
|
ind = sm.get_packs_list().index(settings['smiley_pack'])
|
||||||
|
except:
|
||||||
|
ind = sm.get_packs_list().index('default')
|
||||||
|
self.smiley_pack.setCurrentIndex(ind)
|
||||||
|
self.messages_font_size_label = QtGui.QLabel(self)
|
||||||
|
self.messages_font_size_label.setGeometry(QtCore.QRect(30, 300, 370, 20))
|
||||||
|
self.messages_font_size_label.setFont(font)
|
||||||
|
self.messages_font_size = QtGui.QComboBox(self)
|
||||||
|
self.messages_font_size.setGeometry(QtCore.QRect(30, 330, 160, 30))
|
||||||
|
self.messages_font_size.addItems([str(x) for x in range(10, 19)])
|
||||||
|
self.messages_font_size.setCurrentIndex(settings['message_font_size'] - 10)
|
||||||
|
|
||||||
|
self.unread = QtGui.QPushButton(self)
|
||||||
|
self.unread.setGeometry(QtCore.QRect(30, 380, 340, 40))
|
||||||
|
self.unread.clicked.connect(self.select_color)
|
||||||
|
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
QtCore.QMetaObject.connectSlotsByName(self)
|
QtCore.QMetaObject.connectSlotsByName(self)
|
||||||
|
|
||||||
@ -521,25 +574,54 @@ class InterfaceSettings(CenteredWidget):
|
|||||||
self.setWindowTitle(QtGui.QApplication.translate("interfaceForm", "Interface settings", None, QtGui.QApplication.UnicodeUTF8))
|
self.setWindowTitle(QtGui.QApplication.translate("interfaceForm", "Interface settings", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label.setText(QtGui.QApplication.translate("interfaceForm", "Theme:", None, QtGui.QApplication.UnicodeUTF8))
|
self.label.setText(QtGui.QApplication.translate("interfaceForm", "Theme:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.lang.setText(QtGui.QApplication.translate("interfaceForm", "Language:", None, QtGui.QApplication.UnicodeUTF8))
|
self.lang.setText(QtGui.QApplication.translate("interfaceForm", "Language:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.smileys.setText(QtGui.QApplication.translate("interfaceForm", "Smileys", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.smiley_pack_label.setText(QtGui.QApplication.translate("interfaceForm", "Smiley pack:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.mirror_mode.setText(QtGui.QApplication.translate("interfaceForm", "Mirror mode", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.messages_font_size_label.setText(QtGui.QApplication.translate("interfaceForm", "Messages font size:", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
self.unread.setText(QtGui.QApplication.translate("interfaceForm", "Select unread messages notification color", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
|
def select_color(self):
|
||||||
|
col = QtGui.QColorDialog.getColor()
|
||||||
|
|
||||||
|
if col.isValid():
|
||||||
|
settings = Settings.get_instance()
|
||||||
|
name = col.name()
|
||||||
|
settings['unread_color'] = name
|
||||||
|
settings.save()
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
style = str(self.themeSelect.currentText())
|
settings['theme'] = str(self.themeSelect.currentText())
|
||||||
settings['theme'] = style
|
settings['smileys'] = self.smileys.isChecked()
|
||||||
QtGui.QApplication.setStyle(get_style(style))
|
if settings['mirror_mode'] != self.mirror_mode.isChecked():
|
||||||
|
settings['mirror_mode'] = self.mirror_mode.isChecked()
|
||||||
|
msgBox = QtGui.QMessageBox()
|
||||||
|
text = QtGui.QApplication.translate("interfaceForm", 'Restart app to apply settings', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
msgBox.setWindowTitle(QtGui.QApplication.translate("interfaceForm", 'Restart required', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgBox.setText(text)
|
||||||
|
msgBox.exec_()
|
||||||
|
settings['smiley_pack'] = self.smiley_pack.currentText()
|
||||||
|
smileys.SmileyLoader.get_instance().load_pack()
|
||||||
language = self.lang_choose.currentText()
|
language = self.lang_choose.currentText()
|
||||||
if settings['language'] != language:
|
if settings['language'] != language:
|
||||||
settings['language'] = language
|
settings['language'] = language
|
||||||
index = self.lang_choose.currentIndex()
|
text = self.lang_choose.currentText()
|
||||||
path = Settings.supported_languages()[index][1]
|
path = Settings.supported_languages()[text]
|
||||||
app = QtGui.QApplication.instance()
|
app = QtGui.QApplication.instance()
|
||||||
app.removeTranslator(app.translator)
|
app.removeTranslator(app.translator)
|
||||||
app.translator.load(curr_directory() + '/translations/' + path)
|
app.translator.load(curr_directory() + '/translations/' + path)
|
||||||
app.installTranslator(app.translator)
|
app.installTranslator(app.translator)
|
||||||
|
settings['message_font_size'] = self.messages_font_size.currentIndex() + 10
|
||||||
|
Profile.get_instance().update()
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
|
|
||||||
class AudioSettings(CenteredWidget):
|
class AudioSettings(CenteredWidget):
|
||||||
|
"""
|
||||||
|
Audio calls settings form
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(AudioSettings, self).__init__()
|
super(AudioSettings, self).__init__()
|
||||||
@ -593,6 +675,9 @@ class AudioSettings(CenteredWidget):
|
|||||||
|
|
||||||
|
|
||||||
class PluginsSettings(CenteredWidget):
|
class PluginsSettings(CenteredWidget):
|
||||||
|
"""
|
||||||
|
Plugins settings form
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(PluginsSettings, self).__init__()
|
super(PluginsSettings, self).__init__()
|
||||||
@ -633,8 +718,10 @@ class PluginsSettings(CenteredWidget):
|
|||||||
self.window.show()
|
self.window.show()
|
||||||
else:
|
else:
|
||||||
msgBox = QtGui.QMessageBox()
|
msgBox = QtGui.QMessageBox()
|
||||||
text = (QtGui.QApplication.translate("PluginsForm", 'No GUI found for this plugin', None,
|
text = QtGui.QApplication.translate("PluginsForm", 'No GUI found for this plugin', None,
|
||||||
QtGui.QApplication.UnicodeUTF8))
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
msgBox.setWindowTitle(QtGui.QApplication.translate("PluginsForm", 'Error', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8))
|
||||||
msgBox.setText(text)
|
msgBox.setText(text)
|
||||||
msgBox.exec_()
|
msgBox.exec_()
|
||||||
|
|
||||||
@ -646,13 +733,18 @@ class PluginsSettings(CenteredWidget):
|
|||||||
|
|
||||||
def show_data(self):
|
def show_data(self):
|
||||||
ind = self.comboBox.currentIndex()
|
ind = self.comboBox.currentIndex()
|
||||||
plugin = self.data[ind]
|
if len(self.data):
|
||||||
descr = plugin[2] or QtGui.QApplication.translate("PluginsForm", "No description available", None, QtGui.QApplication.UnicodeUTF8)
|
plugin = self.data[ind]
|
||||||
self.label.setText(descr)
|
descr = plugin[2] or QtGui.QApplication.translate("PluginsForm", "No description available", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
if plugin[1]:
|
self.label.setText(descr)
|
||||||
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
if plugin[1]:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Disable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
else:
|
||||||
|
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
else:
|
else:
|
||||||
self.button.setText(QtGui.QApplication.translate("PluginsForm", "Enable plugin", None, QtGui.QApplication.UnicodeUTF8))
|
self.open.setVisible(False)
|
||||||
|
self.button.setVisible(False)
|
||||||
|
self.label.setText(QtGui.QApplication.translate("PluginsForm", "No plugins found", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
def button_click(self):
|
def button_click(self):
|
||||||
ind = self.comboBox.currentIndex()
|
ind = self.comboBox.currentIndex()
|
||||||
|
@ -4,17 +4,8 @@ MESSAGE_TYPE = {
|
|||||||
'TEXT': 0,
|
'TEXT': 0,
|
||||||
'ACTION': 1,
|
'ACTION': 1,
|
||||||
'FILE_TRANSFER': 2,
|
'FILE_TRANSFER': 2,
|
||||||
'INLINE': 3
|
'INLINE': 3,
|
||||||
}
|
'INFO_MESSAGE': 4
|
||||||
|
|
||||||
FILE_TRANSFER_MESSAGE_STATUS = {
|
|
||||||
'FINISHED': 0,
|
|
||||||
'CANCELLED': 1,
|
|
||||||
'OUTGOING': 2,
|
|
||||||
'INCOMING_NOT_STARTED': 3,
|
|
||||||
'INCOMING_STARTED': 4,
|
|
||||||
'PAUSED_BY_FRIEND': 5,
|
|
||||||
'PAUSED_BY_USER': 6
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -31,6 +22,9 @@ class Message(object):
|
|||||||
def get_owner(self):
|
def get_owner(self):
|
||||||
return self._owner
|
return self._owner
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
self._owner = 0
|
||||||
|
|
||||||
|
|
||||||
class TextMessage(Message):
|
class TextMessage(Message):
|
||||||
"""
|
"""
|
||||||
@ -58,7 +52,7 @@ class TransferMessage(Message):
|
|||||||
self._friend_number, self._file_number = friend_number, file_number
|
self._friend_number, self._file_number = friend_number, file_number
|
||||||
|
|
||||||
def is_active(self, file_number):
|
def is_active(self, file_number):
|
||||||
return self._file_number == file_number and self._status > 1
|
return self._file_number == file_number and self._status not in (2, 3)
|
||||||
|
|
||||||
def get_friend_number(self):
|
def get_friend_number(self):
|
||||||
return self._friend_number
|
return self._friend_number
|
||||||
@ -76,6 +70,18 @@ class TransferMessage(Message):
|
|||||||
return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status
|
return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status
|
||||||
|
|
||||||
|
|
||||||
|
class UnsentFile(Message):
|
||||||
|
def __init__(self, path, data, time):
|
||||||
|
super(UnsentFile, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], 0, time)
|
||||||
|
self._data, self._path = data, path
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
return self._path, self._data, self._time
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class InlineImage(Message):
|
class InlineImage(Message):
|
||||||
"""
|
"""
|
||||||
Inline image
|
Inline image
|
||||||
@ -87,3 +93,9 @@ class InlineImage(Message):
|
|||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
|
|
||||||
|
class InfoMessage(TextMessage):
|
||||||
|
|
||||||
|
def __init__(self, message, time):
|
||||||
|
super(InfoMessage, self).__init__(message, None, time, MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from widgets import CenteredWidget
|
from widgets import CenteredWidget, LineEdit
|
||||||
try:
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
|
||||||
class PasswordArea(QtGui.QLineEdit):
|
class PasswordArea(LineEdit):
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super(PasswordArea, self).__init__(parent)
|
super(PasswordArea, self).__init__(parent)
|
||||||
|
@ -29,6 +29,9 @@ class PluginLoader(util.Singleton):
|
|||||||
Load all plugins in plugins folder
|
Load all plugins in plugins folder
|
||||||
"""
|
"""
|
||||||
path = util.curr_directory() + '/plugins/'
|
path = util.curr_directory() + '/plugins/'
|
||||||
|
if not os.path.exists(path):
|
||||||
|
util.log('Plugin dir not found')
|
||||||
|
return
|
||||||
files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
|
files = [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]
|
||||||
for fl in files:
|
for fl in files:
|
||||||
if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'):
|
if fl in ('plugin_super_class.py', '__init__.py') or not fl.endswith('.py'):
|
||||||
@ -76,6 +79,9 @@ class PluginLoader(util.Singleton):
|
|||||||
self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
|
self._plugins[name][0].lossy_packet(''.join(chr(x) for x in data[l + 1:length]), friend_number)
|
||||||
|
|
||||||
def friend_online(self, friend_number):
|
def friend_online(self, friend_number):
|
||||||
|
"""
|
||||||
|
Friend with specified number is online
|
||||||
|
"""
|
||||||
for elem in self._plugins.values():
|
for elem in self._plugins.values():
|
||||||
if elem[1]:
|
if elem[1]:
|
||||||
elem[0].friend_connected(friend_number)
|
elem[0].friend_connected(friend_number)
|
||||||
|
@ -125,11 +125,12 @@ class PluginSuperClass(object):
|
|||||||
New command. On 'help' this method should provide user list of available commands
|
New command. On 'help' this method should provide user list of available commands
|
||||||
:param command: string with command
|
:param command: string with command
|
||||||
"""
|
"""
|
||||||
msgbox = QtGui.QMessageBox()
|
if command == 'help':
|
||||||
title = QtGui.QApplication.translate("PluginWindow", "List of commands for plugin {}", None, QtGui.QApplication.UnicodeUTF8)
|
msgbox = QtGui.QMessageBox()
|
||||||
msgbox.setWindowTitle(title.format(self._name))
|
title = QtGui.QApplication.translate("PluginWindow", "List of commands for plugin {}", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
msgbox.setText(QtGui.QApplication.translate("PluginWindow", "No commands available", None, QtGui.QApplication.UnicodeUTF8))
|
msgbox.setWindowTitle(title.format(self._name))
|
||||||
msgbox.exec_()
|
msgbox.setText(QtGui.QApplication.translate("PluginWindow", "No commands available", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
msgbox.exec_()
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Translations support
|
# Translations support
|
||||||
@ -142,11 +143,11 @@ class PluginSuperClass(object):
|
|||||||
app = QtGui.QApplication.instance()
|
app = QtGui.QApplication.instance()
|
||||||
langs = self._settings.supported_languages()
|
langs = self._settings.supported_languages()
|
||||||
curr_lang = self._settings['language']
|
curr_lang = self._settings['language']
|
||||||
if curr_lang in map(lambda x: x[0], langs):
|
if curr_lang in langs:
|
||||||
if self._translator is not None:
|
if self._translator is not None:
|
||||||
app.removeTranslator(self._translator)
|
app.removeTranslator(self._translator)
|
||||||
self._translator = QtCore.QTranslator()
|
self._translator = QtCore.QTranslator()
|
||||||
lang_path = filter(lambda x: x[0] == curr_lang, langs)[0][1]
|
lang_path = langs[curr_lang]
|
||||||
self._translator.load(path_to_data(self._short_name) + lang_path)
|
self._translator.load(path_to_data(self._short_name) + lang_path)
|
||||||
app.installTranslator(self._translator)
|
app.installTranslator(self._translator)
|
||||||
|
|
||||||
|
628
src/profile.py
@ -1,11 +1,9 @@
|
|||||||
from list_items import MessageItem, ContactItem, FileTransferItem, InlineImageItem
|
from list_items import *
|
||||||
try:
|
try:
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
from tox import Tox
|
from friend import *
|
||||||
import os
|
|
||||||
from messages import *
|
|
||||||
from settings import *
|
from settings import *
|
||||||
from toxcore_enums_and_consts import *
|
from toxcore_enums_and_consts import *
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
@ -19,269 +17,7 @@ import avwidgets
|
|||||||
import plugin_support
|
import plugin_support
|
||||||
|
|
||||||
|
|
||||||
class Contact(object):
|
class Profile(contact.Contact, Singleton):
|
||||||
"""
|
|
||||||
Class encapsulating TOX contact
|
|
||||||
Properties: name (alias of contact or name), status_message, status (connection status)
|
|
||||||
widget - widget for update
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name, status_message, widget, tox_id):
|
|
||||||
"""
|
|
||||||
:param name: name, example: 'Toxygen user'
|
|
||||||
:param status_message: status message, example: 'Toxing on toxygen'
|
|
||||||
:param widget: ContactItem instance
|
|
||||||
:param tox_id: tox id of contact
|
|
||||||
"""
|
|
||||||
self._name, self._status_message = name, status_message
|
|
||||||
self._status, self._widget = None, widget
|
|
||||||
self._widget.name.setText(name)
|
|
||||||
self._widget.status_message.setText(status_message)
|
|
||||||
self._tox_id = tox_id
|
|
||||||
self.load_avatar()
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# name - current name or alias of user
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
def set_name(self, value):
|
|
||||||
self._name = value.decode('utf-8')
|
|
||||||
self._widget.name.setText(self._name)
|
|
||||||
self._widget.name.repaint()
|
|
||||||
|
|
||||||
name = property(get_name, set_name)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Status message
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_status_message(self):
|
|
||||||
return self._status_message
|
|
||||||
|
|
||||||
def set_status_message(self, value):
|
|
||||||
self._status_message = value.decode('utf-8')
|
|
||||||
self._widget.status_message.setText(self._status_message)
|
|
||||||
self._widget.status_message.repaint()
|
|
||||||
|
|
||||||
status_message = property(get_status_message, set_status_message)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Status
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_status(self):
|
|
||||||
return self._status
|
|
||||||
|
|
||||||
def set_status(self, value):
|
|
||||||
self._widget.connection_status.data = self._status = value
|
|
||||||
self._widget.connection_status.repaint()
|
|
||||||
|
|
||||||
status = property(get_status, set_status)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# TOX ID. WARNING: for friend it will return public key, for profile - full address
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_tox_id(self):
|
|
||||||
return self._tox_id
|
|
||||||
|
|
||||||
tox_id = property(get_tox_id)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Avatars
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def load_avatar(self):
|
|
||||||
"""
|
|
||||||
Tries to load avatar of contact or uses default avatar
|
|
||||||
"""
|
|
||||||
avatar_path = '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
|
||||||
os.chdir(ProfileHelper.get_path() + 'avatars/')
|
|
||||||
if not os.path.isfile(avatar_path): # load default image
|
|
||||||
avatar_path = 'avatar.png'
|
|
||||||
os.chdir(curr_directory() + '/images/')
|
|
||||||
pixmap = QtGui.QPixmap(QtCore.QSize(64, 64))
|
|
||||||
pixmap.load(avatar_path)
|
|
||||||
self._widget.avatar_label.setScaledContents(False)
|
|
||||||
self._widget.avatar_label.setPixmap(pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio))
|
|
||||||
self._widget.avatar_label.repaint()
|
|
||||||
|
|
||||||
def reset_avatar(self):
|
|
||||||
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
|
||||||
if os.path.isfile(avatar_path):
|
|
||||||
os.remove(avatar_path)
|
|
||||||
self.load_avatar()
|
|
||||||
|
|
||||||
def set_avatar(self, avatar):
|
|
||||||
avatar_path = (ProfileHelper.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
|
||||||
with open(avatar_path, 'wb') as f:
|
|
||||||
f.write(avatar)
|
|
||||||
self.load_avatar()
|
|
||||||
|
|
||||||
def get_pixmap(self):
|
|
||||||
return self._widget.avatar_label.pixmap()
|
|
||||||
|
|
||||||
|
|
||||||
class Friend(Contact):
|
|
||||||
"""
|
|
||||||
Friend in list of friends. Can be hidden, properties 'has unread messages' and 'has alias' added
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, message_getter, number, *args):
|
|
||||||
"""
|
|
||||||
:param message_getter: gets messages from db
|
|
||||||
:param number: number of friend.
|
|
||||||
"""
|
|
||||||
super(Friend, self).__init__(*args)
|
|
||||||
self._number = number
|
|
||||||
self._new_messages = False
|
|
||||||
self._visible = True
|
|
||||||
self._alias = False
|
|
||||||
self._message_getter = message_getter
|
|
||||||
self._corr = []
|
|
||||||
self._unsaved_messages = 0
|
|
||||||
self._history_loaded = False
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.set_visibility(False)
|
|
||||||
del self._widget
|
|
||||||
if hasattr(self, '_message_getter'):
|
|
||||||
del self._message_getter
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# History support
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def load_corr(self, first_time=True):
|
|
||||||
"""
|
|
||||||
:param first_time: friend became active, load first part of messages
|
|
||||||
"""
|
|
||||||
if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')):
|
|
||||||
return
|
|
||||||
data = self._message_getter.get(PAGE_SIZE)
|
|
||||||
if data is not None and len(data):
|
|
||||||
data.reverse()
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
data = map(lambda tupl: TextMessage(*tupl), data)
|
|
||||||
self._corr = data + self._corr
|
|
||||||
self._history_loaded = True
|
|
||||||
|
|
||||||
def get_corr_for_saving(self):
|
|
||||||
"""
|
|
||||||
Get data to save in db
|
|
||||||
:return: list of unsaved messages or []
|
|
||||||
"""
|
|
||||||
if hasattr(self, '_message_getter'):
|
|
||||||
del self._message_getter
|
|
||||||
messages = filter(lambda x: x.get_type() <= 1, self._corr)
|
|
||||||
return map(lambda x: x.get_data(), messages[-self._unsaved_messages:]) if self._unsaved_messages else []
|
|
||||||
|
|
||||||
def get_corr(self):
|
|
||||||
return self._corr[:]
|
|
||||||
|
|
||||||
def append_message(self, message):
|
|
||||||
"""
|
|
||||||
:param message: tuple (message, owner, unix_time, message_type)
|
|
||||||
"""
|
|
||||||
self._corr.append(message)
|
|
||||||
if message.get_type() <= 1:
|
|
||||||
self._unsaved_messages += 1
|
|
||||||
|
|
||||||
def get_last_message_text(self):
|
|
||||||
messages = filter(lambda x: x.get_type() <= 1 and not x.get_owner(), self._corr)
|
|
||||||
if messages:
|
|
||||||
return messages[-1].get_data()[0]
|
|
||||||
else:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def last_message_owner(self):
|
|
||||||
messages = filter(lambda x: x.get_type() <= 1, self._corr)
|
|
||||||
if messages:
|
|
||||||
return messages[-1].get_owner()
|
|
||||||
else:
|
|
||||||
return -1
|
|
||||||
|
|
||||||
def clear_corr(self):
|
|
||||||
"""
|
|
||||||
Clear messages list
|
|
||||||
"""
|
|
||||||
if hasattr(self, '_message_getter'):
|
|
||||||
del self._message_getter
|
|
||||||
self._corr = filter(lambda x: x.get_type() == 2 and x.get_status() >= 2, self._corr)
|
|
||||||
self._unsaved_messages = 0
|
|
||||||
|
|
||||||
def update_transfer_data(self, file_number, status, inline=None):
|
|
||||||
"""
|
|
||||||
Update status of active transfer and load inline if needed
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
tr = filter(lambda x: x.get_type() == 2 and x.is_active(file_number), self._corr)[0]
|
|
||||||
tr.set_status(status)
|
|
||||||
if inline: # inline was loaded
|
|
||||||
i = self._corr.index(tr)
|
|
||||||
self._corr.insert(i, inline)
|
|
||||||
return i - len(self._corr)
|
|
||||||
except Exception as ex:
|
|
||||||
log('Update transfer data failed: ' + str(ex))
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Alias support
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def set_name(self, value):
|
|
||||||
"""
|
|
||||||
Set new name or ignore if alias exists
|
|
||||||
:param value: new name
|
|
||||||
"""
|
|
||||||
if not self._alias:
|
|
||||||
super(self.__class__, self).set_name(value)
|
|
||||||
|
|
||||||
def set_alias(self, alias):
|
|
||||||
self._alias = bool(alias)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Visibility in friends' list
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_visibility(self):
|
|
||||||
return self._visible
|
|
||||||
|
|
||||||
def set_visibility(self, value):
|
|
||||||
self._visible = value
|
|
||||||
|
|
||||||
visibility = property(get_visibility, set_visibility)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Unread messages from friend
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_messages(self):
|
|
||||||
return self._new_messages
|
|
||||||
|
|
||||||
def set_messages(self, value):
|
|
||||||
self._widget.connection_status.messages = self._new_messages = value
|
|
||||||
self._widget.connection_status.repaint()
|
|
||||||
|
|
||||||
messages = property(get_messages, set_messages)
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
# Friend's number (can be used in toxcore)
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def get_number(self):
|
|
||||||
return self._number
|
|
||||||
|
|
||||||
def set_number(self, value):
|
|
||||||
self._number = value
|
|
||||||
|
|
||||||
number = property(get_number, set_number)
|
|
||||||
|
|
||||||
|
|
||||||
class Profile(Contact, Singleton):
|
|
||||||
"""
|
"""
|
||||||
Profile of current toxygen user. Contains friends list, tox instance
|
Profile of current toxygen user. Contains friends list, tox instance
|
||||||
"""
|
"""
|
||||||
@ -337,17 +73,29 @@ class Profile(Contact, Singleton):
|
|||||||
|
|
||||||
def set_status(self, status):
|
def set_status(self, status):
|
||||||
super(Profile, self).set_status(status)
|
super(Profile, self).set_status(status)
|
||||||
self._tox.self_set_status(status)
|
if status is not None:
|
||||||
|
self._tox.self_set_status(status)
|
||||||
|
|
||||||
def set_name(self, value):
|
def set_name(self, value):
|
||||||
super(self.__class__, self).set_name(value)
|
tmp = self.name
|
||||||
|
super(Profile, self).set_name(value)
|
||||||
self._tox.self_set_name(self._name.encode('utf-8'))
|
self._tox.self_set_name(self._name.encode('utf-8'))
|
||||||
|
if tmp != value:
|
||||||
|
message = QtGui.QApplication.translate("MainWindow", 'User {} is now known as {}', None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
message = message.format(tmp, value)
|
||||||
|
for friend in self._friends:
|
||||||
|
friend.append_message(InfoMessage(message, time.time()))
|
||||||
|
if self._active_friend + 1:
|
||||||
|
self.create_message_item(message, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
def set_status_message(self, value):
|
def set_status_message(self, value):
|
||||||
super(self.__class__, self).set_status_message(value)
|
super(Profile, 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):
|
def new_nospam(self):
|
||||||
|
"""Sets new nospam part of tox id"""
|
||||||
import random
|
import random
|
||||||
self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32
|
self._tox.self_set_nospam(random.randint(0, 4294967295)) # no spam - uint32
|
||||||
self._tox_id = self._tox.self_get_address()
|
self._tox_id = self._tox.self_get_address()
|
||||||
@ -366,6 +114,7 @@ class Profile(Contact, Singleton):
|
|||||||
filter_str = filter_str.lower()
|
filter_str = filter_str.lower()
|
||||||
for index, friend in enumerate(self._friends):
|
for index, friend in enumerate(self._friends):
|
||||||
friend.visibility = (friend.status is not None or not show_online) and (filter_str in friend.name.lower())
|
friend.visibility = (friend.status is not None or not show_online) and (filter_str in friend.name.lower())
|
||||||
|
friend.visibility = friend.visibility or friend.messages or friend.actions
|
||||||
if friend.visibility:
|
if friend.visibility:
|
||||||
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 70))
|
self._screen.friends_list.item(index).setSizeHint(QtCore.QSize(250, 70))
|
||||||
else:
|
else:
|
||||||
@ -413,10 +162,15 @@ class Profile(Contact, Singleton):
|
|||||||
self.send_typing(False)
|
self.send_typing(False)
|
||||||
self._screen.typing.setVisible(False)
|
self._screen.typing.setVisible(False)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
|
if self._active_friend + 1:
|
||||||
|
try:
|
||||||
|
self._friends[self._active_friend].curr_text = self._screen.messageEdit.toPlainText()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
self._active_friend = value
|
self._active_friend = value
|
||||||
friend = self._friends[value]
|
friend = self._friends[value]
|
||||||
self._friends[value].set_messages(False)
|
self._friends[value].reset_messages()
|
||||||
self._screen.messageEdit.clear()
|
self._screen.messageEdit.setPlainText(friend.curr_text)
|
||||||
self._messages.clear()
|
self._messages.clear()
|
||||||
friend.load_corr()
|
friend.load_corr()
|
||||||
messages = friend.get_corr()[-PAGE_SIZE:]
|
messages = friend.get_corr()[-PAGE_SIZE:]
|
||||||
@ -425,19 +179,28 @@ class Profile(Contact, Singleton):
|
|||||||
data = message.get_data()
|
data = message.get_data()
|
||||||
self.create_message_item(data[0],
|
self.create_message_item(data[0],
|
||||||
convert_time(data[2]),
|
convert_time(data[2]),
|
||||||
friend.name if data[1] else self._name,
|
data[1],
|
||||||
data[3])
|
data[3])
|
||||||
elif message.get_type() == 2:
|
elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
|
||||||
|
if message.get_status() is None:
|
||||||
|
self.create_unsent_file_item(message)
|
||||||
|
continue
|
||||||
item = self.create_file_transfer_item(message)
|
item = self.create_file_transfer_item(message)
|
||||||
if message.get_status() >= 2: # active file transfer
|
if message.get_status() in ACTIVE_FILE_TRANSFERS: # active file transfer
|
||||||
try:
|
try:
|
||||||
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())]
|
||||||
ft.set_state_changed_handler(item.update)
|
ft.set_state_changed_handler(item.update)
|
||||||
ft.signal()
|
ft.signal()
|
||||||
except:
|
except:
|
||||||
print 'Incoming not started transfer - no info found'
|
print 'Incoming not started transfer - no info found'
|
||||||
else: # inline
|
elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline
|
||||||
self.create_inline_item(message.get_data())
|
self.create_inline_item(message.get_data())
|
||||||
|
else: # info message
|
||||||
|
data = message.get_data()
|
||||||
|
self.create_message_item(data[0],
|
||||||
|
convert_time(data[2]),
|
||||||
|
'',
|
||||||
|
data[3])
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
if value in self._call:
|
if value in self._call:
|
||||||
self._screen.active_call()
|
self._screen.active_call()
|
||||||
@ -477,18 +240,54 @@ 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 new_name(self, number, name):
|
||||||
|
friend = self.get_friend_by_number(number)
|
||||||
|
tmp = friend.name
|
||||||
|
friend.set_name(name)
|
||||||
|
name = name.decode('utf-8')
|
||||||
|
if friend.name == name and tmp != name:
|
||||||
|
message = QtGui.QApplication.translate("MainWindow", 'User {} is now known as {}', None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
message = message.format(tmp, name)
|
||||||
|
friend.append_message(InfoMessage(message, time.time()))
|
||||||
|
friend.actions = True
|
||||||
|
if number == self.get_active_number():
|
||||||
|
self.create_message_item(message, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
self._messages.scrollToBottom()
|
||||||
|
self.set_active(None)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
if self._active_friend + 1:
|
if self._active_friend + 1:
|
||||||
self.set_active(self._active_friend)
|
self.set_active(self._active_friend)
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
# Friend connection status callbacks
|
||||||
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def send_files(self, friend_number):
|
||||||
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
files = friend.get_unsent_files()
|
||||||
|
try:
|
||||||
|
for fl in files:
|
||||||
|
data = fl.get_data()
|
||||||
|
if data[1] is not None:
|
||||||
|
self.send_inline(data[1], data[0], friend_number, True)
|
||||||
|
else:
|
||||||
|
self.send_file(data[0], friend_number, True)
|
||||||
|
friend.clear_unsent_files()
|
||||||
|
if friend_number == self.get_active_number():
|
||||||
|
self.update()
|
||||||
|
except Exception as ex:
|
||||||
|
print 'Exception in file sending: ' + str(ex)
|
||||||
|
|
||||||
def friend_exit(self, friend_number):
|
def friend_exit(self, friend_number):
|
||||||
|
"""
|
||||||
|
Friend with specified number quit
|
||||||
|
"""
|
||||||
|
# TODO: fix and add full file resuming support
|
||||||
self.get_friend_by_number(friend_number).status = None
|
self.get_friend_by_number(friend_number).status = None
|
||||||
self.friend_typing(friend_number, False)
|
self.friend_typing(friend_number, False)
|
||||||
if friend_number in self._call:
|
if friend_number in self._call:
|
||||||
self._call.finish_call(friend_number, True)
|
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
|
||||||
@ -498,7 +297,7 @@ class Profile(Contact, Singleton):
|
|||||||
"""
|
"""
|
||||||
Send typing notification to a friend
|
Send typing notification to a friend
|
||||||
"""
|
"""
|
||||||
if Settings.get_instance()['typing_notifications']:
|
if Settings.get_instance()['typing_notifications'] and self._active_friend + 1:
|
||||||
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)
|
||||||
@ -514,6 +313,24 @@ class Profile(Contact, Singleton):
|
|||||||
# Private messages
|
# Private messages
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def receipt(self):
|
||||||
|
i = 0
|
||||||
|
while i < self._messages.count() and not self._messages.itemWidget(self._messages.item(i)).mark_as_sent():
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def send_messages(self, friend_number):
|
||||||
|
"""
|
||||||
|
Send 'offline' messages to friend
|
||||||
|
"""
|
||||||
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
friend.load_corr()
|
||||||
|
messages = friend.unsent_messages()
|
||||||
|
try:
|
||||||
|
for message in messages:
|
||||||
|
self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8'))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
def split_and_send(self, number, message_type, message):
|
def split_and_send(self, number, message_type, message):
|
||||||
"""
|
"""
|
||||||
Message splitting
|
Message splitting
|
||||||
@ -545,16 +362,17 @@ class Profile(Contact, Singleton):
|
|||||||
:param message: text of message
|
:param message: text of message
|
||||||
"""
|
"""
|
||||||
if friend_num == self.get_active_number(): # add message to list
|
if friend_num == self.get_active_number(): # add message to list
|
||||||
user_name = Profile.get_instance().get_active_name()
|
self.create_message_item(message.decode('utf-8'), curr_time(), MESSAGE_OWNER['FRIEND'], message_type)
|
||||||
self.create_message_item(message.decode('utf-8'), curr_time(), user_name, message_type)
|
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
self._friends[self._active_friend].append_message(
|
self._friends[self._active_friend].append_message(
|
||||||
TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type))
|
TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type))
|
||||||
else:
|
else:
|
||||||
friend = self.get_friend_by_number(friend_num)
|
friend = self.get_friend_by_number(friend_num)
|
||||||
friend.set_messages(True)
|
friend.inc_messages()
|
||||||
friend.append_message(
|
friend.append_message(
|
||||||
TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type))
|
TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type))
|
||||||
|
if not friend.visibility:
|
||||||
|
self.update_filtration()
|
||||||
|
|
||||||
def send_message(self, text):
|
def send_message(self, text):
|
||||||
"""
|
"""
|
||||||
@ -564,18 +382,20 @@ class Profile(Contact, Singleton):
|
|||||||
if text.startswith('/plugin '):
|
if text.startswith('/plugin '):
|
||||||
plugin_support.PluginLoader.get_instance().command(text[8:])
|
plugin_support.PluginLoader.get_instance().command(text[8:])
|
||||||
self._screen.messageEdit.clear()
|
self._screen.messageEdit.clear()
|
||||||
elif self.is_active_online() and text:
|
elif text and self._active_friend + 1:
|
||||||
if text.startswith('/me '):
|
if text.startswith('/me '):
|
||||||
message_type = TOX_MESSAGE_TYPE['ACTION']
|
message_type = TOX_MESSAGE_TYPE['ACTION']
|
||||||
text = text[4:]
|
text = text[4:]
|
||||||
else:
|
else:
|
||||||
message_type = TOX_MESSAGE_TYPE['NORMAL']
|
message_type = TOX_MESSAGE_TYPE['NORMAL']
|
||||||
friend = self._friends[self._active_friend]
|
friend = self._friends[self._active_friend]
|
||||||
self.split_and_send(friend.number, message_type, text.encode('utf-8'))
|
friend.inc_receipts()
|
||||||
self.create_message_item(text, curr_time(), self._name, message_type)
|
if friend.status is not None:
|
||||||
|
self.split_and_send(friend.number, message_type, text.encode('utf-8'))
|
||||||
|
self.create_message_item(text, curr_time(), MESSAGE_OWNER['NOT_SENT'], message_type)
|
||||||
self._screen.messageEdit.clear()
|
self._screen.messageEdit.clear()
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
friend.append_message(TextMessage(text, MESSAGE_OWNER['ME'], time.time(), message_type))
|
friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], time.time(), message_type))
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# History support
|
# History support
|
||||||
@ -592,10 +412,16 @@ class Profile(Contact, Singleton):
|
|||||||
if not self._history.friend_exists_in_db(friend.tox_id):
|
if not self._history.friend_exists_in_db(friend.tox_id):
|
||||||
self._history.add_friend_to_db(friend.tox_id)
|
self._history.add_friend_to_db(friend.tox_id)
|
||||||
self._history.save_messages_to_db(friend.tox_id, messages)
|
self._history.save_messages_to_db(friend.tox_id, messages)
|
||||||
|
unsent_messages = friend.unsent_messages()
|
||||||
|
unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1
|
||||||
|
self._history.update_messages(friend.tox_id, unsent_time)
|
||||||
self._history.save()
|
self._history.save()
|
||||||
del self._history
|
del self._history
|
||||||
|
|
||||||
def clear_history(self, num=None):
|
def clear_history(self, num=None):
|
||||||
|
"""
|
||||||
|
Clear chat history
|
||||||
|
"""
|
||||||
if num is not None:
|
if num is not None:
|
||||||
friend = self._friends[num]
|
friend = self._friends[num]
|
||||||
friend.clear_corr()
|
friend.clear_corr()
|
||||||
@ -621,18 +447,33 @@ class Profile(Contact, Singleton):
|
|||||||
data.reverse()
|
data.reverse()
|
||||||
data = data[self._messages.count():self._messages.count() + PAGE_SIZE]
|
data = data[self._messages.count():self._messages.count() + PAGE_SIZE]
|
||||||
for message in data:
|
for message in data:
|
||||||
if message.get_type() <= 1:
|
if message.get_type() <= 1: # text message
|
||||||
data = message.get_data()
|
data = message.get_data()
|
||||||
self.create_message_item(data[0],
|
self.create_message_item(data[0],
|
||||||
convert_time(data[2]),
|
convert_time(data[2]),
|
||||||
friend.name if data[1] else self._name,
|
data[1],
|
||||||
data[3],
|
data[3],
|
||||||
False)
|
False)
|
||||||
elif message.get_type() == 2:
|
elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']:
|
||||||
|
if message.get_status() is None:
|
||||||
|
self.create_unsent_file_item(message)
|
||||||
|
continue
|
||||||
item = self.create_file_transfer_item(message, False)
|
item = self.create_file_transfer_item(message, False)
|
||||||
if message.get_status() >= 2:
|
if message.get_status() in ACTIVE_FILE_TRANSFERS: # 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'
|
||||||
|
elif message.get_type() == MESSAGE_TYPE['INLINE']: # inline
|
||||||
|
self.create_inline_item(message.get_data())
|
||||||
|
else: # info message
|
||||||
|
data = message.get_data()
|
||||||
|
self.create_message_item(data[0],
|
||||||
|
convert_time(data[2]),
|
||||||
|
'',
|
||||||
|
data[3])
|
||||||
|
|
||||||
def export_history(self, directory):
|
def export_history(self, directory):
|
||||||
self._history.export(directory)
|
self._history.export(directory)
|
||||||
@ -653,8 +494,14 @@ class Profile(Contact, Singleton):
|
|||||||
self._screen.friends_list.setItemWidget(elem, item)
|
self._screen.friends_list.setItemWidget(elem, item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def create_message_item(self, text, time, name, message_type, append=True):
|
def create_message_item(self, text, time, owner, message_type, append=True):
|
||||||
item = MessageItem(text, time, name, message_type, self._messages)
|
if message_type == MESSAGE_TYPE['INFO_MESSAGE']:
|
||||||
|
name = ''
|
||||||
|
elif owner == MESSAGE_OWNER['FRIEND']:
|
||||||
|
name = self.get_active_name()
|
||||||
|
else:
|
||||||
|
name = self._name
|
||||||
|
item = MessageItem(text, time, name, owner != MESSAGE_OWNER['NOT_SENT'], message_type, self._messages)
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
||||||
if append:
|
if append:
|
||||||
@ -677,9 +524,24 @@ class Profile(Contact, Singleton):
|
|||||||
self._messages.setItemWidget(elem, item)
|
self._messages.setItemWidget(elem, item)
|
||||||
return item
|
return item
|
||||||
|
|
||||||
def create_inline_item(self, data, append=True):
|
def create_unsent_file_item(self, message, append=True):
|
||||||
item = InlineImageItem(data, self._messages.width())
|
data = message.get_data()
|
||||||
|
item = UnsentFileItem(os.path.basename(data[0]),
|
||||||
|
os.path.getsize(data[0]) if data[1] is None else len(data[1]),
|
||||||
|
self.name,
|
||||||
|
data[2],
|
||||||
|
self._messages.width())
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
|
elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34))
|
||||||
|
if append:
|
||||||
|
self._messages.addItem(elem)
|
||||||
|
else:
|
||||||
|
self._messages.insertItem(0, elem)
|
||||||
|
self._messages.setItemWidget(elem, item)
|
||||||
|
|
||||||
|
def create_inline_item(self, data, append=True):
|
||||||
|
elem = QtGui.QListWidgetItem()
|
||||||
|
item = InlineImageItem(data, self._messages.width(), elem)
|
||||||
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
||||||
if append:
|
if append:
|
||||||
self._messages.addItem(elem)
|
self._messages.addItem(elem)
|
||||||
@ -692,6 +554,9 @@ class Profile(Contact, Singleton):
|
|||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def set_alias(self, num):
|
def set_alias(self, num):
|
||||||
|
"""
|
||||||
|
Set new alias for friend
|
||||||
|
"""
|
||||||
friend = self._friends[num]
|
friend = self._friends[num]
|
||||||
name = friend.name.encode('utf-8')
|
name = friend.name.encode('utf-8')
|
||||||
dialog = QtGui.QApplication.translate('MainWindow',
|
dialog = QtGui.QApplication.translate('MainWindow',
|
||||||
@ -701,10 +566,11 @@ class Profile(Contact, Singleton):
|
|||||||
title = QtGui.QApplication.translate('MainWindow',
|
title = QtGui.QApplication.translate('MainWindow',
|
||||||
'Set alias',
|
'Set alias',
|
||||||
None, QtGui.QApplication.UnicodeUTF8)
|
None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
|
||||||
text, ok = QtGui.QInputDialog.getText(None,
|
text, ok = QtGui.QInputDialog.getText(None,
|
||||||
title,
|
title,
|
||||||
dialog)
|
dialog,
|
||||||
|
QtGui.QLineEdit.Normal,
|
||||||
|
name.decode('utf-8'))
|
||||||
if ok:
|
if ok:
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
aliases = settings['friends_aliases']
|
aliases = settings['friends_aliases']
|
||||||
@ -736,13 +602,15 @@ 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]
|
||||||
|
settings = Settings.get_instance()
|
||||||
try:
|
try:
|
||||||
settings = Settings.get_instance()
|
|
||||||
index = map(lambda x: x[0], settings['friends_aliases']).index(friend.tox_id)
|
index = map(lambda x: x[0], settings['friends_aliases']).index(friend.tox_id)
|
||||||
del settings['friends_aliases'][index]
|
del settings['friends_aliases'][index]
|
||||||
settings.save()
|
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
if friend.tox_id in settings['notes']:
|
||||||
|
del settings['notes'][friend.tox_id]
|
||||||
|
settings.save()
|
||||||
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)
|
||||||
@ -758,6 +626,9 @@ class Profile(Contact, Singleton):
|
|||||||
ProfileHelper.get_instance().save_profile(data)
|
ProfileHelper.get_instance().save_profile(data)
|
||||||
|
|
||||||
def add_friend(self, tox_id):
|
def add_friend(self, tox_id):
|
||||||
|
"""
|
||||||
|
Adds friend to list
|
||||||
|
"""
|
||||||
num = self._tox.friend_add_norequest(tox_id) # num - friend number
|
num = self._tox.friend_add_norequest(tox_id) # num - friend number
|
||||||
item = self.create_friend_item()
|
item = self.create_friend_item()
|
||||||
try:
|
try:
|
||||||
@ -881,7 +752,7 @@ class Profile(Contact, Singleton):
|
|||||||
friend.status = None
|
friend.status = None
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if hasattr(self, '_stop'):
|
if hasattr(self, '_call'):
|
||||||
self._call.stop()
|
self._call.stop()
|
||||||
del self._call
|
del self._call
|
||||||
|
|
||||||
@ -900,12 +771,12 @@ class Profile(Contact, Singleton):
|
|||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
friend = self.get_friend_by_number(friend_number)
|
friend = self.get_friend_by_number(friend_number)
|
||||||
auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
|
auto = settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
|
||||||
inline = (file_name == 'toxygen_inline.png' or file_name == 'utox-inline.png') and settings['allow_inline']
|
inline = (file_name in ALLOWED_FILES) and settings['allow_inline']
|
||||||
if inline and size < 1024 * 1024:
|
if inline and size < 1024 * 1024:
|
||||||
self.accept_transfer(None, '', friend_number, file_number, size, True)
|
self.accept_transfer(None, '', friend_number, file_number, size, True)
|
||||||
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['INCOMING_STARTED'],
|
TOX_FILE_TRANSFER_STATE['RUNNING'],
|
||||||
size,
|
size,
|
||||||
file_name,
|
file_name,
|
||||||
friend_number,
|
friend_number,
|
||||||
@ -926,7 +797,7 @@ class Profile(Contact, Singleton):
|
|||||||
self.accept_transfer(None, path + '/' + new_file_name, friend_number, file_number, size)
|
self.accept_transfer(None, path + '/' + new_file_name, friend_number, file_number, size)
|
||||||
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['INCOMING_STARTED'],
|
TOX_FILE_TRANSFER_STATE['RUNNING'],
|
||||||
size,
|
size,
|
||||||
new_file_name,
|
new_file_name,
|
||||||
friend_number,
|
friend_number,
|
||||||
@ -934,7 +805,7 @@ class Profile(Contact, Singleton):
|
|||||||
else:
|
else:
|
||||||
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED'],
|
TOX_FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'],
|
||||||
size,
|
size,
|
||||||
file_name,
|
file_name,
|
||||||
friend_number,
|
friend_number,
|
||||||
@ -945,7 +816,7 @@ class Profile(Contact, Singleton):
|
|||||||
self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update)
|
self._file_transfers[(friend_number, file_number)].set_state_changed_handler(item.update)
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
else:
|
else:
|
||||||
friend.set_messages(True)
|
friend.actions = True
|
||||||
|
|
||||||
friend.append_message(tm)
|
friend.append_message(tm)
|
||||||
|
|
||||||
@ -956,8 +827,8 @@ class Profile(Contact, Singleton):
|
|||||||
:param file_number: file number
|
:param file_number: file number
|
||||||
:param already_cancelled: was cancelled by friend
|
:param already_cancelled: was cancelled by friend
|
||||||
"""
|
"""
|
||||||
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['CANCELLED'])
|
TOX_FILE_TRANSFER_STATE['CANCELLED'])
|
||||||
if (friend_number, file_number) in self._file_transfers:
|
if (friend_number, file_number) in self._file_transfers:
|
||||||
tr = self._file_transfers[(friend_number, file_number)]
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
if not already_cancelled:
|
if not already_cancelled:
|
||||||
@ -968,7 +839,17 @@ class Profile(Contact, Singleton):
|
|||||||
del tr
|
del tr
|
||||||
del self._file_transfers[(friend_number, file_number)]
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
else:
|
else:
|
||||||
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
if not already_cancelled:
|
||||||
|
self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL'])
|
||||||
|
if friend_number == self.get_active_number():
|
||||||
|
tmp = self._messages.count() + i
|
||||||
|
if tmp >= 0:
|
||||||
|
self._messages.itemWidget(self._messages.item(tmp)).update(TOX_FILE_TRANSFER_STATE['CANCELLED'],
|
||||||
|
0, -1)
|
||||||
|
|
||||||
|
def cancel_not_started_transfer(self, time):
|
||||||
|
self._friends[self._active_friend].delete_one_unsent_file(time)
|
||||||
|
self.update()
|
||||||
|
|
||||||
def pause_transfer(self, friend_number, file_number, by_friend=False):
|
def pause_transfer(self, friend_number, file_number, by_friend=False):
|
||||||
"""
|
"""
|
||||||
@ -976,7 +857,7 @@ class Profile(Contact, Singleton):
|
|||||||
"""
|
"""
|
||||||
tr = self._file_transfers[(friend_number, file_number)]
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
tr.pause(by_friend)
|
tr.pause(by_friend)
|
||||||
t = FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND'] if by_friend else FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_USER']
|
t = TOX_FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else TOX_FILE_TRANSFER_STATE['PAUSED_BY_USER']
|
||||||
self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
|
self.get_friend_by_number(friend_number).update_transfer_data(file_number, t)
|
||||||
|
|
||||||
def resume_transfer(self, friend_number, file_number, by_friend=False):
|
def resume_transfer(self, friend_number, file_number, by_friend=False):
|
||||||
@ -984,7 +865,11 @@ class Profile(Contact, Singleton):
|
|||||||
Resume transfer with specified data
|
Resume transfer with specified data
|
||||||
"""
|
"""
|
||||||
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['OUTGOING'])
|
TOX_FILE_TRANSFER_STATE['RUNNING'])
|
||||||
|
# if (friend_number, file_number) not in self._file_transfers:
|
||||||
|
# print self._file_transfers
|
||||||
|
# print (friend_number, file_number)
|
||||||
|
# return
|
||||||
tr = self._file_transfers[(friend_number, file_number)]
|
tr = self._file_transfers[(friend_number, file_number)]
|
||||||
if by_friend:
|
if by_friend:
|
||||||
tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
tr.state = TOX_FILE_TRANSFER_STATE['RUNNING']
|
||||||
@ -1010,21 +895,37 @@ class Profile(Contact, Singleton):
|
|||||||
if item is not None:
|
if item is not None:
|
||||||
rt.set_state_changed_handler(item.update)
|
rt.set_state_changed_handler(item.update)
|
||||||
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['INCOMING_STARTED'])
|
TOX_FILE_TRANSFER_STATE['RUNNING'])
|
||||||
|
|
||||||
def send_screenshot(self, data):
|
def send_screenshot(self, data):
|
||||||
"""
|
"""
|
||||||
Send screenshot to current active friend
|
Send screenshot to current active friend
|
||||||
:param data: raw data - png
|
:param data: raw data - png
|
||||||
"""
|
"""
|
||||||
friend = self._friends[self._active_friend]
|
self.send_inline(data, 'toxygen_inline.png')
|
||||||
st = SendFromBuffer(self._tox, friend.number, data, 'toxygen_inline.png')
|
|
||||||
|
def send_sticker(self, path):
|
||||||
|
with open(path) as fl:
|
||||||
|
data = fl.read()
|
||||||
|
self.send_inline(data, 'sticker.png')
|
||||||
|
|
||||||
|
def send_inline(self, data, file_name, friend_number=None, is_resend=False):
|
||||||
|
friend_number = friend_number or self.get_active_number()
|
||||||
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
if friend.status is None and not is_resend:
|
||||||
|
m = UnsentFile(file_name, data, time.time())
|
||||||
|
friend.append_message(m)
|
||||||
|
self.update()
|
||||||
|
return
|
||||||
|
elif friend.status is None and is_resend:
|
||||||
|
raise RuntimeError()
|
||||||
|
st = SendFromBuffer(self._tox, friend.number, data, file_name)
|
||||||
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['PAUSED_BY_FRIEND'], # OUTGOING NOT STARTED
|
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
|
||||||
len(data),
|
len(data),
|
||||||
'toxygen_inline.png',
|
file_name,
|
||||||
friend.number,
|
friend.number,
|
||||||
st.get_file_number())
|
st.get_file_number())
|
||||||
item = self.create_file_transfer_item(tm)
|
item = self.create_file_transfer_item(tm)
|
||||||
@ -1032,20 +933,28 @@ class Profile(Contact, Singleton):
|
|||||||
st.set_state_changed_handler(item.update)
|
st.set_state_changed_handler(item.update)
|
||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
def send_file(self, path, number=None):
|
def send_file(self, path, number=None, is_resend=False):
|
||||||
"""
|
"""
|
||||||
Send file to current active friend
|
Send file to current active friend
|
||||||
:param path: file path
|
:param path: file path
|
||||||
:param number: friend_number
|
:param number: friend_number
|
||||||
|
:param is_resend: is 'offline' message
|
||||||
"""
|
"""
|
||||||
friend_number = number or self.get_active_number()
|
friend_number = number or self.get_active_number()
|
||||||
if self.get_friend_by_number(friend_number).status is None:
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
if friend.status is None and not is_resend:
|
||||||
|
m = UnsentFile(path, None, time.time())
|
||||||
|
friend.append_message(m)
|
||||||
|
self.update()
|
||||||
return
|
return
|
||||||
|
elif friend.status is None and is_resend:
|
||||||
|
print 'Error in sending'
|
||||||
|
raise RuntimeError()
|
||||||
st = SendTransfer(path, self._tox, friend_number)
|
st = SendTransfer(path, self._tox, friend_number)
|
||||||
self._file_transfers[(friend_number, st.get_file_number())] = st
|
self._file_transfers[(friend_number, st.get_file_number())] = st
|
||||||
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
tm = TransferMessage(MESSAGE_OWNER['ME'],
|
||||||
time.time(),
|
time.time(),
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['PAUSED_BY_FRIEND'], # OUTGOING NOT STARTED
|
TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'],
|
||||||
os.path.getsize(path),
|
os.path.getsize(path),
|
||||||
os.path.basename(path),
|
os.path.basename(path),
|
||||||
friend_number,
|
friend_number,
|
||||||
@ -1056,46 +965,61 @@ class Profile(Contact, Singleton):
|
|||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
def incoming_chunk(self, friend_number, file_number, position, data):
|
def incoming_chunk(self, friend_number, file_number, position, data):
|
||||||
|
"""
|
||||||
|
Incoming chunk
|
||||||
|
"""
|
||||||
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 in (2, 3): # finished or cancelled
|
if transfer.state not in ACTIVE_FILE_TRANSFERS: # 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)
|
||||||
elif type(transfer) is ReceiveToBuffer:
|
elif type(transfer) is ReceiveToBuffer: # inline image
|
||||||
inline = InlineImage(transfer.get_data())
|
inline = InlineImage(transfer.get_data())
|
||||||
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'],
|
TOX_FILE_TRANSFER_STATE['FINISHED'],
|
||||||
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(), self._messages.width())
|
if count + i + 1 >= 0:
|
||||||
elem = QtGui.QListWidgetItem()
|
elem = QtGui.QListWidgetItem()
|
||||||
elem.setSizeHint(QtCore.QSize(600, item.height()))
|
item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
|
||||||
self._messages.insertItem(count + i + 1, elem)
|
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
||||||
self._messages.setItemWidget(elem, item)
|
self._messages.insertItem(count + i + 1, elem)
|
||||||
|
self._messages.setItemWidget(elem, item)
|
||||||
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'])
|
TOX_FILE_TRANSFER_STATE['FINISHED'])
|
||||||
del self._file_transfers[(friend_number, file_number)]
|
del self._file_transfers[(friend_number, file_number)]
|
||||||
|
|
||||||
def outgoing_chunk(self, friend_number, file_number, position, size):
|
def outgoing_chunk(self, friend_number, file_number, position, size):
|
||||||
|
"""
|
||||||
|
Outgoing chunk
|
||||||
|
"""
|
||||||
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 in (2, 3): # finished or cancelled
|
if transfer.state not in ACTIVE_FILE_TRANSFERS: # 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
|
||||||
inline = InlineImage(transfer.get_data())
|
inline = InlineImage(transfer.get_data())
|
||||||
self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
i = self.get_friend_by_number(friend_number).update_transfer_data(file_number,
|
||||||
FILE_TRANSFER_MESSAGE_STATUS['FINISHED'],
|
TOX_FILE_TRANSFER_STATE[
|
||||||
inline)
|
'FINISHED'],
|
||||||
self.update()
|
inline)
|
||||||
|
if friend_number == self.get_active_number():
|
||||||
|
count = self._messages.count()
|
||||||
|
if count + i + 1 >= 0:
|
||||||
|
elem = QtGui.QListWidgetItem()
|
||||||
|
item = InlineImageItem(transfer.get_data(), self._messages.width(), elem)
|
||||||
|
elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height()))
|
||||||
|
self._messages.insertItem(count + i + 1, elem)
|
||||||
|
self._messages.setItemWidget(elem, item)
|
||||||
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'])
|
TOX_FILE_TRANSFER_STATE['FINISHED'])
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
# Avatars support
|
# Avatars support
|
||||||
@ -1119,7 +1043,7 @@ class Profile(Contact, Singleton):
|
|||||||
:param size: size of avatar or 0 (default avatar)
|
:param size: size of avatar or 0 (default avatar)
|
||||||
"""
|
"""
|
||||||
ra = ReceiveAvatar(self._tox, friend_number, size, file_number)
|
ra = ReceiveAvatar(self._tox, friend_number, size, file_number)
|
||||||
if ra.state != TOX_FILE_TRANSFER_STATE['CANCELED']:
|
if ra.state != TOX_FILE_TRANSFER_STATE['CANCELLED']:
|
||||||
self._file_transfers[(friend_number, file_number)] = ra
|
self._file_transfers[(friend_number, file_number)] = ra
|
||||||
else:
|
else:
|
||||||
self.get_friend_by_number(friend_number).load_avatar()
|
self.get_friend_by_number(friend_number).load_avatar()
|
||||||
@ -1151,6 +1075,15 @@ class Profile(Contact, Singleton):
|
|||||||
if num not in self._call and self.is_active_online(): # start call
|
if num not in self._call and self.is_active_online(): # start call
|
||||||
self._call(num, audio, video)
|
self._call(num, audio, video)
|
||||||
self._screen.active_call()
|
self._screen.active_call()
|
||||||
|
if video:
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Outgoing video call", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
else:
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Outgoing audio call", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
self._friends[self._active_friend].append_message(InfoMessage(text, time.time()))
|
||||||
|
self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
self._messages.scrollToBottom()
|
||||||
elif num in self._call: # finish or cancel call if you call with active friend
|
elif num in self._call: # finish or cancel call if you call with active friend
|
||||||
self.stop_call(num, False)
|
self.stop_call(num, False)
|
||||||
|
|
||||||
@ -1159,15 +1092,20 @@ class Profile(Contact, Singleton):
|
|||||||
Incoming call from friend. Only audio is supported now
|
Incoming call from friend. Only audio is supported now
|
||||||
"""
|
"""
|
||||||
friend = self.get_friend_by_number(friend_number)
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
if video:
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Incoming video call", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
else:
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Incoming audio call", None,
|
||||||
|
QtGui.QApplication.UnicodeUTF8)
|
||||||
|
friend.append_message(InfoMessage(text, time.time()))
|
||||||
self._incoming_calls.add(friend_number)
|
self._incoming_calls.add(friend_number)
|
||||||
if friend_number == self.get_active_number():
|
if friend_number == self.get_active_number():
|
||||||
self._screen.incoming_call()
|
self._screen.incoming_call()
|
||||||
|
self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
self._messages.scrollToBottom()
|
||||||
else:
|
else:
|
||||||
friend.set_messages(True)
|
friend.actions = True
|
||||||
if video:
|
|
||||||
text = QtGui.QApplication.translate("incoming_call", "Incoming video call", None, QtGui.QApplication.UnicodeUTF8)
|
|
||||||
else:
|
|
||||||
text = QtGui.QApplication.translate("incoming_call", "Incoming audio call", None, QtGui.QApplication.UnicodeUTF8)
|
|
||||||
self._call_widget = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
|
self._call_widget = avwidgets.IncomingCallWidget(friend_number, text, friend.name)
|
||||||
self._call_widget.set_pixmap(friend.get_pixmap())
|
self._call_widget.set_pixmap(friend.get_pixmap())
|
||||||
self._call_widget.show()
|
self._call_widget.show()
|
||||||
@ -1188,10 +1126,18 @@ class Profile(Contact, Singleton):
|
|||||||
"""
|
"""
|
||||||
if friend_number in self._incoming_calls:
|
if friend_number in self._incoming_calls:
|
||||||
self._incoming_calls.remove(friend_number)
|
self._incoming_calls.remove(friend_number)
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Call declined", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
|
else:
|
||||||
|
text = QtGui.QApplication.translate("incoming_call", "Call finished", None, QtGui.QApplication.UnicodeUTF8)
|
||||||
self._screen.call_finished()
|
self._screen.call_finished()
|
||||||
self._call.finish_call(friend_number, by_friend) # finish or decline call
|
self._call.finish_call(friend_number, by_friend) # finish or decline call
|
||||||
if hasattr(self, '_call_widget'):
|
if hasattr(self, '_call_widget'):
|
||||||
del self._call_widget
|
del self._call_widget
|
||||||
|
friend = self.get_friend_by_number(friend_number)
|
||||||
|
friend.append_message(InfoMessage(text, time.time()))
|
||||||
|
if friend_number == self.get_active_number():
|
||||||
|
self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
|
|
||||||
def tox_factory(data=None, settings=None):
|
def tox_factory(data=None, settings=None):
|
||||||
|
@ -5,6 +5,7 @@ import locale
|
|||||||
from util import Singleton, curr_directory, log
|
from util import Singleton, curr_directory, log
|
||||||
import pyaudio
|
import pyaudio
|
||||||
from toxencryptsave import LibToxEncryptSave
|
from toxencryptsave import LibToxEncryptSave
|
||||||
|
import smileys
|
||||||
|
|
||||||
|
|
||||||
class Settings(Singleton, dict):
|
class Settings(Singleton, dict):
|
||||||
@ -13,24 +14,25 @@ class Settings(Singleton, dict):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.path = ProfileHelper.get_path() + str(name) + '.json'
|
self.path = ProfileHelper.get_path() + unicode(name) + '.json'
|
||||||
self.name = name
|
self.name = name
|
||||||
if os.path.isfile(self.path):
|
if os.path.isfile(self.path):
|
||||||
with open(self.path) as fl:
|
with open(self.path, 'rb') as fl:
|
||||||
data = fl.read()
|
data = fl.read()
|
||||||
inst = LibToxEncryptSave.get_instance()
|
inst = LibToxEncryptSave.get_instance()
|
||||||
if inst.has_password():
|
|
||||||
data = inst.pass_decrypt(data)
|
|
||||||
try:
|
try:
|
||||||
|
if inst.is_data_encrypted(data):
|
||||||
|
data = inst.pass_decrypt(data)
|
||||||
info = json.loads(data)
|
info = json.loads(data)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
info = Settings.get_default_settings()
|
info = Settings.get_default_settings()
|
||||||
log('Parsing settings error: ' + str(ex))
|
log('Parsing settings error: ' + str(ex))
|
||||||
super(self.__class__, self).__init__(info)
|
super(Settings, self).__init__(info)
|
||||||
self.upgrade()
|
self.upgrade()
|
||||||
else:
|
else:
|
||||||
super(self.__class__, self).__init__(Settings.get_default_settings())
|
super(Settings, self).__init__(Settings.get_default_settings())
|
||||||
self.save()
|
self.save()
|
||||||
|
smileys.SmileyLoader(self)
|
||||||
p = pyaudio.PyAudio()
|
p = pyaudio.PyAudio()
|
||||||
self.audio = {'input': p.get_default_input_device_info()['index'],
|
self.audio = {'input': p.get_default_input_device_info()['index'],
|
||||||
'output': p.get_default_output_device_info()['index']}
|
'output': p.get_default_output_device_info()['index']}
|
||||||
@ -44,13 +46,42 @@ class Settings(Singleton, dict):
|
|||||||
auto = json.loads(data)
|
auto = json.loads(data)
|
||||||
if 'path' in auto and 'name' in auto:
|
if 'path' in auto and 'name' in auto:
|
||||||
return unicode(auto['path']), unicode(auto['name'])
|
return unicode(auto['path']), unicode(auto['name'])
|
||||||
|
return '', ''
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def set_auto_profile(path, name):
|
def set_auto_profile(path, name):
|
||||||
p = Settings.get_default_path() + 'toxygen.json'
|
p = Settings.get_default_path() + 'toxygen.json'
|
||||||
data = json.dumps({'path': unicode(path.decode(locale.getpreferredencoding())), 'name': unicode(name)})
|
with open(p) as fl:
|
||||||
|
data = fl.read()
|
||||||
|
data = json.loads(data)
|
||||||
|
data['path'] = unicode(path.decode(locale.getpreferredencoding()))
|
||||||
|
data['name'] = unicode(name)
|
||||||
with open(p, 'w') as fl:
|
with open(p, 'w') as fl:
|
||||||
fl.write(data)
|
fl.write(json.dumps(data))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reset_auto_profile():
|
||||||
|
p = Settings.get_default_path() + 'toxygen.json'
|
||||||
|
with open(p) as fl:
|
||||||
|
data = fl.read()
|
||||||
|
data = json.loads(data)
|
||||||
|
if 'path' in data:
|
||||||
|
del data['path']
|
||||||
|
del data['name']
|
||||||
|
with open(p, 'w') as fl:
|
||||||
|
fl.write(json.dumps(data))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_active_profile(path, name):
|
||||||
|
path = path.decode(locale.getpreferredencoding()) + name + '.tox'
|
||||||
|
settings = Settings.get_default_path() + 'toxygen.json'
|
||||||
|
if os.path.isfile(settings):
|
||||||
|
with open(settings) as fl:
|
||||||
|
data = fl.read()
|
||||||
|
data = json.loads(data)
|
||||||
|
if 'active_profile' in data:
|
||||||
|
return path in data['active_profile']
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_default_settings():
|
def get_default_settings():
|
||||||
@ -80,16 +111,26 @@ class Settings(Singleton, dict):
|
|||||||
'typing_notifications': False,
|
'typing_notifications': False,
|
||||||
'calls_sound': True,
|
'calls_sound': True,
|
||||||
'blocked': [],
|
'blocked': [],
|
||||||
'plugins': []
|
'plugins': [],
|
||||||
|
'notes': {},
|
||||||
|
'smileys': True,
|
||||||
|
'smiley_pack': 'default',
|
||||||
|
'mirror_mode': False,
|
||||||
|
'width': 920,
|
||||||
|
'height': 500,
|
||||||
|
'x': 400,
|
||||||
|
'y': 400,
|
||||||
|
'message_font_size': 14,
|
||||||
|
'unread_color': 'red'
|
||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def supported_languages():
|
def supported_languages():
|
||||||
return [
|
return {
|
||||||
('English', 'en_EN'),
|
'English': 'en_EN',
|
||||||
('Russian', 'ru_RU'),
|
'Russian': 'ru_RU',
|
||||||
('French', 'fr_FR')
|
'French': 'fr_FR'
|
||||||
]
|
}
|
||||||
|
|
||||||
def upgrade(self):
|
def upgrade(self):
|
||||||
default = Settings.get_default_settings()
|
default = Settings.get_default_settings()
|
||||||
@ -104,7 +145,7 @@ class Settings(Singleton, dict):
|
|||||||
inst = LibToxEncryptSave.get_instance()
|
inst = LibToxEncryptSave.get_instance()
|
||||||
if inst.has_password():
|
if inst.has_password():
|
||||||
text = inst.pass_encrypt(text)
|
text = inst.pass_encrypt(text)
|
||||||
with open(self.path, 'w') as fl:
|
with open(self.path, 'wb') as fl:
|
||||||
fl.write(text)
|
fl.write(text)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -215,18 +256,6 @@ class ProfileHelper(Singleton):
|
|||||||
result.append((path + '/', name))
|
result.append((path + '/', name))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def is_active_profile(path, name):
|
|
||||||
path = path.decode(locale.getpreferredencoding()) + name + '.tox'
|
|
||||||
settings = Settings.get_default_path() + 'toxygen.json'
|
|
||||||
if os.path.isfile(settings):
|
|
||||||
with open(settings) as fl:
|
|
||||||
data = fl.read()
|
|
||||||
data = json.loads(data)
|
|
||||||
if 'active_profile' in data:
|
|
||||||
return path in data['active_profile']
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_path():
|
def get_path():
|
||||||
return ProfileHelper.get_instance().get_dir()
|
return ProfileHelper.get_instance().get_dir()
|
||||||
|
90
src/smileys.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import util
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from collections import OrderedDict
|
||||||
|
try:
|
||||||
|
from PySide import QtCore
|
||||||
|
except ImportError:
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
|
|
||||||
|
class SmileyLoader(util.Singleton):
|
||||||
|
"""
|
||||||
|
Class which loads smileys packs and insert smileys into messages
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, settings):
|
||||||
|
self._settings = settings
|
||||||
|
self._curr_pack = None # current pack name
|
||||||
|
self._smileys = {} # smileys dict. key - smiley (str), value - path to image (str)
|
||||||
|
self._list = [] # smileys list without duplicates
|
||||||
|
self.load_pack()
|
||||||
|
|
||||||
|
def load_pack(self):
|
||||||
|
"""
|
||||||
|
Loads smiley pack
|
||||||
|
"""
|
||||||
|
pack_name = self._settings['smiley_pack']
|
||||||
|
if self._settings['smileys'] and self._curr_pack != pack_name:
|
||||||
|
self._curr_pack = pack_name
|
||||||
|
path = self.get_smileys_path() + 'config.json'
|
||||||
|
try:
|
||||||
|
with open(path) as fl:
|
||||||
|
self._smileys = json.loads(fl.read())
|
||||||
|
fl.seek(0)
|
||||||
|
tmp = json.loads(fl.read(), object_pairs_hook=OrderedDict)
|
||||||
|
print 'Smiley pack {} loaded'.format(pack_name)
|
||||||
|
keys, values, self._list = [], [], []
|
||||||
|
for key, value in tmp.items():
|
||||||
|
value = self.get_smileys_path() + value
|
||||||
|
if value not in values:
|
||||||
|
keys.append(key)
|
||||||
|
values.append(value)
|
||||||
|
self._list = zip(keys, values)
|
||||||
|
except Exception as ex:
|
||||||
|
self._smileys = {}
|
||||||
|
self._list = []
|
||||||
|
print 'Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex)
|
||||||
|
|
||||||
|
def get_smileys_path(self):
|
||||||
|
return util.curr_directory() + '/smileys/' + self._curr_pack + '/'
|
||||||
|
|
||||||
|
def get_packs_list(self):
|
||||||
|
d = util.curr_directory() + '/smileys/'
|
||||||
|
return [x[1] for x in os.walk(d)][0]
|
||||||
|
|
||||||
|
def get_smileys(self):
|
||||||
|
return self._list[:]
|
||||||
|
|
||||||
|
def add_smileys_to_text(self, text, edit):
|
||||||
|
"""
|
||||||
|
Adds smileys to text
|
||||||
|
:param text: message
|
||||||
|
:param edit: MessageEdit instance
|
||||||
|
:return text with smileys
|
||||||
|
"""
|
||||||
|
if not self._settings['smileys'] or not len(self._smileys):
|
||||||
|
return text
|
||||||
|
arr = text.split(' ')
|
||||||
|
for i in range(len(arr)):
|
||||||
|
if arr[i] in self._smileys:
|
||||||
|
file_name = self._smileys[arr[i]] # image name
|
||||||
|
arr[i] = u'<img title=\"{}\" src=\"{}\" />'.format(arr[i], file_name)
|
||||||
|
if file_name.endswith('.gif'): # animated smiley
|
||||||
|
edit.addAnimation(QtCore.QUrl(file_name), self.get_smileys_path() + file_name)
|
||||||
|
return ' '.join(arr)
|
||||||
|
|
||||||
|
|
||||||
|
def sticker_loader():
|
||||||
|
"""
|
||||||
|
:return list of stickers
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
d = util.curr_directory() + '/stickers/'
|
||||||
|
keys = [x[1] for x in os.walk(d)][0]
|
||||||
|
for key in keys:
|
||||||
|
path = d + key + '/'
|
||||||
|
files = filter(lambda f: f.endswith('.png'), os.listdir(path))
|
||||||
|
files = map(lambda f: unicode(path + f), files)
|
||||||
|
result.extend(files)
|
||||||
|
return result
|
BIN
src/smileys/animated/_ao.gif
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/smileys/animated/aa.gif
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
src/smileys/animated/ab.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/smileys/animated/ac.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/smileys/animated/ad.gif
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/smileys/animated/ae.gif
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
src/smileys/animated/af.gif
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/smileys/animated/ag.gif
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/smileys/animated/ah.gif
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
src/smileys/animated/ai.gif
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/smileys/animated/aj.gif
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
src/smileys/animated/ak.gif
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
src/smileys/animated/al.gif
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
src/smileys/animated/am.gif
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/smileys/animated/an.gif
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
src/smileys/animated/ao.gif
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/smileys/animated/ap.gif
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
src/smileys/animated/aq.gif
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/smileys/animated/ar.gif
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
src/smileys/animated/as.gif
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/smileys/animated/at.gif
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
src/smileys/animated/au.gif
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/smileys/animated/av.gif
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
src/smileys/animated/aw.gif
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
src/smileys/animated/ax.gif
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/smileys/animated/ay.gif
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
src/smileys/animated/az.gif
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/smileys/animated/ba.gif
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
src/smileys/animated/bb.gif
Normal file
After Width: | Height: | Size: 868 B |
BIN
src/smileys/animated/bc.gif
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/smileys/animated/bd.gif
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
src/smileys/animated/be.gif
Normal file
After Width: | Height: | Size: 1010 B |
BIN
src/smileys/animated/bf.gif
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
src/smileys/animated/bg.gif
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/smileys/animated/bh.gif
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/smileys/animated/bi.gif
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
src/smileys/animated/bj.gif
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src/smileys/animated/bk.gif
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
src/smileys/animated/bl.gif
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/smileys/animated/bm.gif
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/smileys/animated/bn.gif
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/smileys/animated/bo.gif
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
src/smileys/animated/bp.gif
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
src/smileys/animated/bq.gif
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
src/smileys/animated/br.gif
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
src/smileys/animated/bs.gif
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
src/smileys/animated/bt.gif
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/smileys/animated/bu.gif
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
src/smileys/animated/bv.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/smileys/animated/bw.gif
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
src/smileys/animated/bx.gif
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
src/smileys/animated/by.gif
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/smileys/animated/bz.gif
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/smileys/animated/ca.gif
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
src/smileys/animated/cb.gif
Normal file
After Width: | Height: | Size: 8.0 KiB |