1# Copyright (c) 2017-2021 Cedric Bellegarde <cedric.bellegarde@adishatz.org> 2# This program is free software: you can redistribute it and/or modify 3# it under the terms of the GNU General Public License as published by 4# the Free Software Foundation, either version 3 of the License, or 5# (at your option) any later version. 6# This program is distributed in the hope that it will be useful, 7# but WITHOUT ANY WARRANTY; without even the implied warranty of 8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9# GNU General Public License for more details. 10# You should have received a copy of the GNU General Public License 11# along with this program. If not, see <http://www.gnu.org/licenses/>. 12 13from gi.repository import Gtk, Gdk, Gio, WebKit2, GObject 14 15from gettext import gettext as _ 16from urllib.parse import urlparse 17from time import time 18 19from eolie.define import App, LoadingState 20from eolie.utils import emit_signal 21from eolie.webview_signals_menu import WebViewMenuSignals 22from eolie.webview_signals_js import WebViewJsSignals 23 24 25class WebViewSignals(WebViewMenuSignals, WebViewJsSignals): 26 """ 27 Handle webview signals 28 """ 29 30 gsignals = { 31 "is-playing-audio": (GObject.SignalFlags.RUN_FIRST, None, (bool,)), 32 "readability-content": (GObject.SignalFlags.RUN_FIRST, None, (str,)), 33 "readability-status": (GObject.SignalFlags.RUN_FIRST, None, (bool,)), 34 "title-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)), 35 "uri-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)), 36 "snapshot-changed": (GObject.SignalFlags.RUN_FIRST, None, 37 (GObject.TYPE_PYOBJECT,)), 38 } 39 40 for signal in gsignals: 41 args = gsignals[signal] 42 GObject.signal_new(signal, WebKit2.WebView, 43 args[0], args[1], args[2]) 44 45 def __init__(self): 46 """ 47 Init handler 48 @param webview as WebView 49 """ 50 WebViewMenuSignals.__init__(self) 51 WebViewJsSignals.__init__(self) 52 self.reset_last_click_event() 53 self.__cancellable = Gio.Cancellable() 54 self.__uri = "" 55 self.__title = "" 56 self.connect("notify::title", self.__on_title_changed) 57 self.connect("notify::uri", self.__on_uri_changed) 58 self.connect("scroll-event", self.__on_scroll_event) 59 self.connect("run-file-chooser", self.__on_run_file_chooser) 60 self.connect("button-press-event", self.__on_button_press_event) 61 62 def reset_last_click_event(self): 63 """ 64 Reset last click event 65 """ 66 self._last_click_event_x = 0 67 self._last_click_event_y = 0 68 self._last_click_time = 0 69 70 def set_title(self, title): 71 """ 72 Set title 73 @param title as str 74 """ 75 if title: 76 self.__title = title 77 emit_signal(self, "title-changed", title) 78 79 def set_uri(self, uri): 80 """ 81 Set delayed uri 82 @param uri as str 83 """ 84 self.__uri = uri.rstrip("/") 85 emit_signal(self, "uri-changed", uri) 86 87 @property 88 def uri(self): 89 """ 90 Get webview uri 91 @return str 92 """ 93 return self.__uri 94 95 @property 96 def title(self): 97 """ 98 Get webview title 99 @return str 100 """ 101 return self.__title 102 103####################### 104# PRIVATE # 105####################### 106 def __on_button_press_event(self, webview, event): 107 """ 108 Hide Titlebar popover 109 @param webview as WebView 110 @param event as Gdk.Event 111 """ 112 self._last_click_event_x = event.x 113 self._last_click_event_y = event.y 114 self._last_click_time = time() 115 if event.button == 8: 116 self.go_back() 117 return True 118 elif event.button == 9: 119 self.go_forward() 120 return True 121 elif self.get_ancestor(Gtk.Popover) is None: 122 return self.window.close_popovers() 123 124 def __on_run_file_chooser(self, webview, request): 125 """ 126 Run own file chooser 127 @param webview as WebView 128 @param request as WebKit2.FileChooserRequest 129 """ 130 dialog = Gtk.FileChooserNative.new(_("Select files to upload"), 131 self.window, 132 Gtk.FileChooserAction.OPEN, 133 _("Open"), 134 _("Cancel")) 135 dialog.set_select_multiple(request.get_select_multiple()) 136 chooser_uri = App().websettings.get("chooser_uri", webview.uri) 137 if chooser_uri is not None: 138 dialog.set_current_folder_uri(chooser_uri) 139 response = dialog.run() 140 if response in [Gtk.ResponseType.DELETE_EVENT, 141 Gtk.ResponseType.CANCEL]: 142 request.cancel() 143 else: 144 request.select_files(dialog.get_filenames()) 145 App().websettings.set("chooser_uri", 146 webview.uri, 147 dialog.get_current_folder_uri()) 148 return True 149 150 def __on_scroll_event(self, webview, event): 151 """ 152 Adapt scroll speed to device 153 @param webview as WebView 154 @param event as Gdk.EventScroll 155 """ 156 source = event.get_source_device().get_source() 157 if event.state & Gdk.ModifierType.CONTROL_MASK: 158 if source == Gdk.InputSource.MOUSE: 159 if event.delta_y < 0.5: 160 webview.zoom_in() 161 elif event.delta_y > 0.5: 162 webview.zoom_out() 163 else: 164 if event.delta_y > 0.5: 165 webview.zoom_in() 166 elif event.delta_y < - 0.5: 167 webview.zoom_out() 168 return True 169 elif source == Gdk.InputSource.MOUSE: 170 event.delta_x *= 2 171 event.delta_y *= 2 172 173 def __on_uri_changed(self, webview, param): 174 """ 175 Handle JS updates 176 @param webview as WebKit2.WebView 177 @param param as GObject.ParamSpec 178 """ 179 uri = webview.get_property(param.name).rstrip("/") 180 if not uri.startswith("javascript:"): 181 self.__uri = uri 182 emit_signal(self, "uri-changed", uri) 183 self._set_user_agent(uri) 184 185 def __on_title_changed(self, webview, param): 186 """ 187 We launch Readability.js at page loading finished. 188 @param webview as WebKit2.WebView 189 @param param as GObject.ParamSpec 190 """ 191 title = webview.get_property(param.name) 192 if title: 193 parsed = urlparse(webview.uri) 194 is_http = parsed.scheme in ["http", "https"] 195 if is_http: 196 self.__title = "%s: %s" % (parsed.netloc, title) 197 emit_signal(self, "title-changed", self.__title) 198 else: 199 self.__title = title 200 emit_signal(self, "title-changed", None) 201 if self.loading_state in [LoadingState.ERROR, 202 LoadingState.STOPPED] or\ 203 webview.is_ephemeral or\ 204 not is_http: 205 return 206 mtime = round(time(), 2) 207 history_id = App().history.add(self.__title, self.__uri, mtime) 208 App().history.set_page_state(self.__uri, mtime) 209 if App().sync_worker is not None: 210 App().sync_worker.push_history(history_id) 211