diff --git a/src/friend.py b/src/friend.py index 7ff31bc..67d20dc 100644 --- a/src/friend.py +++ b/src/friend.py @@ -2,6 +2,7 @@ import contact from messages import * from history import * import util +import file_transfers as ft class Friend(contact.Contact): @@ -92,13 +93,29 @@ class Friend(contact.Contact): else: return '' - def unsent_messages(self): + def get_unsent_messages(self): """ :return list of unsent messages """ messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) return list(messages) + def get_unsent_messages_for_saving(self): + """ + :return list of unsent messages for saving + """ + if self._unsaved_messages: + messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr[-self._unsaved_messages:]) + return list(map(lambda x: x.get_data(), messages)) + else: + return [] + + def delete_message(self, time): + elem = list(filter(lambda x: type(x) is TextMessage and x.get_data()[2] == time, self._corr))[0] + if elem in self.get_corr_for_saving(): + self._unsaved_messages -= 1 + self._corr.remove(elem) + def mark_as_sent(self): try: message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0] @@ -106,15 +123,22 @@ class Friend(contact.Contact): except Exception as ex: util.log('Mark as sent ex: ' + str(ex)) - def clear_corr(self): + def clear_corr(self, save_unsent=False): """ Clear messages list """ if hasattr(self, '_message_getter'): del self._message_getter # don't delete data about active file transfer - self._corr = list(filter(lambda x: x.get_type() in (2, 3) and x.get_status() >= 2, self._corr)) - self._unsaved_messages = 0 + if not save_unsent: + self._corr = list(filter(lambda x: x.get_type() in (2, 3) and + x.get_status() in ft.ACTIVE_FILE_TRANSFERS, self._corr)) + self._unsaved_messages = 0 + else: + self._corr = list(filter(lambda x: (x.get_type() in (2, 3) and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) + or (x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT']), + self._corr)) + self._unsaved_messages = len(self.get_unsent_messages()) def get_curr_text(self): return self._curr_text diff --git a/src/history.py b/src/history.py index 92e6d91..5489f6c 100644 --- a/src/history.py +++ b/src/history.py @@ -132,6 +132,19 @@ class History: db.close() pass + def delete_message(self, tox_id, time): + chdir(settings.ProfileHelper.get_path()) + db = connect(self._name + '.hstr') + try: + cursor = db.cursor() + cursor.execute('DELETE FROM id' + tox_id + ' WHERE unix_time = ' + str(time) + ';') + db.commit() + except: + db.rollback() + raise + finally: + db.close() + def delete_messages(self, tox_id): chdir(settings.ProfileHelper.get_path()) db = connect(self._name + '.hstr') diff --git a/src/list_items.py b/src/list_items.py index 0aef7d8..0379ad2 100644 --- a/src/list_items.py +++ b/src/list_items.py @@ -121,14 +121,15 @@ class MessageItem(QtGui.QWidget): 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 = time + self.t = True else: - self.time.setText(time) + self.time.setText(convert_time(time)) + self.t = False self.message = MessageEdit(text, parent.width() - 150, message_type, self) if message_type != TOX_MESSAGE_TYPE['NORMAL']: @@ -138,10 +139,23 @@ class MessageItem(QtGui.QWidget): self.message.setGeometry(QtCore.QRect(100, 0, parent.width() - 150, 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 = QtGui.QMenu() + delete_item = self.listMenu.addAction(QtGui.QApplication.translate("MainWindow", 'Delete message', None, QtGui.QApplication.UnicodeUTF8)) + self.connect(delete_item, QtCore.SIGNAL("triggered()"), 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 hasattr(self, 't'): - self.time.setText(self.t) - del self.t + if self.t: + self.time.setText(convert_time(self._time)) + self.t = False return True return False diff --git a/src/menu.py b/src/menu.py index 787cd4e..bfcbccd 100644 --- a/src/menu.py +++ b/src/menu.py @@ -360,45 +360,51 @@ class PrivacySettings(CenteredWidget): def initUI(self): self.setObjectName("privacySettings") - self.resize(350, 550) - self.setMinimumSize(QtCore.QSize(350, 550)) - self.setMaximumSize(QtCore.QSize(350, 550)) + self.resize(350, 600) + self.setMinimumSize(QtCore.QSize(350, 600)) + self.setMaximumSize(QtCore.QSize(350, 600)) self.saveHistory = QtGui.QCheckBox(self) self.saveHistory.setGeometry(QtCore.QRect(10, 20, 291, 22)) + self.saveUnsentOnly = QtGui.QCheckBox(self) + self.saveUnsentOnly.setGeometry(QtCore.QRect(10, 60, 291, 22)) + self.fileautoaccept = QtGui.QCheckBox(self) - self.fileautoaccept.setGeometry(QtCore.QRect(10, 60, 271, 22)) + self.fileautoaccept.setGeometry(QtCore.QRect(10, 100, 271, 22)) self.typingNotifications = QtGui.QCheckBox(self) - self.typingNotifications.setGeometry(QtCore.QRect(10, 100, 350, 30)) + self.typingNotifications.setGeometry(QtCore.QRect(10, 140, 350, 30)) self.inlines = QtGui.QCheckBox(self) - self.inlines.setGeometry(QtCore.QRect(10, 140, 350, 30)) + self.inlines.setGeometry(QtCore.QRect(10, 180, 350, 30)) self.auto_path = QtGui.QLabel(self) - self.auto_path.setGeometry(QtCore.QRect(10, 190, 350, 30)) + self.auto_path.setGeometry(QtCore.QRect(10, 230, 350, 30)) self.path = QtGui.QPlainTextEdit(self) - self.path.setGeometry(QtCore.QRect(10, 225, 330, 45)) + self.path.setGeometry(QtCore.QRect(10, 265, 330, 45)) self.change_path = QtGui.QPushButton(self) - self.change_path.setGeometry(QtCore.QRect(10, 280, 330, 30)) + self.change_path.setGeometry(QtCore.QRect(10, 320, 330, 30)) settings = Settings.get_instance() self.typingNotifications.setChecked(settings['typing_notifications']) self.fileautoaccept.setChecked(settings['allow_auto_accept']) self.saveHistory.setChecked(settings['save_history']) self.inlines.setChecked(settings['allow_inline']) + self.saveUnsentOnly.setChecked(settings['save_unsent_only']) + self.saveUnsentOnly.setEnabled(settings['save_history']) + self.saveHistory.stateChanged.connect(self.update) self.path.setPlainText(settings['auto_accept_path'] or curr_directory()) self.change_path.clicked.connect(self.new_path) self.block_user_label = QtGui.QLabel(self) - self.block_user_label.setGeometry(QtCore.QRect(10, 320, 330, 30)) + self.block_user_label.setGeometry(QtCore.QRect(10, 360, 330, 30)) self.block_id = QtGui.QPlainTextEdit(self) - self.block_id.setGeometry(QtCore.QRect(10, 350, 330, 30)) + self.block_id.setGeometry(QtCore.QRect(10, 390, 330, 30)) self.block = QtGui.QPushButton(self) - self.block.setGeometry(QtCore.QRect(10, 390, 330, 30)) + self.block.setGeometry(QtCore.QRect(10, 430, 330, 30)) self.block.clicked.connect(lambda: Profile.get_instance().block_user(self.block_id.toPlainText()) or self.close()) self.blocked_users_label = QtGui.QLabel(self) - self.blocked_users_label.setGeometry(QtCore.QRect(10, 430, 330, 30)) + self.blocked_users_label.setGeometry(QtCore.QRect(10, 470, 330, 30)) self.comboBox = QtGui.QComboBox(self) - self.comboBox.setGeometry(QtCore.QRect(10, 460, 330, 30)) + self.comboBox.setGeometry(QtCore.QRect(10, 500, 330, 30)) self.comboBox.addItems(settings['blocked']) self.unblock = QtGui.QPushButton(self) - self.unblock.setGeometry(QtCore.QRect(10, 500, 330, 30)) + self.unblock.setGeometry(QtCore.QRect(10, 540, 330, 30)) self.unblock.clicked.connect(lambda: self.unblock_user()) self.retranslateUi() QtCore.QMetaObject.connectSlotsByName(self) @@ -415,6 +421,12 @@ class PrivacySettings(CenteredWidget): self.blocked_users_label.setText(QtGui.QApplication.translate("privacySettings", "Blocked users:", None, QtGui.QApplication.UnicodeUTF8)) self.unblock.setText(QtGui.QApplication.translate("privacySettings", "Unblock", None, QtGui.QApplication.UnicodeUTF8)) self.block.setText(QtGui.QApplication.translate("privacySettings", "Block user", None, QtGui.QApplication.UnicodeUTF8)) + self.saveUnsentOnly.setText(QtGui.QApplication.translate("privacySettings", "Save unsent messages only", None, QtGui.QApplication.UnicodeUTF8)) + + def update(self, new_state): + self.saveUnsentOnly.setEnabled(new_state) + if not new_state: + self.saveUnsentOnly.setChecked(False) def unblock_user(self): if not self.comboBox.count(): @@ -429,6 +441,7 @@ class PrivacySettings(CenteredWidget): settings = Settings.get_instance() settings['typing_notifications'] = self.typingNotifications.isChecked() settings['allow_auto_accept'] = self.fileautoaccept.isChecked() + if settings['save_history'] and not self.saveHistory.isChecked(): # clear history reply = QtGui.QMessageBox.question(None, QtGui.QApplication.translate("privacySettings", @@ -444,6 +457,21 @@ class PrivacySettings(CenteredWidget): settings['save_history'] = self.saveHistory.isChecked() else: settings['save_history'] = self.saveHistory.isChecked() + if self.saveUnsentOnly.isChecked() and not settings['save_unsent_only']: + reply = QtGui.QMessageBox.question(None, + QtGui.QApplication.translate("privacySettings", + 'Chat history', + None, QtGui.QApplication.UnicodeUTF8), + QtGui.QApplication.translate("privacySettings", + 'History will be cleaned! Continue?', + None, QtGui.QApplication.UnicodeUTF8), + QtGui.QMessageBox.Yes, + QtGui.QMessageBox.No) + if reply == QtGui.QMessageBox.Yes: + Profile.get_instance().clear_history(None, True) + settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() + else: + settings['save_unsent_only'] = self.saveUnsentOnly.isChecked() settings['auto_accept_path'] = self.path.toPlainText() settings['allow_inline'] = self.inlines.isChecked() settings.save() diff --git a/src/profile.py b/src/profile.py index cdea8d0..5db1cb5 100644 --- a/src/profile.py +++ b/src/profile.py @@ -7,7 +7,7 @@ from friend import * from settings import * from toxcore_enums_and_consts import * from ctypes import * -from util import curr_time, log, Singleton, curr_directory, convert_time +from util import log, Singleton, curr_directory from tox_dns import tox_dns from history import * from file_transfers import * @@ -90,7 +90,7 @@ class Profile(contact.Contact, Singleton): for friend in self._friends: friend.append_message(InfoMessage(message, time.time())) if self._active_friend + 1: - self.create_message_item(message, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() def set_status_message(self, value): @@ -181,7 +181,7 @@ class Profile(contact.Contact, Singleton): if message.get_type() <= 1: data = message.get_data() self.create_message_item(data[0], - convert_time(data[2]), + data[2], data[1], data[3]) elif message.get_type() == MESSAGE_TYPE['FILE_TRANSFER']: @@ -201,7 +201,7 @@ class Profile(contact.Contact, Singleton): else: # info message data = message.get_data() self.create_message_item(data[0], - convert_time(data[2]), + data[2], '', data[3]) self._messages.scrollToBottom() @@ -255,7 +255,7 @@ class Profile(contact.Contact, Singleton): friend.append_message(InfoMessage(message, time.time())) friend.actions = True if number == self.get_active_number(): - self.create_message_item(message, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self.create_message_item(message, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() self.set_active(None) @@ -328,7 +328,7 @@ class Profile(contact.Contact, Singleton): """ friend = self.get_friend_by_number(friend_number) friend.load_corr() - messages = friend.unsent_messages() + messages = friend.get_unsent_messages() try: for message in messages: self.split_and_send(friend_number, message.get_data()[-1], message.get_data()[0].encode('utf-8')) @@ -367,10 +367,11 @@ class Profile(contact.Contact, Singleton): :param message: text of message """ if friend_num == self.get_active_number(): # add message to list - self.create_message_item(message, curr_time(), MESSAGE_OWNER['FRIEND'], message_type) + t = time.time() + self.create_message_item(message, t, MESSAGE_OWNER['FRIEND'], message_type) self._messages.scrollToBottom() self._friends[self._active_friend].append_message( - TextMessage(message, MESSAGE_OWNER['FRIEND'], time.time(), message_type)) + TextMessage(message, MESSAGE_OWNER['FRIEND'], t, message_type)) else: friend = self.get_friend_by_number(friend_num) friend.inc_messages() @@ -397,10 +398,17 @@ class Profile(contact.Contact, Singleton): friend.inc_receipts() if friend.status is not None: self.split_and_send(friend.number, message_type, text.encode('utf-8')) - self.create_message_item(text, curr_time(), MESSAGE_OWNER['NOT_SENT'], message_type) + t = time.time() + self.create_message_item(text, t, MESSAGE_OWNER['NOT_SENT'], message_type) self._screen.messageEdit.clear() self._messages.scrollToBottom() - friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], time.time(), message_type)) + friend.append_message(TextMessage(text, MESSAGE_OWNER['NOT_SENT'], t, message_type)) + + def delete_message(self, time): + friend = self._friends[self._active_friend] + friend.delete_message(time) + self._history.delete_message(friend.tox_id, time) + self.update() # ----------------------------------------------------------------------------------------------------------------- # History support @@ -410,35 +418,38 @@ class Profile(contact.Contact, Singleton): """ Save history to db """ + s = Settings.get_instance() if hasattr(self, '_history'): - if Settings.get_instance()['save_history']: + if s['save_history']: for friend in self._friends: - messages = friend.get_corr_for_saving() + if not s['save_unsent_only']: + messages = friend.get_corr_for_saving() + else: + messages = friend.get_unsent_messages_for_saving() if not self._history.friend_exists_in_db(friend.tox_id): self._history.add_friend_to_db(friend.tox_id) self._history.save_messages_to_db(friend.tox_id, messages) - unsent_messages = friend.unsent_messages() + unsent_messages = friend.get_unsent_messages() unsent_time = unsent_messages[0].get_data()[2] if len(unsent_messages) else time.time() + 1 self._history.update_messages(friend.tox_id, unsent_time) self._history.save() del self._history - def clear_history(self, num=None): + def clear_history(self, num=None, save_unsent=False): """ Clear chat history """ if num is not None: friend = self._friends[num] - friend.clear_corr() + friend.clear_corr(save_unsent) if self._history.friend_exists_in_db(friend.tox_id): self._history.delete_messages(friend.tox_id) self._history.delete_friend_from_db(friend.tox_id) else: # clear all history for number in range(len(self._friends)): - self.clear_history(number) + self.clear_history(number, save_unsent) if num is None or num == self.get_active_number(): - self._messages.clear() - self._messages.repaint() + self.update() def load_history(self): """ @@ -455,7 +466,7 @@ class Profile(contact.Contact, Singleton): if message.get_type() <= 1: # text message data = message.get_data() self.create_message_item(data[0], - convert_time(data[2]), + data[2], data[1], data[3], False) @@ -476,7 +487,7 @@ class Profile(contact.Contact, Singleton): else: # info message data = message.get_data() self.create_message_item(data[0], - convert_time(data[2]), + data[2], '', data[3]) @@ -1090,7 +1101,7 @@ class Profile(contact.Contact, Singleton): text = QtGui.QApplication.translate("incoming_call", "Outgoing audio call", None, QtGui.QApplication.UnicodeUTF8) self._friends[self._active_friend].append_message(InfoMessage(text, time.time())) - self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() elif num in self._call: # finish or cancel call if you call with active friend self.stop_call(num, False) @@ -1110,7 +1121,7 @@ class Profile(contact.Contact, Singleton): self._incoming_calls.add(friend_number) if friend_number == self.get_active_number(): self._screen.incoming_call() - self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() else: friend.actions = True @@ -1144,7 +1155,7 @@ class Profile(contact.Contact, Singleton): friend = self.get_friend_by_number(friend_number) friend.append_message(InfoMessage(text, time.time())) if friend_number == self.get_active_number(): - self.create_message_item(text, curr_time(), '', MESSAGE_TYPE['INFO_MESSAGE']) + self.create_message_item(text, time.time(), '', MESSAGE_TYPE['INFO_MESSAGE']) self._messages.scrollToBottom() diff --git a/src/settings.py b/src/settings.py index ad7cd9f..e0c0aa9 100644 --- a/src/settings.py +++ b/src/settings.py @@ -122,7 +122,8 @@ class Settings(dict, Singleton): 'x': 400, 'y': 400, 'message_font_size': 14, - 'unread_color': 'red' + 'unread_color': 'red', + 'save_unsent_only': False } @staticmethod