diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/Cooperation/ChatWidget.py --- a/src/eric7/Cooperation/ChatWidget.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Cooperation/ChatWidget.py Wed Jul 13 14:55:47 2022 +0200 @@ -29,7 +29,7 @@ class ChatWidget(QWidget, Ui_ChatWidget): """ Class implementing the chat dialog. - + @signal connected(connected) emitted to signal a change of the connected state (bool) @signal editorCommand(hashStr, filename, message) emitted when an editor @@ -39,43 +39,40 @@ @signal sendEdit() emitted to send a shared edit session @signal cancelEdit() emitted to cancel a shared edit session """ + connected = pyqtSignal(bool) editorCommand = pyqtSignal(str, str, str) - + shareEditor = pyqtSignal(bool) startEdit = pyqtSignal() sendEdit = pyqtSignal() cancelEdit = pyqtSignal() - + def __init__(self, ui, port=-1, parent=None): """ Constructor - + @param ui reference to the user interface object (UserInterface) @param port port to be used for the cooperation server (integer) @param parent reference to the parent widget (QWidget) """ super().__init__(parent) self.setupUi(self) - - self.shareButton.setIcon( - UI.PixmapCache.getIcon("sharedEditDisconnected")) - self.startEditButton.setIcon( - UI.PixmapCache.getIcon("sharedEditStart")) - self.sendEditButton.setIcon( - UI.PixmapCache.getIcon("sharedEditSend")) - self.cancelEditButton.setIcon( - UI.PixmapCache.getIcon("sharedEditCancel")) - + + self.shareButton.setIcon(UI.PixmapCache.getIcon("sharedEditDisconnected")) + self.startEditButton.setIcon(UI.PixmapCache.getIcon("sharedEditStart")) + self.sendEditButton.setIcon(UI.PixmapCache.getIcon("sharedEditSend")) + self.cancelEditButton.setIcon(UI.PixmapCache.getIcon("sharedEditCancel")) + self.hostEdit.lineEdit().setClearButtonEnabled(True) - + self.__ui = ui self.__client = CooperationClient(self) self.__myNickName = self.__client.nickName() - + self.__initChatMenu() self.__initUsersMenu() - + self.messageEdit.returnPressed.connect(self.__handleMessage) self.sendButton.clicked.connect(self.__handleMessage) self.__client.newMessage.connect(self.appendMessage) @@ -84,22 +81,22 @@ self.__client.connectionError.connect(self.__showErrorMessage) self.__client.cannotConnect.connect(self.__initialConnectionRefused) self.__client.editorCommand.connect(self.__editorCommandMessage) - + self.serverButton.setText(self.tr("Start Server")) self.serverLed.setColor(QColor(Qt.GlobalColor.red)) if port == -1: port = Preferences.getCooperation("ServerPort") - + self.serverPortSpin.setValue(port) - + self.__setConnected(False) - + if Preferences.getCooperation("AutoStartServer"): self.on_serverButton_clicked() - + self.recent = [] self.__loadHostsHistory() - + def __loadHostsHistory(self): """ Private method to load the recently connected hosts. @@ -112,18 +109,18 @@ self.hostEdit.clear() self.hostEdit.addItems(self.__recent) self.hostEdit.clearEditText() - + def __saveHostsHistory(self): """ Private method to save the list of recently connected hosts. """ Preferences.Prefs.rsettings.setValue(recentNameHosts, self.__recent) Preferences.Prefs.rsettings.sync() - + def __setHostsHistory(self, host): """ Private method to remember the given host as the most recent entry. - + @param host host entry to remember (string) """ if host in self.__recent: @@ -132,7 +129,7 @@ self.__saveHostsHistory() self.hostEdit.clear() self.hostEdit.addItems(self.__recent) - + def __clearHostsHistory(self): """ Private slot to clear the hosts history. @@ -141,7 +138,7 @@ self.__saveHostsHistory() self.hostEdit.clear() self.hostEdit.addItems(self.__recent) - + def __handleMessage(self): """ Private slot handling the Return key pressed in the message edit. @@ -149,115 +146,127 @@ text = self.messageEdit.text() if text == "": return - + if text.startswith("/"): self.__showErrorMessage( - self.tr("! Unknown command: {0}\n") - .format(text.split()[0])) + self.tr("! Unknown command: {0}\n").format(text.split()[0]) + ) else: self.__client.sendMessage(text) self.appendMessage(self.__myNickName, text) - + self.messageEdit.clear() - + def __newParticipant(self, nick): """ Private slot handling a new participant joining. - + @param nick nick name of the new participant (string) """ if nick == "": return - + color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.gray) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) self.chatEdit.append(self.tr("* {0} has joined.\n").format(nick)) self.chatEdit.setTextColor(color) - + QListWidgetItem( UI.PixmapCache.getIcon( - "chatUser{0}".format(1 + self.usersList.count() % 6)), - nick, self.usersList) - + "chatUser{0}".format(1 + self.usersList.count() % 6) + ), + nick, + self.usersList, + ) + if not self.__connected: self.__setConnected(True) - + if not self.isVisible(): self.__ui.showNotification( UI.PixmapCache.getPixmap("cooperation48"), - self.tr("New User"), self.tr("{0} has joined.") - .format(nick)) + self.tr("New User"), + self.tr("{0} has joined.").format(nick), + ) def __participantLeft(self, nick): """ Private slot handling a participant leaving the session. - + @param nick nick name of the participant (string) """ if nick == "": return - + items = self.usersList.findItems(nick, Qt.MatchFlag.MatchExactly) for item in items: self.usersList.takeItem(self.usersList.row(item)) del item - + color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.gray) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) self.chatEdit.append(self.tr("* {0} has left.\n").format(nick)) self.chatEdit.setTextColor(color) - + if not self.__client.hasConnections(): self.__setConnected(False) - + if not self.isVisible(): self.__ui.showNotification( UI.PixmapCache.getPixmap("cooperation48"), - self.tr("User Left"), self.tr("{0} has left.") - .format(nick)) - + self.tr("User Left"), + self.tr("{0} has left.").format(nick), + ) + def appendMessage(self, from_, message): """ Public slot to append a message to the display. - + @param from_ originator of the message (string) @param message message to be appended (string) """ if from_ == "" or message == "": return - + self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + " <" + from_ + ">:") + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + " <" + + from_ + + ">:" + ) self.chatEdit.append(message + "\n") bar = self.chatEdit.verticalScrollBar() bar.setValue(bar.maximum()) - + if not self.isVisible(): self.__ui.showNotification( UI.PixmapCache.getPixmap("cooperation48"), - self.tr("Message from <{0}>").format(from_), message) - + self.tr("Message from <{0}>").format(from_), + message, + ) + @pyqtSlot(str) def on_hostEdit_editTextChanged(self, host): """ Private slot handling the entry of a host to connect to. - + @param host host to connect to (string) """ if not self.__connected: self.connectButton.setEnabled(host != "") - + def __getConnectionParameters(self): """ Private method to determine the connection parameters. - + @return tuple with hostname and port (string, integer) """ hostEntry = self.hostEdit.currentText() @@ -273,7 +282,7 @@ port = Preferences.getCooperation("ServerPort") self.hostEdit.setEditText("{0}@{1}".format(host, port)) return host, port - + @pyqtSlot() def on_connectButton_clicked(self): """ @@ -290,14 +299,14 @@ else: self.__client.disconnectConnections() self.__setConnected(False) - + @pyqtSlot() def on_clearHostsButton_clicked(self): """ Private slot to clear the hosts list. """ self.__clearHostsHistory() - + @pyqtSlot() def on_serverButton_clicked(self): """ @@ -307,14 +316,11 @@ self.__client.close() self.serverButton.setText(self.tr("Start Server")) self.serverPortSpin.setEnabled(True) - if (self.serverPortSpin.value() != - Preferences.getCooperation("ServerPort")): - self.serverPortSpin.setValue( - Preferences.getCooperation("ServerPort")) + if self.serverPortSpin.value() != Preferences.getCooperation("ServerPort"): + self.serverPortSpin.setValue(Preferences.getCooperation("ServerPort")) self.serverLed.setColor(QColor(Qt.GlobalColor.red)) else: - res, port = self.__client.startListening( - self.serverPortSpin.value()) + res, port = self.__client.startListening(self.serverPortSpin.value()) if res: self.serverButton.setText(self.tr("Stop Server")) self.serverPortSpin.setValue(port) @@ -322,14 +328,13 @@ self.serverLed.setColor(QColor(Qt.GlobalColor.green)) else: self.__showErrorMessage( - self.tr("! Server Error: {0}\n").format( - self.__client.errorString()) + self.tr("! Server Error: {0}\n").format(self.__client.errorString()) ) - + def __setConnected(self, connected): """ Private slot to set the connected state. - + @param connected new connected state (boolean) """ if connected: @@ -347,91 +352,91 @@ self.hostEdit.setEnabled(not connected) self.serverButton.setEnabled(not connected) self.sharingGroup.setEnabled(connected) - + if connected: vm = ericApp().getObject("ViewManager") aw = vm.activeWindow() if aw: self.checkEditorActions(aw) - + def __showErrorMessage(self, message): """ Private slot to show an error message. - + @param message error message to show (string) """ color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.red) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) self.chatEdit.append(message + "\n") self.chatEdit.setTextColor(color) - + def __initialConnectionRefused(self): """ Private slot to handle the refusal of the initial connection. """ self.__setConnected(False) - + def preferencesChanged(self): """ Public slot to handle a change of preferences. """ if not self.__client.isListening(): - self.serverPortSpin.setValue( - Preferences.getCooperation("ServerPort")) + self.serverPortSpin.setValue(Preferences.getCooperation("ServerPort")) if Preferences.getCooperation("AutoStartServer"): self.on_serverButton_clicked() - + def getClient(self): """ Public method to get a reference to the cooperation client. - + @return reference to the cooperation client (CooperationClient) """ return self.__client - + def __editorCommandMessage(self, hashStr, fileName, message): """ Private slot to handle editor command messages from the client. - + @param hashStr hash of the project (string) @param fileName project relative file name of the editor (string) @param message command message (string) """ self.editorCommand.emit(hashStr, fileName, message) - + from QScintilla.Editor import Editor - if (message.startswith(Editor.StartEditToken + Editor.Separator) or - message.startswith(Editor.EndEditToken + Editor.Separator)): + + if message.startswith( + Editor.StartEditToken + Editor.Separator + ) or message.startswith(Editor.EndEditToken + Editor.Separator): vm = ericApp().getObject("ViewManager") aw = vm.activeWindow() if aw: self.checkEditorActions(aw) - + @pyqtSlot(bool) def on_shareButton_clicked(self, checked): """ Private slot to share the current editor. - + @param checked flag indicating the button state (boolean) """ if checked: - self.shareButton.setIcon( - UI.PixmapCache.getIcon("sharedEditConnected")) + self.shareButton.setIcon(UI.PixmapCache.getIcon("sharedEditConnected")) else: - self.shareButton.setIcon( - UI.PixmapCache.getIcon("sharedEditDisconnected")) + self.shareButton.setIcon(UI.PixmapCache.getIcon("sharedEditDisconnected")) self.startEditButton.setEnabled(checked) - + self.shareEditor.emit(checked) - + @pyqtSlot(bool) def on_startEditButton_clicked(self, checked): """ Private slot to start a shared edit session. - + @param checked flag indicating the button state (boolean) """ if checked: @@ -439,9 +444,9 @@ self.cancelEditButton.setEnabled(True) self.shareButton.setEnabled(False) self.startEditButton.setEnabled(False) - + self.startEdit.emit() - + @pyqtSlot() def on_sendEditButton_clicked(self): """ @@ -452,9 +457,9 @@ self.shareButton.setEnabled(True) self.startEditButton.setEnabled(True) self.startEditButton.setChecked(False) - + self.sendEdit.emit() - + @pyqtSlot() def on_cancelEditButton_clicked(self): """ @@ -465,72 +470,69 @@ self.shareButton.setEnabled(True) self.startEditButton.setEnabled(True) self.startEditButton.setChecked(False) - + self.cancelEdit.emit() - + def checkEditorActions(self, editor): """ Public slot to set action according to an editor's state. - + @param editor reference to the editor (Editor) """ shareable, sharing, editing, remoteEditing = editor.getSharingStatus() - + self.shareButton.setChecked(sharing) if sharing: - self.shareButton.setIcon( - UI.PixmapCache.getIcon("sharedEditConnected")) + self.shareButton.setIcon(UI.PixmapCache.getIcon("sharedEditConnected")) else: - self.shareButton.setIcon( - UI.PixmapCache.getIcon("sharedEditDisconnected")) + self.shareButton.setIcon(UI.PixmapCache.getIcon("sharedEditDisconnected")) self.startEditButton.setChecked(editing) - + self.shareButton.setEnabled(shareable and not editing) - self.startEditButton.setEnabled( - sharing and not editing and not remoteEditing) + self.startEditButton.setEnabled(sharing and not editing and not remoteEditing) self.sendEditButton.setEnabled(editing) self.cancelEditButton.setEnabled(editing) - + def __initChatMenu(self): """ Private slot to initialize the chat edit context menu. """ self.__chatMenu = QMenu(self) self.__copyChatAct = self.__chatMenu.addAction( - UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy"), self.__copyChat) + UI.PixmapCache.getIcon("editCopy"), self.tr("Copy"), self.__copyChat + ) self.__chatMenu.addSeparator() self.__cutAllChatAct = self.__chatMenu.addAction( - UI.PixmapCache.getIcon("editCut"), - self.tr("Cut all"), self.__cutAllChat) + UI.PixmapCache.getIcon("editCut"), self.tr("Cut all"), self.__cutAllChat + ) self.__copyAllChatAct = self.__chatMenu.addAction( - UI.PixmapCache.getIcon("editCopy"), - self.tr("Copy all"), self.__copyAllChat) + UI.PixmapCache.getIcon("editCopy"), self.tr("Copy all"), self.__copyAllChat + ) self.__chatMenu.addSeparator() self.__clearChatAct = self.__chatMenu.addAction( - UI.PixmapCache.getIcon("editDelete"), - self.tr("Clear"), self.__clearChat) + UI.PixmapCache.getIcon("editDelete"), self.tr("Clear"), self.__clearChat + ) self.__chatMenu.addSeparator() self.__saveChatAct = self.__chatMenu.addAction( - UI.PixmapCache.getIcon("fileSave"), - self.tr("Save"), self.__saveChat) - + UI.PixmapCache.getIcon("fileSave"), self.tr("Save"), self.__saveChat + ) + self.on_chatEdit_copyAvailable(False) - + @pyqtSlot(bool) def on_chatEdit_copyAvailable(self, yes): """ Private slot to react to text selection/deselection of the chat edit. - + @param yes flag signaling the availability of selected text (boolean) """ self.__copyChatAct.setEnabled(yes) - + @pyqtSlot(QPoint) def on_chatEdit_customContextMenuRequested(self, pos): """ Private slot to show the context menu for the chat. - + @param pos the position of the mouse pointer (QPoint) """ enable = self.chatEdit.toPlainText() != "" @@ -538,13 +540,13 @@ self.__copyAllChatAct.setEnabled(enable) self.__cutAllChatAct.setEnabled(enable) self.__chatMenu.popup(self.chatEdit.mapToGlobal(pos)) - + def __clearChat(self): """ Private slot to clear the contents of the chat display. """ self.chatEdit.clear() - + def __saveChat(self): """ Private slot to save the contents of the chat display. @@ -557,7 +559,8 @@ "", self.tr("Text Files (*.txt);;All Files (*)"), None, - EricFileDialog.DontConfirmOverwrite) + EricFileDialog.DontConfirmOverwrite, + ) if fname: fpath = pathlib.Path(fname) if not fpath.suffix: @@ -568,12 +571,15 @@ res = EricMessageBox.yesNo( self, self.tr("Save Chat"), - self.tr("<p>The file <b>{0}</b> already exists." - " Overwrite it?</p>").format(fpath), - icon=EricMessageBox.Warning) + self.tr( + "<p>The file <b>{0}</b> already exists." + " Overwrite it?</p>" + ).format(fpath), + icon=EricMessageBox.Warning, + ) if not res: return - + try: with fpath.open("w", encoding="utf-8") as f: f.write(txt) @@ -581,17 +587,19 @@ EricMessageBox.critical( self, self.tr("Error saving Chat"), - self.tr("""<p>The chat contents could not be""" - """ written to <b>{0}</b></p>""" - """<p>Reason: {1}</p>""") - .format(fpath, str(err))) - + self.tr( + """<p>The chat contents could not be""" + """ written to <b>{0}</b></p>""" + """<p>Reason: {1}</p>""" + ).format(fpath, str(err)), + ) + def __copyChat(self): """ Private slot to copy the contents of the chat display to the clipboard. """ self.chatEdit.copy() - + def __copyAllChat(self): """ Private slot to copy the contents of the chat display to the clipboard. @@ -600,7 +608,7 @@ if txt: cb = QApplication.clipboard() cb.setText(txt) - + def __cutAllChat(self): """ Private slot to cut the contents of the chat display to the clipboard. @@ -610,7 +618,7 @@ cb = QApplication.clipboard() cb.setText(txt) self.chatEdit.clear() - + def __initUsersMenu(self): """ Private slot to initialize the users list context menu. @@ -618,19 +626,23 @@ self.__usersMenu = QMenu(self) self.__kickUserAct = self.__usersMenu.addAction( UI.PixmapCache.getIcon("chatKickUser"), - self.tr("Kick User"), self.__kickUser) + self.tr("Kick User"), + self.__kickUser, + ) self.__banUserAct = self.__usersMenu.addAction( - UI.PixmapCache.getIcon("chatBanUser"), - self.tr("Ban User"), self.__banUser) + UI.PixmapCache.getIcon("chatBanUser"), self.tr("Ban User"), self.__banUser + ) self.__banKickUserAct = self.__usersMenu.addAction( UI.PixmapCache.getIcon("chatBanKickUser"), - self.tr("Ban and Kick User"), self.__banKickUser) - + self.tr("Ban and Kick User"), + self.__banKickUser, + ) + @pyqtSlot(QPoint) def on_usersList_customContextMenuRequested(self, pos): """ Private slot to show the context menu for the users list. - + @param pos the position of the mouse pointer (QPoint) """ itm = self.usersList.itemAt(pos) @@ -638,56 +650,63 @@ self.__banUserAct.setEnabled(itm is not None) self.__banKickUserAct.setEnabled(itm is not None) self.__usersMenu.popup(self.usersList.mapToGlobal(pos)) - + def __kickUser(self): """ Private slot to disconnect a user. """ itm = self.usersList.currentItem() self.__client.kickUser(itm.text()) - + color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.darkYellow) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") - self.chatEdit.append(self.tr("* {0} has been kicked.\n").format( - itm.text().split("@")[0])) + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) + self.chatEdit.append( + self.tr("* {0} has been kicked.\n").format(itm.text().split("@")[0]) + ) self.chatEdit.setTextColor(color) - + def __banUser(self): """ Private slot to ban a user. """ itm = self.usersList.currentItem() self.__client.banUser(itm.text()) - + color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.darkYellow) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") - self.chatEdit.append(self.tr("* {0} has been banned.\n").format( - itm.text().split("@")[0])) + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) + self.chatEdit.append( + self.tr("* {0} has been banned.\n").format(itm.text().split("@")[0]) + ) self.chatEdit.setTextColor(color) - + def __banKickUser(self): """ Private slot to ban and kick a user. """ itm = self.usersList.currentItem() self.__client.banKickUser(itm.text()) - + color = self.chatEdit.textColor() self.chatEdit.setTextColor(Qt.GlobalColor.darkYellow) self.chatEdit.append( - QDateTime.currentDateTime().toString( - Qt.DateFormat.SystemLocaleLongDate) + ":") + QDateTime.currentDateTime().toString(Qt.DateFormat.SystemLocaleLongDate) + + ":" + ) self.chatEdit.append( - self.tr("* {0} has been banned and kicked.\n") - .format(itm.text().split("@")[0])) + self.tr("* {0} has been banned and kicked.\n").format( + itm.text().split("@")[0] + ) + ) self.chatEdit.setTextColor(color) - + def shutdown(self): """ Public method to shut down the cooperation system.