diff --git a/src/callbacks.py b/src/callbacks.py index 4930e6a..f67fb5e 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -67,35 +67,26 @@ def friend_connection_status(tox, friend_num, new_status, user_data): invoke_in_main_thread(profile.update_filtration) -def friend_name(window): - """ - :param window: main window - :return: function for callback friend_name. It updates friend's name - and calls window repaint - """ - def wrapped(tox, friend_num, name, size, user_data): - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - print 'New name: ', str(friend_num), str(name) - invoke_in_main_thread(friend.set_name, name) - if profile.get_active_number() == friend_num: - invoke_in_main_thread(profile.set_active) - return wrapped +def friend_name(tox, friend_num, name, size, user_data): + profile = Profile.get_instance() + friend = profile.get_friend_by_number(friend_num) + print 'New name: ', str(friend_num), str(name) + invoke_in_main_thread(friend.set_name, name) + if profile.get_active_number() == friend_num: + invoke_in_main_thread(profile.set_active) -def friend_status_message(): +def friend_status_message(tox, friend_num, status_message, size, user_data): """ :return: function for callback friend_status_message. It updates friend's status message and calls window repaint """ - def wrapped(tox, friend_num, status_message, size, user_data): - profile = Profile.get_instance() - friend = profile.get_friend_by_number(friend_num) - invoke_in_main_thread(friend.set_status_message, status_message) - print 'User #{} has new status: {}'.format(friend_num, status_message) - if profile.get_active_number() == friend_num: - invoke_in_main_thread(profile.set_active) - return wrapped + profile = Profile.get_instance() + friend = profile.get_friend_by_number(friend_num) + invoke_in_main_thread(friend.set_status_message, status_message) + print 'User #{} has new status: {}'.format(friend_num, status_message) + if profile.get_active_number() == friend_num: + invoke_in_main_thread(profile.set_active) def friend_message(window): @@ -136,6 +127,6 @@ def init_callbacks(tox, window): tox.callback_friend_message(friend_message(window), 0) tox.callback_self_connection_status(self_connection_status(tox), 0) tox.callback_friend_connection_status(friend_connection_status, 0) - tox.callback_friend_name(friend_name(window), 0) - tox.callback_friend_status_message(friend_status_message(), 0) + tox.callback_friend_name(friend_name, 0) + tox.callback_friend_status_message(friend_status_message, 0) tox.callback_friend_request(friend_request, 0) diff --git a/src/main.py b/src/main.py index de67cfd..0d8d80f 100644 --- a/src/main.py +++ b/src/main.py @@ -120,5 +120,5 @@ class ToxIterateThread(QtCore.QThread): if __name__ == '__main__': - # TODO: add command line options? + # TODO: add command line options (example: portable, multiprofile) main() diff --git a/src/mainscreen.py b/src/mainscreen.py index 9ccbfcf..bc31787 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -230,7 +230,7 @@ class MainWindow(QtGui.QMainWindow): self.profile = Profile(self.tox, self) def closeEvent(self, *args, **kwargs): - self.profile.save() + self.profile.save_history() # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click in menu @@ -267,17 +267,17 @@ class MainWindow(QtGui.QMainWindow): self.int_s = InterfaceSettings() self.int_s.show() -# ----------------------------------------------------------------------------------------------------------------- -# Messages -# ----------------------------------------------------------------------------------------------------------------- + # ----------------------------------------------------------------------------------------------------------------- + # Messages + # ----------------------------------------------------------------------------------------------------------------- def send_message(self): text = self.messageEdit.toPlainText() self.profile.send_message(text) -# ----------------------------------------------------------------------------------------------------------------- -# Functions which called when user open context menu in friends list -# ----------------------------------------------------------------------------------------------------------------- + # ----------------------------------------------------------------------------------------------------------------- + # Functions which called when user open context menu in friends list + # ----------------------------------------------------------------------------------------------------------------- def friend_right_click(self, pos): item = self.friends_list.itemAt(pos) @@ -313,9 +313,9 @@ class MainWindow(QtGui.QMainWindow): num = self.friends_list.indexFromItem(item).row() self.profile.clear_history(num) -# ----------------------------------------------------------------------------------------------------------------- -# Functions which called when user click somewhere else -# ----------------------------------------------------------------------------------------------------------------- + # ----------------------------------------------------------------------------------------------------------------- + # Functions which called when user click somewhere else + # ----------------------------------------------------------------------------------------------------------------- def friend_click(self, index): print 'row:', index.row() diff --git a/src/menu.py b/src/menu.py index 2381984..1c1d035 100644 --- a/src/menu.py +++ b/src/menu.py @@ -173,8 +173,11 @@ class ProfileSettings(CenteredWidget): Profile.get_instance().set_avatar(data) def export_profile(self): - directory = QtGui.QFileDialog.getExistingDirectory() - ProfileHelper.export_profile(directory + '/') + # TODO: export history + directory = QtGui.QFileDialog.getExistingDirectory() + '/' + ProfileHelper.export_profile(directory) + settings = Settings.get_instance() + settings.export(directory) def closeEvent(self, event): profile = Profile.get_instance() diff --git a/src/profile.py b/src/profile.py index 8dcfca9..b0e8bc6 100644 --- a/src/profile.py +++ b/src/profile.py @@ -5,7 +5,7 @@ import os from settings import Settings from toxcore_enums_and_consts import * from ctypes import * -from util import curr_time, log, Singleton, curr_directory +from util import curr_time, log, Singleton, curr_directory, convert_time from tox_dns import tox_dns from history import * import time @@ -170,6 +170,7 @@ class Friend(Contact): def __init__(self, message_getter, number, *args): """ + :param message_getter: gets messages from db :param number: number of friend. """ super(Friend, self).__init__(*args) @@ -180,6 +181,7 @@ class Friend(Contact): self._message_getter = message_getter self._corr = [] self._unsaved_messages = 0 + self._history_loaded = False def __del__(self): self.set_visibility(False) @@ -189,25 +191,41 @@ class Friend(Contact): # History support # ----------------------------------------------------------------------------------------------------------------- - def load_corr(self): + def load_corr(self, first_time=True): + """ + :param first_time: friend became active, load first part of messages + """ + if first_time and self._history_loaded: + return data = self._message_getter.get(42) if data is not None and len(data): data.reverse() else: return self._corr = data + self._corr + self._history_loaded = True def get_corr_for_saving(self): + """ + Get data to save in db + :return: list of unsaved messages or [] + """ return self._corr[-self._unsaved_messages:] if self._unsaved_messages else [] def get_corr(self): return self._corr def append_message(self, message): + """ + :param message: tuple (message, owner, unix_time, message_type) + """ self._corr.append(message) self._unsaved_messages += 1 def clear_corr(self): + """ + Clear messages list + """ self._corr = [] # ----------------------------------------------------------------------------------------------------------------- @@ -215,6 +233,10 @@ class Friend(Contact): # ----------------------------------------------------------------------------------------------------------------- def set_name(self, value): + """ + Set new name or ignore if alias exists + :param value: new name + """ if not self._alias: super(self.__class__, self).set_name(value) @@ -268,19 +290,19 @@ class Profile(Contact, Singleton): :param tox: tox instance :param screen: ref to main screen """ + super(Profile, self).__init__(tox.self_get_name(), + tox.self_get_status_message(), + screen.user_info, + tox.self_get_address()) self.screen = screen - self._widget = screen.user_info self._messages = screen.messages self.tox = tox - self._name = tox.self_get_name() - self._status_message = tox.self_get_status_message() - self._status = None settings = Settings.get_instance() self.show_online = settings['show_online_friends'] screen.online_contacts.setChecked(self.show_online) aliases = settings['friends_aliases'] data = tox.self_get_friend_list() - self.history = History(tox.self_get_public_key()) + self.history = History(tox.self_get_public_key()) # connection to db self._friends, self._active_friend = [], -1 for i in data: # creates list of friends tox_id = tox.friend_get_public_key(i) @@ -297,18 +319,7 @@ class Profile(Contact, Singleton): friend = Friend(message_getter, i, name, status_message, item, tox_id) friend.set_alias(alias) self._friends.append(friend) - self.set_name(tox.self_get_name().encode('utf-8')) - self.set_status_message(tox.self_get_status_message().encode('utf-8')) self.filtration(self.show_online) - self._tox_id = tox.self_get_address() - self.load_avatar() - - def save(self): - print 'In save' - for friend in self._friends: - messages = friend.get_corr_for_saving() - self.history.save_messages_to_db(friend.tox_id, messages) - del self.history # ----------------------------------------------------------------------------------------------------------------- # Edit current user's data @@ -375,6 +386,8 @@ class Profile(Contact, Singleton): """ if value is None and self._active_friend == -1: # nothing to update return + if value == self._active_friend: + return if value == -1: # all friends were deleted self.screen.account_name.setText('') self.screen.account_status.setText('') @@ -386,10 +399,19 @@ class Profile(Contact, Singleton): try: if value is not None: self._active_friend = value + friend = self._friends[value] self._friends[self._active_friend].set_messages(False) self.screen.messageEdit.clear() - self.screen.messages.clear() - friend = self._friends[self._active_friend] + self.screen.messages.clear() + friend.load_corr() + messages = friend.get_corr()[-42:] + for message in messages: + self.create_message_item(message[0], + convert_time(message[2]), + friend.name if message[1] else self._name, + message[3]) + else: + friend = self._friends[self._active_friend] self.screen.account_name.setText(friend.name) self.screen.account_status.setText(friend.status_message) avatar_path = (Settings.get_default_path() + 'avatars/{}.png').format(friend.tox_id[:TOX_PUBLIC_KEY_SIZE * 2]) @@ -399,13 +421,6 @@ class Profile(Contact, Singleton): pixmap.scaled(64, 64, QtCore.Qt.KeepAspectRatio) self.screen.account_avatar.setPixmap(avatar_path) self.screen.account_avatar.repaint() - friend.load_corr() # TODO: call not every time and compute time - messages = friend.get_corr()[-42:] - for message in messages: - self.create_message_item(message[0], - curr_time(), - friend.name if message[1] else self._name, - message[3]) except: # no friend found. ignore log('Incorrect friend value: ' + str(value)) @@ -443,7 +458,7 @@ class Profile(Contact, Singleton): friend.set_messages(True) friend.append_message((message.decode('utf-8'), MESSAGE_OWNER['FRIEND'], - time.time(), + int(time.time()), message_type)) def send_message(self, text): @@ -458,14 +473,43 @@ class Profile(Contact, Singleton): else: message_type = TOX_MESSAGE_TYPE['NORMAL'] friend = self._friends[self._active_friend] + # TODO: add message splitting self.tox.friend_send_message(friend.number, message_type, text.encode('utf-8')) self.create_message_item(text, curr_time(), self._name, message_type) self.screen.messageEdit.clear() friend.append_message((text, MESSAGE_OWNER['ME'], - time.time(), + int(time.time()), message_type)) + # ----------------------------------------------------------------------------------------------------------------- + # History support + # ----------------------------------------------------------------------------------------------------------------- + + def save_history(self): + """ + Save history to db + """ + print 'In save' + if Settings.get_instance()['save_history']: + for friend in self._friends: + messages = friend.get_corr_for_saving() + self.history.save_messages_to_db(friend.tox_id, messages) + del self.history + + def clear_history(self, num=None): + if num is not None: + friend = self._friends[num] + friend.clear_corr() + self.history.delete_messages(friend.tox_id) + else: # clear all history + for friend in self._friends: + friend.clear_corr() + self.history.delete_messages(friend.tox_id) + self.history.delete_friend_from_db(friend.tox_id) + if num is None or num == self.get_active_number(): + self._messages.clear() + # ----------------------------------------------------------------------------------------------------------------- # Factories for friend and message items # ----------------------------------------------------------------------------------------------------------------- @@ -489,8 +533,9 @@ class Profile(Contact, Singleton): self._messages.addItem(elem) self._messages.setItemWidget(elem, item) self._messages.repaint() + # ----------------------------------------------------------------------------------------------------------------- - # Work with friends (remove, set alias, clear history) + # Work with friends (remove, set alias, get public key) # ----------------------------------------------------------------------------------------------------------------- def set_alias(self, num): @@ -520,18 +565,6 @@ class Profile(Contact, Singleton): settings.save() self.set_active() - def clear_history(self, num=None): - if num is not None: - friend = self._friends[num] - friend.clear_corr() - self.history.delete_messages(friend.tox_id) - else: # clear all history - for friend in self._friends: - friend.clear_corr() - self.history.delete_messages(friend.tox_id) - self.history.delete_friend_from_db(friend.tox_id) - self.set_active() - def friend_public_key(self, num): return self._friends[num].tox_id diff --git a/src/settings.py b/src/settings.py index 61c8ba9..651b692 100644 --- a/src/settings.py +++ b/src/settings.py @@ -8,6 +8,7 @@ class Settings(Singleton, dict): def __init__(self, name=''): self.path = Settings.get_default_path() + str(name) + '.json' + self.name = name if os.path.isfile(self.path): with open(self.path) as fl: data = fl.read() @@ -64,6 +65,11 @@ class Settings(Singleton, dict): with open(self.path, 'w') as fl: fl.write(text) + def export(self, path): + text = json.dumps(self) + with open(path + str(self.name) + '.json', 'w') as fl: + fl.write(text) + @staticmethod def get_default_path(): if system() == 'Linux': diff --git a/src/util.py b/src/util.py index 338ddf1..3e9cb4c 100644 --- a/src/util.py +++ b/src/util.py @@ -16,7 +16,15 @@ def curr_directory(): def curr_time(): - return time.strftime("%H:%M") + return time.strftime('%H:%M') + + +def convert_time(t): + sec = int(t) - time.timezone + m, s = divmod(sec, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + return '%02d:%02d' % (h, m) def get_style(style):