Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add File Manager DBus interface #10

Merged
merged 4 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions data/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ if desktop_utils.found()
test('Validate desktop file', desktop_utils, args: [desktop_file])
endif

servicedir = get_option('prefix') / get_option('datadir') / 'dbus-1' / 'services'


configure_file(
input: 'org.freedesktop.FileManager1.service.in',
output: 'org.freedesktop.FileManager1.service',
configuration: conf,
install_dir: servicedir
)


appstream_file = i18n.merge_file(
input: configure_file(
input: 'hu.kramo.Hyperplane.metainfo.xml.in',
Expand Down
3 changes: 3 additions & 0 deletions data/org.freedesktop.FileManager1.service.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[D-BUS Service]
Name=org.freedesktop.FileManager1
Exec=@prefix@/@bindir@/hyperplane --gapplication-service
3 changes: 2 additions & 1 deletion hu.kramo.Hyperplane.Devel.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"--socket=wayland",
"--filesystem=xdg-data/Trash",
"--talk-name=org.gtk.vfs.*",
"--filesystem=xdg-run/gvfsd"
"--filesystem=xdg-run/gvfsd",
"--own-name=org.freedesktop.FileManager1"
],
"cleanup" : [
"/include",
Expand Down
139 changes: 139 additions & 0 deletions hyperplane/filemanager_dbus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# filemanager_dbus.py
#
# Copyright 2023 Benedek Dévényi
# Copyright 2023 kramo
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later


"""https://www.freedesktop.org/wiki/Specifications/file-manager-interface/"""
from __future__ import annotations

from gi.repository import Gio, GLib

from hyperplane import shared
from hyperplane.properties import HypPropertiesWindow

INTERFACE_DESC = """
<node xmlns:doc="http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name='org.freedesktop.FileManager1'>
<method name='ShowFolders'>
<arg type='as' name='URIs' direction='in'/>
<arg type='s' name='StartupId' direction='in'/>
</method>
<method name='ShowItems'>
<arg type='as' name='URIs' direction='in'/>
<arg type='s' name='StartupId' direction='in'/>
</method>
<method name='ShowItemProperties'>
<arg type='as' name='URIs' direction='in'/>
<arg type='s' name='StartupId' direction='in'/>
</method>
</interface>
</node>
"""


NAME = "org.freedesktop.FileManager1"
PATH = "/org/freedesktop/FileManager1"


class FileManagerDBusServer:
"""https://www.freedesktop.org/wiki/Specifications/file-manager-interface/"""
def __init__(self) -> None:
self._name_id = Gio.bus_own_name(
Gio.BusType.SESSION,
NAME,
Gio.BusNameOwnerFlags.NONE,
self.__on_bus_acquired,
None,
None,
)

def __del__(self):
Gio.bus_unown_name(self._name_id)

def __on_bus_acquired(self, connection: Gio.DBusConnection, _):
for interface in Gio.DBusNodeInfo.new_for_xml(INTERFACE_DESC).interfaces:
try:
connection.register_object(
object_path=PATH,
interface_info=interface,
method_call_closure=self.__on_method_call,
)
except Exception: # pylint: disable=broad-exception-caught
# Another instance already exported at this path
...

def __on_method_call(
self,
_connection: Gio.DBusConnection,
_sender: str,
_object_path: str,
interface_name: str,
method_name: str,
parameters: GLib.Variant,
invocation: Gio.DBusMethodInvocation,
) -> None:
args = tuple(parameters.unpack())

match method_name:
case "ShowFolders":
gfiles = tuple(Gio.File.new_for_uri(uri) for uri in args[0])

for gfile in gfiles:
shared.app.do_activate(gfile)

case "ShowItems":
gfiles = tuple(Gio.File.new_for_uri(uri) for uri in args[0])

for gfile in gfiles:
if not (parent := gfile.get_parent()):
continue

win = shared.app.do_activate(parent)
win.select_uri = gfile.get_uri()

case "ShowItemProperties":
gfiles = tuple(Gio.File.new_for_uri(uri) for uri in args[0])

for gfile in gfiles:
if not (parent := gfile.get_parent()):
continue

win = shared.app.do_activate(parent)

properties = HypPropertiesWindow(gfile)
properties.set_transient_for(win)
properties.present()

case "Introspect":
variant = GLib.Variant("(s)", (INTERFACE_DESC,))
invocation.return_value(variant)
return

case _:
invocation.return_dbus_error(
f"{interface_name}.Error.NotSupported",
"Unsupported property",
)

invocation.return_value(None)
2 changes: 1 addition & 1 deletion hyperplane/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from pathlib import Path
from typing import Any, Optional

from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk, Pango
from gi.repository import Adw, Gdk, Gio, GLib, GObject, Gtk
from gi.repository.GLib import idle_add

from hyperplane import shared
Expand Down
18 changes: 16 additions & 2 deletions hyperplane/items_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def __init__(
Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
Gio.FILE_ATTRIBUTE_STANDARD_EDIT_NAME,
Gio.FILE_ATTRIBUTE_STANDARD_TARGET_URI, # For Recent
Gio.FILE_ATTRIBUTE_TRASH_DELETION_DATE, # For Trash
Gio.FILE_ATTRIBUTE_TRASH_DELETION_DATE, # For Trash
Gio.FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
# For list view
Gio.FILE_ATTRIBUTE_STANDARD_SIZE,
Expand Down Expand Up @@ -443,7 +443,21 @@ def __item_bind(
self, _factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem
) -> None:
list_item.get_child().bind()
self.items[list_item.get_position()] = list_item.get_child()
pos = list_item.get_position()

self.items[pos] = list_item.get_child()

# For the org.freedesktop.FileManager1 DBus service's ShowItems
if (
list_item.get_item().get_attribute_object("standard::file").get_uri()
!= self.get_root().select_uri
):
return

self.get_root().select_uri = None

# Not scrolling there because Grid/ListView really don't like that during population
self.multi_selection.select_item(pos, True)

def __item_unbind(
self, _factory: Gtk.SignalListItemFactory, list_item: Gtk.ListItem
Expand Down
4 changes: 3 additions & 1 deletion hyperplane/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from gi.repository import Adw, Gio, GLib

from hyperplane import shared
from hyperplane.filemanager_dbus import FileManagerDBusServer
from hyperplane.logging.logging_config import logging_config
from hyperplane.preferences import HypPreferencesWindow
from hyperplane.window import HypWindow
Expand All @@ -49,6 +50,7 @@ def __init__(self) -> None:
flags=Gio.ApplicationFlags.HANDLES_OPEN,
)
logging_config()
FileManagerDBusServer()

# Create home
shared.home_path.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -96,7 +98,7 @@ def do_activate(
self,
gfile: Optional[Gio.File] = None,
tags: Optional[Iterable[str]] = None,
) -> set[HypWindow]:
) -> HypWindow:
"""Called when the application is activated."""

if not (gfile or tags):
Expand Down
1 change: 1 addition & 0 deletions hyperplane/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ endif
hyperplane_sources = [
'__init__.py',
'editable_row.py',
'filemanager_dbus.py',
'file_properties.py',
'item_filter.py',
'item_sorter.py',
Expand Down
1 change: 1 addition & 0 deletions hyperplane/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def __init__(
**kwargs,
) -> None:
super().__init__(**kwargs)
self.select_uri = None

if shared.PROFILE == "development":
self.add_css_class("devel")
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ conf.set('PREFIX', prefix)
conf.set('PROFILE', profile)
conf.set('localedir', get_option('prefix') / get_option('localedir'))
conf.set('pkgdatadir', pkgdatadir)
conf.set('bindir', get_option('bindir'))
conf.set('prefix', get_option('prefix'))

subdir('data')
subdir('hyperplane')
Expand Down