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, GObject, GLib, Pango 14 15from gettext import gettext as _ 16from datetime import datetime 17 18from eolie.define import App, Type, LoadingType, MARGIN_SMALL 19from eolie.utils import wanted_loading_type, emit_signal 20 21 22class Row(Gtk.ListBoxRow): 23 """ 24 A row 25 """ 26 __gsignals__ = { 27 'edited': (GObject.SignalFlags.RUN_FIRST, None, ()), 28 'moved': (GObject.SignalFlags.RUN_FIRST, None, (GLib.Variant,)) 29 } 30 31 def __init__(self, item, window): 32 """ 33 Init row 34 @param item as Item 35 @param window as Window 36 """ 37 self.__item = item 38 self.__window = window 39 self.__search = "" 40 eventbox = None 41 favicon = None 42 Gtk.ListBoxRow.__init__(self) 43 item_id = item.get_property("id") 44 item_type = item.get_property("type") 45 uri = item.get_property("uri") 46 title = item.get_property("title") 47 grid = Gtk.Grid() 48 grid.set_property("margin", MARGIN_SMALL) 49 grid.set_column_spacing(10) 50 grid.set_hexpand(True) 51 grid.set_property("valign", Gtk.Align.CENTER) 52 if item_type in [Type.BOOKMARK, Type.SEARCH, Type.HISTORY]: 53 favicon = Gtk.Image() 54 self.__set_favicon(favicon) 55 elif item_type == Type.SUGGESTION: 56 favicon = Gtk.Image.new_from_icon_name("system-search-symbolic", 57 Gtk.IconSize.MENU) 58 favicon.show() 59 elif item_type == Type.WEBVIEW: 60 favicon = Gtk.Image.new_from_icon_name("view-paged-symbolic", 61 Gtk.IconSize.MENU) 62 favicon.show() 63 if favicon is not None: 64 grid.attach(favicon, 0, 0, 1, 2) 65 66 uri = item.get_property("uri") 67 self.__title = Gtk.Label.new() 68 self.__title.set_ellipsize(Pango.EllipsizeMode.END) 69 self.__title.set_property("halign", Gtk.Align.START) 70 self.__title.set_hexpand(True) 71 self.__title.set_property('has-tooltip', True) 72 self.__title.connect('query-tooltip', self.__on_query_tooltip) 73 self.__title.show() 74 if uri: 75 self.__title.set_markup("%s\n<span alpha='40000'>%s</span>" % 76 (GLib.markup_escape_text(title), 77 GLib.markup_escape_text(uri))) 78 else: 79 self.__title.set_text(title) 80 grid.attach(self.__title, 1, 0, 1, 2) 81 82 if item_type == Type.HISTORY: 83 dt = datetime.fromtimestamp(item.get_property("atime")) 84 hour = str(dt.hour).rjust(2, "0") 85 minute = str(dt.minute).rjust(2, "0") 86 atime = Gtk.Label.new("%s:%s" % (hour, minute)) 87 atime.set_property("valign", Gtk.Align.CENTER) 88 atime.get_style_context().add_class("dim-label") 89 atime.set_margin_end(2) 90 atime.show() 91 grid.attach(atime, 2, 0, 1, 2) 92 93 if item_type == Type.HISTORY: 94 delete_button = Gtk.Button.new_from_icon_name( 95 "user-trash-symbolic", 96 Gtk.IconSize.MENU) 97 delete_button.get_image().set_opacity(0.5) 98 delete_button.set_margin_end(5) 99 delete_button.set_property("valign", Gtk.Align.CENTER) 100 delete_button.connect("clicked", self.__on_delete_clicked) 101 delete_button.get_style_context().add_class("overlay-button") 102 delete_button.set_tooltip_text(_("Delete page from history")) 103 delete_button.show() 104 grid.attach(delete_button, 3, 0, 1, 2) 105 elif item_type == Type.BOOKMARK: 106 edit_button = Gtk.Button.new_from_icon_name( 107 "document-edit-symbolic", 108 Gtk.IconSize.MENU) 109 edit_button.get_image().set_opacity(0.5) 110 edit_button.set_margin_end(5) 111 edit_button.connect("clicked", self.__on_edit_clicked) 112 edit_button.get_style_context().add_class("overlay-button") 113 edit_button.set_property("valign", Gtk.Align.CENTER) 114 edit_button.set_tooltip_text(_("Edit bookmark")) 115 edit_button.show() 116 grid.attach(edit_button, 2, 0, 1, 2) 117 elif item_type == Type.TAG: 118 if item_id == Type.NONE: 119 icon_name = "folder-visiting-symbolic" 120 elif item_id == Type.POPULARS: 121 icon_name = "emote-love-symbolic" 122 elif item_id == Type.RECENTS: 123 icon_name = "document-open-recent-symbolic" 124 else: 125 icon_name = "folder-symbolic" 126 open_button = Gtk.Button.new_from_icon_name( 127 icon_name, 128 Gtk.IconSize.MENU) 129 open_button.connect("clicked", self.__on_open_clicked) 130 open_button.get_style_context().add_class("overlay-button-alt") 131 open_button.set_tooltip_text(_("Open all pages with this tag")) 132 open_button.show() 133 grid.attach(open_button, 0, 0, 1, 1) 134 grid.show() 135 style_context = self.get_style_context() 136 style_context.add_class("row") 137 eventbox = Gtk.EventBox() 138 eventbox.add(grid) 139 eventbox.set_size_request(-1, 30) 140 eventbox.connect("button-release-event", 141 self.__on_button_release_event) 142 eventbox.show() 143 self.add(eventbox) 144 145 def set_title(self, title): 146 """ 147 Set row title 148 @param title as str 149 """ 150 self.__item.set_property("title", title) 151 self.__title.set_text(title) 152 153 @property 154 def item(self): 155 """ 156 Get item 157 @return Item 158 """ 159 return self.__item 160 161####################### 162# PRIVATE # 163####################### 164 def __set_favicon(self, favicon): 165 """ 166 Try to get a favicon for current URI 167 @param favicon as Gtk.Image 168 @param uri as str 169 """ 170 uri = self.__item.get_property("uri") 171 favicon_path = App().art.get_favicon_path(uri) 172 if favicon_path is not None: 173 favicon.set_from_file(favicon_path) 174 else: 175 favicon.set_from_icon_name("web-browser-symbolic", 176 Gtk.IconSize.LARGE_TOOLBAR) 177 favicon.show() 178 179 def __on_query_tooltip(self, widget, x, y, keyboard, tooltip): 180 """ 181 Show tooltip if needed 182 @param widget as Gtk.Widget 183 @param x as int 184 @param y as int 185 @param keyboard as bool 186 @param tooltip as Gtk.Tooltip 187 """ 188 text = '' 189 layout = widget.get_layout() 190 label = widget.get_text() 191 if layout.is_ellipsized(): 192 text = "%s" % (GLib.markup_escape_text(label)) 193 widget.set_tooltip_markup(text) 194 195 def __on_button_release_event(self, eventbox, event): 196 """ 197 Handle button press in popover 198 @param eventbox as Gtk.EventBox 199 @param event as Gdk.Event 200 """ 201 # Lets user select item 202 if event.state & Gdk.ModifierType.CONTROL_MASK or\ 203 event.state & Gdk.ModifierType.SHIFT_MASK: 204 return False 205 # Event is for internal button 206 if eventbox.get_window() != event.window: 207 return True 208 uri = self.__item.get_property("uri") 209 type_id = self.__item.get_property("type") 210 if type_id in [Type.HISTORY, Type.SUGGESTION, 211 Type.SEARCH, Type.BOOKMARK]: 212 if event.button == 1: 213 self.__window.container.webview.load_uri(uri) 214 self.__window.container.set_expose(False) 215 self.__window.close_popovers() 216 else: 217 self.__window.container.add_webview_for_uri( 218 uri, LoadingType.FOREGROUND) 219 if event.button == 2: 220 self.__window.close_popovers() 221 elif type_id == Type.WEBVIEW: 222 title = self.__item.get_property("title") 223 for webview in self.__window.container.webviews: 224 if webview.uri == uri and webview.title == title: 225 self.__window.container.set_visible_webview(webview) 226 self.__window.close_popovers() 227 break 228 else: 229 emit_signal(self, "activate") 230 # We force focus to stay on title entry 231 GLib.idle_add(self.__window.toolbar.title.entry.focus) 232 233 def __on_edit_clicked(self, button): 234 """ 235 Edit self 236 @param button as Gtk.Button 237 """ 238 emit_signal(self, "edited") 239 240 def __on_open_clicked(self, button): 241 """ 242 Open all bookmarks 243 @param button as Gtk.Button 244 """ 245 self.__window.close_popovers() 246 tag_id = self.__item.get_property("id") 247 if tag_id == Type.POPULARS: 248 items = App().bookmarks.get_populars(50) 249 elif tag_id == Type.RECENTS: 250 items = App().bookmarks.get_recents() 251 elif tag_id == Type.UNCLASSIFIED: 252 items = App().bookmarks.get_unclassified() 253 else: 254 items = App().bookmarks.get_bookmarks(tag_id) 255 i = 0 256 for (bid, uri, title) in items: 257 loading_type = wanted_loading_type(i) 258 self.__window.container.add_webview_for_uri(uri, loading_type) 259 i += 1 260 261 def __on_delete_clicked(self, button): 262 """ 263 Delete self 264 @param button as Gtk.Button 265 """ 266 history_id = self.__item.get_property("id") 267 guid = App().history.get_guid(history_id) 268 if App().sync_worker is not None: 269 App().sync_worker.remove_from_history(guid) 270 App().history.remove(history_id) 271 GLib.idle_add(self.destroy) 272