diff --git a/src/list_items.py b/src/list_items.py index cb9360b..8af7b13 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -2,7 +2,8 @@ from toxcore_enums_and_consts import * from PySide import QtGui, QtCore import profile from file_transfers import TOX_FILE_TRANSFER_STATE -from util import curr_directory +from util import curr_directory, convert_time +from messages import FILE_TRANSFER_MESSAGE_STATUS class MessageEdit(QtGui.QTextEdit): @@ -56,7 +57,7 @@ class MessageItem(QtGui.QWidget): if message_type == TOX_MESSAGE_TYPE['ACTION']: self.name.setStyleSheet("QLabel { color: #4169E1; }") - self.message.setStyleSheet("QPlainTextEdit { color: #4169E1; }") + self.message.setStyleSheet("QTextEdit { color: #4169E1; }") else: if text[0] == '>': self.message.setStyleSheet("QPlainTextEdit { color: green; }") @@ -134,13 +135,17 @@ class StatusCircle(QtGui.QWidget): class FileTransferItem(QtGui.QListWidget): - def __init__(self, file_name, size, time, user, friend_number, file_number, show_accept, parent=None): + def __init__(self, file_name, size, time, user, friend_number, file_number, state, parent=None): + QtGui.QListWidget.__init__(self, parent) self.resize(QtCore.QSize(600, 50)) - self.setStyleSheet('QWidget { background-color: green; }') + if state != FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']: + self.setStyleSheet('QWidget { background-color: green; }') + else: + self.setStyleSheet('QWidget { background-color: #B40404; }') self.name = QtGui.QLabel(self) - self.name.setGeometry(QtCore.QRect(0, 15, 95, 20)) + self.name.setGeometry(QtCore.QRect(1, 15, 95, 20)) self.name.setTextFormat(QtCore.Qt.PlainText) font = QtGui.QFont() font.setFamily("Times New Roman") @@ -152,37 +157,40 @@ class FileTransferItem(QtGui.QListWidget): self.name.setStyleSheet('QLabel { color: black; }') self.time = QtGui.QLabel(self) - self.time.setGeometry(QtCore.QRect(550, 0, 50, 50)) + self.time.setGeometry(QtCore.QRect(550, 2, 50, 46)) font.setPointSize(10) font.setBold(False) self.time.setFont(font) self.time.setObjectName("time") - self.time.setText(time) + self.time.setText(convert_time(time)) self.time.setStyleSheet('QLabel { color: black; }') self.cancel = QtGui.QPushButton(self) - self.cancel.setGeometry(QtCore.QRect(500, 0, 50, 50)) + self.cancel.setGeometry(QtCore.QRect(500, 2, 46, 46)) pixmap = QtGui.QPixmap(curr_directory() + '/images/decline.png') icon = QtGui.QIcon(pixmap) self.cancel.setIcon(icon) self.cancel.setIconSize(QtCore.QSize(30, 30)) + self.cancel.setVisible(state > 1) self.cancel.clicked.connect(lambda: self.cancel_transfer(friend_number, file_number)) self.accept = QtGui.QPushButton(self) - self.accept.setGeometry(QtCore.QRect(450, 0, 50, 50)) + self.accept.setGeometry(QtCore.QRect(450, 2, 46, 46)) pixmap = QtGui.QPixmap(curr_directory() + '/images/accept.png') icon = QtGui.QIcon(pixmap) self.accept.setIcon(icon) self.accept.setIconSize(QtCore.QSize(30, 30)) self.accept.clicked.connect(lambda: self.accept_transfer(friend_number, file_number, size)) - self.accept.setVisible(show_accept) + self.accept.setVisible(state == FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED']) self.pb = QtGui.QProgressBar(self) self.pb.setGeometry(QtCore.QRect(100, 15, 100, 20)) self.pb.setValue(0) + if state < 2: + self.pb.setVisible(False) self.file_name = QtGui.QLabel(self) - self.file_name.setGeometry(QtCore.QRect(210, 0, 230, 50)) + self.file_name.setGeometry(QtCore.QRect(210, 2, 230, 46)) font.setPointSize(12) self.file_name.setFont(font) self.file_name.setObjectName("time") diff --git a/src/messages.py b/src/messages.py index 5ea4889..3c82738 100644 --- a/src/messages.py +++ b/src/messages.py @@ -1 +1,76 @@ -# TODO: messages refactor + +PAGE_SIZE = 42 + +MESSAGE_TYPE = { + 'TEXT': 0, + 'ACTION': 1, + 'FILE_TRANSFER': 2, + 'INLINE': 3 +} + +FILE_TRANSFER_MESSAGE_STATUS = { + 'FINISHED': 0, + 'CANCELLED': 1, + 'OUTGOING': 2, + 'INCOMING_NOT_STARTED': 3, + 'INCOMING_STARTED': 4 +} + + +class Message(object): + + def __init__(self, message_type, owner, time): + self._time = time + self._type = message_type + self._owner = owner + + def get_type(self): + return self._type + + +class TextMessage(Message): + + def __init__(self, message, owner, time, message_type): + super(TextMessage, self).__init__(message_type, owner, time) + self._message = message + + def get_data(self): + return self._message, self._owner, self._time, self._type + + +class TransferMessage(Message): + + def __init__(self, owner, time, status, size, name, friend_number, file_number): + super(TransferMessage, self).__init__(MESSAGE_TYPE['FILE_TRANSFER'], owner, time) + self._status = status + self._size = size + self._file_name = name + self._friend_number, self._file_number = friend_number, file_number + + def is_active(self, file_number): + return self._file_number == file_number and self._status > 1 + + def get_friend_number(self): + return self._friend_number + + def get_file_number(self): + return self._file_number + + def get_status(self): + return self._status + + def set_status(self, value): + self._status = value + + def get_data(self): + return self._file_name, self._size, self._time, self._owner, self._friend_number, self._file_number, self._status + + +class InlineImage(Message): + + def __init__(self, owner, time, data): + super(InlineImage, self).__init__(MESSAGE_TYPE['INLINE'], owner, time) + self._data = data + + def get_data(self): + return self._data diff --git a/src/profile.py b/src/profile.py index 19472ca..ec3fabd 100644 --- a/src/profile.py +++ b/src/profile.py @@ -2,6 +2,7 @@ from list_items import MessageItem, ContactItem, FileTransferItem from PySide import QtCore, QtGui from tox import Tox import os +from messages import * from settings import Settings from toxcore_enums_and_consts import * from ctypes import * @@ -221,12 +222,12 @@ class Friend(Contact): """ if (first_time and self._history_loaded) or (not hasattr(self, '_message_getter')): return - data = self._message_getter.get(42) + data = self._message_getter.get(PAGE_SIZE) if data is not None and len(data): data.reverse() else: return [] - self._corr = data + self._corr + self._corr = map(lambda tupl: TextMessage(*tupl), data) + self._corr self._history_loaded = True return data @@ -237,7 +238,8 @@ class Friend(Contact): """ if hasattr(self, '_message_getter'): del self._message_getter - return self._corr[-self._unsaved_messages:] if self._unsaved_messages else [] + 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 @@ -247,7 +249,8 @@ class Friend(Contact): :param message: tuple (message, owner, unix_time, message_type) """ self._corr.append(message) - self._unsaved_messages += 1 + if message.get_type() <= 1: + self._unsaved_messages += 1 def clear_corr(self): """ @@ -256,6 +259,17 @@ class Friend(Contact): if hasattr(self, '_message_getter'): del self._message_getter self._corr = [] + self._unsaved_messages = 0 + + def update_transfer_data(self, file_number, status): + """ + Update status of active transfer + """ + try: + tr = filter(lambda x: x.get_type() == 2 and x.is_active(file_number), self._corr)[0] + tr.set_status(status) + except: + pass # ----------------------------------------------------------------------------------------------------------------- # Alias support @@ -436,10 +450,17 @@ class Profile(Contact, Singleton): friend.load_corr() messages = friend.get_corr() for message in messages: - self.create_message_item(message[0], - convert_time(message[2]), - friend.name if message[1] else self._name, - message[3]) + if message.get_type() <= 1: + data = message.get_data() + self.create_message_item(data[0], + convert_time(data[2]), + friend.name if data[1] else self._name, + data[3]) + elif message.get_type() == 2: + item = self.create_file_transfer_item(message) + if message.get_status() in (2, 4): + ft = self._file_transfers[(message.get_friend_number(), message.get_file_number())] + ft.set_state_changed_handler(item.update) self._messages.scrollToBottom() else: friend = self._friends[self._active_friend] @@ -507,17 +528,13 @@ class Profile(Contact, Singleton): user_name = Profile.get_instance().get_active_name() self.create_message_item(message.decode('utf-8'), curr_time(), user_name, message_type) self._messages.scrollToBottom() - self._friends[self._active_friend].append_message((message.decode('utf-8'), - MESSAGE_OWNER['FRIEND'], - time.time(), - message_type)) + self._friends[self._active_friend].append_message( + TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type)) else: friend = self.get_friend_by_number(friend_num) friend.set_messages(True) - friend.append_message((message.decode('utf-8'), - MESSAGE_OWNER['FRIEND'], - int(time.time()), - message_type)) + friend.append_message( + TextMessage(message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], time.time(), message_type)) def send_message(self, text): """ @@ -535,10 +552,7 @@ class Profile(Contact, Singleton): self.create_message_item(text, curr_time(), self._name, message_type) self._screen.messageEdit.clear() self._messages.scrollToBottom() - friend.append_message((text, - MESSAGE_OWNER['ME'], - time.time(), - message_type)) + friend.append_message(TextMessage(text, MESSAGE_OWNER['ME'], time.time(), message_type)) # ----------------------------------------------------------------------------------------------------------------- # History support @@ -581,10 +595,11 @@ class Profile(Contact, Singleton): return data.reverse() for message in data: - self.create_message_item(message[0], - convert_time(message[2]), - friend.name if message[1] else self._name, - message[3], + data = message.get_data() + self.create_message_item(data[0], + convert_time(data[2]), + friend.name if data[1] else self._name, + data[3], False) def export_history(self, directory): @@ -617,12 +632,16 @@ class Profile(Contact, Singleton): self._messages.setItemWidget(elem, item) self._messages.repaint() - def create_file_transfer_item(self, file_name, size, friend_number, file_number, show_accept): - friend = self.get_friend_by_number(friend_number) - item = FileTransferItem(file_name, size, curr_time(), friend.name, friend_number, file_number, show_accept) - elem = QtGui.QListWidgetItem(self._messages) + def create_file_transfer_item(self, tm, append=True): + data = list(tm.get_data()) + data[3] = self.get_friend_by_number(data[4]).name if data[3] else self._name + item = FileTransferItem(*data) + elem = QtGui.QListWidgetItem() elem.setSizeHint(QtCore.QSize(600, 50)) - self._messages.addItem(elem) + if append: + self._messages.addItem(elem) + else: + self._messages.insertItem(0, elem) self._messages.setItemWidget(elem, item) self._messages.repaint() return item @@ -761,7 +780,6 @@ class Profile(Contact, Singleton): :param size: file size in bytes :param file_name: file name without path """ - # TODO: save transfer data in message list settings = Settings.get_instance() friend = self.get_friend_by_number(friend_number) file_name = file_name.decode('utf-8') @@ -773,15 +791,29 @@ class Profile(Contact, Singleton): d = file_name.rindex('.') else: # no extension d = len(file_name) - new_file_name = file_name[:d] + '({})'.format(i) + file_name[d:] + new_file_name = file_name[:d] + ' ({})'.format(i) + file_name[d:] i += 1 - item = self.create_file_transfer_item(new_file_name, size, friend_number, file_number, False) - self.accept_transfer(item, path + '/' + new_file_name, friend_number, file_number) + self.accept_transfer(None, path + '/' + new_file_name, friend_number, file_number) + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + FILE_TRANSFER_MESSAGE_STATUS['INCOMING_STARTED'], + size, + new_file_name, + friend_number, + file_number) else: - if self.get_active_number() != friend_number: - friend = self.get_friend_by_number(friend_number) - friend.set_messages(True) - self.create_file_transfer_item(file_name, size, friend_number, file_number, True) + tm = TransferMessage(MESSAGE_OWNER['FRIEND'], + time.time(), + FILE_TRANSFER_MESSAGE_STATUS['INCOMING_NOT_STARTED'], + size, + file_name, + friend_number, + file_number) + if friend_number == self.get_active_number(): + self.create_file_transfer_item(tm) + else: + friend.set_messages(True) + friend.append_message(tm) def cancel_transfer(self, friend_number, file_number, already_cancelled=False): """ @@ -797,6 +829,10 @@ class Profile(Contact, Singleton): else: tr.cancelled() del self._file_transfers[(friend_number, file_number)] + else: + self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['CANCEL']) + self.get_friend_by_number(friend_number).update_transfer_data(file_number, + FILE_TRANSFER_MESSAGE_STATUS['CANCELLED']) def accept_transfer(self, item, path, friend_number, file_number, size): """ @@ -809,17 +845,27 @@ class Profile(Contact, Singleton): rt = ReceiveTransfer(path, self._tox, friend_number, size, file_number) self._file_transfers[(friend_number, file_number)] = rt self._tox.file_control(friend_number, file_number, TOX_FILE_CONTROL['RESUME']) - rt.set_state_changed_handler(item.update) + if item is not None: + rt.set_state_changed_handler(item.update) + self.get_friend_by_number(friend_number).update_transfer_data(file_number, FILE_TRANSFER_MESSAGE_STATUS['INCOMING_STARTED']) def send_screenshot(self, data): """ Sen screenshot to current active friend - :param data: raw data + :param data: raw data - png """ - friend_number = self.get_active_number() - st = SendFromBuffer(self._tox, friend_number, data, 'toxygen_inline.png') - self._file_transfers[(friend_number, st.get_file_number())] = st - item = self.create_file_transfer_item('toxygen_inline.png', len(data), friend_number, st.get_file_number(), False) + friend = self._friends[self._active_friend] + st = SendFromBuffer(self._tox, friend.number, data, 'toxygen_inline.png') + self._file_transfers[(friend.number, st.get_file_number())] = st + tm = TransferMessage(MESSAGE_OWNER['ME'], + time.time(), + FILE_TRANSFER_MESSAGE_STATUS['OUTGOING'], + len(data), + 'toxygen_inline.png', + friend.number, + st.get_file_number()) + item = self.create_file_transfer_item(tm) + friend.append_message(tm) st.set_state_changed_handler(item.update) def send_file(self, path): @@ -830,8 +876,16 @@ class Profile(Contact, Singleton): friend_number = self.get_active_number() st = SendTransfer(path, self._tox, friend_number) self._file_transfers[(friend_number, st.get_file_number())] = st - item = self.create_file_transfer_item(os.path.basename(path), os.path.getsize(path), friend_number, st.get_file_number(), False) + tm = TransferMessage(MESSAGE_OWNER['ME'], + time.time(), + FILE_TRANSFER_MESSAGE_STATUS['OUTGOING'], + os.path.getsize(path), + os.path.basename(path), + friend_number, + st.get_file_number()) + item = self.create_file_transfer_item(tm) st.set_state_changed_handler(item.update) + self._friends[self._active_friend].append_message(tm) def incoming_chunk(self, friend_number, file_number, position, data): if (friend_number, file_number) in self._file_transfers: @@ -841,6 +895,8 @@ class Profile(Contact, Singleton): if type(transfer) is ReceiveAvatar: self.get_friend_by_number(friend_number).load_avatar() self.set_active(None) + else: + self.get_friend_by_number(friend_number).update_transfer_data(file_number, FILE_TRANSFER_MESSAGE_STATUS['FINISHED']) del self._file_transfers[(friend_number, file_number)] def outgoing_chunk(self, friend_number, file_number, position, size): @@ -849,6 +905,8 @@ class Profile(Contact, Singleton): transfer.send_chunk(position, size) if transfer.state: del self._file_transfers[(friend_number, file_number)] + if type(transfer) is not SendAvatar: + self.get_friend_by_number(friend_number).update_transfer_data(file_number, FILE_TRANSFER_MESSAGE_STATUS['FINISHED']) # ----------------------------------------------------------------------------------------------------------------- # Avatars support diff --git a/src/styles/style.qss b/src/styles/style.qss index 204b798..56f8648 100644 --- a/src/styles/style.qss +++ b/src/styles/style.qss @@ -45,7 +45,7 @@ QWidget { color: silver; background-color: #302F2F; - selection-background-color:#3d8ec9; + selection-background-color:#008B8B; selection-color: black; background-clip: border; border-image: none; @@ -60,7 +60,7 @@ QWidget:item:hover QWidget:item:selected { - background-color: #3d8ec9; + background-color: #008B8B; } QCheckBox @@ -229,13 +229,13 @@ QMenuBar::item QMenuBar::item:selected { background: transparent; - border: 1px solid #3A3939; + border: 1px solid #008B8B; } QMenuBar::item:pressed { border: 1px solid #3A3939; - background-color: #3d8ec9; + background-color: #008B8B; color: black; margin-bottom:-1px; padding-bottom:1px; @@ -1264,3 +1264,8 @@ QPushButton:hover border-color: green; } +#toxygen +{ + color: qlineargradient(spread:pad, x1:0 y1:10, x2:60 y2:10, stop:0 #FF0000, stop:1 #00FF00); +} +