diff --git a/toxygen/app.py b/toxygen/app.py index ce15093..40fb7c6 100644 --- a/toxygen/app.py +++ b/toxygen/app.py @@ -323,7 +323,7 @@ class App: self._toxes, self._version) self._tray = tray.init_tray(profile, self._settings, self._ms) self._ms.set_dependencies(widgets_factory, self._tray, self._contacts_manager, self._messenger, profile, - self._plugin_loader) + self._plugin_loader, self._file_transfer_handler) self._tray.show() self._ms.show() diff --git a/toxygen/common/event.py b/toxygen/common/event.py index e3ecbf9..687a34d 100644 --- a/toxygen/common/event.py +++ b/toxygen/common/event.py @@ -8,9 +8,13 @@ class Event: def __iadd__(self, callback): self.add_callback(callback) + return self + def __isub__(self, callback): self.remove_callback(callback) + return self + def __call__(self, *args, **kwargs): for callback in self._callbacks: callback(*args, **kwargs) diff --git a/toxygen/contacts/contact.py b/toxygen/contacts/contact.py index c5c0dac..71248db 100644 --- a/toxygen/contacts/contact.py +++ b/toxygen/contacts/contact.py @@ -101,6 +101,9 @@ class Contact(basecontact.BaseContact): for message in self._corr: message.remove_widget() + def get_message(self, _filter): + return list(filter(lambda m: _filter(m), self._corr))[0] + @staticmethod def _get_text_message(params): (message, author_type, author_name, unix_time, message_type, unique_id) = params diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index b4c1803..b826a37 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,8 +1,4 @@ -import utils.util as util -import utils.ui as util_ui from contacts.friend import Friend -from PyQt5 import QtCore, QtGui -from wrapper.toxcore_enums_and_consts import * from messenger.messages import * @@ -71,10 +67,10 @@ class ContactsManager: self._screen.messageEdit.clear() return try: - # self.send_typing(False) # TODO: fix self._screen.typing.setVisible(False) current_contact = self.get_curr_contact() if current_contact is not None: + current_contact.typing_notification_handler.send(self._tox, False) current_contact.remove_messages_widgets() # TODO: if required self._unsubscribe_from_events(current_contact) @@ -96,7 +92,12 @@ class ContactsManager: contact.load_corr() corr = contact.get_corr()[-PAGE_SIZE:] for message in corr: - self._messages_items_factory.create_message_item(message) + if message.type == MESSAGE_TYPE['FILE_TRANSFER']: + self._messages_items_factory.create_file_transfer_item(message) + elif message.type == MESSAGE_TYPE['INLINE']: + self._messages_items_factory.create_inline_item(message.data) + else: + self._messages_items_factory.create_message_item(message) # if value in self._call: # self._screen.active_call() # elif value in self._incoming_calls: diff --git a/toxygen/contacts/friend.py b/toxygen/contacts/friend.py index 9602eb1..4b189a9 100644 --- a/toxygen/contacts/friend.py +++ b/toxygen/contacts/friend.py @@ -18,14 +18,13 @@ class Friend(contact.Contact): # File transfers support # ----------------------------------------------------------------------------------------------------------------- - def update_transfer_data(self, file_number, status, inline=None): + def update_transfer_data(self, file_number, inline): # TODO: rewrite """ Update status of active transfer and load inline if needed """ try: tr = list(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) diff --git a/toxygen/file_transfers/file_transfers.py b/toxygen/file_transfers/file_transfers.py index 91aeb86..92d92a6 100644 --- a/toxygen/file_transfers/file_transfers.py +++ b/toxygen/file_transfers/file_transfers.py @@ -1,7 +1,7 @@ from wrapper.toxcore_enums_and_consts import TOX_FILE_KIND, TOX_FILE_CONTROL from os.path import basename, getsize, exists, dirname from os import remove, rename, chdir -from time import time, sleep +from time import time from wrapper.tox import Tox from common.event import Event @@ -47,7 +47,7 @@ class FileTransfer: self._done = 0 self._state_changed_event = Event() self._finished_event = Event() - self._file_id = None + self._file_id = self._file = None def set_tox(self, tox): self._tox = tox @@ -85,13 +85,12 @@ class FileTransfer: def cancel(self): self.send_control(TOX_FILE_CONTROL['CANCEL']) - if hasattr(self, '_file'): + if self._file is not None: self._file.close() self.signal() def cancelled(self): - if hasattr(self, '_file'): - sleep(0.1) + if self._file is not None: self._file.close() self.state = FILE_TRANSFER_STATE['CANCELLED'] self.signal() diff --git a/toxygen/file_transfers/file_transfers_handler.py b/toxygen/file_transfers/file_transfers_handler.py index 6269295..3d51767 100644 --- a/toxygen/file_transfers/file_transfers_handler.py +++ b/toxygen/file_transfers/file_transfers_handler.py @@ -1,8 +1,5 @@ -from file_transfers.file_transfers import * from messenger.messages import * -from history.database import MESSAGE_AUTHOR from ui.contact_items import * -from PyQt5 import QtWidgets import utils.util as util @@ -46,18 +43,18 @@ class FileTransfersHandler: self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) return self._tox.file_seek(friend_number, file_number, pos) - self.accept_transfer(None, data[0], friend_number, file_number, size, False, pos) + self.accept_transfer(data[0], friend_number, file_number, size, False, pos) elif inline and size < 1024 * 1024: - self.accept_transfer(None, '', friend_number, file_number, size, True) + self.accept_transfer('', friend_number, file_number, size, True) elif auto: 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(path + '/' + file_name, friend_number, file_number, size) else: accepted = False self._file_transfers_message_service.add_incoming_transfer_message( - friend, accepted, size, file_name,file_number) + friend, accepted, size, file_name, file_number) def cancel_transfer(self, friend_number, file_number, already_cancelled=False): """ @@ -66,8 +63,6 @@ class FileTransfersHandler: :param file_number: file number :param already_cancelled: was cancelled by friend """ - i = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['CANCELLED']) if (friend_number, file_number) in self._file_transfers: tr = self._file_transfers[(friend_number, file_number)] if not already_cancelled: @@ -77,15 +72,8 @@ class FileTransfersHandler: if (friend_number, file_number) in self._file_transfers: del tr del self._file_transfers[(friend_number, file_number)] - else: - if not already_cancelled: - self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) - if friend_number == self.get_active_number() and self.is_active_a_friend(): - tmp = self._messages.count() + i - if tmp >= 0: - self._messages.itemWidget( - self._messages.item(tmp)).update_transfer_state(FILE_TRANSFER_STATE['CANCELLED'], - 0, -1) + elif not already_cancelled: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) def cancel_not_started_transfer(self, cancel_time): self.get_curr_friend().delete_one_unsent_file(cancel_time) @@ -96,15 +84,11 @@ class FileTransfersHandler: """ tr = self._file_transfers[(friend_number, file_number)] tr.pause(by_friend) - t = FILE_TRANSFER_STATE['PAUSED_BY_FRIEND'] if by_friend else FILE_TRANSFER_STATE['PAUSED_BY_USER'] - self._get_friend_by_number(friend_number).update_transfer_data(file_number, t) def resume_transfer(self, friend_number, file_number, by_friend=False): """ Resume transfer with specified data """ - # self.get_friend_by_number(friend_number).update_transfer_data(file_number, - # TOX_FILE_TRANSFER_STATE['RUNNING']) tr = self._file_transfers[(friend_number, file_number)] if by_friend: tr.state = FILE_TRANSFER_STATE['RUNNING'] @@ -112,9 +96,8 @@ class FileTransfersHandler: else: tr.send_control(TOX_FILE_CONTROL['RESUME']) - def accept_transfer(self, item, path, friend_number, file_number, size, inline=False, from_position=0): + def accept_transfer(self, path, friend_number, file_number, size, inline=False, from_position=0): """ - :param item: transfer item. :param path: path for saving :param friend_number: friend number :param file_number: file number @@ -122,32 +105,23 @@ class FileTransfersHandler: :param inline: is inline image :param from_position: position for start """ - path, file_name = os.path.split(path) - new_file_name, i = file_name, 1 - if not from_position: - while os.path.isfile(path + '/' + new_file_name): # file with same name already exists - if '.' in file_name: # has extension - d = file_name.rindex('.') - else: # no extension - d = len(file_name) - new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] - i += 1 - path = os.path.join(path, new_file_name) + path = self._generate_valid_path(path, from_position) + friend = self._get_friend_by_number(friend_number) if not inline: rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number, from_position) else: rt = ReceiveToBuffer(self._tox, friend_number, size, file_number) rt.set_transfer_finished_handler(self.transfer_finished) + message = friend.get_message(lambda m: m.type == MESSAGE_TYPE['FILE_TRANSFER'] + and m.state == FILE_TRANSFER_STATE['INCOMING_NOT_STARTED'] + and m.file_number == file_number) + rt.set_state_changed_handler(message.transfer_updated) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - if item is not None: - rt.set_state_changed_handler(item.update_transfer_state) - self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['RUNNING']) def send_screenshot(self, data, friend_number): """ - Send screenshot to current active friend + Send screenshot :param data: raw data - png """ self.send_inline(data, 'toxygen_inline.png', friend_number) @@ -169,7 +143,8 @@ class FileTransfersHandler: st.set_transfer_finished_handler(self.transfer_finished) file_number = st.get_file_number() self._file_transfers[(friend.number, file_number)] = st - self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) def send_file(self, path, friend_number, is_resend=False, file_id=None): """ @@ -192,7 +167,8 @@ class FileTransfersHandler: file_number = st.get_file_number() self._file_transfers[(friend_number, file_number)] = st file_name = os.path.basename(path) - self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + tm = self._file_transfers_message_service.add_outgoing_transfer_message(friend, st.size, file_name, file_number) + st.set_state_changed_handler(tm.transfer_updated) def incoming_chunk(self, friend_number, file_number, position, data): """ @@ -214,13 +190,8 @@ class FileTransfersHandler: elif t is ReceiveToBuffer or (t is SendFromBuffer and self._settings['allow_inline']): # inline image print('inline') inline = InlineImage(transfer.get_data()) - index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['FINISHED'], - inline) + index = self._get_friend_by_number(friend_number).update_transfer_data(file_number, inline) self._file_transfers_message_service.add_inline_message(transfer, index) - elif t is not SendAvatar: - self._get_friend_by_number(friend_number).update_transfer_data(file_number, - FILE_TRANSFER_STATE['FINISHED']) del self._file_transfers[(friend_number, file_number)] del transfer @@ -279,3 +250,19 @@ class FileTransfersHandler: def _get_friend_by_number(self, friend_number): return self._contact_provider.get_friend_by_number(friend_number) + + @staticmethod + def _generate_valid_path(path, from_position): + path, file_name = os.path.split(path) + new_file_name, i = file_name, 1 + if not from_position: + while os.path.isfile(path + '/' + new_file_name): # file with same name already exists + if '.' in file_name: # has extension + d = file_name.rindex('.') + else: # no extension + d = len(file_name) + new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] + i += 1 + path = os.path.join(path, new_file_name) + + return path diff --git a/toxygen/file_transfers/file_transfers_messages_service.py b/toxygen/file_transfers/file_transfers_messages_service.py index 963eeb6..06b1e14 100644 --- a/toxygen/file_transfers/file_transfers_messages_service.py +++ b/toxygen/file_transfers/file_transfers_messages_service.py @@ -24,6 +24,8 @@ class FileTransfersMessagesService: friend.append_message(tm) + return tm + def add_outgoing_transfer_message(self, friend, size, file_name, file_number): author = MessageAuthor(self._profile.name, MESSAGE_AUTHOR['ME']) status = FILE_TRANSFER_STATE['OUTGOING_NOT_STARTED'] @@ -35,6 +37,8 @@ class FileTransfersMessagesService: friend.append_message(tm) + return tm + def add_inline_message(self, transfer, index): if self._is_active(transfer.friend_number): count = self._messages.count() diff --git a/toxygen/messenger/messages.py b/toxygen/messenger/messages.py index 34aff38..b14dfd5 100644 --- a/toxygen/messenger/messages.py +++ b/toxygen/messenger/messages.py @@ -164,6 +164,11 @@ class TransferMessage(Message): file_name = property(get_file_name) + def transfer_updated(self, state, percentage, time): + self._state = state + if self._widget is not None: + self._widget.update_transfer_state(state, percentage, time) + def _create_widget(self, *args): return FileTransferItem(self, *args) diff --git a/toxygen/ui/items_factories.py b/toxygen/ui/items_factories.py index 674291c..a9b1c1a 100644 --- a/toxygen/ui/items_factories.py +++ b/toxygen/ui/items_factories.py @@ -45,7 +45,7 @@ class MessagesItemsFactory: return item - def create_inline_item(self, data, append, position=0): + def create_inline_item(self, data, append=True, position=0): elem = QtWidgets.QListWidgetItem() item = InlineImageItem(data, self._messages.width(), elem) elem.setSizeHint(QtCore.QSize(self._messages.width(), item.height())) @@ -57,7 +57,7 @@ class MessagesItemsFactory: return item - def create_unsent_file_item(self, tm, append): + def create_unsent_file_item(self, tm, append=True): item = UnsentFileItem(self._file_transfers_handler, self._settings, tm, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) @@ -70,7 +70,7 @@ class MessagesItemsFactory: return item def create_file_transfer_item(self, tm, append=True): - item = tm.get_widget(self._file_transfers_handler, self._settings, tm, self._messages.width()) + item = tm.get_widget(self._file_transfers_handler, self._settings, self._messages.width()) elem = QtWidgets.QListWidgetItem() elem.setSizeHint(QtCore.QSize(self._messages.width() - 30, 34)) if append: diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index 3d9fd0f..c267a12 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -19,14 +19,17 @@ class MainWindow(QtWidgets.QMainWindow): self.setAcceptDrops(True) self._saved = False self._profile = None + self._file_transfer_handler = None self.initUI() - def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader): + def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, + file_transfer_handler): self._widget_factory = widget_factory self._tray = tray self._contacts_manager = contacts_manager self._profile = profile self._plugins_loader = plugins_loader + self._file_transfer_handler = file_transfer_handler self.messageEdit.set_messenger(messenger) def show(self): @@ -519,7 +522,7 @@ class MainWindow(QtWidgets.QMainWindow): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: - self._contacts_manager.send_file(name[0], self._contacts_manager.get_contact().number) + self._file_transfer_handler.send_file(name[0], self._contacts_manager.get_active_number()) def send_screenshot(self, hide=False): self.menu.hide() diff --git a/toxygen/ui/messages_widgets.py b/toxygen/ui/messages_widgets.py index c5b2120..5d86b64 100644 --- a/toxygen/ui/messages_widgets.py +++ b/toxygen/ui/messages_widgets.py @@ -218,10 +218,10 @@ class MessageItem(QtWidgets.QWidget): class FileTransferItem(QtWidgets.QListWidget): - def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): + def __init__(self, transfer_message, file_transfer_handler, settings, width, parent=None): QtWidgets.QListWidget.__init__(self, parent) - self._file_transfers_handler = file_transfers_handler + self._file_transfer_handler = file_transfer_handler self.resize(QtCore.QSize(width, 34)) if transfer_message.state == FILE_TRANSFER_STATE['CANCELLED']: self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') @@ -308,7 +308,7 @@ class FileTransferItem(QtWidgets.QListWidget): self.paused = False def cancel_transfer(self, friend_number, file_number): - self._file_transfers_handler.cancel_transfer(friend_number, file_number) + self._file_transfer_handler.cancel_transfer(friend_number, file_number) self.setStyleSheet('QListWidget { border: 1px solid #B40404; }') self.cancel.setVisible(False) self.accept_or_pause.setVisible(False) @@ -319,7 +319,7 @@ class FileTransferItem(QtWidgets.QListWidget): directory = util_ui.directory_dialog(util_ui.tr('Choose folder')) self.pb.setVisible(True) if directory: - self._file_transfer_handler.accept_transfer(self, directory + '/' + self.saved_name, + self._file_transfer_handler.accept_transfer(directory + '/' + self.saved_name, friend_number, file_number, size) self.button_update('pause') elif self.state == FILE_TRANSFER_STATE['PAUSED_BY_USER']: # resume @@ -390,8 +390,8 @@ class FileTransferItem(QtWidgets.QListWidget): class UnsentFileItem(FileTransferItem): - def __init__(self, file_transfers_handler, settings, transfer_message, width, parent=None): - super().__init__(file_transfers_handler, settings, transfer_message, width, parent) + def __init__(self, file_transfer_handler, settings, transfer_message, width, parent=None): + super().__init__(file_transfer_handler, settings, transfer_message, width, parent) self._time = time self.pb.setVisible(False) movie = QtGui.QMovie(util.join_path(util.get_images_directory(), 'spinner.gif')) @@ -399,7 +399,7 @@ class UnsentFileItem(FileTransferItem): movie.start() def cancel_transfer(self, *args): - self._file_transfers_handler.cancel_not_started_transfer(self._time) + self._file_transfer_handler.cancel_not_started_transfer(self._time) class InlineImageItem(QtWidgets.QScrollArea):