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 asyncio
from xdg import DesktopEntry
from pathlib import Path
import subprocess
import logging
from gi.repository import Gio
import os
import gettext
_ = 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():
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))
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 asyncio
import subprocess
import logging
from pathlib import Path
import gettext
_ = 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:
def __init__(self, config: dict):
self.config = config
self.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"]:
return
if self.config["enable_path"] and query.startswith("/") or query.startswith("~/"):
# Provide an entry to open the entered path
def 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()}")
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):
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 asyncio
from xdg import DesktopEntry
from pathlib import Path
import subprocess
import logging
from gi.repository import Gio, GLib
import gettext
_ = gettext.gettext
logging.basicConfig(level=logging.DEBUG)
config_template = {
"terminal": "gnome-terminal",
"shell": "bash -c"
}
class 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():
subprocess.Popen(f"{terminal_app} -- {shell} '{query}'", shell=True)
yield {
"name": _("Run"),
"description": query,
"image": ("logo", "utilities-terminal"),
"execute": execute
}