1# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
2
3# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
4#
5# This file is part of qutebrowser.
6#
7# qutebrowser is free software: you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation, either version 3 of the License, or
10# (at your option) any later version.
11#
12# qutebrowser is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with qutebrowser.  If not, see <https://www.gnu.org/licenses/>.
19
20"""URL displayed in the statusbar."""
21
22import enum
23
24from PyQt5.QtCore import (pyqtSlot, pyqtProperty,  # type: ignore[attr-defined]
25                          QUrl)
26
27from qutebrowser.mainwindow.statusbar import textbase
28from qutebrowser.config import stylesheet
29from qutebrowser.utils import usertypes, urlutils
30
31
32class UrlType(enum.Enum):
33
34    """The type/color of the URL being shown.
35
36    Note this has entries for success/error/warn from widgets.webview:LoadStatus.
37    """
38
39    success = enum.auto()
40    success_https = enum.auto()
41    error = enum.auto()
42    warn = enum.auto()
43    hover = enum.auto()
44    normal = enum.auto()
45
46
47class UrlText(textbase.TextBase):
48
49    """URL displayed in the statusbar.
50
51    Attributes:
52        _normal_url: The normal URL to be displayed as a UrlType instance.
53        _normal_url_type: The type of the normal URL as a UrlType instance.
54        _hover_url: The URL we're currently hovering over.
55        _ssl_errors: Whether SSL errors occurred while loading.
56        _urltype: The URL type to show currently (normal/ok/error/warn/hover).
57                  Accessed via the urltype property.
58    """
59
60    STYLESHEET = """
61        QLabel#UrlText[urltype="normal"] {
62            color: {{ conf.colors.statusbar.url.fg }};
63        }
64
65        QLabel#UrlText[urltype="success"] {
66            color: {{ conf.colors.statusbar.url.success.http.fg }};
67        }
68
69        QLabel#UrlText[urltype="success_https"] {
70            color: {{ conf.colors.statusbar.url.success.https.fg }};
71        }
72
73        QLabel#UrlText[urltype="error"] {
74            color: {{ conf.colors.statusbar.url.error.fg }};
75        }
76
77        QLabel#UrlText[urltype="warn"] {
78            color: {{ conf.colors.statusbar.url.warn.fg }};
79        }
80
81        QLabel#UrlText[urltype="hover"] {
82            color: {{ conf.colors.statusbar.url.hover.fg }};
83        }
84    """
85
86    def __init__(self, parent=None):
87        super().__init__(parent)
88        self._urltype = None
89        self.setObjectName(self.__class__.__name__)
90        stylesheet.set_register(self)
91        self._hover_url = None
92        self._normal_url = None
93        self._normal_url_type = UrlType.normal
94
95    @pyqtProperty(str)
96    def urltype(self):
97        """Getter for self.urltype, so it can be used as Qt property.
98
99        Return:
100            The urltype as a string (!)
101        """
102        if self._urltype is None:
103            return ""
104        else:
105            return self._urltype.name
106
107    def _update_url(self):
108        """Update the displayed URL if the url or the hover url changed."""
109        old_urltype = self._urltype
110        if self._hover_url is not None:
111            self.setText(self._hover_url)
112            self._urltype = UrlType.hover
113        elif self._normal_url is not None:
114            self.setText(self._normal_url)
115            self._urltype = self._normal_url_type
116        else:
117            self.setText('')
118            self._urltype = UrlType.normal
119        if old_urltype != self._urltype:
120            # We can avoid doing an unpolish here because the new style will
121            # always override the old one.
122            self.style().polish(self)
123
124    @pyqtSlot(usertypes.LoadStatus)
125    def on_load_status_changed(self, status):
126        """Slot for load_status_changed. Sets URL color accordingly.
127
128        Args:
129            status: The usertypes.LoadStatus.
130        """
131        assert isinstance(status, usertypes.LoadStatus), status
132        if status in [usertypes.LoadStatus.success,
133                      usertypes.LoadStatus.success_https,
134                      usertypes.LoadStatus.error,
135                      usertypes.LoadStatus.warn]:
136            self._normal_url_type = UrlType[status.name]
137        else:
138            self._normal_url_type = UrlType.normal
139        self._update_url()
140
141    @pyqtSlot(QUrl)
142    def set_url(self, url):
143        """Setter to be used as a Qt slot.
144
145        Args:
146            url: The URL to set as QUrl, or None.
147        """
148        if url is None:
149            self._normal_url = None
150        elif not url.isValid():
151            self._normal_url = "Invalid URL!"
152        else:
153            self._normal_url = urlutils.safe_display_string(url)
154        self._normal_url_type = UrlType.normal
155        self._update_url()
156
157    @pyqtSlot(str)
158    def set_hover_url(self, link):
159        """Setter to be used as a Qt slot.
160
161        Saves old shown URL in self._old_url and restores it later if a link is
162        "un-hovered" when it gets called with empty parameters.
163
164        Args:
165            link: The link which was hovered (string)
166        """
167        if link:
168            qurl = QUrl(link)
169            if qurl.isValid():
170                self._hover_url = urlutils.safe_display_string(qurl)
171            else:
172                self._hover_url = '(invalid URL!) {}'.format(link)
173        else:
174            self._hover_url = None
175        self._update_url()
176
177    def on_tab_changed(self, tab):
178        """Update URL if the tab changed."""
179        self._hover_url = None
180        if tab.url().isValid():
181            self._normal_url = urlutils.safe_display_string(tab.url())
182        else:
183            self._normal_url = ''
184        self.on_load_status_changed(tab.load_status())
185        self._update_url()
186