eric7/Network/IRC/IrcNetworkWidget.py

branch
eric7
changeset 8312
800c432b34c8
parent 8259
2bbec88047dd
child 8318
962bce857696
diff -r 4e8b98454baa -r 800c432b34c8 eric7/Network/IRC/IrcNetworkWidget.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/Network/IRC/IrcNetworkWidget.py	Sat May 15 18:45:04 2021 +0200
@@ -0,0 +1,519 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2012 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the network part of the IRC widget.
+"""
+
+from PyQt5.QtCore import pyqtSlot, pyqtSignal, QPoint, QFileInfo, QUrl, QThread
+from PyQt5.QtGui import QDesktopServices
+from PyQt5.QtWidgets import QWidget, QApplication, QMenu
+
+from E5Gui import E5MessageBox, E5FileDialog
+
+from .Ui_IrcNetworkWidget import Ui_IrcNetworkWidget
+
+from .IrcUtilities import ircFilter, ircTimestamp
+
+import UI.PixmapCache
+import Preferences
+import Utilities
+
+
+class IrcNetworkWidget(QWidget, Ui_IrcNetworkWidget):
+    """
+    Class implementing the network part of the IRC widget.
+    
+    @signal connectNetwork(str,bool,bool) emitted to connect or disconnect from
+        a network
+    @signal editNetwork(str) emitted to edit a network configuration
+    @signal joinChannel(str) emitted to join a channel
+    @signal nickChanged(str) emitted to change the nick name
+    @signal sendData(str) emitted to send a message to the channel
+    @signal away(bool) emitted to indicate the away status
+    @signal autoConnected() emitted after an automatic connection was initiated
+    """
+    connectNetwork = pyqtSignal(str, bool, bool)
+    editNetwork = pyqtSignal(str)
+    joinChannel = pyqtSignal(str)
+    nickChanged = pyqtSignal(str)
+    sendData = pyqtSignal(str)
+    away = pyqtSignal(bool)
+    autoConnected = pyqtSignal()
+    
+    def __init__(self, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget (QWidget)
+        """
+        super().__init__(parent)
+        self.setupUi(self)
+        
+        self.connectButton.setIcon(UI.PixmapCache.getIcon("ircConnect"))
+        self.editButton.setIcon(UI.PixmapCache.getIcon("ircConfigure"))
+        self.joinButton.setIcon(UI.PixmapCache.getIcon("ircJoinChannel"))
+        self.awayButton.setIcon(UI.PixmapCache.getIcon("ircUserPresent"))
+        
+        self.joinButton.setEnabled(False)
+        self.nickCombo.setEnabled(False)
+        self.awayButton.setEnabled(False)
+        
+        self.channelCombo.lineEdit().returnPressed.connect(
+            self.on_joinButton_clicked)
+        self.nickCombo.lineEdit().returnPressed.connect(
+            self.on_nickCombo_currentIndexChanged)
+        
+        self.setConnected(False)
+        
+        self.__initMessagesMenu()
+        
+        self.__manager = None
+        self.__connected = False
+        self.__registered = False
+        self.__away = False
+    
+    def initialize(self, manager):
+        """
+        Public method to initialize the widget.
+        
+        @param manager reference to the network manager (IrcNetworkManager)
+        """
+        self.__manager = manager
+        
+        self.networkCombo.addItems(self.__manager.getNetworkNames())
+        
+        self.__manager.networksChanged.connect(self.__refreshNetworks)
+        self.__manager.identitiesChanged.connect(self.__refreshNetworks)
+    
+    def autoConnect(self):
+        """
+        Public method to perform the IRC auto connection.
+        """
+        self.__autoConnect()
+    
+    def __autoConnect(self):
+        """
+        Private method to perform the IRC auto connection.
+        """
+        for networkName in self.__manager.getNetworkNames():
+            if self.__manager.getNetwork(networkName).autoConnect():
+                row = self.networkCombo.findText(networkName)
+                self.networkCombo.setCurrentIndex(row)
+                self.on_connectButton_clicked()
+                self.autoConnected.emit()
+                break
+    
+    @pyqtSlot(bool)
+    def __onlineStateChanged(self, online):
+        """
+        Private slot handling online state changes.
+        
+        @param online online state
+        @type bool
+        """
+        self.connectButton.setEnabled(online)
+        if online:
+            # delay a bit because the signal seems to be sent before the
+            # network interface is fully up
+            QThread.msleep(200)
+            self.__autoConnect()
+        else:
+            network = self.networkCombo.currentText()
+            self.connectNetwork.emit(network, online, True)
+    
+    @pyqtSlot()
+    def __refreshNetworks(self):
+        """
+        Private slot to refresh all network related widgets.
+        """
+        currentNetwork = self.networkCombo.currentText()
+        currentNick = self.nickCombo.currentText()
+        currentChannel = self.channelCombo.currentText()
+        blocked = self.networkCombo.blockSignals(True)
+        self.networkCombo.clear()
+        self.networkCombo.addItems(self.__manager.getNetworkNames())
+        self.networkCombo.blockSignals(blocked)
+        row = self.networkCombo.findText(currentNetwork)
+        if row == -1:
+            row = 0
+        blocked = self.nickCombo.blockSignals(True)
+        self.networkCombo.setCurrentIndex(row)
+        self.nickCombo.setEditText(currentNick)
+        self.nickCombo.blockSignals(blocked)
+        self.channelCombo.setEditText(currentChannel)
+    
+    @pyqtSlot()
+    def on_connectButton_clicked(self):
+        """
+        Private slot to connect to a network.
+        """
+        network = self.networkCombo.currentText()
+        self.connectNetwork.emit(network, not self.__connected, False)
+    
+    @pyqtSlot()
+    def on_awayButton_clicked(self):
+        """
+        Private slot to toggle the away status.
+        """
+        if self.__away:
+            self.handleAwayCommand("")
+        else:
+            networkName = self.networkCombo.currentText()
+            identityName = (
+                self.__manager.getNetwork(networkName).getIdentityName()
+            )
+            identity = self.__manager.getIdentity(identityName)
+            if identity:
+                awayMessage = identity.getAwayMessage()
+            else:
+                awayMessage = ""
+            self.handleAwayCommand(awayMessage)
+    
+    @pyqtSlot(str)
+    def handleAwayCommand(self, awayMessage):
+        """
+        Public slot to process an away command.
+        
+        @param awayMessage message to be set for being away
+        @type str
+        """
+        if awayMessage and not self.__away:
+            # set being away
+            # don't send away, if the status is already set
+            self.sendData.emit("AWAY :" + awayMessage)
+            self.awayButton.setIcon(UI.PixmapCache.getIcon("ircUserAway"))
+            self.__away = True
+            self.away.emit(self.__away)
+        elif not awayMessage and self.__away:
+            # cancel being away
+            self.sendData.emit("AWAY")
+            self.awayButton.setIcon(
+                UI.PixmapCache.getIcon("ircUserPresent"))
+            self.__away = False
+            self.away.emit(self.__away)
+    
+    @pyqtSlot()
+    def on_editButton_clicked(self):
+        """
+        Private slot to edit a network.
+        """
+        network = self.networkCombo.currentText()
+        self.editNetwork.emit(network)
+    
+    @pyqtSlot(str)
+    def on_channelCombo_editTextChanged(self, txt):
+        """
+        Private slot to react upon changes of the channel.
+        
+        @param txt current text of the channel combo (string)
+        """
+        on = bool(txt) and self.__registered
+        self.joinButton.setEnabled(on)
+    
+    @pyqtSlot()
+    def on_joinButton_clicked(self):
+        """
+        Private slot to join a channel.
+        """
+        channel = self.channelCombo.currentText()
+        self.joinChannel.emit(channel)
+    
+    @pyqtSlot(int)
+    def on_networkCombo_currentIndexChanged(self, index):
+        """
+        Private slot to handle selections of a network.
+        
+        @param index index of the selected entry
+        @type int
+        """
+        networkName = self.networkCombo.itemText(index)
+        network = self.__manager.getNetwork(networkName)
+        self.nickCombo.clear()
+        self.channelCombo.clear()
+        if network:
+            channels = network.getChannelNames()
+            self.channelCombo.addItems(channels)
+            self.channelCombo.setEnabled(True)
+            identity = self.__manager.getIdentity(
+                network.getIdentityName())
+            if identity:
+                self.nickCombo.addItems(identity.getNickNames())
+        else:
+            self.channelCombo.setEnabled(False)
+    
+    def getNetworkChannels(self):
+        """
+        Public method to get the list of channels associated with the
+        selected network.
+        
+        @return associated channels (list of IrcChannel)
+        """
+        networkName = self.networkCombo.currentText()
+        network = self.__manager.getNetwork(networkName)
+        return network.getChannels()
+    
+    @pyqtSlot(int)
+    @pyqtSlot()
+    def on_nickCombo_currentIndexChanged(self, nick=0):
+        """
+        Private slot to use another nick name.
+        
+        @param nick index of the selected nick name (unused)
+        """
+        if self.__connected:
+            self.nickChanged.emit(self.nickCombo.currentText())
+    
+    def getNickname(self):
+        """
+        Public method to get the currently selected nick name.
+        
+        @return selected nick name (string)
+        """
+        return self.nickCombo.currentText()
+    
+    def setNickName(self, nick):
+        """
+        Public slot to set the nick name in use.
+        
+        @param nick nick name in use (string)
+        """
+        self.nickCombo.blockSignals(True)
+        self.nickCombo.setEditText(nick)
+        self.nickCombo.blockSignals(False)
+    
+    def addMessage(self, msg):
+        """
+        Public method to add a message.
+        
+        @param msg message to be added (string)
+        """
+        s = '<font color="{0}">{1} {2}</font>'.format(
+            Preferences.getIrc("NetworkMessageColour"),
+            ircTimestamp(),
+            msg
+        )
+        self.messages.append(s)
+    
+    def addServerMessage(self, msgType, msg, filterMsg=True):
+        """
+        Public method to add a server message.
+        
+        @param msgType txpe of the message (string)
+        @param msg message to be added (string)
+        @param filterMsg flag indicating to filter the message (boolean)
+        """
+        if filterMsg:
+            msg = ircFilter(msg)
+        s = '<font color="{0}">{1} <b>[</b>{2}<b>]</b> {3}</font>'.format(
+            Preferences.getIrc("ServerMessageColour"),
+            ircTimestamp(),
+            msgType,
+            msg
+        )
+        self.messages.append(s)
+    
+    def addErrorMessage(self, msgType, msg):
+        """
+        Public method to add an error message.
+        
+        @param msgType txpe of the message (string)
+        @param msg message to be added (string)
+        """
+        s = '<font color="{0}">{1} <b>[</b>{2}<b>]</b> {3}</font>'.format(
+            Preferences.getIrc("ErrorMessageColour"),
+            ircTimestamp(),
+            msgType,
+            msg
+        )
+        self.messages.append(s)
+    
+    def setConnected(self, connected):
+        """
+        Public slot to set the connection state.
+        
+        @param connected flag indicating the connection state (boolean)
+        """
+        self.__connected = connected
+        if self.__connected:
+            self.connectButton.setIcon(
+                UI.PixmapCache.getIcon("ircDisconnect"))
+            self.connectButton.setToolTip(
+                self.tr("Press to disconnect from the network"))
+        else:
+            self.connectButton.setIcon(
+                UI.PixmapCache.getIcon("ircConnect"))
+            self.connectButton.setToolTip(
+                self.tr("Press to connect to the selected network"))
+    
+    def isConnected(self):
+        """
+        Public method to check, if the network is connected.
+        
+        @return flag indicating a connected network (boolean)
+        """
+        return self.__connected
+    
+    def setRegistered(self, registered):
+        """
+        Public slot to set the registered state.
+        
+        @param registered flag indicating the registration state (boolean)
+        """
+        self.__registered = registered
+        on = bool(self.channelCombo.currentText()) and self.__registered
+        self.joinButton.setEnabled(on)
+        self.nickCombo.setEnabled(registered)
+        self.awayButton.setEnabled(registered)
+        if registered:
+            self.awayButton.setIcon(
+                UI.PixmapCache.getIcon("ircUserPresent"))
+            self.__away = False
+    
+    def __clearMessages(self):
+        """
+        Private slot to clear the contents of the messages display.
+        """
+        self.messages.clear()
+    
+    def __copyMessages(self):
+        """
+        Private slot to copy the selection of the messages display to
+        the clipboard.
+        """
+        self.messages.copy()
+    
+    def __copyAllMessages(self):
+        """
+        Private slot to copy the contents of the messages display to
+        the clipboard.
+        """
+        txt = self.messages.toPlainText()
+        if txt:
+            cb = QApplication.clipboard()
+            cb.setText(txt)
+    
+    def __cutAllMessages(self):
+        """
+        Private slot to cut the contents of the messages display to
+        the clipboard.
+        """
+        txt = self.messages.toPlainText()
+        if txt:
+            cb = QApplication.clipboard()
+            cb.setText(txt)
+        self.messages.clear()
+    
+    def __saveMessages(self):
+        """
+        Private slot to save the contents of the messages display.
+        """
+        hasText = not self.messages.document().isEmpty()
+        if hasText:
+            if Utilities.isWindowsPlatform():
+                htmlExtension = "htm"
+            else:
+                htmlExtension = "html"
+            fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+                self,
+                self.tr("Save Messages"),
+                "",
+                self.tr(
+                    "HTML Files (*.{0});;Text Files (*.txt);;All Files (*)")
+                .format(htmlExtension),
+                None,
+                E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+            if fname:
+                ext = QFileInfo(fname).suffix()
+                if not ext:
+                    ex = selectedFilter.split("(*")[1].split(")")[0]
+                    if ex:
+                        fname += ex
+                    ext = QFileInfo(fname).suffix()
+                if QFileInfo(fname).exists():
+                    res = E5MessageBox.yesNo(
+                        self,
+                        self.tr("Save Messages"),
+                        self.tr("<p>The file <b>{0}</b> already exists."
+                                " Overwrite it?</p>").format(fname),
+                        icon=E5MessageBox.Warning)
+                    if not res:
+                        return
+                    fname = Utilities.toNativeSeparators(fname)
+                
+                try:
+                    txt = (
+                        self.messages.toHtml()
+                        if ext.lower() in ["htm", "html"] else
+                        self.messages.toPlainText()
+                    )
+                    with open(fname, "w", encoding="utf-8") as f:
+                        f.write(txt)
+                except OSError as err:
+                    E5MessageBox.critical(
+                        self,
+                        self.tr("Error saving Messages"),
+                        self.tr(
+                            """<p>The messages contents could not be written"""
+                            """ to <b>{0}</b></p><p>Reason: {1}</p>""")
+                        .format(fname, str(err)))
+    
+    def __initMessagesMenu(self):
+        """
+        Private slot to initialize the context menu of the messages pane.
+        """
+        self.__messagesMenu = QMenu(self)
+        self.__copyMessagesAct = self.__messagesMenu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy"), self.__copyMessages)
+        self.__messagesMenu.addSeparator()
+        self.__cutAllMessagesAct = self.__messagesMenu.addAction(
+            UI.PixmapCache.getIcon("editCut"),
+            self.tr("Cut all"), self.__cutAllMessages)
+        self.__copyAllMessagesAct = self.__messagesMenu.addAction(
+            UI.PixmapCache.getIcon("editCopy"),
+            self.tr("Copy all"), self.__copyAllMessages)
+        self.__messagesMenu.addSeparator()
+        self.__clearMessagesAct = self.__messagesMenu.addAction(
+            UI.PixmapCache.getIcon("editDelete"),
+            self.tr("Clear"), self.__clearMessages)
+        self.__messagesMenu.addSeparator()
+        self.__saveMessagesAct = self.__messagesMenu.addAction(
+            UI.PixmapCache.getIcon("fileSave"),
+            self.tr("Save"), self.__saveMessages)
+        
+        self.on_messages_copyAvailable(False)
+    
+    @pyqtSlot(bool)
+    def on_messages_copyAvailable(self, yes):
+        """
+        Private slot to react to text selection/deselection of the
+        messages edit.
+        
+        @param yes flag signaling the availability of selected text (boolean)
+        """
+        self.__copyMessagesAct.setEnabled(yes)
+    
+    @pyqtSlot(QPoint)
+    def on_messages_customContextMenuRequested(self, pos):
+        """
+        Private slot to show the context menu of the messages pane.
+        
+        @param pos position the menu should be opened at (QPoint)
+        """
+        enable = not self.messages.document().isEmpty()
+        self.__cutAllMessagesAct.setEnabled(enable)
+        self.__copyAllMessagesAct.setEnabled(enable)
+        self.__saveMessagesAct.setEnabled(enable)
+        self.__messagesMenu.popup(self.messages.mapToGlobal(pos))
+    
+    @pyqtSlot(QUrl)
+    def on_messages_anchorClicked(self, url):
+        """
+        Private slot to open links in the default browser.
+        
+        @param url URL to be opened (QUrl)
+        """
+        QDesktopServices.openUrl(url)

eric ide

mercurial