new context menu generation - builder, generators

This commit is contained in:
ingvar1995 2018-05-11 21:27:46 +03:00
parent 7898363dcb
commit c6192de9dd
7 changed files with 178 additions and 78 deletions

View file

@ -2,6 +2,7 @@ from history.database import *
from contacts import basecontact, common from contacts import basecontact, common
import utils.util as util import utils.util as util
from messenger.messages import * from messenger.messages import *
from contacts.contact_menu import *
from file_transfers import file_transfers as ft from file_transfers import file_transfers as ft
import re import re
@ -89,7 +90,7 @@ class Contact(basecontact.BaseContact):
self._unsaved_messages += 1 self._unsaved_messages += 1
def get_last_message_text(self): def get_last_message_text(self):
messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_OWNER['FRIEND'], self._corr)) messages = list(filter(lambda x: x.get_type() <= 1 and x.get_owner() != MESSAGE_AUTHOR['FRIEND'], self._corr))
if messages: if messages:
return messages[-1].get_data()[0] return messages[-1].get_data()[0]
else: else:
@ -103,19 +104,19 @@ class Contact(basecontact.BaseContact):
""" """
:return list of unsent messages :return list of unsent messages
""" """
messages = filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) messages = filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr)
return list(messages) return list(messages)
def get_unsent_messages_for_saving(self): def get_unsent_messages_for_saving(self):
""" """
:return list of unsent messages for saving :return list of unsent messages for saving
""" """
messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr) messages = filter(lambda x: x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr)
return list(map(lambda x: x.get_data(), messages)) return list(map(lambda x: x.get_data(), messages))
def mark_as_sent(self): def mark_as_sent(self):
try: try:
message = list(filter(lambda x: x.get_owner() == MESSAGE_OWNER['NOT_SENT'], self._corr))[0] message = list(filter(lambda x: x.get_owner() == MESSAGE_AUTHOR['NOT_SENT'], self._corr))[0]
message.mark_as_sent() message.mark_as_sent()
except Exception as ex: except Exception as ex:
util.log('Mark as sent ex: ' + str(ex)) util.log('Mark as sent ex: ' + str(ex))
@ -140,7 +141,7 @@ class Contact(basecontact.BaseContact):
def save_message(x): def save_message(x):
if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None): if x.get_type() == 2 and (x.get_status() >= 2 or x.get_status() is None):
return True return True
return x.get_owner() == MESSAGE_OWNER['NOT_SENT'] return x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']
old = filter(save_message, self._corr[:-SAVE_MESSAGES]) old = filter(save_message, self._corr[:-SAVE_MESSAGES])
self._corr = list(old) + self._corr[-SAVE_MESSAGES:] self._corr = list(old) + self._corr[-SAVE_MESSAGES:]
@ -162,7 +163,7 @@ class Contact(basecontact.BaseContact):
self._unsaved_messages = 0 self._unsaved_messages = 0
else: else:
self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS) self._corr = list(filter(lambda x: (x.get_type() == 2 and x.get_status() in ft.ACTIVE_FILE_TRANSFERS)
or (x.get_type() <= 1 and x.get_owner() == MESSAGE_OWNER['NOT_SENT']), or (x.get_type() <= 1 and x.get_owner() == MESSAGE_AUTHOR['NOT_SENT']),
self._corr)) self._corr))
self._unsaved_messages = len(self.get_unsent_messages()) self._unsaved_messages = len(self.get_unsent_messages())
@ -294,3 +295,10 @@ class Contact(basecontact.BaseContact):
return common.BaseTypingNotificationHandler.DEFAULT_HANDLER return common.BaseTypingNotificationHandler.DEFAULT_HANDLER
typing_notification_handler = property(get_typing_notification_handler) typing_notification_handler = property(get_typing_notification_handler)
# -----------------------------------------------------------------------------------------------------------------
# Context menu support
# -----------------------------------------------------------------------------------------------------------------
def get_context_menu_generator(self):
return BaseContactMenuGenerator(self)

View file

@ -0,0 +1,144 @@
from PyQt5 import QtWidgets
import utils.ui as util_ui
# -----------------------------------------------------------------------------------------------------------------
# Builder
# -----------------------------------------------------------------------------------------------------------------
def _create_menu(menu_name):
return QtWidgets.QMenu(menu_name or '')
class ContactMenuBuilder:
def __init__(self):
self._actions = []
self._submenus = []
self._name = None
def with_name(self, name):
self._name = name
return self
def with_action(self, text, handler):
self._actions.append((text, handler))
return self
def with_actions(self, actions):
self._actions.extend(actions)
return self
def with_submenu(self, submenu):
self._add_submenu(submenu)
return self
def with_optional_submenu(self, submenu):
if submenu is not None:
self._add_submenu(submenu)
return self
def build(self): # TODO: actions order
menu = _create_menu(self._name)
for text, handler in self._actions:
action = menu.addAction(text)
action.triggered.connect(handler)
for submenu in self._submenus:
menu.addMenu(submenu)
return menu
def _add_submenu(self, submenu):
self._submenus.append(submenu)
# -----------------------------------------------------------------------------------------------------------------
# Generators
# -----------------------------------------------------------------------------------------------------------------
class BaseContactMenuGenerator:
def __init__(self, contact):
self._contact = contact
def generate(self, plugin_loader, contacts_manager, main_screen, settings, number):
return ContactMenuBuilder().build()
class FriendMenuGenerator(BaseContactMenuGenerator):
def generate(self, plugin_loader, contacts_manager, main_screen, settings, number):
history_menu = self._generate_history_menu(main_screen, number)
copy_menu = self._generate_copy_menu(main_screen)
plugins_menu = self._generate_plugins_menu(plugin_loader, number)
allowed = self._contact.tox_id in settings['auto_accept_from_friends']
auto = util_ui.tr('Disallow auto accept') if allowed else util_ui.tr('Allow auto accept')
builder = ContactMenuBuilder()
menu = (builder
.with_action(util_ui.tr('Set alias'), lambda: main_screen.set_alias(number))
.with_action(util_ui.tr('Chat history'), lambda: main_screen.clear_history(number))
.with_submenu(history_menu)
.with_submenu(copy_menu)
.with_action(auto, lambda: main_screen.auto_accept(number, not allowed))
.with_action(util_ui.tr('Remove friend'), lambda: main_screen.remove_friend(number))
.with_action(util_ui.tr('Block friend'), lambda: main_screen.block_friend(number))
.with_action(util_ui.tr('Notes'), lambda: main_screen.show_note(self._contact))
.with_optional_submenu(plugins_menu)
).build()
return menu
# -----------------------------------------------------------------------------------------------------------------
# Private methods
# -----------------------------------------------------------------------------------------------------------------
@staticmethod
def _generate_history_menu(main_screen, number):
history_menu_builder = ContactMenuBuilder()
history_menu = (history_menu_builder
.with_name(util_ui.tr('Chat history'))
.with_action(util_ui.tr('Clear history'), lambda: main_screen.clear_history(number))
.with_action(util_ui.tr('Export as text'), lambda: main_screen.export_history(number))
.with_action(util_ui.tr('Export as HTML'), lambda: main_screen.export_history(number, False))
).build()
return history_menu
def _generate_copy_menu(self, main_screen):
copy_menu_builder = ContactMenuBuilder()
copy_menu = (copy_menu_builder
.with_name(util_ui.tr('Copy'))
.with_action(util_ui.tr('Name'), lambda: main_screen.copy_text(self._contact.name))
.with_action(util_ui.tr('Status message'), lambda: main_screen.copy_text(self._contact.name))
.with_action(util_ui.tr('Public key'), lambda: main_screen.copy_text(self._contact.tox_id))
).build()
return copy_menu
@staticmethod
def _generate_plugins_menu(plugin_loader, number):
if plugin_loader is None:
return None
plugins_actions = plugin_loader.get_menu(number)
if not len(plugins_actions):
return None
plugins_menu_builder = ContactMenuBuilder()
plugins_menu = (plugins_menu_builder
.with_name(util_ui.tr('Plugins'))
.with_actions(plugins_actions)
)
return plugins_menu
def _generate_groups_menu(self, contacts_manager):
chats = contacts_manager.get_group_chats()
if not len(chats) or self._contact.status is None:
return None

View file

@ -1,4 +1,3 @@
import utils.util as util
import common.tox_save as tox_save import common.tox_save as tox_save

View file

@ -1,6 +1,7 @@
from contacts import contact, common from contacts import contact, common
from messenger.messages import * from messenger.messages import *
import os import os
from contacts.contact_menu import *
class Friend(contact.Contact): class Friend(contact.Contact):
@ -46,6 +47,7 @@ class Friend(contact.Contact):
if message.get_data()[1] is not None: if message.get_data()[1] is not None:
return True return True
return os.path.exists(message.get_data()[0]) return os.path.exists(message.get_data()[0])
self._corr = list(filter(is_valid, self._corr)) self._corr = list(filter(is_valid, self._corr))
def delete_one_unsent_file(self, time): def delete_one_unsent_file(self, time):
@ -81,3 +83,10 @@ class Friend(contact.Contact):
def get_typing_notification_handler(self): def get_typing_notification_handler(self):
return self._typing_notification_handler return self._typing_notification_handler
# -----------------------------------------------------------------------------------------------------------------
# Context menu support
# -----------------------------------------------------------------------------------------------------------------
def get_context_menu_generator(self):
return FriendMenuGenerator(self)

View file

@ -138,7 +138,7 @@ class PluginLoader():
if name in self._plugins and self._plugins[name][1]: if name in self._plugins and self._plugins[name][1]:
self._plugins[name][0].command(text[len(name) + 1:]) self._plugins[name][0].command(text[len(name) + 1:])
def get_menu(self, menu, num): def get_menu(self, num):
""" """
Return list of items for menu Return list of items for menu
""" """
@ -146,7 +146,7 @@ class PluginLoader():
for elem in self._plugins.values(): for elem in self._plugins.values():
if elem[1]: if elem[1]:
try: try:
result.extend(elem[0].get_menu(menu, num)) result.extend(elem[0].get_menu(num))
except: except:
continue continue
return result return result

View file

@ -76,12 +76,11 @@ class PluginSuperClass:
""" """
return self.__doc__ return self.__doc__
def get_menu(self, menu, row_number): def get_menu(self, row_number):
""" """
This method creates items for menu which called on right click in list of friends This method creates items for menu which called on right click in list of friends
:param menu: menu instance
:param row_number: number of selected row in list of contacts :param row_number: number of selected row in list of contacts
:return list of QAction's :return list of tuples (text, handler)
""" """
return [] return []

View file

@ -576,63 +576,14 @@ class MainWindow(QtWidgets.QMainWindow):
def friend_right_click(self, pos): def friend_right_click(self, pos):
# TODO: move to contact? # TODO: move to contact?
item = self.friends_list.itemAt(pos) item = self.friends_list.itemAt(pos)
num = self.friends_list.indexFromItem(item).row() number = self.friends_list.indexFromItem(item).row()
contact = self._contacts_manager.get_contact(num) contact = self._contacts_manager.get_contact(number)
if contact is None: if contact is None:
return return
allowed = contact.tox_id in self._settings['auto_accept_from_friends']
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() generator = contact.get_context_menu_generator()
is_friend = type(contact) is Friend self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self,
if is_friend: self._settings, number)
set_alias_item = self.listMenu.addAction(util_ui.tr('Set alias'))
set_alias_item.triggered.connect(lambda: self.set_alias(num))
history_menu = self.listMenu.addMenu(util_ui.tr('Chat history'))
clear_history_item = history_menu.addAction(util_ui.tr('Clear history'))
export_to_text_item = history_menu.addAction(util_ui.tr('Export as text'))
export_to_html_item = history_menu.addAction(util_ui.tr('Export as HTML'))
copy_menu = self.listMenu.addMenu(util_ui.tr('Copy'))
copy_name_item = copy_menu.addAction(util_ui.tr('Name'))
copy_status_item = copy_menu.addAction(util_ui.tr('Status message'))
if is_friend:
copy_key_item = copy_menu.addAction(util_ui.tr('Public key'))
auto_accept_item = self.listMenu.addAction(auto)
remove_item = self.listMenu.addAction(util_ui.tr('Remove friend'))
block_item = self.listMenu.addAction(util_ui.tr('Block friend'))
notes_item = self.listMenu.addAction(util_ui.tr('Notes'))
chats = self._contacts_manager.get_group_chats()
if len(chats) and contact.status is not None:
invite_menu = self.listMenu.addMenu(util_ui.tr('Invite to group chat'))
for i in range(len(chats)):
name, number = chats[i]
item = invite_menu.addAction(name)
item.triggered.connect(lambda: self.invite_friend_to_gc(num, number))
if self._plugins_loader is not None:
submenu = self._plugins_loader.get_menu(self.listMenu, num)
if len(submenu):
plug = self.listMenu.addMenu(util_ui.tr('Plugins'))
plug.addActions(submenu)
copy_key_item.triggered.connect(lambda: self.copy_friend_key(num))
remove_item.triggered.connect(lambda: self.remove_friend(num))
block_item.triggered.connect(lambda: self.block_friend(num))
auto_accept_item.triggered.connect(lambda: self.auto_accept(num, not allowed))
notes_item.triggered.connect(lambda: self.show_note(contact))
else:
leave_item = self.listMenu.addAction(util_ui.tr('Leave chat'))
set_title_item = self.listMenu.addAction(util_ui.tr('Set title'))
leave_item.triggered.connect(lambda: self.leave_gc(num))
set_title_item.triggered.connect(lambda: self.set_title(num))
clear_history_item.triggered.connect(lambda: self.clear_history(num))
copy_name_item.triggered.connect(lambda: self.copy_name(contact))
copy_status_item.triggered.connect(lambda: self.copy_status(contact))
export_to_text_item.triggered.connect(lambda: self.export_history(num))
export_to_html_item.triggered.connect(lambda: self.export_history(num, False))
parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0))
self.listMenu.move(parent_position + pos) self.listMenu.move(parent_position + pos)
self.listMenu.show() self.listMenu.show()
@ -672,20 +623,10 @@ class MainWindow(QtWidgets.QMainWindow):
friend = self.profile.get_contact(num) friend = self.profile.get_contact(num)
self._contacts_manager.block_user(friend.tox_id) self._contacts_manager.block_user(friend.tox_id)
def copy_friend_key(self, num):
tox_id = self._contacts_manager.friend_public_key(num)
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(tox_id)
@staticmethod @staticmethod
def copy_name(friend): def copy_text(text):
clipboard = QtWidgets.QApplication.clipboard() clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(friend.name) clipboard.setText(text)
@staticmethod
def copy_status(friend):
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(friend.status_message)
def clear_history(self, num): def clear_history(self, num):
self._contacts_manager.clear_history(num) self._contacts_manager.clear_history(num)