roundabout,
created on Thursday, 19 December 2024, 18:36:29 (1734633389),
received on Thursday, 19 December 2024, 19:29:28 (1734636568)
Author identity: vlad <vlad.muntoiu@gmail.com>
94cbb20338b57491315819500600b665924ffd32
.idea/providers.iml
@@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <module type="PYTHON_MODULE" version="4"> <component name="NewModuleRootManager"> <content url="file://$MODULE_DIR$" /><content url="file://$MODULE_DIR$"> <excludeFolder url="file://$MODULE_DIR$/.flatpak-builder" /> <excludeFolder url="file://$MODULE_DIR$/.git" /> <excludeFolder url="file://$MODULE_DIR$/build-extension" /> </content><orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> </component>
applications/__init__.py
@@ -1,69 +0,0 @@
import asynciofrom xdg import DesktopEntryfrom pathlib import Pathimport subprocessimport loggingfrom gi.repository import Gioimport osimport gettext_ = gettext.gettextdef _get_all_menu_entries():if os.environ.get("XDG_DATA_HOME"):user_directories = [Path(dir).expanduser() / "applications" for dir in os.environ["XDG_DATA_HOME"].split(":")]else:user_directories = [Path.home() / ".local/share/applications",]if os.environ.get("XDG_DATA_DIRS"):system_directories = [Path(dir) / "applications" for dir in os.environ["XDG_DATA_DIRS"].split(":")]else:system_directories = [Path("/usr/share/applications"),Path("/usr/local/share/applications"),]directories = user_directories + system_directoriesfor directory in directories:if directory.exists():for desktop_file in directory.glob("*.desktop"):entry = DesktopEntry.DesktopEntry(str(desktop_file))if entry.getHidden() or entry.getNoDisplay():continueyield entry, desktop_filedef _match_query(entry: DesktopEntry, query: str):return (query.lower() in entry.getName().lower()or query.lower() in entry.getComment().lower()or query.lower() in entry.getExec().lower()or any(query.lower() in keyword.lower() for keyword in entry.getKeywords()))class Provider():def __init__(self, config: dict):self.config = configself.name = _("Applications")self.icon = "applications-system"self.description = _("Search for installed applications on your device (the same you would find in your application menu).")async def search(self, query: str):for entry, desktop_file in _get_all_menu_entries():if _match_query(entry, query):def execute(desktop_file=desktop_file):app_info = Gio.DesktopAppInfo.new_from_filename(str(desktop_file))app_info.launch_uris([], None)yield {"name": entry.getName() or desktop_file.stem,"description": entry.getComment() or "","image": ("logo", entry.getIcon() or "application-x-executable"),"execute": execute}
files/__init__.py
@@ -1,73 +0,0 @@
import asyncioimport subprocessimport loggingfrom pathlib import Pathimport gettext_ = gettext.gettextlogging.basicConfig(level=logging.DEBUG)config_template = {"path": "~/","min_chars": 3,"case_insensitive": True,"limit": 16,"enable_search": True,"enable_path": True}class Provider:def __init__(self, config: dict):self.config = configself.name = _("Files")self.icon = "system-file-manager"self.description = _("Search for files and directories on your device.")async def search(self, query: str):if len(query) < self.config["min_chars"]:returnif self.config["enable_path"] and query.startswith("/") or query.startswith("~/"):# Provide an entry to open the entered pathdef execute():subprocess.Popen(["xdg-open", str(Path(query).expanduser().resolve())])yield {"name": _("Open in file manager"),"description": str(Path(query).expanduser().resolve()),"image": ("logo", "system-file-manager"),"execute": execute}process = await asyncio.create_subprocess_exec("locate", query,"-r", str(Path(self.config["path"]).expanduser().resolve()) + "/*","-i" if self.config["case_insensitive"] else "",*("-l", str(self.config["limit"])) if self.config["limit"] > 0 else (),stdout=subprocess.PIPE,stderr=subprocess.PIPE)stdout, stderr = await process.communicate()if process.returncode != 0:logging.error(f"Cannot locate files: {stderr.decode().strip()}")returnfor line in stdout.decode().splitlines():file_path = Path(line.strip())if not file_path.exists() or not (file_path.is_file() or file_path.is_dir()):continuedef execute(file_path=file_path):subprocess.Popen(["xdg-open", str(file_path)])yield {"name": file_path.name,"description": str(file_path),"image": ("logo", "system-file-manager"),"execute": execute}
src/applications/__init__.py
@@ -0,0 +1,69 @@
import asyncio from xdg import DesktopEntry from pathlib import Path import subprocess import logging from gi.repository import Gio import os import gettext import izvor_utils as izvor _ = gettext.gettext def _get_all_menu_entries(): if os.environ.get("XDG_DATA_HOME"): user_directories = [Path(dir).expanduser() / "applications" for dir in os.environ["XDG_DATA_HOME"].split(":")] else: user_directories = [ Path.home() / ".local/share/applications", ] if os.environ.get("XDG_DATA_DIRS"): system_directories = [Path(dir) / "applications" for dir in os.environ["XDG_DATA_DIRS"].split(":")] else: system_directories = [ Path("/usr/share/applications"), Path("/usr/local/share/applications"), ] directories = user_directories + system_directories for directory in directories: if directory.exists(): for desktop_file in directory.glob("*.desktop"): entry = DesktopEntry.DesktopEntry(str(desktop_file)) if entry.getHidden() or entry.getNoDisplay(): continue yield entry, desktop_file def _match_query(entry: DesktopEntry, query: str): return ( query.lower() in entry.getName().lower() or query.lower() in entry.getComment().lower() or query.lower() in entry.getExec().lower() or any(query.lower() in keyword.lower() for keyword in entry.getKeywords()) ) class Provider(izvor.Provider): def __init__(self, config: dict): self.config = config self.name = _("Applications") self.icon = "applications-system" self.description = _("Search for installed applications on your device (the same you would find in your application menu).") async def search(self, query: str): for entry, desktop_file in _get_all_menu_entries(): if _match_query(entry, query): def execute(desktop_file=desktop_file): app_info = Gio.DesktopAppInfo.new_from_filename(str(desktop_file)) izvor.run_desktop_entry(app_info) yield { "name": entry.getName() or desktop_file.stem, "description": entry.getComment() or "", "image": ("logo", entry.getIcon() or "application-x-executable"), "execute": execute }
src/files/__init__.py
@@ -0,0 +1,76 @@
import asyncio import subprocess import logging from pathlib import Path import gettext import izvor_utils as izvor _ = gettext.gettext logging.basicConfig(level=logging.DEBUG) config_template = { "path": "~/", "min_chars": 3, "case_insensitive": True, "limit": 16, "enable_search": True, "enable_path": True } class Provider(izvor.Provider): def __init__(self, config: dict): super().__init__( name=_("Files"), icon="system-file-manager", description=_("Search for files and directories on your device."), config=config ) async def search(self, query: str): if len(query) < self.config["min_chars"]: return if self.config["enable_path"] and query.startswith("/") or query.startswith("~/"): # Provide an entry to open the entered path def execute(): izvor.xdg_open(str(Path(query).expanduser().resolve())) yield { "name": _("Open in file manager"), "description": str(Path(query).expanduser().resolve()), "image": ("logo", "system-file-manager"), "execute": execute } process = await asyncio.create_subprocess_exec( "locate", query, "-r", str(Path(self.config["path"]).expanduser().resolve()) + "/*", "-i" if self.config["case_insensitive"] else "", *("-l", str(self.config["limit"])) if self.config["limit"] > 0 else (), stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout, stderr = await process.communicate() if process.returncode != 0: logging.error(f"Cannot locate files: {stderr.decode().strip()}") return for line in stdout.decode().splitlines(): file_path = Path(line.strip()) if not file_path.exists() or not (file_path.is_file() or file_path.is_dir()): continue def execute(file_path=file_path): izvor.xdg_open(str(file_path)) yield { "name": file_path.name, "description": str(file_path), "image": ("logo", "system-file-manager"), "execute": execute }
src/terminal/__init__.py
@@ -0,0 +1,41 @@
import asyncio from xdg import DesktopEntry from pathlib import Path import subprocess import logging from gi.repository import Gio, GLib import gettext import izvor_utils as izvor _ = gettext.gettext logging.basicConfig(level=logging.DEBUG) config_template = { "terminal": "gnome-terminal", "shell": "bash -c" } class Provider(izvor.Provider): def __init__(self, config: dict): self.config = config self.name = _("Terminal") self.icon = "utilities-terminal" self.description = _("Run search queries as commands in a terminal.") async def search(self, query: str): terminal_app = self.config["terminal"] shell = self.config["shell"] def execute(): izvor.launch_command(f"{terminal_app} -- {shell} '{query}'", shell=True) yield { "name": _("Run"), "description": query, "image": ("logo", "utilities-terminal"), "execute": execute }
terminal/__init__.py
@@ -1,40 +0,0 @@
import asynciofrom xdg import DesktopEntryfrom pathlib import Pathimport subprocessimport loggingfrom gi.repository import Gio, GLibimport gettext_ = gettext.gettextlogging.basicConfig(level=logging.DEBUG)config_template = {"terminal": "gnome-terminal","shell": "bash -c"}class Provider():def __init__(self, config: dict):self.config = configself.name = _("Terminal")self.icon = "utilities-terminal"self.description = _("Run search queries as commands in a terminal.")async def search(self, query: str):terminal_app = self.config["terminal"]shell = self.config["shell"]def execute():subprocess.Popen(f"{terminal_app} -- {shell} '{query}'", shell=True)yield {"name": _("Run"),"description": query,"image": ("logo", "utilities-terminal"),"execute": execute}