"""Sidebar bookmarks, stored in ~/.config/SaraFem/.sfem_bookmarks.toml.""" from dataclasses import dataclass from pathlib import Path import toml from Core import BookmarksPath, ConfigDir DefaultIcon = "BOOKMARK_OUTLINE" TrashPath = Path.home() / ".local" / "share" / "Trash" / "files" # One-time marker: without it, re-adding the Trash bookmark to old files (see # LoadBookmarks) would run on every load and undo the user removing it. _TrashMigrationMarker = ConfigDir / ".trash_bookmark_migrated" # A small curated set for the icon picker, rather than the whole Material set. IconChoices = [ "BOOKMARK_OUTLINE", "HOME_OUTLINED", "FOLDER_OUTLINED", "DESCRIPTION_OUTLINED", "DOWNLOAD_OUTLINED", "IMAGE_OUTLINED", "MUSIC_NOTE_OUTLINED", "VIDEOCAM_OUTLINED", "CODE_OUTLINED", "STAR_OUTLINE", "FAVORITE_OUTLINE", "WORK_OUTLINE", ] @dataclass class Bookmark: Name: str Path: Path Icon: str = DefaultIcon def DefaultBookmarks() -> list[Bookmark]: return [ Bookmark("Home", Path.home(), "HOME_OUTLINED"), Bookmark("Documents", Path.home() / "Documents", "DESCRIPTION_OUTLINED"), Bookmark("Downloads", Path.home() / "Downloads", "DOWNLOAD_OUTLINED"), Bookmark("Pictures", Path.home() / "Pictures", "IMAGE_OUTLINED"), Bookmark("Trash", TrashPath, "DELETE_OUTLINE"), ] def SaveBookmarks(Bookmarks: list[Bookmark]) -> None: ConfigDir.mkdir(parents=True, exist_ok=True) Data = {"Bookmark": [{"Name": B.Name, "Path": str(B.Path), "Icon": B.Icon} for B in Bookmarks]} BookmarksPath.write_text(toml.dumps(Data)) def LoadBookmarks() -> list[Bookmark]: if not BookmarksPath.exists(): Bookmarks = DefaultBookmarks() SaveBookmarks(Bookmarks) return Bookmarks try: Data = toml.loads(BookmarksPath.read_text()) except toml.TomlDecodeError: return DefaultBookmarks() Entries = Data.get("Bookmark", []) Bookmarks = [ Bookmark(Entry["Name"], Path(Entry["Path"]), Entry.get("Icon", DefaultIcon)) for Entry in Entries if "Name" in Entry and "Path" in Entry ] if not Bookmarks: return DefaultBookmarks() # Trash used to be a hardcoded Sidebar entry, not a real bookmark (so it # couldn't be edited). Migrate existing bookmark files that predate that — # but only once, so deliberately removing Trash afterwards actually sticks. if not _TrashMigrationMarker.exists(): if not any(B.Path == TrashPath for B in Bookmarks): Bookmarks.append(Bookmark("Trash", TrashPath, "DELETE_OUTLINE")) SaveBookmarks(Bookmarks) ConfigDir.mkdir(parents=True, exist_ok=True) _TrashMigrationMarker.touch() return Bookmarks def AddBookmark(Name: str, TargetPath: Path, Icon: str = DefaultIcon) -> None: Bookmarks = LoadBookmarks() if any(B.Path == TargetPath for B in Bookmarks): return # already bookmarked Bookmarks.append(Bookmark(Name, TargetPath, Icon)) SaveBookmarks(Bookmarks)