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, GLib 14 15from locale import strcoll 16from time import time 17 18from eolie.define import App 19from eolie.widget_bookmark_rating import BookmarkRatingWidget 20 21 22class TagWidget(Gtk.FlowBoxChild): 23 """ 24 Tag widget with some visual effects 25 """ 26 27 def __init__(self, title, bookmark_id): 28 """ 29 Init widget 30 @param title as str 31 @param bookmark_id as int 32 """ 33 Gtk.FlowBoxChild.__init__(self) 34 self.__bookmark_id = bookmark_id 35 self.__title = title 36 grid = Gtk.Grid() 37 grid.show() 38 grid.get_style_context().add_class("linked") 39 self.__entry = Gtk.Entry() 40 self.__entry.get_style_context().add_class("tag") 41 self.__entry.show() 42 self.__entry.set_text(title) 43 self.__button = Gtk.Button.new_from_icon_name("window-close-symbolic", 44 Gtk.IconSize.BUTTON) 45 self.__button.show() 46 self.__button.get_style_context().add_class("tag") 47 grid.add(self.__entry) 48 grid.add(self.__button) 49 self.add(grid) 50 self.__entry.connect("changed", self.__on_entry_changed) 51 self.__button.connect("clicked", self.__on_button_clicked) 52 53 @property 54 def label(self): 55 """ 56 Get label 57 @return str 58 """ 59 return self.__entry.get_text() 60 61####################### 62# PRIVATE # 63####################### 64 def __on_button_clicked(self, button): 65 """ 66 Save/Remove tag 67 @param button as Gtk.Button 68 """ 69 if button.get_style_context().has_class("suggested-action"): 70 title = self.__entry.get_text() 71 App().bookmarks.rename_tag(self.__title, title) 72 # Update mtime for all tagged bookmarks 73 if App().sync_worker is not None: 74 mtime = round(time(), 2) 75 tag_id = App().bookmarks.get_tag_id(title) 76 if tag_id is not None: 77 for (bookmark_id, bookmark_uri, bookmark_title) in\ 78 App().bookmarks.get_bookmarks(tag_id): 79 App().bookmarks.set_mtime(bookmark_id, mtime + 1) 80 App().sync_worker.push_bookmark(bookmark_id) 81 self.__entry.set_text(title) 82 self.__title = title 83 self.__on_entry_changed(self.__entry) 84 else: 85 tag_title = self.__entry.get_text() 86 tag_id = App().bookmarks.get_tag_id(tag_title) 87 if tag_id is not None: 88 App().bookmarks.del_tag_from(tag_id, self.__bookmark_id) 89 App().bookmarks.clean_tags() 90 self.destroy() 91 92 def __on_entry_changed(self, entry): 93 """ 94 Update button state 95 @param entry as Gtk.Entry 96 """ 97 self.__button.set_sensitive(True) 98 style_context = self.__button.get_style_context() 99 image = self.__button.get_image() 100 title = entry.get_text() 101 tag_id = App().bookmarks.get_tag_id(title) 102 if title == self.__title: 103 style_context.remove_class("suggested-action") 104 image.set_from_icon_name("window-close-symbolic", 105 Gtk.IconSize.BUTTON) 106 elif tag_id is not None or not title: 107 self.__button.set_sensitive(False) 108 image.set_from_icon_name("dialog-error-symbolic", 109 Gtk.IconSize.BUTTON) 110 else: 111 style_context.add_class("suggested-action") 112 image.set_from_icon_name("object-select-symbolic", 113 Gtk.IconSize.BUTTON) 114 115 116class BookmarkEditWidget(Gtk.Bin): 117 """ 118 Widget allowing to edit a bookmark 119 """ 120 121 def __init__(self, bookmark_id, back_enabled=True): 122 """ 123 Init widget 124 @param bookmark id as int 125 @param enable back button as bool 126 """ 127 Gtk.Bin.__init__(self) 128 self.__bookmark_id = bookmark_id 129 builder = Gtk.Builder() 130 builder.add_from_resource("/org/gnome/Eolie/BookmarkEdit.ui") 131 builder.connect_signals(self) 132 self.__flowbox = builder.get_object("flowbox") 133 self.__flowbox.set_sort_func(self.__sort_tags) 134 self.__add_tag_button = builder.get_object("add_tag_button") 135 self.__rename_tag_button = builder.get_object("rename_tag_button") 136 self.__remove_tag_button = builder.get_object("remove_tag_button") 137 self.__title_entry = builder.get_object("title_entry") 138 self.__uri_entry = builder.get_object("uri_entry") 139 self.__title_entry.set_text(App().bookmarks.get_title(bookmark_id)) 140 self.__uri_entry.set_text(App().bookmarks.get_uri(bookmark_id)) 141 builder.get_object("startup_button").set_active( 142 App().bookmarks.get_startup(bookmark_id)) 143 self.__new_tag_entry = builder.get_object("new_tag_entry") 144 # Init new tag completion model 145 self.__completion_model = Gtk.ListStore(str) 146 self.__completion = Gtk.EntryCompletion.new() 147 self.__completion.set_model(self.__completion_model) 148 self.__completion.set_text_column(0) 149 self.__completion.set_inline_completion(False) 150 self.__completion.set_popup_completion(True) 151 self.__new_tag_entry.set_completion(self.__completion) 152 for (tag_id, title) in App().bookmarks.get_all_tags(): 153 self.__completion_model.append([title]) 154 155 for title in App().bookmarks.get_tags(bookmark_id): 156 tag = TagWidget(title, bookmark_id) 157 tag.show() 158 self.__flowbox.add(tag) 159 if not back_enabled: 160 builder.get_object("back_button").hide() 161 bookmark_rating = BookmarkRatingWidget(bookmark_id) 162 bookmark_rating.show() 163 builder.get_object("bookmark_grid").attach(bookmark_rating, 2, 1, 1, 1) 164 self.add(builder.get_object("widget")) 165 self.connect("unmap", self.__on_unmap) 166 self.__updated = False 167 168####################### 169# PROTECTED # 170####################### 171 def _on_back_clicked(self, button): 172 """ 173 Destroy self 174 @param button as Gtk.Button 175 """ 176 GLib.idle_add(self.hide) 177 GLib.timeout_add(2000, self.destroy) 178 179 def _on_del_clicked(self, button): 180 """ 181 Remove item 182 @param button as Gtk.Button 183 """ 184 self.disconnect_by_func(self.__on_unmap) 185 if App().sync_worker is not None: 186 guid = App().bookmarks.get_guid(self.__bookmark_id) 187 App().sync_worker.remove_from_bookmarks(guid) 188 App().bookmarks.remove(self.__bookmark_id) 189 if isinstance(self.get_parent(), Gtk.Popover): 190 self.get_parent().hide() 191 else: 192 self.get_parent().set_visible_child_name("bookmarks") 193 194 def _on_new_tag_entry_activate(self, entry, ignore1=None, ignore2=None): 195 """ 196 Add new tag 197 @param entry as Gtk.Entry 198 """ 199 tag_title = self.__new_tag_entry.get_text() 200 if not tag_title: 201 return 202 if not App().bookmarks.has_tag(self.__bookmark_id, tag_title): 203 tag_id = App().bookmarks.get_tag_id(tag_title) 204 if tag_id is None: 205 tag_id = App().bookmarks.add_tag(tag_title) 206 App().bookmarks.add_tag_to(tag_id, self.__bookmark_id) 207 tag = TagWidget(tag_title, self.__bookmark_id) 208 tag.show() 209 self.__flowbox.add(tag) 210 entry.set_text("") 211 212 def _on_flowbox_size_allocate(self, scrolled, allocation): 213 """ 214 Set scrolled size allocation based on viewport allocation 215 @param scrolled as Gtk.ScrolledWindow 216 @param flowbox allocation as Gtk.Allocation 217 """ 218 height = allocation.height 219 if height > 300: 220 height = 300 221 scrolled.set_size_request(-1, height) 222 223 def _on_entry_changed(self, entry): 224 """ 225 Mark bookmark as updated 226 @param entry as Gtk.Entry 227 """ 228 self.__updated = True 229 230 def _on_load_at_startup_toggled(self, button): 231 """ 232 Set bookmark startup state 233 @param button as Gtk.ToggleButton 234 """ 235 App().bookmarks.set_startup(self.__bookmark_id, button.get_active()) 236 237####################### 238# PRIVATE # 239####################### 240 def __sort_tags(self, child1, child2): 241 """ 242 Sort tags 243 @param child1 as TagWidget 244 @param child2 as TagWidget 245 """ 246 return strcoll(child1.label, child2.label) 247 248 def __on_unmap(self, widget): 249 """ 250 Save uri and title 251 @param widget as Gtk.Widget 252 """ 253 if self.__updated: 254 App().bookmarks.set_title(self.__bookmark_id, 255 self.__title_entry.get_text()) 256 App().bookmarks.set_uri(self.__bookmark_id, 257 self.__uri_entry.get_text()) 258 App().bookmarks.clean_tags() 259 if App().sync_worker is not None: 260 App().bookmarks.set_mtime(self.__bookmark_id, 261 round(time(), 2) + 1) 262 App().sync_worker.push_bookmark(self.__bookmark_id) 263