more refacrtoring - contact provider, deps creation
This commit is contained in:
parent
68328d9846
commit
a9d2d3d809
@ -15,6 +15,11 @@ from plugin_support.plugin_support import PluginLoader
|
|||||||
from ui.main_screen import MainWindow
|
from ui.main_screen import MainWindow
|
||||||
from ui import tray
|
from ui import tray
|
||||||
import util.ui as util_ui
|
import util.ui as util_ui
|
||||||
|
import util.util as util
|
||||||
|
from contacts.profile import Profile
|
||||||
|
from file_transfers.file_transfers_handler import FileTransfersHandler
|
||||||
|
from contacts.contact_provider import ContactProvider
|
||||||
|
from contacts.friend_factory import FriendFactory
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
@ -23,7 +28,7 @@ class App:
|
|||||||
self._version = version
|
self._version = version
|
||||||
self._app = self._settings = self._profile_manager = self._plugin_loader = None
|
self._app = self._settings = self._profile_manager = self._plugin_loader = None
|
||||||
self._tox = self._ms = self._init = self._main_loop = self._av_loop = None
|
self._tox = self._ms = self._init = self._main_loop = self._av_loop = None
|
||||||
self._uri = self._toxes = self._tray = None
|
self._uri = self._toxes = self._tray = self._file_transfer_handler = self._contacts_provider = None
|
||||||
if uri is not None and uri.startswith('tox:'):
|
if uri is not None and uri.startswith('tox:'):
|
||||||
self._uri = uri[4:]
|
self._uri = uri[4:]
|
||||||
self._path = path_to_profile
|
self._path = path_to_profile
|
||||||
@ -93,7 +98,11 @@ class App:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray)
|
self._ms = MainWindow(self._settings, self._tox, self.reset, self._tray)
|
||||||
profile = self._ms.profile
|
self._friend_factory = FriendFactory(None, self._profile_manager, self._settings, self._tox)
|
||||||
|
self._contacts_provider = ContactProvider(self._tox, self._friend_factory)
|
||||||
|
self._file_transfer_handler = FileTransfersHandler(self._tox, self._settings, self._contacts_provider)
|
||||||
|
profile = Profile(self._profile_manager, self._tox, self._ms, self._file_transfer_handler)
|
||||||
|
self._ms.profile = profile
|
||||||
self._ms.show()
|
self._ms.show()
|
||||||
|
|
||||||
self._tray = tray.init_tray(profile, self._settings, self._ms)
|
self._tray = tray.init_tray(profile, self._settings, self._ms)
|
||||||
@ -110,7 +119,17 @@ class App:
|
|||||||
self._ms.add_contact(self._uri)
|
self._ms.add_contact(self._uri)
|
||||||
|
|
||||||
self._app.lastWindowClosed.connect(self._app.quit)
|
self._app.lastWindowClosed.connect(self._app.quit)
|
||||||
|
# main
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
self._app.exec_()
|
self._app.exec_()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('Closing Toxygen...')
|
||||||
|
break
|
||||||
|
except Exception as ex:
|
||||||
|
util.log('Unhandled exception: ' + str(ex))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
self._plugin_loader.stop()
|
self._plugin_loader.stop()
|
||||||
self.stop_threads()
|
self.stop_threads()
|
||||||
@ -132,7 +151,7 @@ class App:
|
|||||||
self._tox = self.create_tox(data)
|
self._tox = self.create_tox(data)
|
||||||
self.start_threads()
|
self.start_threads()
|
||||||
|
|
||||||
self._plugin_loader.set_tox(self._tox)
|
# TODO: foreach in list of tox savers set_tox
|
||||||
|
|
||||||
return self._tox
|
return self._tox
|
||||||
|
|
||||||
@ -178,7 +197,7 @@ class App:
|
|||||||
|
|
||||||
def start_threads(self):
|
def start_threads(self):
|
||||||
# init thread
|
# init thread
|
||||||
self._init = threads.InitThread(self._tox, self._plugin_loader)
|
self._init = threads.InitThread(self._tox, self._plugin_loader, self._settings)
|
||||||
self._init.start()
|
self._init.start()
|
||||||
|
|
||||||
# starting threads for tox iterate and toxav iterate
|
# starting threads for tox iterate and toxav iterate
|
||||||
|
@ -38,13 +38,12 @@ def save_nodes(nodes):
|
|||||||
fl.write(nodes)
|
fl.write(nodes)
|
||||||
|
|
||||||
|
|
||||||
def download_nodes_list():
|
def download_nodes_list(settings):
|
||||||
url = 'https://nodes.tox.chat/json'
|
url = 'https://nodes.tox.chat/json'
|
||||||
s = settings.Settings.get_instance()
|
if not settings['download_nodes_list']:
|
||||||
if not s['download_nodes_list']:
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if not s['proxy_type']: # no proxy
|
if not settings['proxy_type']: # no proxy
|
||||||
try:
|
try:
|
||||||
req = urllib.request.Request(url)
|
req = urllib.request.Request(url)
|
||||||
req.add_header('Content-Type', 'application/json')
|
req.add_header('Content-Type', 'application/json')
|
||||||
@ -57,9 +56,9 @@ def download_nodes_list():
|
|||||||
netman = QtNetwork.QNetworkAccessManager()
|
netman = QtNetwork.QNetworkAccessManager()
|
||||||
proxy = QtNetwork.QNetworkProxy()
|
proxy = QtNetwork.QNetworkProxy()
|
||||||
proxy.setType(
|
proxy.setType(
|
||||||
QtNetwork.QNetworkProxy.Socks5Proxy if s['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy)
|
QtNetwork.QNetworkProxy.Socks5Proxy if settings['proxy_type'] == 2 else QtNetwork.QNetworkProxy.HttpProxy)
|
||||||
proxy.setHostName(s['proxy_host'])
|
proxy.setHostName(settings['proxy_host'])
|
||||||
proxy.setPort(s['proxy_port'])
|
proxy.setPort(settings['proxy_port'])
|
||||||
netman.setProxy(proxy)
|
netman.setProxy(proxy)
|
||||||
try:
|
try:
|
||||||
request = QtNetwork.QNetworkRequest()
|
request = QtNetwork.QNetworkRequest()
|
||||||
|
@ -106,7 +106,7 @@ class BaseContact:
|
|||||||
return self._widget.avatar_label.pixmap()
|
return self._widget.avatar_label.pixmap()
|
||||||
|
|
||||||
def get_avatar_path(self):
|
def get_avatar_path(self):
|
||||||
directory = util.join_path(self._profile_manager.get_path(), 'avatars')
|
directory = util.join_path(self._profile_manager.get_dir(), 'avatars')
|
||||||
avatar_path = util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]))
|
avatar_path = util.join_path(directory, '{}.png'.format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2]))
|
||||||
if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image
|
if not os.path.isfile(avatar_path) or not os.path.getsize(avatar_path): # load default image
|
||||||
avatar_path = util.join_path(util.get_images_directory(), self.get_default_avatar_name())
|
avatar_path = util.join_path(util.get_images_directory(), self.get_default_avatar_name())
|
||||||
|
38
toxygen/contacts/contact_provider.py
Normal file
38
toxygen/contacts/contact_provider.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import util.util as util
|
||||||
|
|
||||||
|
|
||||||
|
class ContactProvider(util.ToxSave):
|
||||||
|
|
||||||
|
def __init__(self, tox, friend_factory):
|
||||||
|
super().__init__(tox)
|
||||||
|
self._friend_factory = friend_factory
|
||||||
|
self._cache = {} # key - contact's public key, value - contact instance
|
||||||
|
|
||||||
|
def get_friend_by_number(self, friend_number):
|
||||||
|
public_key = self._tox.friend_get_public_key(friend_number)
|
||||||
|
|
||||||
|
return self.get_friend_by_public_key(public_key)
|
||||||
|
|
||||||
|
def get_friend_by_public_key(self, public_key):
|
||||||
|
friend = self._get_contact_from_cache(public_key)
|
||||||
|
if friend is not None:
|
||||||
|
return friend
|
||||||
|
friend = self._friend_factory.create_friend_by_public_key(public_key)
|
||||||
|
self._add_to_cache(public_key, friend)
|
||||||
|
|
||||||
|
return friend
|
||||||
|
|
||||||
|
def get_gc_by_number(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_gc_by_public_key(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def clear_cache(self):
|
||||||
|
self._cache.clear()
|
||||||
|
|
||||||
|
def _get_contact_from_cache(self, public_key):
|
||||||
|
return self._cache[public_key] if public_key in self._cache else None
|
||||||
|
|
||||||
|
def _add_to_cache(self, public_key, contact):
|
||||||
|
self._cache[public_key] = contact
|
38
toxygen/contacts/friend_factory.py
Normal file
38
toxygen/contacts/friend_factory.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from contacts.friend import Friend
|
||||||
|
|
||||||
|
|
||||||
|
class FriendFactory:
|
||||||
|
|
||||||
|
def __init__(self, history, profile_manager, settings, tox):
|
||||||
|
self._history, self._profile_manager = history, profile_manager
|
||||||
|
self._settings, self._tox = settings, tox
|
||||||
|
|
||||||
|
def create_friend_by_number(self, friend_number):
|
||||||
|
aliases = self._settings['friends_aliases']
|
||||||
|
tox_id = self._tox.friend_get_public_key(friend_number)
|
||||||
|
try:
|
||||||
|
alias = list(filter(lambda x: x[0] == tox_id, aliases))[0][1]
|
||||||
|
except:
|
||||||
|
alias = ''
|
||||||
|
item = self.create_friend_item()
|
||||||
|
name = alias or self._tox.friend_get_name(friend_number) or tox_id
|
||||||
|
status_message = self._tox.friend_get_status_message(friend_number)
|
||||||
|
if not self._history.friend_exists_in_db(tox_id):
|
||||||
|
self._history.add_friend_to_db(tox_id)
|
||||||
|
message_getter = self._history.messages_getter(tox_id)
|
||||||
|
friend = Friend(self._profile_manager, message_getter, friend_number, name, status_message, item, tox_id)
|
||||||
|
friend.set_alias(alias)
|
||||||
|
|
||||||
|
return friend
|
||||||
|
|
||||||
|
def create_friend_by_public_key(self, public_key):
|
||||||
|
friend_number = self._tox.friend_by_public_key(public_key)
|
||||||
|
|
||||||
|
return self.create_friend_by_number(friend_number)
|
||||||
|
|
||||||
|
def create_friend_item(self):
|
||||||
|
"""
|
||||||
|
Method-factory
|
||||||
|
:return: new widget for friend instance
|
||||||
|
"""
|
||||||
|
return self._factory.friend_item()
|
@ -40,7 +40,8 @@ class Profile(basecontact.BaseContact):
|
|||||||
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
|
self._file_transfers = {} # dict of file transfers. key - tuple (friend_number, file_number)
|
||||||
self._load_history = True
|
self._load_history = True
|
||||||
self._waiting_for_reconnection = False
|
self._waiting_for_reconnection = False
|
||||||
self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages)
|
#self._factory = items_factory.ItemsFactory(self._screen.friends_list, self._messages)
|
||||||
|
self._contacts_manager = None
|
||||||
#self._show_avatars = settings['show_avatars']
|
#self._show_avatars = settings['show_avatars']
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -77,7 +78,7 @@ class Profile(basecontact.BaseContact):
|
|||||||
self._messages.scrollToBottom()
|
self._messages.scrollToBottom()
|
||||||
|
|
||||||
def set_status_message(self, value):
|
def set_status_message(self, value):
|
||||||
super(Profile, self).set_status_message(value)
|
super().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):
|
||||||
@ -85,6 +86,7 @@ class Profile(basecontact.BaseContact):
|
|||||||
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()
|
||||||
|
|
||||||
return self._tox_id
|
return self._tox_id
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
@ -296,7 +298,7 @@ class Profile(basecontact.BaseContact):
|
|||||||
self.status = None
|
self.status = None
|
||||||
for friend in self._contacts:
|
for friend in self._contacts:
|
||||||
friend.number = self._tox.friend_by_public_key(friend.tox_id) # numbers update
|
friend.number = self._tox.friend_by_public_key(friend.tox_id) # numbers update
|
||||||
self.update_filtration()
|
self._contacts_manager.update_filtration()
|
||||||
|
|
||||||
def reconnect(self):
|
def reconnect(self):
|
||||||
self._waiting_for_reconnection = False
|
self._waiting_for_reconnection = False
|
||||||
|
@ -48,7 +48,7 @@ class FileTransfer(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, path, tox, friend_number, size, file_number=None):
|
def __init__(self, path, tox, friend_number, size, file_number=None):
|
||||||
QtCore.QObject.__init__(self)
|
super().__init__(self)
|
||||||
self._path = path
|
self._path = path
|
||||||
self._tox = tox
|
self._tox = tox
|
||||||
self._friend_number = friend_number
|
self._friend_number = friend_number
|
||||||
@ -134,7 +134,7 @@ class SendTransfer(FileTransfer):
|
|||||||
size = getsize(path)
|
size = getsize(path)
|
||||||
else:
|
else:
|
||||||
size = 0
|
size = 0
|
||||||
super(SendTransfer, self).__init__(path, tox, friend_number, size)
|
super().__init__(path, tox, friend_number, size)
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
|
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,
|
||||||
bytes(basename(path), 'utf-8') if path else b'')
|
bytes(basename(path), 'utf-8') if path else b'')
|
||||||
@ -168,11 +168,11 @@ class SendAvatar(SendTransfer):
|
|||||||
|
|
||||||
def __init__(self, path, tox, friend_number):
|
def __init__(self, path, tox, friend_number):
|
||||||
if path is None:
|
if path is None:
|
||||||
hash = None
|
avatar_hash = None
|
||||||
else:
|
else:
|
||||||
with open(path, 'rb') as fl:
|
with open(path, 'rb') as fl:
|
||||||
hash = Tox.hash(fl.read())
|
avatar_hash = Tox.hash(fl.read())
|
||||||
super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], hash)
|
super(SendAvatar, self).__init__(path, tox, friend_number, TOX_FILE_KIND['AVATAR'], avatar_hash)
|
||||||
|
|
||||||
|
|
||||||
class SendFromBuffer(FileTransfer):
|
class SendFromBuffer(FileTransfer):
|
||||||
@ -181,7 +181,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().__init__(None, tox, friend_number, len(data))
|
||||||
self.state = TOX_FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED']
|
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'],
|
self._file_number = tox.file_send(friend_number, TOX_FILE_KIND['DATA'],
|
||||||
@ -206,10 +206,10 @@ class SendFromBuffer(FileTransfer):
|
|||||||
class SendFromFileBuffer(SendTransfer):
|
class SendFromFileBuffer(SendTransfer):
|
||||||
|
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
super(SendFromFileBuffer, self).__init__(*args)
|
super().__init__(*args)
|
||||||
|
|
||||||
def send_chunk(self, position, size):
|
def send_chunk(self, position, size):
|
||||||
super(SendFromFileBuffer, self).send_chunk(position, size)
|
super().send_chunk(position, size)
|
||||||
if not size:
|
if not size:
|
||||||
chdir(dirname(self._path))
|
chdir(dirname(self._path))
|
||||||
remove(self._path)
|
remove(self._path)
|
||||||
@ -222,7 +222,7 @@ class SendFromFileBuffer(SendTransfer):
|
|||||||
class ReceiveTransfer(FileTransfer):
|
class ReceiveTransfer(FileTransfer):
|
||||||
|
|
||||||
def __init__(self, path, tox, friend_number, size, file_number, position=0):
|
def __init__(self, path, tox, friend_number, size, file_number, position=0):
|
||||||
super(ReceiveTransfer, self).__init__(path, tox, friend_number, size, file_number)
|
super().__init__(path, tox, friend_number, size, file_number)
|
||||||
self._file = open(self._path, 'wb')
|
self._file = open(self._path, 'wb')
|
||||||
self._file_size = position
|
self._file_size = position
|
||||||
self._file.truncate(position)
|
self._file.truncate(position)
|
||||||
@ -231,11 +231,12 @@ class ReceiveTransfer(FileTransfer):
|
|||||||
self._done = position
|
self._done = position
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
super(ReceiveTransfer, self).cancel()
|
super().cancel()
|
||||||
remove(self._path)
|
remove(self._path)
|
||||||
|
|
||||||
def total_size(self):
|
def total_size(self):
|
||||||
self._missed.add(self._file_size)
|
self._missed.add(self._file_size)
|
||||||
|
|
||||||
return min(self._missed)
|
return min(self._missed)
|
||||||
|
|
||||||
def write_chunk(self, position, data):
|
def write_chunk(self, position, data):
|
||||||
@ -273,7 +274,7 @@ class ReceiveToBuffer(FileTransfer):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tox, friend_number, size, file_number):
|
def __init__(self, tox, friend_number, size, file_number):
|
||||||
super(ReceiveToBuffer, self).__init__(None, tox, friend_number, size, file_number)
|
super().__init__(None, tox, friend_number, size, file_number)
|
||||||
self._data = bytes()
|
self._data = bytes()
|
||||||
self._data_size = 0
|
self._data_size = 0
|
||||||
|
|
||||||
@ -306,7 +307,7 @@ class ReceiveAvatar(ReceiveTransfer):
|
|||||||
|
|
||||||
def __init__(self, tox, friend_number, size, file_number):
|
def __init__(self, tox, friend_number, size, file_number):
|
||||||
path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number))
|
path = settings.ProfileManager.get_path() + 'avatars/{}.png'.format(tox.friend_get_public_key(friend_number))
|
||||||
super(ReceiveAvatar, self).__init__(path + '.tmp', tox, friend_number, size, file_number)
|
super().__init__(path + '.tmp', tox, friend_number, size, file_number)
|
||||||
if size > self.MAX_AVATAR_SIZE:
|
if size > self.MAX_AVATAR_SIZE:
|
||||||
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
self.send_control(TOX_FILE_CONTROL['CANCEL'])
|
||||||
self._file.close()
|
self._file.close()
|
||||||
@ -333,7 +334,7 @@ class ReceiveAvatar(ReceiveTransfer):
|
|||||||
self.send_control(TOX_FILE_CONTROL['RESUME'])
|
self.send_control(TOX_FILE_CONTROL['RESUME'])
|
||||||
|
|
||||||
def write_chunk(self, position, data):
|
def write_chunk(self, position, data):
|
||||||
super(ReceiveAvatar, self).write_chunk(position, data)
|
super().write_chunk(position, data)
|
||||||
if self.state:
|
if self.state:
|
||||||
avatar_path = self._path[:-4]
|
avatar_path = self._path[:-4]
|
||||||
if exists(avatar_path):
|
if exists(avatar_path):
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
from file_transfers.file_transfers import *
|
from file_transfers.file_transfers import *
|
||||||
from messenger.messages import *
|
from messenger.messages import *
|
||||||
|
from history.database import MESSAGE_OWNER
|
||||||
import os
|
import os
|
||||||
|
import util.util as util
|
||||||
|
|
||||||
|
|
||||||
class FileTransfersHandler:
|
class FileTransfersHandler:
|
||||||
|
|
||||||
def __init__(self, tox, settings):
|
def __init__(self, tox, settings, contact_provider):
|
||||||
self._tox = tox
|
self._tox = tox
|
||||||
self._settings = settings
|
self._settings = settings
|
||||||
|
self._contact_provider = contact_provider
|
||||||
self._file_transfers = {}
|
self._file_transfers = {}
|
||||||
|
# key = (friend number, file number), value - transfer instance
|
||||||
self._paused_file_transfers = dict(settings['paused_file_transfers'])
|
self._paused_file_transfers = dict(settings['paused_file_transfers'])
|
||||||
# key - file id, value: [path, friend number, is incoming, start position]
|
# key - file id, value: [path, friend number, is incoming, start position]
|
||||||
|
|
||||||
@ -24,9 +28,9 @@ class FileTransfersHandler:
|
|||||||
:param size: file size in bytes
|
:param size: file size in bytes
|
||||||
:param file_name: file name without path
|
:param file_name: file name without path
|
||||||
"""
|
"""
|
||||||
friend = self.get_friend_by_number(friend_number)
|
friend = self._get_friend_by_number(friend_number)
|
||||||
auto = self._settings['allow_auto_accept'] and friend.tox_id in settings['auto_accept_from_friends']
|
auto = self._settings['allow_auto_accept'] and friend.tox_id in self._settings['auto_accept_from_friends']
|
||||||
inline = is_inline(file_name) and settings['allow_inline']
|
inline = is_inline(file_name) and self._settings['allow_inline']
|
||||||
file_id = self._tox.file_get_file_id(friend_number, file_number)
|
file_id = self._tox.file_get_file_id(friend_number, file_number)
|
||||||
accepted = True
|
accepted = True
|
||||||
if file_id in self._paused_file_transfers:
|
if file_id in self._paused_file_transfers:
|
||||||
@ -55,7 +59,7 @@ class FileTransfersHandler:
|
|||||||
file_number)
|
file_number)
|
||||||
|
|
||||||
elif auto:
|
elif auto:
|
||||||
path = settings['auto_accept_path'] or curr_directory()
|
path = self._settings['auto_accept_path'] or util.curr_directory()
|
||||||
self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size)
|
self.accept_transfer(None, path + '/' + file_name, friend_number, file_number, size)
|
||||||
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
tm = TransferMessage(MESSAGE_OWNER['FRIEND'],
|
||||||
time.time(),
|
time.time(),
|
||||||
@ -90,7 +94,7 @@ class FileTransfersHandler:
|
|||||||
:param file_number: file number
|
:param file_number: file number
|
||||||
:param already_cancelled: was cancelled by friend
|
:param already_cancelled: was cancelled by friend
|
||||||
"""
|
"""
|
||||||
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,
|
||||||
TOX_FILE_TRANSFER_STATE['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)]
|
||||||
@ -128,8 +132,8 @@ class FileTransfersHandler:
|
|||||||
"""
|
"""
|
||||||
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,
|
||||||
TOX_FILE_TRANSFER_STATE['RUNNING'])
|
# TOX_FILE_TRANSFER_STATE['RUNNING'])
|
||||||
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']
|
||||||
@ -261,7 +265,7 @@ class FileTransfersHandler:
|
|||||||
self.get_friend_by_number(friend_number).load_avatar()
|
self.get_friend_by_number(friend_number).load_avatar()
|
||||||
if friend_number == self.get_active_number() and self.is_active_a_friend():
|
if friend_number == self.get_active_number() and self.is_active_a_friend():
|
||||||
self.set_active(None)
|
self.set_active(None)
|
||||||
elif t is ReceiveToBuffer or (t is SendFromBuffer and Settings.get_instance()['allow_inline']): # inline image
|
elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings.get_instance()['allow_inline']): # inline image
|
||||||
print('inline')
|
print('inline')
|
||||||
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,
|
||||||
@ -286,13 +290,10 @@ class FileTransfersHandler:
|
|||||||
# Avatars support
|
# Avatars support
|
||||||
# -----------------------------------------------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
def send_avatar(self, friend_number):
|
def send_avatar(self, friend_number, avatar_path=None):
|
||||||
"""
|
"""
|
||||||
:param friend_number: number of friend who should get new avatar
|
:param friend_number: number of friend who should get new avatar
|
||||||
"""
|
"""
|
||||||
avatar_path = (ProfileManager.get_path() + 'avatars/{}.png').format(self._tox_id[:TOX_PUBLIC_KEY_SIZE * 2])
|
|
||||||
if not os.path.isfile(avatar_path): # reset image
|
|
||||||
avatar_path = None
|
|
||||||
sa = SendAvatar(avatar_path, self._tox, friend_number)
|
sa = SendAvatar(avatar_path, self._tox, friend_number)
|
||||||
self._file_transfers[(friend_number, sa.get_file_number())] = sa
|
self._file_transfers[(friend_number, sa.get_file_number())] = sa
|
||||||
|
|
||||||
@ -311,3 +312,6 @@ class FileTransfersHandler:
|
|||||||
self.get_friend_by_number(friend_number).load_avatar()
|
self.get_friend_by_number(friend_number).load_avatar()
|
||||||
if self.get_active_number() == friend_number and self.is_active_a_friend():
|
if self.get_active_number() == friend_number and self.is_active_a_friend():
|
||||||
self.set_active(None)
|
self.set_active(None)
|
||||||
|
|
||||||
|
def _get_friend_by_number(self, friend_number):
|
||||||
|
return self._contact_provider.get_friend_by_number(friend_number)
|
||||||
|
@ -2,7 +2,6 @@ from sqlite3 import connect
|
|||||||
from user_data import settings
|
from user_data import settings
|
||||||
from os import chdir
|
from os import chdir
|
||||||
import os.path
|
import os.path
|
||||||
from user_data.toxes import ToxES
|
|
||||||
|
|
||||||
|
|
||||||
PAGE_SIZE = 42
|
PAGE_SIZE = 42
|
||||||
@ -14,10 +13,11 @@ SAVE_MESSAGES = 500
|
|||||||
MESSAGE_OWNER = {
|
MESSAGE_OWNER = {
|
||||||
'ME': 0,
|
'ME': 0,
|
||||||
'FRIEND': 1,
|
'FRIEND': 1,
|
||||||
'NOT_SENT': 2
|
'NOT_SENT': 2,
|
||||||
|
'GC_PEER': 3
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: unique message id and ngc support, db name as profile name
|
# TODO: unique message id and ngc support, profile name as db name
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
@ -58,9 +58,8 @@ class Database:
|
|||||||
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 = ToxES.get_instance()
|
if self._toxes.has_password():
|
||||||
if encr.has_password():
|
data = self._toxes.pass_encrypt(data)
|
||||||
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)
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from history.database import MESSAGE_OWNER
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_TYPE = {
|
MESSAGE_TYPE = {
|
||||||
@ -9,28 +10,54 @@ MESSAGE_TYPE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MessageAuthor:
|
||||||
|
|
||||||
|
def __init__(self, author_name, author_type):
|
||||||
|
self.name = author_name
|
||||||
|
self.type = author_type
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
|
|
||||||
def __init__(self, message_id, message_type, owner, time):
|
def __init__(self, message_id, message_type, author, time):
|
||||||
self._time = time
|
self._time = time
|
||||||
self._type = message_type
|
self._type = message_type
|
||||||
self._owner = owner
|
self._author = author
|
||||||
self._message_id = message_id
|
self._message_id = message_id
|
||||||
|
self._widget = None
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
def get_owner(self):
|
type = property(get_type)
|
||||||
return self._owner
|
|
||||||
|
|
||||||
def mark_as_sent(self):
|
def get_author(self):
|
||||||
self._owner = 0
|
return self._author
|
||||||
|
|
||||||
|
author = property(get_author)
|
||||||
|
|
||||||
def get_message_id(self):
|
def get_message_id(self):
|
||||||
return self._message_id
|
return self._message_id
|
||||||
|
|
||||||
message_id = property(get_message_id)
|
message_id = property(get_message_id)
|
||||||
|
|
||||||
|
def get_widget(self):
|
||||||
|
if self._widget is None:
|
||||||
|
self._widget = self._create_widget()
|
||||||
|
|
||||||
|
return self._widget
|
||||||
|
|
||||||
|
widget = property(get_widget)
|
||||||
|
|
||||||
|
def remove_widget(self):
|
||||||
|
self._widget = None
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
self._author.author_type = MESSAGE_OWNER['ME']
|
||||||
|
|
||||||
|
def _create_widget(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TextMessage(Message):
|
class TextMessage(Message):
|
||||||
"""
|
"""
|
||||||
@ -38,12 +65,15 @@ class TextMessage(Message):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, id, message, owner, time, message_type):
|
def __init__(self, id, message, owner, time, message_type):
|
||||||
super(TextMessage, self).__init__(id, message_type, owner, time)
|
super().__init__(id, message_type, owner, time)
|
||||||
self._message = message
|
self._message = message
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
return self._message, self._owner, self._time, self._type
|
return self._message, self._owner, self._time, self._type
|
||||||
|
|
||||||
|
def _create_widget(self):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class GroupChatMessage(TextMessage):
|
class GroupChatMessage(TextMessage):
|
||||||
|
|
||||||
@ -61,7 +91,7 @@ class TransferMessage(Message):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, id, owner, time, status, size, name, friend_number, file_number):
|
def __init__(self, id, owner, time, status, size, name, friend_number, file_number):
|
||||||
super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time)
|
super().__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time)
|
||||||
self._status = status
|
self._status = status
|
||||||
self._size = size
|
self._size = size
|
||||||
self._file_name = name
|
self._file_name = name
|
||||||
@ -88,7 +118,7 @@ class TransferMessage(Message):
|
|||||||
|
|
||||||
class UnsentFile(Message):
|
class UnsentFile(Message):
|
||||||
def __init__(self, id, path, data, time):
|
def __init__(self, id, path, data, time):
|
||||||
super(UnsentFile, self).__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time)
|
super().__init__(id, MESSAGE_TYPE['FILE_TRANSFER'], 0, time)
|
||||||
self._data, self._path = data, path
|
self._data, self._path = data, path
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
@ -104,7 +134,7 @@ class InlineImage(Message):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, id, data):
|
def __init__(self, id, data):
|
||||||
super(InlineImage, self).__init__(id, MESSAGE_TYPE['INLINE'], None, None)
|
super().__init__(id, MESSAGE_TYPE['INLINE'], None, None)
|
||||||
self._data = data
|
self._data = data
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
@ -114,4 +144,4 @@ class InlineImage(Message):
|
|||||||
class InfoMessage(TextMessage):
|
class InfoMessage(TextMessage):
|
||||||
|
|
||||||
def __init__(self, id, message, time):
|
def __init__(self, id, message, time):
|
||||||
super(InfoMessage, self).__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE'])
|
super().__init__(id, message, None, time, MESSAGE_TYPE['INFO_MESSAGE'])
|
||||||
|
@ -18,13 +18,13 @@ class BaseThread(threading.Thread):
|
|||||||
|
|
||||||
class InitThread(BaseThread):
|
class InitThread(BaseThread):
|
||||||
|
|
||||||
def __init__(self, tox, plugin_loader):
|
def __init__(self, tox, plugin_loader, settings):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._tox, self._plugin_loader = tox, plugin_loader
|
self._tox, self._plugin_loader, self._settings = tox, plugin_loader, settings
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
# download list of nodes if needed
|
# download list of nodes if needed
|
||||||
download_nodes_list()
|
download_nodes_list(self._settings)
|
||||||
# start plugins
|
# start plugins
|
||||||
self._plugin_loader.load()
|
self._plugin_loader.load()
|
||||||
# bootstrap
|
# bootstrap
|
||||||
|
@ -25,7 +25,7 @@ class SmileyLoader:
|
|||||||
pack_name = self._settings['smiley_pack']
|
pack_name = self._settings['smiley_pack']
|
||||||
if self._settings['smileys'] and self._curr_pack != pack_name:
|
if self._settings['smileys'] and self._curr_pack != pack_name:
|
||||||
self._curr_pack = pack_name
|
self._curr_pack = pack_name
|
||||||
path = self.get_smileys_path() + 'config.json'
|
path = util.join_path(self.get_smileys_path(), 'config.json')
|
||||||
try:
|
try:
|
||||||
with open(path, encoding='utf8') as fl:
|
with open(path, encoding='utf8') as fl:
|
||||||
self._smileys = json.loads(fl.read())
|
self._smileys = json.loads(fl.read())
|
||||||
@ -45,7 +45,7 @@ class SmileyLoader:
|
|||||||
print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex))
|
print('Smiley pack {} was not loaded. Error: {}'.format(pack_name, ex))
|
||||||
|
|
||||||
def get_smileys_path(self):
|
def get_smileys_path(self):
|
||||||
return util.curr_directory() + '/smileys/' + self._curr_pack + '/' if self._curr_pack is not None else None
|
return util.join_path(util.get_smileys_directory(), self._curr_pack) if self._curr_pack is not None else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_packs_list(self):
|
def get_packs_list(self):
|
||||||
|
@ -3,9 +3,9 @@ from ui.list_items import *
|
|||||||
|
|
||||||
class ItemsFactory:
|
class ItemsFactory:
|
||||||
|
|
||||||
def __init__(self, friends_list, messages):
|
def __init__(self, settings, plugin_loader, smiley_loader, main_screen):
|
||||||
self._friends = friends_list
|
self._settings, self._plugin_loader = settings, plugin_loader
|
||||||
self._messages = messages
|
self._smiley_loader, self._main_screen = smiley_loader, main_screen
|
||||||
|
|
||||||
def friend_item(self):
|
def friend_item(self):
|
||||||
item = ContactItem()
|
item = ContactItem()
|
||||||
|
@ -10,208 +10,6 @@ from user_data import settings
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
class MessageEdit(QtWidgets.QTextBrowser):
|
|
||||||
|
|
||||||
def __init__(self, text, width, message_type, parent=None):
|
|
||||||
super(MessageEdit, self).__init__(parent)
|
|
||||||
self.urls = {}
|
|
||||||
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
||||||
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
|
||||||
self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
|
|
||||||
self.document().setTextWidth(width)
|
|
||||||
self.setOpenExternalLinks(True)
|
|
||||||
self.setAcceptRichText(True)
|
|
||||||
self.setOpenLinks(False)
|
|
||||||
path = smileys.SmileyLoader.get_instance().get_smileys_path()
|
|
||||||
if path is not None:
|
|
||||||
self.setSearchPaths([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.setFamily(settings.Settings.get_instance()['font'])
|
|
||||||
font.setPixelSize(settings.Settings.get_instance()['message_font_size'])
|
|
||||||
font.setBold(False)
|
|
||||||
self.setFont(font)
|
|
||||||
self.resize(width, self.document().size().height())
|
|
||||||
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()))
|
|
||||||
quote = menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Quote selected text'))
|
|
||||||
quote.triggered.connect(self.quote_text)
|
|
||||||
text = self.textCursor().selection().toPlainText()
|
|
||||||
if not text:
|
|
||||||
quote.setEnabled(False)
|
|
||||||
else:
|
|
||||||
import plugin_support
|
|
||||||
submenu = plugin_support.PluginLoader.get_instance().get_message_menu(menu, text)
|
|
||||||
if len(submenu):
|
|
||||||
plug = menu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins'))
|
|
||||||
plug.addActions(submenu)
|
|
||||||
menu.popup(event.globalPos())
|
|
||||||
menu.exec_(event.globalPos())
|
|
||||||
del menu
|
|
||||||
|
|
||||||
def quote_text(self):
|
|
||||||
text = self.textCursor().selection().toPlainText()
|
|
||||||
if text:
|
|
||||||
from ui import main_screen
|
|
||||||
window = main_screen.MainWindow.get_instance()
|
|
||||||
text = '>' + '\n>'.join(text.split('\n'))
|
|
||||||
if window.messageEdit.toPlainText():
|
|
||||||
text = '\n' + text
|
|
||||||
window.messageEdit.appendPlainText(text)
|
|
||||||
|
|
||||||
def on_anchor_clicked(self, url):
|
|
||||||
text = str(url.toString())
|
|
||||||
if text.startswith('tox:'):
|
|
||||||
from ui 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 = h.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(QtWidgets.QWidget):
|
|
||||||
"""
|
|
||||||
Message in messages list
|
|
||||||
"""
|
|
||||||
def __init__(self, text, time, user='', sent=True, message_type=TOX_MESSAGE_TYPE['NORMAL'], parent=None):
|
|
||||||
QtWidgets.QWidget.__init__(self, parent)
|
|
||||||
self.name = DataLabel(self)
|
|
||||||
self.name.setGeometry(QtCore.QRect(2, 2, 95, 23))
|
|
||||||
self.name.setTextFormat(QtCore.Qt.PlainText)
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setFamily(settings.Settings.get_instance()['font'])
|
|
||||||
font.setPointSize(11)
|
|
||||||
font.setBold(True)
|
|
||||||
self.name.setFont(font)
|
|
||||||
self.name.setText(user)
|
|
||||||
|
|
||||||
self.time = QtWidgets.QLabel(self)
|
|
||||||
self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25))
|
|
||||||
font.setPointSize(10)
|
|
||||||
font.setBold(False)
|
|
||||||
self.time.setFont(font)
|
|
||||||
self._time = time
|
|
||||||
if not sent:
|
|
||||||
movie = QtGui.QMovie(curr_directory() + '/images/spinner.gif')
|
|
||||||
self.time.setMovie(movie)
|
|
||||||
movie.start()
|
|
||||||
self.t = True
|
|
||||||
else:
|
|
||||||
self.time.setText(convert_time(time))
|
|
||||||
self.t = False
|
|
||||||
|
|
||||||
self.message = MessageEdit(text, parent.width() - 160, 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() - 160, self.message.height()))
|
|
||||||
self.setFixedHeight(self.message.height())
|
|
||||||
|
|
||||||
def mouseReleaseEvent(self, event):
|
|
||||||
if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x():
|
|
||||||
self.listMenu = QtWidgets.QMenu()
|
|
||||||
delete_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Delete message'))
|
|
||||||
delete_item.triggered.connect(self.delete)
|
|
||||||
parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0))
|
|
||||||
self.listMenu.move(parent_position)
|
|
||||||
self.listMenu.show()
|
|
||||||
|
|
||||||
def delete(self):
|
|
||||||
pr = profile.Profile.get_instance()
|
|
||||||
pr.delete_message(self._time)
|
|
||||||
|
|
||||||
def mark_as_sent(self):
|
|
||||||
if self.t:
|
|
||||||
self.time.setText(convert_time(self._time))
|
|
||||||
self.t = False
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def set_avatar(self, pixmap):
|
|
||||||
self.name.setAlignment(QtCore.Qt.AlignCenter)
|
|
||||||
self.message.setAlignment(QtCore.Qt.AlignVCenter)
|
|
||||||
self.setFixedHeight(max(self.height(), 36))
|
|
||||||
self.name.setFixedHeight(self.height())
|
|
||||||
self.message.setFixedHeight(self.height())
|
|
||||||
self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
|
|
||||||
|
|
||||||
def select_text(self, text):
|
|
||||||
tmp = self.message.toHtml()
|
|
||||||
text = h.escape(text)
|
|
||||||
strings = re.findall(text, tmp, flags=re.IGNORECASE)
|
|
||||||
for s in strings:
|
|
||||||
tmp = self.replace_all(tmp, s)
|
|
||||||
self.message.setHtml(tmp)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def replace_all(text, substring):
|
|
||||||
i, l = 0, len(substring)
|
|
||||||
while i < len(text) - l + 1:
|
|
||||||
index = text[i:].find(substring)
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
i += index
|
|
||||||
lgt, rgt = text[i:].find('<'), text[i:].find('>')
|
|
||||||
if rgt < lgt:
|
|
||||||
i += rgt + 1
|
|
||||||
continue
|
|
||||||
sub = '<font color="red"><b>{}</b></font>'.format(substring)
|
|
||||||
text = text[:i] + sub + text[i + l:]
|
|
||||||
i += len(sub)
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
class ContactItem(QtWidgets.QWidget):
|
class ContactItem(QtWidgets.QWidget):
|
||||||
"""
|
"""
|
||||||
Contact in friends list
|
Contact in friends list
|
||||||
|
@ -21,6 +21,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self._saved = False
|
self._saved = False
|
||||||
if settings['show_welcome_screen']:
|
if settings['show_welcome_screen']:
|
||||||
self.ws = WelcomeScreen()
|
self.ws = WelcomeScreen()
|
||||||
|
self.profile = None
|
||||||
|
|
||||||
def setup_menu(self, window):
|
def setup_menu(self, window):
|
||||||
self.menubar = QtWidgets.QMenuBar(window)
|
self.menubar = QtWidgets.QMenuBar(window)
|
||||||
@ -108,42 +109,42 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
if event.type() == QtCore.QEvent.WindowActivate:
|
if event.type() == QtCore.QEvent.WindowActivate:
|
||||||
self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
self.tray.setIcon(QtGui.QIcon(curr_directory() + '/images/icon.png'))
|
||||||
self.messages.repaint()
|
self.messages.repaint()
|
||||||
return super(MainWindow, self).event(event)
|
return super().event(event)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.lockApp.setText(QtWidgets.QApplication.translate("MainWindow", "Lock"))
|
self.lockApp.setText(util_ui.tr("Lock"))
|
||||||
self.menuPlugins.setTitle(QtWidgets.QApplication.translate("MainWindow", "Plugins"))
|
self.menuPlugins.setTitle(util_ui.tr("Plugins"))
|
||||||
self.pluginData.setText(QtWidgets.QApplication.translate("MainWindow", "List of plugins"))
|
self.pluginData.setText(util_ui.tr("List of plugins"))
|
||||||
self.menuProfile.setTitle(QtWidgets.QApplication.translate("MainWindow", "Profile"))
|
self.menuProfile.setTitle(util_ui.tr("Profile"))
|
||||||
self.menuSettings.setTitle(QtWidgets.QApplication.translate("MainWindow", "Settings"))
|
self.menuSettings.setTitle(util_ui.tr("Settings"))
|
||||||
self.menuAbout.setTitle(QtWidgets.QApplication.translate("MainWindow", "About"))
|
self.menuAbout.setTitle(util_ui.tr("About"))
|
||||||
self.actionAdd_friend.setText(QtWidgets.QApplication.translate("MainWindow", "Add contact"))
|
self.actionAdd_friend.setText(util_ui.tr("Add contact"))
|
||||||
self.actionAdd_gc.setText(QtWidgets.QApplication.translate("MainWindow", "Create group chat"))
|
self.actionAdd_gc.setText(util_ui.tr("Create group chat"))
|
||||||
self.actionprofilesettings.setText(QtWidgets.QApplication.translate("MainWindow", "Profile"))
|
self.actionprofilesettings.setText(util_ui.tr("Profile"))
|
||||||
self.actionPrivacy_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Privacy"))
|
self.actionPrivacy_settings.setText(util_ui.tr("Privacy"))
|
||||||
self.actionInterface_settings.setText(QtWidgets.QApplication.translate("MainWindow", "Interface"))
|
self.actionInterface_settings.setText(util_ui.tr("Interface"))
|
||||||
self.actionNotifications.setText(QtWidgets.QApplication.translate("MainWindow", "Notifications"))
|
self.actionNotifications.setText(util_ui.tr("Notifications"))
|
||||||
self.actionNetwork.setText(QtWidgets.QApplication.translate("MainWindow", "Network"))
|
self.actionNetwork.setText(util_ui.tr("Network"))
|
||||||
self.actionAbout_program.setText(QtWidgets.QApplication.translate("MainWindow", "About program"))
|
self.actionAbout_program.setText(util_ui.tr("About program"))
|
||||||
self.actionSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Settings"))
|
self.actionSettings.setText(util_ui.tr("Settings"))
|
||||||
self.audioSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Audio"))
|
self.audioSettings.setText(util_ui.tr("Audio"))
|
||||||
self.videoSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Video"))
|
self.videoSettings.setText(util_ui.tr("Video"))
|
||||||
self.updateSettings.setText(QtWidgets.QApplication.translate("MainWindow", "Updates"))
|
self.updateSettings.setText(util_ui.tr("Updates"))
|
||||||
self.contact_name.setPlaceholderText(QtWidgets.QApplication.translate("MainWindow", "Search"))
|
self.contact_name.setPlaceholderText(util_ui.tr("Search"))
|
||||||
self.sendMessageButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Send message"))
|
self.sendMessageButton.setToolTip(util_ui.tr("Send message"))
|
||||||
self.callButton.setToolTip(QtWidgets.QApplication.translate("MainWindow", "Start audio call with friend"))
|
self.callButton.setToolTip(util_ui.tr("Start audio call with friend"))
|
||||||
self.online_contacts.clear()
|
self.online_contacts.clear()
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "All"))
|
self.online_contacts.addItem(util_ui.tr("All"))
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online"))
|
self.online_contacts.addItem(util_ui.tr("Online"))
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first"))
|
self.online_contacts.addItem(util_ui.tr("Online first"))
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Name"))
|
self.online_contacts.addItem(util_ui.tr("Name"))
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online and by name"))
|
self.online_contacts.addItem(util_ui.tr("Online and by name"))
|
||||||
self.online_contacts.addItem(QtWidgets.QApplication.translate("MainWindow", "Online first and by name"))
|
self.online_contacts.addItem(util_ui.tr("Online first and by name"))
|
||||||
ind = self._settings['sorting']
|
ind = self._settings['sorting']
|
||||||
d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5}
|
d = {0: 0, 1: 1, 2: 2, 3: 4, 1 | 4: 4, 2 | 4: 5}
|
||||||
self.online_contacts.setCurrentIndex(d[ind])
|
self.online_contacts.setCurrentIndex(d[ind])
|
||||||
self.importPlugin.setText(QtWidgets.QApplication.translate("MainWindow", "Import plugin"))
|
self.importPlugin.setText(util_ui.tr("Import plugin"))
|
||||||
self.reloadPlugins.setText(QtWidgets.QApplication.translate("MainWindow", "Reload plugins"))
|
self.reloadPlugins.setText(util_ui.tr("Reload plugins"))
|
||||||
|
|
||||||
def setup_right_bottom(self, Form):
|
def setup_right_bottom(self, Form):
|
||||||
Form.resize(650, 60)
|
Form.resize(650, 60)
|
||||||
@ -353,20 +354,19 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.user_info = name
|
self.user_info = name
|
||||||
self.friend_info = info
|
self.friend_info = info
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.profile = Profile(tox, self)
|
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
s = Settings.get_instance()
|
if not self._settings['close_to_tray'] or self._settings.closing:
|
||||||
if not s['close_to_tray'] or s.closing:
|
if self._saved:
|
||||||
if not self._saved:
|
return
|
||||||
self._saved = True
|
self._saved = True
|
||||||
self.profile.save_history()
|
self.profile.save_history()
|
||||||
self.profile.close()
|
self.profile.close()
|
||||||
s['x'] = self.geometry().x()
|
self._settings['x'] = self.geometry().x()
|
||||||
s['y'] = self.geometry().y()
|
self._settings['y'] = self.geometry().y()
|
||||||
s['width'] = self.width()
|
self._settings['width'] = self.width()
|
||||||
s['height'] = self.height()
|
self._settings['height'] = self.height()
|
||||||
s.save()
|
self._settings.save()
|
||||||
QtWidgets.QApplication.closeAllWindows()
|
QtWidgets.QApplication.closeAllWindows()
|
||||||
event.accept()
|
event.accept()
|
||||||
elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
|
elif QtWidgets.QSystemTrayIcon.isSystemTrayAvailable():
|
||||||
@ -374,7 +374,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
def close_window(self):
|
def close_window(self):
|
||||||
Settings.get_instance().closing = True
|
self._settings.closing = True
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def resizeEvent(self, *args, **kwargs):
|
def resizeEvent(self, *args, **kwargs):
|
||||||
@ -471,7 +471,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def import_plugin(self):
|
def import_plugin(self):
|
||||||
import util
|
import util
|
||||||
directory = QtWidgets.QFileDialog.getExistingDirectory(self,
|
directory = QtWidgets.QFileDialog.getExistingDirectory(self,
|
||||||
QtWidgets.QApplication.translate("MainWindow", 'Choose folder with plugin'),
|
util_ui.tr('Choose folder with plugin'),
|
||||||
util.curr_directory(),
|
util.curr_directory(),
|
||||||
QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
|
QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontUseNativeDialog)
|
||||||
if directory:
|
if directory:
|
||||||
@ -480,9 +480,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
util.copy(src, dest)
|
util.copy(src, dest)
|
||||||
msgBox = QtWidgets.QMessageBox()
|
msgBox = QtWidgets.QMessageBox()
|
||||||
msgBox.setWindowTitle(
|
msgBox.setWindowTitle(
|
||||||
QtWidgets.QApplication.translate("MainWindow", "Restart Toxygen"))
|
util_ui.tr("Restart Toxygen"))
|
||||||
msgBox.setText(
|
msgBox.setText(
|
||||||
QtWidgets.QApplication.translate("MainWindow", 'Plugin will be loaded after restart'))
|
util_ui.tr('Plugin will be loaded after restart'))
|
||||||
msgBox.exec_()
|
msgBox.exec_()
|
||||||
|
|
||||||
def lock_app(self):
|
def lock_app(self):
|
||||||
@ -492,9 +492,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
else:
|
else:
|
||||||
msgBox = QtWidgets.QMessageBox()
|
msgBox = QtWidgets.QMessageBox()
|
||||||
msgBox.setWindowTitle(
|
msgBox.setWindowTitle(
|
||||||
QtWidgets.QApplication.translate("MainWindow", "Cannot lock app"))
|
util_ui.tr("Cannot lock app"))
|
||||||
msgBox.setText(
|
msgBox.setText(
|
||||||
QtWidgets.QApplication.translate("MainWindow", 'Error. Profile password is not set.'))
|
util_ui.tr('Error. Profile password is not set.'))
|
||||||
msgBox.exec_()
|
msgBox.exec_()
|
||||||
|
|
||||||
def show_menu(self):
|
def show_menu(self):
|
||||||
@ -517,7 +517,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def send_file(self):
|
def send_file(self):
|
||||||
self.menu.hide()
|
self.menu.hide()
|
||||||
if self.profile.active_friend + 1and self.profile.is_active_a_friend():
|
if self.profile.active_friend + 1and self.profile.is_active_a_friend():
|
||||||
choose = QtWidgets.QApplication.translate("MainWindow", 'Choose file')
|
choose = util_ui.tr('Choose file')
|
||||||
name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog)
|
name = QtWidgets.QFileDialog.getOpenFileName(self, choose, options=QtWidgets.QFileDialog.DontUseNativeDialog)
|
||||||
if name[0]:
|
if name[0]:
|
||||||
self.profile.send_file(name[0])
|
self.profile.send_file(name[0])
|
||||||
@ -583,33 +583,33 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
return
|
return
|
||||||
settings = Settings.get_instance()
|
settings = Settings.get_instance()
|
||||||
allowed = friend.tox_id in settings['auto_accept_from_friends']
|
allowed = friend.tox_id in settings['auto_accept_from_friends']
|
||||||
auto = QtWidgets.QApplication.translate("MainWindow", 'Disallow auto accept') if allowed else QtWidgets.QApplication.translate("MainWindow", 'Allow auto accept')
|
auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept')
|
||||||
if item is not None:
|
if item is not None:
|
||||||
self.listMenu = QtWidgets.QMenu()
|
self.listMenu = QtWidgets.QMenu()
|
||||||
is_friend = type(friend) is Friend
|
is_friend = type(friend) is Friend
|
||||||
if is_friend:
|
if is_friend:
|
||||||
set_alias_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set alias'))
|
set_alias_item = self.listMenu.addAction(util_ui.tr('Set alias'))
|
||||||
set_alias_item.triggered.connect(lambda: self.set_alias(num))
|
set_alias_item.triggered.connect(lambda: self.set_alias(num))
|
||||||
|
|
||||||
history_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Chat history'))
|
history_menu = self.listMenu.addMenu(util_ui.tr('Chat history'))
|
||||||
clear_history_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Clear history'))
|
clear_history_item = history_menu.addAction(util_ui.tr('Clear history'))
|
||||||
export_to_text_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as text'))
|
export_to_text_item = history_menu.addAction(util_ui.tr('Export as text'))
|
||||||
export_to_html_item = history_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Export as HTML'))
|
export_to_html_item = history_menu.addAction(util_ui.tr('Export as HTML'))
|
||||||
|
|
||||||
copy_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Copy'))
|
copy_menu = self.listMenu.addMenu(util_ui.tr('Copy'))
|
||||||
copy_name_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Name'))
|
copy_name_item = copy_menu.addAction(util_ui.tr('Name'))
|
||||||
copy_status_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Status message'))
|
copy_status_item = copy_menu.addAction(util_ui.tr('Status message'))
|
||||||
if is_friend:
|
if is_friend:
|
||||||
copy_key_item = copy_menu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Public key'))
|
copy_key_item = copy_menu.addAction(util_ui.tr('Public key'))
|
||||||
|
|
||||||
auto_accept_item = self.listMenu.addAction(auto)
|
auto_accept_item = self.listMenu.addAction(auto)
|
||||||
remove_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Remove friend'))
|
remove_item = self.listMenu.addAction(util_ui.tr('Remove friend'))
|
||||||
block_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Block friend'))
|
block_item = self.listMenu.addAction(util_ui.tr('Block friend'))
|
||||||
notes_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Notes'))
|
notes_item = self.listMenu.addAction(util_ui.tr('Notes'))
|
||||||
|
|
||||||
chats = self.profile.get_group_chats()
|
chats = self.profile.get_group_chats()
|
||||||
if len(chats) and self.profile.is_active_online():
|
if len(chats) and self.profile.is_active_online():
|
||||||
invite_menu = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Invite to group chat'))
|
invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat'))
|
||||||
for i in range(len(chats)):
|
for i in range(len(chats)):
|
||||||
name, number = chats[i]
|
name, number = chats[i]
|
||||||
item = invite_menu.addAction(name)
|
item = invite_menu.addAction(name)
|
||||||
@ -619,7 +619,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
if plugins_loader is not None:
|
if plugins_loader is not None:
|
||||||
submenu = plugins_loader.get_menu(self.listMenu, num)
|
submenu = plugins_loader.get_menu(self.listMenu, num)
|
||||||
if len(submenu):
|
if len(submenu):
|
||||||
plug = self.listMenu.addMenu(QtWidgets.QApplication.translate("MainWindow", 'Plugins'))
|
plug = self.listMenu.addMenu(util_ui.tr('Plugins'))
|
||||||
plug.addActions(submenu)
|
plug.addActions(submenu)
|
||||||
copy_key_item.triggered.connect(lambda: self.copy_friend_key(num))
|
copy_key_item.triggered.connect(lambda: self.copy_friend_key(num))
|
||||||
remove_item.triggered.connect(lambda: self.remove_friend(num))
|
remove_item.triggered.connect(lambda: self.remove_friend(num))
|
||||||
@ -627,8 +627,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed))
|
auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed))
|
||||||
notes_item.triggered.connect(lambda: self.show_note(friend))
|
notes_item.triggered.connect(lambda: self.show_note(friend))
|
||||||
else:
|
else:
|
||||||
leave_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Leave chat'))
|
leave_item = self.listMenu.addAction(util_ui.tr('Leave chat'))
|
||||||
set_title_item = self.listMenu.addAction(QtWidgets.QApplication.translate("MainWindow", 'Set title'))
|
set_title_item = self.listMenu.addAction(util_ui.tr('Set title'))
|
||||||
leave_item.triggered.connect(lambda: self.leave_gc(num))
|
leave_item.triggered.connect(lambda: self.leave_gc(num))
|
||||||
set_title_item.triggered.connect(lambda: self.set_title(num))
|
set_title_item.triggered.connect(lambda: self.set_title(num))
|
||||||
clear_history_item.triggered.connect(lambda: self.clear_history(num))
|
clear_history_item.triggered.connect(lambda: self.clear_history(num))
|
||||||
@ -643,7 +643,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|||||||
def show_note(self, friend):
|
def show_note(self, friend):
|
||||||
s = Settings.get_instance()
|
s = Settings.get_instance()
|
||||||
note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else ''
|
note = s['notes'][friend.tox_id] if friend.tox_id in s['notes'] else ''
|
||||||
user = QtWidgets.QApplication.translate("MainWindow", 'Notes about user')
|
user = util_ui.tr('Notes about user')
|
||||||
user = '{} {}'.format(user, friend.name)
|
user = '{} {}'.format(user, friend.name)
|
||||||
|
|
||||||
def save_note(text):
|
def save_note(text):
|
||||||
|
@ -16,6 +16,7 @@ class AddContact(CenteredWidget):
|
|||||||
super(AddContact, self).__init__()
|
super(AddContact, self).__init__()
|
||||||
self.initUI(tox_id)
|
self.initUI(tox_id)
|
||||||
self._adding = False
|
self._adding = False
|
||||||
|
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||||
|
|
||||||
def initUI(self, tox_id):
|
def initUI(self, tox_id):
|
||||||
self.setObjectName('AddContact')
|
self.setObjectName('AddContact')
|
||||||
|
212
toxygen/ui/messages_widgets.py
Normal file
212
toxygen/ui/messages_widgets.py
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
|
from wrapper.toxcore_enums_and_consts import *
|
||||||
|
import ui.widgets as widgets
|
||||||
|
import util.ui as util_ui
|
||||||
|
import util.util as util
|
||||||
|
import ui.menu as menu
|
||||||
|
import html as h
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class MessageEdit(QtWidgets.QTextBrowser):
|
||||||
|
|
||||||
|
def __init__(self, settings, message_edit, smileys_loader, plugin_loader, text, width, message_type, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.urls = {}
|
||||||
|
self._message_edit = message_edit
|
||||||
|
self._smileys_loader = smileys_loader
|
||||||
|
self._plugin_loader = plugin_loader
|
||||||
|
self._add_contact = None
|
||||||
|
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
|
||||||
|
self.setWordWrapMode(QtGui.QTextOption.WrapAtWordBoundaryOrAnywhere)
|
||||||
|
self.document().setTextWidth(width)
|
||||||
|
self.setOpenExternalLinks(True)
|
||||||
|
self.setAcceptRichText(True)
|
||||||
|
self.setOpenLinks(False)
|
||||||
|
path = smileys_loader.get_smileys_path()
|
||||||
|
if path is not None:
|
||||||
|
self.setSearchPaths([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.setFamily(settings['font'])
|
||||||
|
font.setPixelSize(settings['message_font_size'])
|
||||||
|
font.setBold(False)
|
||||||
|
self.setFont(font)
|
||||||
|
self.resize(width, self.document().size().height())
|
||||||
|
self.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)
|
||||||
|
self.anchorClicked.connect(self.on_anchor_clicked)
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
menu = widgets.create_menu(self.createStandardContextMenu(event.pos()))
|
||||||
|
quote = menu.addAction(util_ui.tr('Quote selected text'))
|
||||||
|
quote.triggered.connect(self.quote_text)
|
||||||
|
text = self.textCursor().selection().toPlainText()
|
||||||
|
if not text:
|
||||||
|
quote.setEnabled(False)
|
||||||
|
else:
|
||||||
|
sub_menu = self._plugin_loader.get_message_menu(menu, text)
|
||||||
|
if len(sub_menu):
|
||||||
|
plugins_menu = menu.addMenu(util_ui.tr('Plugins'))
|
||||||
|
plugins_menu.addActions(sub_menu)
|
||||||
|
menu.popup(event.globalPos())
|
||||||
|
menu.exec_(event.globalPos())
|
||||||
|
del menu
|
||||||
|
|
||||||
|
def quote_text(self):
|
||||||
|
text = self.textCursor().selection().toPlainText()
|
||||||
|
if not text:
|
||||||
|
return
|
||||||
|
text = '>' + '\n>'.join(text.split('\n'))
|
||||||
|
if self._message_edit.toPlainText():
|
||||||
|
text = '\n' + text
|
||||||
|
self._message_edit.appendPlainText(text)
|
||||||
|
|
||||||
|
def on_anchor_clicked(self, url):
|
||||||
|
text = str(url.toString())
|
||||||
|
if text.startswith('tox:'):
|
||||||
|
self._add_contact = menu.AddContact(text[4:])
|
||||||
|
self._add_contact.show()
|
||||||
|
else:
|
||||||
|
QtGui.QDesktopServices.openUrl(url)
|
||||||
|
self.clearFocus()
|
||||||
|
|
||||||
|
def addAnimation(self, url, file_name):
|
||||||
|
movie = QtGui.QMovie(self)
|
||||||
|
movie.setFileName(file_name)
|
||||||
|
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 = h.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 = self._smileys_loader.add_smileys_to_text(text, self) # smileys
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class MessageItem(QtWidgets.QWidget):
|
||||||
|
"""
|
||||||
|
Message in messages list
|
||||||
|
"""
|
||||||
|
def __init__(self, settings, message_edit_factory, text_message, parent=None):
|
||||||
|
QtWidgets.QWidget.__init__(self, parent)
|
||||||
|
self.name = widgets.DataLabel(self)
|
||||||
|
self.name.setGeometry(QtCore.QRect(2, 2, 95, 23))
|
||||||
|
self.name.setTextFormat(QtCore.Qt.PlainText)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setFamily(settings['font'])
|
||||||
|
font.setPointSize(11)
|
||||||
|
font.setBold(True)
|
||||||
|
self.name.setFont(font)
|
||||||
|
self.name.setText(text_message.user)
|
||||||
|
|
||||||
|
self.time = QtWidgets.QLabel(self)
|
||||||
|
self.time.setGeometry(QtCore.QRect(parent.width() - 60, 0, 50, 25))
|
||||||
|
font.setPointSize(10)
|
||||||
|
font.setBold(False)
|
||||||
|
self.time.setFont(font)
|
||||||
|
self._time = time
|
||||||
|
if not sent:
|
||||||
|
movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif'))
|
||||||
|
self.time.setMovie(movie)
|
||||||
|
movie.start()
|
||||||
|
self.t = True
|
||||||
|
else:
|
||||||
|
self.time.setText(util.convert_time(time))
|
||||||
|
self.t = False
|
||||||
|
|
||||||
|
self.message = MessageEdit(text, parent.width() - 160, 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() - 160, self.message.height()))
|
||||||
|
self.setFixedHeight(self.message.height())
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, event):
|
||||||
|
if event.button() == QtCore.Qt.RightButton and event.x() > self.time.x():
|
||||||
|
self.listMenu = QtWidgets.QMenu()
|
||||||
|
delete_item = self.listMenu.addAction(util_ui.tr('Delete message'))
|
||||||
|
delete_item.triggered.connect(self.delete)
|
||||||
|
parent_position = self.time.mapToGlobal(QtCore.QPoint(0, 0))
|
||||||
|
self.listMenu.move(parent_position)
|
||||||
|
self.listMenu.show()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
pr = profile.Profile.get_instance()
|
||||||
|
pr.delete_message(self._time)
|
||||||
|
|
||||||
|
def mark_as_sent(self):
|
||||||
|
if self.t:
|
||||||
|
self.time.setText(convert_time(self._time))
|
||||||
|
self.t = False
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_avatar(self, pixmap):
|
||||||
|
self.name.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
self.message.setAlignment(QtCore.Qt.AlignVCenter)
|
||||||
|
self.setFixedHeight(max(self.height(), 36))
|
||||||
|
self.name.setFixedHeight(self.height())
|
||||||
|
self.message.setFixedHeight(self.height())
|
||||||
|
self.name.setPixmap(pixmap.scaled(30, 30, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation))
|
||||||
|
|
||||||
|
def select_text(self, text):
|
||||||
|
tmp = self.message.toHtml()
|
||||||
|
text = h.escape(text)
|
||||||
|
strings = re.findall(text, tmp, flags=re.IGNORECASE)
|
||||||
|
for s in strings:
|
||||||
|
tmp = self.replace_all(tmp, s)
|
||||||
|
self.message.setHtml(tmp)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def replace_all(text, substring):
|
||||||
|
i, l = 0, len(substring)
|
||||||
|
while i < len(text) - l + 1:
|
||||||
|
index = text[i:].find(substring)
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
i += index
|
||||||
|
lgt, rgt = text[i:].find('<'), text[i:].find('>')
|
||||||
|
if rgt < lgt:
|
||||||
|
i += rgt + 1
|
||||||
|
continue
|
||||||
|
sub = '<font color="red"><b>{}</b></font>'.format(substring)
|
||||||
|
text = text[:i] + sub + text[i + l:]
|
||||||
|
i += len(sub)
|
||||||
|
return text
|
||||||
|
|
@ -66,14 +66,14 @@ class QRightClickButton(QtWidgets.QPushButton):
|
|||||||
|
|
||||||
rightClicked = QtCore.pyqtSignal()
|
rightClicked = QtCore.pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent=None):
|
||||||
super(QRightClickButton, self).__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
if event.button() == QtCore.Qt.RightButton:
|
if event.button() == QtCore.Qt.RightButton:
|
||||||
self.rightClicked.emit()
|
self.rightClicked.emit()
|
||||||
else:
|
else:
|
||||||
super(QRightClickButton, self).mousePressEvent(event)
|
super().mousePressEvent(event)
|
||||||
|
|
||||||
|
|
||||||
class RubberBand(QtWidgets.QRubberBand):
|
class RubberBand(QtWidgets.QRubberBand):
|
||||||
|
@ -11,11 +11,11 @@ class ProfileManager:
|
|||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._toxes = toxes
|
self._toxes = toxes
|
||||||
self._path = path
|
self._path = path
|
||||||
self._directory = os.path.basename(path)
|
self._directory = os.path.dirname(path)
|
||||||
# create /avatars if not exists:
|
# create /avatars if not exists:
|
||||||
directory = util.join_path(self._directory, 'avatars')
|
avatars_directory = util.join_path(self._directory, 'avatars')
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(avatars_directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(avatars_directory)
|
||||||
|
|
||||||
def open_profile(self):
|
def open_profile(self):
|
||||||
with open(self._path, 'rb') as fl:
|
with open(self._path, 'rb') as fl:
|
||||||
|
@ -55,6 +55,11 @@ def get_stickers_directory():
|
|||||||
return get_app_directory('stickers')
|
return get_app_directory('stickers')
|
||||||
|
|
||||||
|
|
||||||
|
@cached
|
||||||
|
def get_smileys_directory():
|
||||||
|
return get_app_directory('smileys')
|
||||||
|
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def get_translations_directory():
|
def get_translations_directory():
|
||||||
return get_app_directory('translations')
|
return get_app_directory('translations')
|
||||||
@ -143,3 +148,12 @@ def is_re_valid(regex):
|
|||||||
|
|
||||||
def get_platform():
|
def get_platform():
|
||||||
return platform.system()
|
return platform.system()
|
||||||
|
|
||||||
|
|
||||||
|
class ToxSave:
|
||||||
|
|
||||||
|
def __init__(self, tox):
|
||||||
|
self._tox = tox
|
||||||
|
|
||||||
|
def set_tox(self, tox):
|
||||||
|
self._tox = tox
|
||||||
|
Loading…
Reference in New Issue
Block a user