src/eric7/WebBrowser/UrlBar/UrlBar.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/WebBrowser/UrlBar/UrlBar.py	Thu Jul 07 11:23:56 2022 +0200
@@ -0,0 +1,479 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the URL bar widget.
+"""
+
+from PyQt6.QtCore import pyqtSlot, Qt, QUrl, QDateTime, QTimer, QPoint
+from PyQt6.QtGui import QColor, QPalette, QIcon
+from PyQt6.QtWidgets import QDialog, QApplication, QLineEdit
+from PyQt6.QtWebEngineCore import QWebEnginePage
+try:
+    from PyQt6.QtNetwork import QSslCertificate     # __IGNORE_EXCEPTION__
+except ImportError:
+    QSslCertificate = None      # __IGNORE_WARNING__
+
+from EricWidgets.EricLineEdit import EricClearableLineEdit, EricLineEditSide
+
+from WebBrowser.WebBrowserWindow import WebBrowserWindow
+
+from WebBrowser.SafeBrowsing.SafeBrowsingLabel import SafeBrowsingLabel
+
+from .FavIconLabel import FavIconLabel
+from .SslLabel import SslLabel
+
+import UI.PixmapCache
+import Preferences
+import Utilities
+
+
+class UrlBar(EricClearableLineEdit):
+    """
+    Class implementing a line edit for entering URLs.
+    """
+    def __init__(self, mainWindow, parent=None):
+        """
+        Constructor
+        
+        @param mainWindow reference to the main window
+        @type WebBrowserWindow
+        @param parent reference to the parent widget
+        @type WebBrowserView
+        """
+        super().__init__(parent)
+        self.setPlaceholderText(self.tr("Enter the URL here."))
+        self.setWhatsThis(self.tr("Enter the URL here."))
+        
+        self.__mw = mainWindow
+        self.__browser = None
+        self.__privateMode = WebBrowserWindow.isPrivate()
+        
+        self.__bmActiveIcon = UI.PixmapCache.getIcon("bookmark16")
+        self.__bmInactiveIcon = QIcon(
+            self.__bmActiveIcon.pixmap(16, 16, QIcon.Mode.Disabled))
+        
+        self.__safeBrowsingLabel = SafeBrowsingLabel(self)
+        self.addWidget(self.__safeBrowsingLabel, EricLineEditSide.LEFT)
+        self.__safeBrowsingLabel.setVisible(False)
+        
+        self.__favicon = FavIconLabel(self)
+        self.addWidget(self.__favicon, EricLineEditSide.LEFT)
+        
+        self.__sslLabel = SslLabel(self)
+        self.addWidget(self.__sslLabel, EricLineEditSide.LEFT)
+        self.__sslLabel.setVisible(False)
+        
+        self.__rssAction = self.addAction(
+            UI.PixmapCache.getIcon("rss16"),
+            QLineEdit.ActionPosition.TrailingPosition)
+        self.__rssAction.setVisible(False)
+        
+        self.__bookmarkAction = self.addAction(
+            self.__bmInactiveIcon,
+            QLineEdit.ActionPosition.TrailingPosition)
+        self.__bookmarkAction.setVisible(False)
+        
+        self.__safeBrowsingLabel.clicked.connect(self.__showThreatInfo)
+        self.__bookmarkAction.triggered.connect(self.__showBookmarkInfo)
+        self.__rssAction.triggered.connect(self.__rssTriggered)
+        
+        self.__mw.bookmarksManager().entryChanged.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryAdded.connect(
+            self.__bookmarkChanged)
+        self.__mw.bookmarksManager().entryRemoved.connect(
+            self.__bookmarkChanged)
+        self.__mw.speedDial().pagesChanged.connect(
+            self.__bookmarkChanged)
+    
+    def setBrowser(self, browser):
+        """
+        Public method to set the browser connection.
+        
+        @param browser reference to the browser widget
+        @type WebBrowserView
+        """
+        self.__browser = browser
+        self.__favicon.setBrowser(browser)
+        
+        self.__browser.urlChanged.connect(self.__browserUrlChanged)
+        self.__browser.loadProgress.connect(self.__loadProgress)
+        self.__browser.loadFinished.connect(self.__loadFinished)
+        self.__browser.loadStarted.connect(self.__loadStarted)
+        
+        self.__browser.safeBrowsingBad.connect(
+            self.__safeBrowsingLabel.setThreatInfo)
+        
+        self.__sslLabel.clicked.connect(self.__browser.page().showSslInfo)
+        self.__browser.page().sslConfigurationChanged.connect(
+            self.__sslConfigurationChanged)
+    
+    def browser(self):
+        """
+        Public method to get the associated browser.
+       
+        @return reference to the associated browser
+        @rtype WebBrowserView
+        """
+        return self.__browser
+    
+    @pyqtSlot(QUrl)
+    def __browserUrlChanged(self, url):
+        """
+        Private slot to handle a URL change of the associated browser.
+        
+        @param url new URL of the browser
+        @type QUrl
+        """
+        strUrl = url.toString()
+        if strUrl in ["eric:speeddial", "eric:home",
+                      "about:blank", "about:config"]:
+            strUrl = ""
+        
+        if self.text() != strUrl:
+            self.setText(strUrl)
+        self.setCursorPosition(0)
+    
+    @pyqtSlot()
+    def __checkBookmark(self):
+        """
+        Private slot to check the current URL for the bookmarked state.
+        """
+        manager = self.__mw.bookmarksManager()
+        if manager.bookmarkForUrl(self.__browser.url()) is not None:
+            self.__bookmarkAction.setIcon(self.__bmActiveIcon)
+            bookmarks = manager.bookmarksForUrl(self.__browser.url())
+            from WebBrowser.Bookmarks.BookmarkNode import BookmarkNode
+            for bookmark in bookmarks:
+                manager.setTimestamp(bookmark, BookmarkNode.TsVisited,
+                                     QDateTime.currentDateTime())
+        elif self.__mw.speedDial().pageForUrl(self.__browser.url()).url != "":
+            self.__bookmarkAction.setIcon(self.__bmActiveIcon)
+        else:
+            self.__bookmarkAction.setIcon(self.__bmInactiveIcon)
+    
+    @pyqtSlot()
+    def __loadStarted(self):
+        """
+        Private slot to perform actions before the page is loaded.
+        """
+        self.__bookmarkAction.setVisible(False)
+        self.__rssAction.setVisible(False)
+        self.__sslLabel.setVisible(False)
+    
+    @pyqtSlot(int)
+    def __loadProgress(self, progress):
+        """
+        Private slot to track the load progress.
+        
+        @param progress load progress in percent
+        @type int
+        """
+        foregroundColor = QApplication.palette().color(QPalette.ColorRole.Text)
+        
+        backgroundColor = (
+            Preferences.getWebBrowser("PrivateModeUrlColor")
+            if self.__privateMode else
+            QApplication.palette().color(QPalette.ColorRole.Base)
+        )
+        
+        if not self.__browser.getSafeBrowsingStatus():
+            # malicious web site
+            backgroundColor = Preferences.getWebBrowser(
+                "MaliciousUrlColor")
+        elif self.__browser.url().scheme() == "https":
+            if WebBrowserWindow.networkManager().isInsecureHost(
+                self.__browser.url().host()
+            ):
+                backgroundColor = Preferences.getWebBrowser(
+                    "InsecureUrlColor")
+            else:
+                backgroundColor = Preferences.getWebBrowser(
+                    "SecureUrlColor")
+        
+        if progress in (0, 100):
+            styleSheet = (
+                f"color: {foregroundColor.name()}; "
+                f"background-color: {backgroundColor.name()};"
+            )
+        else:
+            highlight = QApplication.palette().color(
+                QPalette.ColorRole.Highlight)
+            r = (highlight.red() + 2 * backgroundColor.red()) // 3
+            g = (highlight.green() + 2 * backgroundColor.green()) // 3
+            b = (highlight.blue() + 2 * backgroundColor.blue()) // 3
+            
+            loadingColor = QColor(r, g, b)
+            if abs(loadingColor.lightness() -
+                    backgroundColor.lightness()) < 20:
+                r = (2 * highlight.red() + backgroundColor.red()) // 3
+                g = (2 * highlight.green() + backgroundColor.green()) // 3
+                b = (2 * highlight.blue() + backgroundColor.blue()) // 3
+                loadingColor = QColor(r, g, b)
+            
+            styleSheet = (
+                f"color: {foregroundColor.name()}; "
+                f"background-color: qlineargradient("
+                f"spread: pad, x1: 0, y1: 0, x2: 1, y2: 0, "
+                f"stop: 0 {loadingColor.name()}, "
+                f"stop: {progress / 100.0 - 0.001} {loadingColor.name()}, "
+                f"stop: {progress / 100.0 + 0.001} {backgroundColor.name()}, "
+                f"stop: 1 {backgroundColor.name()});"
+            )
+        
+        self.setStyleSheet(styleSheet)
+        self.repaint()
+    
+    @pyqtSlot(bool)
+    def __loadFinished(self, ok):
+        """
+        Private slot to set some data after the page was loaded.
+        
+        @param ok flag indicating a successful load
+        @type bool
+        """
+        if self.__browser.url().scheme() in ["eric", "about"]:
+            self.__bookmarkAction.setVisible(False)
+        else:
+            self.__checkBookmark()
+            self.__bookmarkAction.setVisible(True)
+        
+        self.__browserUrlChanged(self.__browser.url())
+        self.__safeBrowsingLabel.setVisible(
+            not self.__browser.getSafeBrowsingStatus())
+        
+        if ok:
+            QTimer.singleShot(0, self.__setRssButton)
+    
+    @pyqtSlot()
+    def preferencesChanged(self):
+        """
+        Public slot to handle a change of preferences.
+        """
+        self.update()
+    
+    @pyqtSlot()
+    def __showBookmarkInfo(self):
+        """
+        Private slot to show a dialog with some bookmark info.
+        """
+        from .BookmarkActionSelectionDialog import (
+            BookmarkActionSelectionDialog
+        )
+        url = self.__browser.url()
+        dlg = BookmarkActionSelectionDialog(url)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            action = dlg.getAction()
+            if action == BookmarkActionSelectionDialog.AddBookmark:
+                self.__browser.addBookmark()
+            elif action == BookmarkActionSelectionDialog.EditBookmark:
+                bookmark = (
+                    self.__mw.bookmarksManager().bookmarkForUrl(url)
+                )
+                from .BookmarkInfoDialog import BookmarkInfoDialog
+                dlg = BookmarkInfoDialog(bookmark, self.__browser)
+                dlg.exec()
+            elif action == BookmarkActionSelectionDialog.AddSpeeddial:
+                self.__mw.speedDial().addPage(
+                    url, self.__browser.title())
+            elif action == BookmarkActionSelectionDialog.RemoveSpeeddial:
+                self.__mw.speedDial().removePage(url)
+    
+    @pyqtSlot()
+    def __bookmarkChanged(self):
+        """
+        Private slot to handle bookmark or speed dial changes.
+        """
+        self.__checkBookmark()
+    
+    def focusOutEvent(self, evt):
+        """
+        Protected method to handle focus out event.
+        
+        @param evt reference to the focus event
+        @type QFocusEvent
+        """
+        if self.text() == "" and self.__browser is not None:
+            self.__browserUrlChanged(self.__browser.url())
+        super().focusOutEvent(evt)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method called by a mouse press event.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        if evt.button() == Qt.MouseButton.XButton1:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.WebAction.Back)
+        elif evt.button() == Qt.MouseButton.XButton2:
+            self.__mw.currentBrowser().triggerPageAction(
+                QWebEnginePage.WebAction.Forward)
+        else:
+            super().mousePressEvent(evt)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method to handle mouse double click events.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        if evt.button() == Qt.MouseButton.LeftButton:
+            self.selectAll()
+        else:
+            super().mouseDoubleClickEvent(evt)
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key presses.
+        
+        @param evt reference to the key press event
+        @type QKeyEvent
+        """
+        if evt.key() == Qt.Key.Key_Escape:
+            if self.__browser is not None:
+                self.setText(
+                    str(self.__browser.url().toEncoded(), encoding="utf-8"))
+                self.selectAll()
+            completer = self.completer()
+            if completer:
+                completer.popup().hide()
+            return
+        
+        currentText = self.text().strip()
+        if (
+            evt.key() in [Qt.Key.Key_Enter, Qt.Key.Key_Return] and
+            not currentText.lower().startswith(("http://", "https://"))
+        ):
+            append = ""
+            if evt.modifiers() == Qt.KeyboardModifier.ControlModifier:
+                append = ".com"
+            elif (
+                evt.modifiers() == (
+                    Qt.KeyboardModifier.ControlModifier |
+                    Qt.KeyboardModifier.ShiftModifier
+                )
+            ):
+                append = ".org"
+            elif evt.modifiers() == Qt.KeyboardModifier.ShiftModifier:
+                append = ".net"
+            
+            if append != "":
+                url = QUrl("http://www." + currentText)
+                host = url.host()
+                if not host.lower().endswith(append):
+                    host += append
+                    url.setHost(host)
+                    self.setText(url.toString())
+        
+        super().keyPressEvent(evt)
+    
+    def dragEnterEvent(self, evt):
+        """
+        Protected method to handle drag enter events.
+        
+        @param evt reference to the drag enter event
+        @type QDragEnterEvent
+        """
+        mimeData = evt.mimeData()
+        if mimeData.hasUrls() or mimeData.hasText():
+            evt.acceptProposedAction()
+        
+        super().dragEnterEvent(evt)
+    
+    def dropEvent(self, evt):
+        """
+        Protected method to handle drop events.
+        
+        @param evt reference to the drop event
+        @type QDropEvent
+        """
+        mimeData = evt.mimeData()
+        
+        url = QUrl()
+        if mimeData.hasUrls():
+            url = mimeData.urls()[0]
+        elif mimeData.hasText():
+            url = QUrl.fromEncoded(mimeData.text().encode("utf-8"),
+                                   QUrl.ParsingMode.TolerantMode)
+        
+        if url.isEmpty() or not url.isValid():
+            super().dropEvent(evt)
+            return
+        
+        self.setText(str(url.toEncoded(), encoding="utf-8"))
+        self.selectAll()
+        
+        evt.acceptProposedAction()
+    
+    @pyqtSlot()
+    def __setRssButton(self):
+        """
+        Private slot to show the RSS button.
+        """
+        self.__rssAction.setVisible(self.__browser.checkRSS())
+    
+    @pyqtSlot()
+    def __rssTriggered(self):
+        """
+        Private slot to handle clicking the RSS icon.
+        """
+        from WebBrowser.Feeds.FeedsDialog import FeedsDialog
+        feeds = self.__browser.getRSS()
+        dlg = FeedsDialog(feeds, self.__browser)
+        dlg.exec()
+    
+    @pyqtSlot(QPoint)
+    def __showThreatInfo(self, pos):
+        """
+        Private slot to show the threat info widget.
+        
+        @param pos position to show the info at
+        @type QPoint
+        """
+        threatInfo = self.__safeBrowsingLabel.getThreatInfo()
+        if threatInfo:
+            from WebBrowser.SafeBrowsing.SafeBrowsingInfoWidget import (
+                SafeBrowsingInfoWidget
+            )
+            widget = SafeBrowsingInfoWidget(threatInfo, self.__browser)
+            widget.showAt(pos)
+    
+    @pyqtSlot()
+    def __sslConfigurationChanged(self):
+        """
+        Private slot to handle a change of the associated web page SSL
+        configuration.
+        """
+        sslConfiguration = self.__browser.page().getSslConfiguration()
+        if sslConfiguration is not None and QSslCertificate is not None:
+            sslCertificate = self.__browser.page().getSslCertificate()
+            if sslCertificate is not None:
+                org = Utilities.decodeString(", ".join(
+                    sslCertificate.subjectInfo(
+                        QSslCertificate.SubjectInfo.Organization)))
+                if org == "":
+                    cn = Utilities.decodeString(", ".join(
+                        sslCertificate.subjectInfo(
+                            QSslCertificate.SubjectInfo.CommonName)))
+                    if cn != "":
+                        org = cn.split(".", 1)[1]
+                    if org == "":
+                        org = self.tr("Unknown")
+                self.__sslLabel.setText(" {0} ".format(org))
+                self.__sslLabel.setVisible(True)
+                valid = not sslCertificate.isBlacklisted()
+                if valid:
+                    config = self.__browser.page().getSslConfiguration()
+                    if config is None or config.sessionCipher().isNull():
+                        valid = False
+                self.__sslLabel.setValidity(valid)
+            else:
+                self.__sslLabel.setVisible(False)
+        else:
+            self.__sslLabel.setVisible(False)

eric ide

mercurial