# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- import os from PyQt5 import uic from PyQt5 import QtWidgets, QtGui from qtpy.QtGui import (QColor, QTextCharFormat, QFont, QSyntaxHighlighter) from ui.contact_items import * from ui.widgets import MultilineEdit from ui.main_screen_widgets import * import utils.util as util import utils.ui as util_ui from user_data.settings import Settings import logging global LOG LOG = logging.getLogger('app.'+'mains') iMAX = 70 try: # https://github.com/pyqtconsole/pyqtconsole from pyqtconsole.console import PythonConsole import pyqtconsole.highlighter as hl except Exception as e: LOG.warn(e) PythonConsole = None else: if True: # I want to do reverse video but I cant figure how bg='white' def hl_format(color, style=''): """Return a QTextCharFormat with the given attributes. """ _color = QColor() _color.setNamedColor(color) _format = QTextCharFormat() _format.setForeground(_color) if 'bold' in style: _format.setFontWeight(QFont.Bold) if 'italic' in style: _format.setFontItalic(True) _bgcolor = QColor() _bgcolor.setNamedColor(bg) _format.setBackground(_bgcolor) return _format aFORMATS = { 'keyword': hl_format('blue', 'bold'), 'operator': hl_format('red'), 'brace': hl_format('darkGray'), 'defclass': hl_format('black', 'bold'), 'string': hl_format('magenta'), 'string2': hl_format('darkMagenta'), 'comment': hl_format('darkGreen', 'italic'), 'self': hl_format('black', 'italic'), 'numbers': hl_format('brown'), 'inprompt': hl_format('darkBlue', 'bold'), 'outprompt': hl_format('darkRed', 'bold'), } else: bg = 'black' def hl_format(color, style=''): """Return a QTextCharFormat with the given attributes. unused """ _color = QColor() _color.setNamedColor(color) _format = QTextCharFormat() _format.setForeground(_color) if 'bold' in style: _format.setFontWeight(QFont.Bold) if 'italic' in style: _format.setFontItalic(True) _bgcolor = QColor() _bgcolor.setNamedColor(bg) _format.setBackground(_bgcolor) return _format aFORMATS = { 'keyword': hl_format('blue', 'bold'), 'operator': hl_format('red'), 'brace': hl_format('lightGray'), 'defclass': hl_format('white', 'bold'), 'string': hl_format('magenta'), 'string2': hl_format('lightMagenta'), 'comment': hl_format('lightGreen', 'italic'), 'self': hl_format('white', 'italic'), 'numbers': hl_format('lightBrown'), 'inprompt': hl_format('lightBlue', 'bold'), 'outprompt': hl_format('lightRed', 'bold'), } class QTextEditLogger(logging.Handler): def __init__(self, parent, app): super().__init__() self.widget = QtWidgets.QPlainTextEdit(parent) self.widget.setReadOnly(True) if app and app._settings: size = app._settings['message_font_size'] font_name = app._settings['font'] else: size = 12 font_name = "Courier New" font = QtGui.QFont(font_name, size, QtGui.QFont.Bold) self.widget.setFont(font) def emit(self, record): msg = self.format(record) self.widget.appendPlainText(msg) class LogDialog(QtWidgets.QDialog, QtWidgets.QPlainTextEdit): def __init__(self, parent=None, app=None): global iMAX super().__init__(parent) logTextBox = QTextEditLogger(self, app) # You can format what is printed to text box - %(levelname)s logTextBox.setFormatter(logging.Formatter('%(name)s %(asctime)-4s - %(message)s')) logTextBox.setLevel(app._args.loglevel) logging.getLogger().addHandler(logTextBox) self._button = QtWidgets.QPushButton(self) self._button.setText('Copy All') self._logTextBox = logTextBox layout = QtWidgets.QVBoxLayout() # Add the new logging box widget to the layout layout.addWidget(logTextBox.widget) layout.addWidget(self._button) self.setLayout(layout) settings = Settings.get_default_settings(app._args) #self.setBaseSize( self.resize(min(iMAX * settings['message_font_size'], parent.width()), 350) # Connect signal to slot self._button.clicked.connect(self.test) def test(self): # FixMe: 65:8: E1101: Instance of 'QTextEditLogger' has no 'selectAll' member (no-member) # :66:8: E1101: Instance of 'QTextEditLogger' has no 'copy' member (no-member) self._logTextBox.selectAll() self._logTextBox.copy() class MainWindow(QtWidgets.QMainWindow): def __init__(self, settings, tray, app): super().__init__() self._settings = settings self._contacts_manager = None self._tray = tray self._app = app self._tox = app._tox self._widget_factory = None self._modal_window = None self._plugins_loader = None self.setAcceptDrops(True) self._saved = False self._smiley_window = None self._profile = None self._toxes = None self._messenger = None self._file_transfer_handler = self._history_loader = self._groups_service = self._calls_manager = None self._should_show_group_peers_list = False self.initUI() global iMAX if iMAX == 100: # take a rough guess of 2/3 the default width at the default font iMAX = settings['width'] * 2/3 / settings['message_font_size'] self._me = LogDialog(self, app) self._pe = None self._we = None def set_dependencies(self, widget_factory, tray, contacts_manager, messenger, profile, plugins_loader, file_transfer_handler, history_loader, calls_manager, groups_service, toxes, app): 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._history_loader = history_loader self._calls_manager = calls_manager self._groups_service = groups_service self._toxes = toxes self._app = app self._messenger = messenger self._contacts_manager.active_contact_changed.add_callback(self._new_contact_selected) self.messageEdit.set_dependencies(messenger, contacts_manager, file_transfer_handler) self.update_gc_invites_button_state() def show(self): super().show() self._contacts_manager.update() if self._settings['show_welcome_screen']: self._modal_window = self._widget_factory.create_welcome_window() def setup_menu(self, window): self.menubar = QtWidgets.QMenuBar(window) self.menubar.setObjectName("menubar") self.menubar.setNativeMenuBar(True) # was False self.menubar.setMinimumSize(self.width(), 250) self.menubar.setMaximumSize(self.width(), 32) self.menubar.setBaseSize(self.width(), 250) self.actionTest_tox = QtWidgets.QAction(window) self.actionTest_tox.setObjectName("actionTest_tox") self.actionTest_nmap = QtWidgets.QAction(window) self.actionTest_nmap.setObjectName("actionTest_nmap") self.actionTest_main = QtWidgets.QAction(window) self.actionTest_main.setObjectName("actionTest_main") self.actionQuit_program = QtWidgets.QAction(window) self.actionQuit_program.setObjectName("actionQuit_program") self.menuProfile = QtWidgets.QMenu(self.menubar) self.menuProfile.setObjectName("menuProfile") self.menuGC = QtWidgets.QMenu(self.menubar) self.menuSettings = QtWidgets.QMenu(self.menubar) self.menuSettings.setObjectName("menuSettings") self.menuPlugins = QtWidgets.QMenu(self.menubar) self.menuPlugins.setObjectName("menuPlugins") self.menuAbout = QtWidgets.QMenu(self.menubar) # alignment=QtCore.Qt.AlignRight self.menuAbout.setObjectName("menuAbout") self.actionAdd_friend = QtWidgets.QAction(window) self.actionAdd_friend.setObjectName("actionAdd_friend") self.actionProfile_settings = QtWidgets.QAction(window) self.actionProfile_settings.setObjectName("actionProfile_settings") self.actionPrivacy_settings = QtWidgets.QAction(window) self.actionPrivacy_settings.setObjectName("actionPrivacy_settings") self.actionInterface_settings = QtWidgets.QAction(window) self.actionInterface_settings.setObjectName("actionInterface_settings") self.actionNotifications = QtWidgets.QAction(window) self.actionNotifications.setObjectName("actionNotifications") self.actionNetwork = QtWidgets.QAction(window) self.actionNetwork.setObjectName("actionNetwork") self.actionAbout_program = QtWidgets.QAction(window) self.actionAbout_program.setObjectName("actionAbout_program") self.actionLog_console = QtWidgets.QAction(window) self.actionLog_console.setObjectName("actionLog_console") self.actionPython_console = QtWidgets.QAction(window) self.actionPython_console.setObjectName("actionLog_console") self.actionWeechat_console = QtWidgets.QAction(window) self.actionWeechat_console.setObjectName("actionLog_console") self.updateSettings = QtWidgets.QAction(window) self.actionSettings = QtWidgets.QAction(window) self.actionSettings.setObjectName("actionSettings") self.audioSettings = QtWidgets.QAction(window) self.videoSettings = QtWidgets.QAction(window) self.pluginData = QtWidgets.QAction(window) self.importPlugin = QtWidgets.QAction(window) self.reloadPlugins = QtWidgets.QAction(window) self.reloadToxchat = QtWidgets.QAction(window) self.lockApp = QtWidgets.QAction(window) self.createGC = QtWidgets.QAction(window) self.joinGC = QtWidgets.QAction(window) self.gc_invites = QtWidgets.QAction(window) self.menuProfile.addAction(self.actionAdd_friend) self.menuProfile.addAction(self.actionSettings) self.menuProfile.addAction(self.lockApp) self.menuProfile.addAction(self.actionTest_tox) self.menuProfile.addAction(self.actionTest_nmap) self.menuProfile.addAction(self.actionTest_main) self.menuProfile.addAction(self.actionQuit_program) self.menuGC.addAction(self.createGC) self.menuGC.addAction(self.joinGC) self.menuGC.addAction(self.gc_invites) self.menuSettings.addAction(self.actionProfile_settings) self.menuSettings.addAction(self.actionPrivacy_settings) self.menuSettings.addAction(self.actionInterface_settings) self.menuSettings.addAction(self.actionNotifications) self.menuSettings.addAction(self.actionNetwork) self.menuSettings.addAction(self.audioSettings) self.menuSettings.addAction(self.videoSettings) ## self.menuSettings.addAction(self.updateSettings) self.menuPlugins.addAction(self.pluginData) self.menuPlugins.addAction(self.importPlugin) self.menuPlugins.addAction(self.reloadPlugins) self.menuPlugins.addAction(self.reloadToxchat) self.menuPlugins.addAction(self.actionLog_console) self.menuPlugins.addAction(self.actionPython_console) self.menuPlugins.addAction(self.actionWeechat_console) self.menuAbout.addAction(self.actionAbout_program) self.menubar.addAction(self.menuProfile.menuAction()) self.menubar.addAction(self.menuGC.menuAction()) self.menubar.addAction(self.menuSettings.menuAction()) self.menubar.addAction(self.menuPlugins.menuAction()) self.menubar.addAction(self.menuAbout.menuAction()) self.actionTest_nmap.triggered.connect(self.test_nmap) self.actionTest_main.triggered.connect(self.test_main) self.actionTest_tox.triggered.connect(self.test_tox) self.actionQuit_program.triggered.connect(self.quit_program) self.actionAbout_program.triggered.connect(self.about_program) self.actionLog_console.triggered.connect(self.log_console) self.actionPython_console.triggered.connect(self.python_console) self.actionWeechat_console.triggered.connect(self.weechat_console) self.actionNetwork.triggered.connect(self.network_settings) self.actionAdd_friend.triggered.connect(self.add_contact_triggered) self.createGC.triggered.connect(self.create_gc) self.joinGC.triggered.connect(self.join_gc) self.actionProfile_settings.triggered.connect(self.profile_settings) self.actionPrivacy_settings.triggered.connect(self.privacy_settings) self.actionInterface_settings.triggered.connect(self.interface_settings) self.actionNotifications.triggered.connect(self.notification_settings) self.audioSettings.triggered.connect(self.audio_settings) self.videoSettings.triggered.connect(self.video_settings) ## self.updateSettings.triggered.connect(self.update_settings) self.pluginData.triggered.connect(self.plugins_menu) self.lockApp.triggered.connect(self.lock_app) self.importPlugin.triggered.connect(self.import_plugin) self.reloadPlugins.triggered.connect(self.reload_plugins) self.reloadToxchat.triggered.connect(self.reload_toxchat) self.gc_invites.triggered.connect(self._open_gc_invites_list) def languageChange(self, *args, **kwargs): self.retranslateUi() def event(self, event): if event.type() == QtCore.QEvent.WindowActivate: if hasattr(self, '_tray') and self._tray: self._tray.setIcon(QtGui.QIcon(util.join_path(util.get_images_directory(), 'icon.png'))) self.messages.repaint() return super().event(event) def status(self, line): """For now, this uses the unused space on the menubar line It could be a status line at the bottom, or a statusline with history.""" self.menuAbout.setTitle(line[:iMAX]) return line def retranslateUi(self): self.lockApp.setText(util_ui.tr("Lock")) self.menuPlugins.setTitle(util_ui.tr("Plugins")) self.menuGC.setTitle(util_ui.tr("Group chats")) self.pluginData.setText(util_ui.tr("List of plugins")) self.menuProfile.setTitle(util_ui.tr("Profile")) self.menuSettings.setTitle(util_ui.tr("Settings")) self.menuAbout.setTitle(util_ui.tr("About")) self.actionAdd_friend.setText(util_ui.tr("Add contact")) self.createGC.setText(util_ui.tr("Create group chat")) self.joinGC.setText(util_ui.tr("Join group chat")) self.gc_invites.setText(util_ui.tr("Group invites")) self.actionProfile_settings.setText(util_ui.tr("Profile")) self.actionPrivacy_settings.setText(util_ui.tr("Privacy")) self.actionInterface_settings.setText(util_ui.tr("Interface")) self.actionNotifications.setText(util_ui.tr("Notifications")) self.actionNetwork.setText(util_ui.tr("Network")) self.actionAbout_program.setText(util_ui.tr("About program")) self.actionLog_console.setText(util_ui.tr("Console Log")) self.actionPython_console.setText(util_ui.tr("Python Console")) self.actionWeechat_console.setText(util_ui.tr("Weechat Console")) self.actionTest_tox.setText(util_ui.tr("Bootstrap")) self.actionTest_nmap.setText(util_ui.tr("Test Nodes")) self.actionTest_main.setText(util_ui.tr("Test Program")) self.actionQuit_program.setText(util_ui.tr("Quit program")) self.actionSettings.setText(util_ui.tr("Settings")) self.audioSettings.setText(util_ui.tr("Audio")) self.videoSettings.setText(util_ui.tr("Video")) self.updateSettings.setText(util_ui.tr("Updates")) self.importPlugin.setText(util_ui.tr("Import plugin")) self.reloadPlugins.setText(util_ui.tr("Reload plugins")) self.reloadToxchat.setText(util_ui.tr("Reload tox.chat")) self.searchLineEdit.setPlaceholderText(util_ui.tr("Search")) self.sendMessageButton.setToolTip(util_ui.tr("Send message")) self.callButton.setToolTip(util_ui.tr("Start audio call with friend")) self.contactsFilterComboBox.clear() self.contactsFilterComboBox.addItem(util_ui.tr("All")) self.contactsFilterComboBox.addItem(util_ui.tr("Online")) self.contactsFilterComboBox.addItem(util_ui.tr("Online first")) self.contactsFilterComboBox.addItem(util_ui.tr("Name")) self.contactsFilterComboBox.addItem(util_ui.tr("Online and by name")) self.contactsFilterComboBox.addItem(util_ui.tr("Online first and by name")) self.contactsFilterComboBox.addItem(util_ui.tr("Kind")) def setup_right_bottom(self, Form): Form.resize(650, 60) self.messageEdit = MessageArea(Form, self) self.messageEdit.setGeometry(QtCore.QRect(0, 3, 450, 55)) font = QtGui.QFont() font.setPointSize(11) font.setBold(True) font.setFamily(self._settings['font']) self.messageEdit.setFont(font) self.sendMessageButton = QtWidgets.QPushButton(Form) self.sendMessageButton.setGeometry(QtCore.QRect(565, 3, 60, 55)) self.menuButton = MenuButton(Form, self.show_menu) self.menuButton.setGeometry(QtCore.QRect(QtCore.QRect(455, 3, 55, 55))) pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'send.png')) icon = QtGui.QIcon(pixmap) self.sendMessageButton.setIcon(icon) self.sendMessageButton.setIconSize(QtCore.QSize(45, 60)) pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) icon = QtGui.QIcon(pixmap) self.menuButton.setIcon(icon) self.menuButton.setIconSize(QtCore.QSize(40, 40)) self.sendMessageButton.clicked.connect(self.send_message) QtCore.QMetaObject.connectSlotsByName(Form) def setup_left_column(self, left_column): uic.loadUi(util.get_views_path('ms_left_column'), left_column) pixmap = QtGui.QPixmap() pixmap.load(util.join_path(util.get_images_directory(), 'search.png')) left_column.searchLabel.setPixmap(pixmap) self.name = DataLabel(left_column) self.name.setGeometry(QtCore.QRect(75, 15, 150, 25)) font = QtGui.QFont() font.setFamily(self._settings['font']) font.setPointSize(14) font.setBold(True) self.name.setFont(font) self.status_message = DataLabel(left_column) self.status_message.setGeometry(QtCore.QRect(75, 35, 170, 25)) self.connection_status = StatusCircle(left_column) self.connection_status.setGeometry(QtCore.QRect(230, 10, 32, 32)) left_column.contactsFilterComboBox.activated[int].connect(lambda x: self._filtering()) self.avatar_label = left_column.avatarLabel self.searchLineEdit = left_column.searchLineEdit self.contacts_filter = self.contactsFilterComboBox = left_column.contactsFilterComboBox self.groupInvitesPushButton = left_column.groupInvitesPushButton self.groupInvitesPushButton.clicked.connect(self._open_gc_invites_list) self.avatar_label.mouseReleaseEvent = self.profile_settings self.status_message.mouseReleaseEvent = self.profile_settings self.name.mouseReleaseEvent = self.profile_settings self.friends_list = left_column.friendsListWidget self.friends_list.itemSelectionChanged.connect(self._selected_contact_changed) self.friends_list.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.friends_list.customContextMenuRequested.connect(self._friend_right_click) self.friends_list.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.friends_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.friends_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.friends_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) def setup_right_top(self, Form): Form.resize(650, 75) self.account_avatar = QtWidgets.QLabel(Form) self.account_avatar.setGeometry(QtCore.QRect(10, 5, 64, 64)) self.account_avatar.setScaledContents(False) self.account_name = DataLabel(Form) self.account_name.setGeometry(QtCore.QRect(100, 0, 400, 25)) self.account_name.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) font = QtGui.QFont() font.setFamily(self._settings['font']) font.setPointSize(14) font.setBold(True) self.account_name.setFont(font) self.account_status = DataLabel(Form) self.account_status.setGeometry(QtCore.QRect(100, 20, 400, 25)) self.account_status.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse) font.setPointSize(12) font.setBold(False) self.account_status.setFont(font) self.account_status.setObjectName("account_status") self.callButton = QtWidgets.QPushButton(Form) self.callButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) self.callButton.setObjectName("callButton") self.callButton.clicked.connect(lambda: self._calls_manager.call_click(True)) self.videocallButton = QtWidgets.QPushButton(Form) self.videocallButton.setGeometry(QtCore.QRect(550, 5, 50, 50)) self.videocallButton.setObjectName("videocallButton") self.videocallButton.clicked.connect(lambda: self._calls_manager.call_click(True, True)) self.groupMenuButton = QtWidgets.QPushButton(Form) self.groupMenuButton.setGeometry(QtCore.QRect(470, 10, 50, 50)) self.groupMenuButton.clicked.connect(self._toggle_gc_peers_list) self.groupMenuButton.setVisible(False) pixmap = QtGui.QPixmap(util.join_path(util.get_images_directory(), 'menu.png')) icon = QtGui.QIcon(pixmap) self.groupMenuButton.setIcon(icon) self.groupMenuButton.setIconSize(QtCore.QSize(45, 60)) self.update_call_state('call') self.typing = QtWidgets.QLabel(Form) self.typing.setGeometry(QtCore.QRect(500, 25, 50, 30)) pixmap = QtGui.QPixmap(QtCore.QSize(50, 30)) pixmap.load(util.join_path(util.get_images_directory(), 'typing.png')) self.typing.setScaledContents(False) self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio)) self.typing.setVisible(False) QtCore.QMetaObject.connectSlotsByName(Form) def setup_right_center(self, widget): self.messages = QtWidgets.QListWidget(widget) self.messages.setGeometry(0, 0, 620, 310) self.messages.setObjectName("messages") self.messages.setSpacing(1) self.messages.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.messages.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.messages.focusOutEvent = lambda event: self.messages.clearSelection() self.messages.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) def load(pos): if not pos: contact = self._contacts_manager.get_curr_contact() self._history_loader.load_history(contact) self.messages.verticalScrollBar().setValue(1) self.messages.verticalScrollBar().valueChanged.connect(load) self.messages.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.messages.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.peers_list = QtWidgets.QListWidget(widget) self.peers_list.setGeometry(0, 0, 0, 0) self.peers_list.setObjectName("peersList") self.peers_list.setSpacing(1) self.peers_list.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.peers_list.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.peers_list.verticalScrollBar().setContextMenuPolicy(QtCore.Qt.NoContextMenu) self.peers_list.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) def initUI(self): self.setMinimumSize(920, 500) s = self._settings self.setGeometry(s['x'], s['y'], s['width'], s['height']) self.setWindowTitle('Toxygen') menu = QtWidgets.QWidget() main = QtWidgets.QWidget() grid = QtWidgets.QGridLayout() info = QtWidgets.QWidget() left_column = QtWidgets.QWidget() messages = QtWidgets.QWidget() message_buttons = QtWidgets.QWidget() self.setup_right_center(messages) self.setup_right_top(info) self.setup_right_bottom(message_buttons) self.setup_left_column(left_column) self.setup_menu(menu) if not s['mirror_mode']: grid.addWidget(left_column, 1, 0, 4, 1) grid.addWidget(messages, 2, 1, 2, 1) grid.addWidget(info, 1, 1) grid.addWidget(message_buttons, 4, 1) grid.setColumnMinimumWidth(1, 500) grid.setColumnMinimumWidth(0, 270) else: grid.addWidget(left_column, 1, 1, 4, 1) grid.addWidget(messages, 2, 0, 2, 1) grid.addWidget(info, 1, 0) grid.addWidget(message_buttons, 4, 0) grid.setColumnMinimumWidth(0, 500) grid.setColumnMinimumWidth(1, 270) grid.addWidget(menu, 0, 0, 1, 2) grid.setSpacing(0) grid.setContentsMargins(0, 0, 0, 0) grid.setRowMinimumHeight(0, 25) grid.setRowMinimumHeight(1, 75) grid.setRowMinimumHeight(2, 25) grid.setRowMinimumHeight(3, 320) grid.setRowMinimumHeight(4, 55) grid.setColumnStretch(1, 1) grid.setRowStretch(3, 1) main.setLayout(grid) self.setCentralWidget(main) self.messageEdit.setFocus() self.friend_info = info self.retranslateUi() def closeEvent(self, event): close_setting = self._settings['close_app'] if close_setting == 0 or self._settings.closing: if self._saved: return self._saved = True self._settings['x'] = self.geometry().x() self._settings['y'] = self.geometry().y() self._settings['width'] = self.width() self._settings['height'] = self.height() self._settings.save() util_ui.close_all_windows() event.accept() elif close_setting == 2 and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): event.ignore() self.hide() else: event.ignore() self.showMinimized() def close_window(self): self._settings.closing = True self.close() def resizeEvent(self, *args, **kwargs): width = self.width() - 270 if not self._should_show_group_peers_list: self.messages.setGeometry(0, 0, width, self.height() - 155) self.peers_list.setGeometry(0, 0, 0, 0) else: self.messages.setGeometry(0, 0, width * 3 // 4, self.height() - 155) self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155) invites_button_visible = self.groupInvitesPushButton.isVisible() # LOG.debug(f"invites_button_visible={invites_button_visible}") self.friends_list.setGeometry(0, 125 if invites_button_visible else 100, 270, self.height() - 150 if invites_button_visible else self.height() - 125) self.videocallButton.setGeometry(QtCore.QRect(self.width() - 330, 10, 50, 50)) self.callButton.setGeometry(QtCore.QRect(self.width() - 390, 10, 50, 50)) self.groupMenuButton.setGeometry(QtCore.QRect(self.width() - 450, 10, 50, 50)) self.typing.setGeometry(QtCore.QRect(self.width() - 450, 20, 50, 30)) self.messageEdit.setGeometry(QtCore.QRect(55, 0, self.width() - 395, 55)) self.menuButton.setGeometry(QtCore.QRect(0, 0, 55, 55)) self.sendMessageButton.setGeometry(QtCore.QRect(self.width() - 340, 0, 70, 55)) self.account_name.setGeometry(QtCore.QRect(100, 15, self.width() - 560, 25)) self.account_status.setGeometry(QtCore.QRect(100, 35, self.width() - 560, 25)) self.messageEdit.setFocus() def keyPressEvent(self, event): key, modifiers = event.key(), event.modifiers() if key == QtCore.Qt.Key_Escape and QtWidgets.QSystemTrayIcon.isSystemTrayAvailable(): self.hide() elif key == QtCore.Qt.Key_C and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): rows = list(map(lambda x: self.messages.row(x), self.messages.selectedItems())) indexes = (rows[0] - self.messages.count(), rows[-1] - self.messages.count()) s = self._history_loader.export_history(self._contacts_manager.get_curr_friend(), True, indexes) self.copy_text(s) elif key == QtCore.Qt.Key_Z and modifiers & QtCore.Qt.ControlModifier and self.messages.selectedIndexes(): self.messages.clearSelection() elif key == QtCore.Qt.Key_F and modifiers & QtCore.Qt.ControlModifier: self.show_search_field() else: super().keyPressEvent(event) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click in menu # ----------------------------------------------------------------------------------------------------------------- def log_console(self): self._me.show() def python_console(self): if not PythonConsole: return app = self._app if app and app._settings: size = app._settings['message_font_size'] font_name = app._settings['font'] else: size = 12 font_name = "Courier New" size = font_width = 10 font_name = "DejaVu Sans Mono" try: if not self._pe: self._pe = PythonConsole(formats=aFORMATS) self._pe.setWindowTitle('variable: app is the application') # self._pe.edit.setStyleSheet('foreground: white; background-color: black;}') # Fix the pyconsole geometry font = self._pe.edit.document().defaultFont() font.setFamily(font_name) font.setBold(True) if font_width is None: font_width = QFontMetrics(font).width('M') self._pe.setFont(font) geometry = self._pe.geometry() geometry.setWidth(font_width*80+20) geometry.setHeight(font_width*40) self._pe.setGeometry(geometry) self._pe.resize(font_width*80+20, font_width*40) self._pe.show() self._pe.eval_queued() # or self._pe.eval_in_thread() return except Exception as e: LOG.debug(e) def weechat_console(self): try: from qweechat.qweechat import MainWindow as MainWindow LOG.info("Adding WeechatConsole") except Exception as e: LOG.exception(f"ERROR WeechatConsole {e} {sys.path}") MainWindow = None return LOG.debug(f"calling {MainWindow}") if not MainWindow: return class WeechatConsole(MainWindow): def __init__(self, *args): MainWindow.__init__(self, *args) def closeEvent(self, event): """Called when QWeeChat window is closed.""" self.network.disconnect_weechat() if self.network.debug_dialog: self.network.debug_dialog.close() config.write(self.config) app = self._app if app and app._settings: size = app._settings['message_font_size'] font_name = app._settings['font'] else: size = 12 font_name = "Courier New" size = font_width = 10 font_name = "DejaVu Sans Mono" try: if not self._we: LOG.debug("creating WeechatConsole") self._we = WeechatConsole() # self._we.setWindowTitle('variable: app is the application') # self._we.edit.setStyleSheet('foreground: white; background-color: black;}') # Fix the pyconsole geometry try: font = self._we.buffers[0].widget.chat.defaultFont() font.setFamily(font_name) font.setBold(True) if font_width is None: font_width = QFontMetrics(font).width('M') self._we.setFont(font) except Exception as e: LOG.debug(e) font_width = size geometry = self._we.geometry() geometry.setWidth(font_width*80+20) geometry.setHeight(font_width*40) self._we.setGeometry(geometry) self._we.resize(font_width*80+20, font_width*40) self._we.show() # or self._we.eval_in_thread() return except Exception as e: LOG.exception(f"Error creating WeechatConsole {e}") def about_program(self): # TODO: replace with window text = util_ui.tr('Toxygen is Tox client written in Python.\nVersion: ') text += '' + '\nGitHub: https://git.plastiras.org/emdee/toxygen' title = util_ui.tr('About') util_ui.message_box(text, title) def network_settings(self): self._modal_window = self._widget_factory.create_network_settings_window() self._modal_window.show() def plugins_menu(self): self._modal_window = self._widget_factory.create_plugins_settings_window() self._modal_window.show() def add_contact_triggered(self, _): self.add_contact() def add_contact(self, link=''): self._modal_window = self._widget_factory.create_add_contact_window(link) self._modal_window.show() def create_gc(self): self._modal_window = self._widget_factory.create_group_screen_window() self._modal_window.show() def join_gc(self): self._modal_window = self._widget_factory.create_join_group_screen_window() self._modal_window.show() def profile_settings(self, _): self._modal_window = self._widget_factory.create_profile_settings_window() self._modal_window.show() def privacy_settings(self): self._modal_window = self._widget_factory.create_privacy_settings_window() self._modal_window.show() def notification_settings(self): self._modal_window = self._widget_factory.create_notification_settings_window() self._modal_window.show() def interface_settings(self): self._modal_window = self._widget_factory.create_interface_settings_window() self._modal_window.show() def audio_settings(self): self._modal_window = self._widget_factory.create_audio_settings_window() self._modal_window.show() def video_settings(self): self._modal_window = self._widget_factory.create_video_settings_window() self._modal_window.show() def update_settings(self): self._modal_window = self._widget_factory.create_update_settings_window() self._modal_window.show() def reload_plugins(self): if hasattr(self, '_plugin_loader') and self._plugin_loader is not None: self._plugin_loader.reload() def reload_toxchat(self): pass @staticmethod def import_plugin(): directory = util_ui.directory_dialog(util_ui.tr('Choose folder with plugins')) if directory and os.path.isdir(directory): src = directory + '/' dest = util.get_plugins_directory() util.copy(src, dest) util_ui.message_box(util_ui.tr('Plugin will be loaded after restart'), util_ui.tr("Restart Toxygen")) def lock_app(self): if self._toxes.has_password(): self._settings.locked = True self.hide() else: util_ui.message_box(util_ui.tr('Error. Profile password is not set.'), util_ui.tr("Cannot lock app")) def test_tox(self): self._app._test_tox() def test_nmap(self): self._app._test_nmap() def test_main(self): self._app._test_main() def quit_program(self): try: self.close_window() self._app._stop_app() except KeyboardInterrupt: pass sys.stderr.write('sys.exit' +'\n') # unreached? sys.exit(0) def show_menu(self): if not hasattr(self, 'menu'): self.menu = DropdownMenu(self) self.menu.setGeometry(QtCore.QRect(0 if self._settings['mirror_mode'] else 270, self.height() - 120, 180, 120)) self.menu.show() # ----------------------------------------------------------------------------------------------------------------- # Messages, calls and file transfers # ----------------------------------------------------------------------------------------------------------------- def send_message(self): self._messenger.send_message() def send_file(self): self.menu.hide() if self._contacts_manager.is_active_a_friend(): caption = util_ui.tr('Choose file') name = util_ui.file_dialog(caption) if name[0]: self._file_transfer_handler.send_file(name[0], self._contacts_manager.get_active_number()) def send_screenshot(self, hide=False): self.menu.hide() if self._contacts_manager.is_active_a_friend(): self.sw = self._widget_factory.create_screenshot_window(self) self.sw.show() if hide: self.hide() def send_smiley(self): self.menu.hide() if self._contacts_manager.get_curr_contact() is None: return self._smiley_window = self._widget_factory.create_smiley_window(self) rect = QtCore.QRect(self.menu.x(), self.menu.y() - self.menu.height(), self._smiley_window.width(), self._smiley_window.height()) self._smiley_window.setGeometry(rect) self._smiley_window.show() def send_sticker(self): self.menu.hide() if self._contacts_manager.is_active_a_friend(): self.sticker = self._widget_factory.create_sticker_window() self.sticker.setGeometry(QtCore.QRect(self.x() if self._settings['mirror_mode'] else 270 + self.x(), self.y() + self.height() - 200, self.sticker.width(), self.sticker.height())) self.sticker.show() def active_call(self): self.update_call_state('finish_call') def incoming_call(self): self.update_call_state('incoming_call') def call_finished(self): self.update_call_state('call') def update_call_state(self, state): pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}.png'.format(state))) icon = QtGui.QIcon(pixmap) self.callButton.setIcon(icon) self.callButton.setIconSize(QtCore.QSize(50, 50)) pixmap = QtGui.QPixmap(os.path.join(util.get_images_directory(), '{}_video.png'.format(state))) icon = QtGui.QIcon(pixmap) self.videocallButton.setIcon(icon) self.videocallButton.setIconSize(QtCore.QSize(35, 35)) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user open context menu in friends list # ----------------------------------------------------------------------------------------------------------------- def _friend_right_click(self, pos): item = self.friends_list.itemAt(pos) number = self.friends_list.indexFromItem(item).row() contact = self._contacts_manager.get_contact(number) if contact is None or item is None: return generator = contact.get_context_menu_generator() self.listMenu = generator.generate(self._plugins_loader, self._contacts_manager, self, self._settings, number, self._groups_service, self._history_loader) parent_position = self.friends_list.mapToGlobal(QtCore.QPoint(0, 0)) self.listMenu.move(parent_position + pos) self.listMenu.show() def show_note(self, friend): note = self._settings['notes'][friend.tox_id] if friend.tox_id in self._settings['notes'] else '' user = util_ui.tr('Notes about user') user = '{} {}'.format(user, friend.name) def save_note(text): if friend.tox_id in self._settings['notes']: del self._settings['notes'][friend.tox_id] if text: self._settings['notes'][friend.tox_id] = text self._settings.save() self.note = MultilineEdit(user, note, save_note) self.note.show() def set_alias(self, num): self._contacts_manager.set_alias(num) def remove_friend(self, num): self._contacts_manager.delete_friend(num) def block_friend(self, num): friend = self._contacts_manager.get_contact(num) self._contacts_manager.block_user(friend.tox_id) @staticmethod def copy_text(text): util_ui.copy_to_clipboard(text) def auto_accept(self, num, value): tox_id = self._contacts_manager.friend_public_key(num) if value: self._settings['auto_accept_from_friends'].append(tox_id) else: self._settings['auto_accept_from_friends'].remove(tox_id) self._settings.save() def invite_friend_to_gc(self, friend_number, group_number): self._contacts_manager.invite_friend(friend_number, group_number) def select_contact_row(self, row_index): self.friends_list.setCurrentRow(row_index) # ----------------------------------------------------------------------------------------------------------------- # Functions which called when user click somewhere else # ----------------------------------------------------------------------------------------------------------------- def _selected_contact_changed(self): num = self.friends_list.currentRow() if self._contacts_manager.active_contact != num: self._contacts_manager.active_contact = num self.groupMenuButton.setVisible(self._contacts_manager.is_active_a_group()) def mouseReleaseEvent(self, event): pos = self.connection_status.pos() x, y = pos.x(), pos.y() + 25 if (x < event.x() < x + 32) and (y < event.y() < y + 32): self._profile.change_status() else: super().mouseReleaseEvent(event) def _filtering(self): index = self.contactsFilterComboBox.currentIndex() search_text = self.searchLineEdit.text() self._contacts_manager.filtration_and_sorting(index, search_text) def show_search_field(self): if hasattr(self, 'search_field') and self.search_field.isVisible(): #? self.search_field.show() return if not hasattr(self._contacts_manager, 'get_curr_friend') or \ self._contacts_manager.get_curr_friend() is None: #? return pass self.search_field = self._widget_factory.create_search_screen(self.messages) x, y = self.messages.x(), self.messages.y() + self.messages.height() - 40 self.search_field.setGeometry(x, y, self.messages.width(), 40) self.messages.setGeometry(x, self.messages.y(), self.messages.width(), self.messages.height() - 40) if self._should_show_group_peers_list: self.peers_list.setFixedHeight(self.peers_list.height() - 40) self.search_field.show() def _toggle_gc_peers_list(self): self._should_show_group_peers_list = not self._should_show_group_peers_list self.resizeEvent() if self._should_show_group_peers_list: self._groups_service.generate_peers_list() def _new_contact_selected(self, _): if self._should_show_group_peers_list: self._toggle_gc_peers_list() index = self.friends_list.currentRow() if self._contacts_manager.active_contact != index: self.friends_list.setCurrentRow(self._contacts_manager.active_contact) self.resizeEvent() def _open_gc_invites_list(self): self._modal_window = self._widget_factory.create_group_invites_window() self._modal_window.show() def update_gc_invites_button_state(self): invites_count = self._groups_service.group_invites_count LOG.debug(f"update_gc_invites_button_state invites_count={invites_count}") # Fixme self.groupInvitesPushButton.setVisible(True) # invites_count > 0 text = util_ui.tr(f'{invites_count} new invites to group chats') self.groupInvitesPushButton.setText(text) self.resizeEvent()