Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
infirit committed Dec 11, 2024
1 parent 02e1651 commit b669ef1
Show file tree
Hide file tree
Showing 7 changed files with 396 additions and 161 deletions.
4 changes: 2 additions & 2 deletions apps/blueman-sendto.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ from blueman.Functions import (
from blueman.main.Sendto import Sender
from blueman.bluez.Manager import Manager
from blueman.bluez.errors import DBusNoSuchAdapterError
from blueman.gui.DeviceSelectorDialog import DeviceSelectorDialog
from blueman.gui.DeviceSelectorDialog import DeviceSelector

# Workaround introspection bug, gnome bug 622084
signal.signal(signal.SIGINT, signal.SIG_DFL)
Expand Down Expand Up @@ -120,7 +120,7 @@ class SendTo:

def select_device(self):
adapter_name = os.path.split(self.adapter_path)[-1]
d = DeviceSelectorDialog(discover=True, adapter_name=adapter_name)
d = DeviceSelector(discover=True, adapter_name=adapter_name)
resp = d.run()
d.close()
if resp == Gtk.ResponseType.ACCEPT:
Expand Down
195 changes: 158 additions & 37 deletions blueman/gui/DeviceSelectorDialog.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,177 @@
from gettext import gettext as _
from typing import Optional, Tuple
from typing import Any, Optional, Tuple
import logging

from blueman.bluez.Device import Device
from blueman.gui.DeviceList import DeviceList
from blueman.gui.DeviceSelectorWidget import DeviceSelectorWidget
from blueman.bluez.Device import Device, AnyDevice
from blueman.bluez.Manager import Manager
from blueman.Functions import adapter_path_to_name
from blueman.bluemantyping import ObjectPath
from blueman.main.Builder import Builder

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk


class DeviceSelectorDialog(Gtk.Dialog):
selection: Optional[Tuple[str, Optional[Device]]]
class DeviceRow(Gtk.ListBoxRow):
def __init__(
self,
device_path: ObjectPath,
adapter_path: ObjectPath = "/org/bluez/hci0",
device_icon: str = "blueman",
alias: str = _("Unnamed device"),
paired: bool = False,
trusted: bool = False
) -> None:
super().__init__()

def __init__(self, title: str = _("Select Device"), parent: Optional[Gtk.Container] = None, discover: bool = True,
adapter_name: Optional[str] = None) -> None:
super().__init__(title=title, name="DeviceSelectorDialog", parent=parent, icon_name="blueman", resizable=False)
self.add_buttons(_("_Cancel"), Gtk.ResponseType.REJECT, _("_OK"), Gtk.ResponseType.ACCEPT)
self.adapter_path = adapter_path
self.device_path = device_path
builder = Builder("sendto-rowbox.ui")
self.__box = builder.get_widget("row_box", Gtk.Box)
self._device_icon = builder.get_widget("row_icon", Gtk.Image)
self._row_label = builder.get_widget("row_label", Gtk.Label)
self._row_paired = builder.get_widget("revealer_paired", Gtk.Revealer)
self._row_trusted = builder.get_widget("revealer_trusted", Gtk.Revealer)

self.vbox.props.halign = Gtk.Align.CENTER
self.vbox.props.valign = Gtk.Align.CENTER
self.vbox.props.hexpand = True
self.vbox.props.vexpand = True
self.vbox.props.margin = 6
self.add(self.__box)

self.selector = DeviceSelectorWidget(adapter_name=adapter_name, visible=True)
self.vbox.pack_start(self.selector, True, True, 0)
self.device_icon_name = device_icon
self.row_description = f"{alias} ({adapter_path_to_name(adapter_path)})"
self.paired = paired
self.trusted = trusted

selected_device = self.selector.List.get_selected_device()
if selected_device is not None:
self.selection = selected_device["Adapter"], selected_device
@property
def device_icon_name(self) -> str:
return self._device_icon.get_icon_name()

@device_icon_name.setter
def device_icon_name(self, icon_name: str) -> None:
self._device_icon.set_from_icon_name(icon_name, size=Gtk.IconSize.SMALL_TOOLBAR)

@property
def row_description(self) -> str:
return self._row_label.get_label()

@row_description.setter
def row_description(self, text: str) -> None:
self._row_label.set_label(text)

@property
def paired(self) -> bool:
return self._row_paired.get_reveal_child()

@paired.setter
def paired(self, paired: bool) -> None:
self._row_paired.set_reveal_child(paired)

@property
def trusted(self) -> bool:
return self._row_trusted.get_reveal_child()

@trusted.setter
def trusted(self, trusted: bool):
self._row_trusted.set_reveal_child(trusted)


class DeviceSelector:
selection: Optional[Tuple[ObjectPath, Optional[ObjectPath]]]

def __init__(self, discover: bool = True, adapter_name: str = "any") -> None:
self._rows: dict[ObjectPath, DeviceRow] = {}
builder = Builder("sendto-device-dialog.ui")
self.dialog = builder.get_widget("select_device_dialog", Gtk.Dialog)
self._adapter_combo = builder.get_widget("adapter_combo", Gtk.ComboBox)

self._listbox = builder.get_widget("device_listbox", Gtk.ListBox)
self._listbox.set_filter_func(self.__list_filter_func)
self._listbox.connect("row-selected", self.__on_row_selected)

self._manager = Manager()
self.__any_device = AnyDevice()

adapter_names = sorted([adapter_path_to_name(a.get_object_path()) for a in self._manager.get_adapters()])
for name in adapter_names:
pos = int(name[-1]) + 1
self._adapter_combo.insert(pos, name, name)

self._adapter_combo.connect("changed", lambda _: self._listbox.invalidate_filter())
if adapter_name in adapter_names:
self._adapter_combo.set_active_id(adapter_name)
else:
self.selection = None
logging.debug(adapter_names)
logging.debug(adapter_name)
logging.warning(f"Adapter {adapter_name} not found!")

self.selector.List.connect("device-selected", self.on_device_selected)
self.selector.List.connect("adapter-changed", self.on_adapter_changed)
if discover:
self.selector.List.discover_devices()
self._manager.connect_signal("adapter-added", self.__on_manager_signal, "adapter-added")
self._manager.connect_signal("adapter-removed", self.__on_manager_signal, "adapter-removed")
self._manager.connect_signal("device-created", self.__on_manager_signal, "device-added")
self._manager.connect_signal("device-removed", self.__on_manager_signal, "device-removed")

self.selector.List.connect("row-activated", self.on_row_activated)
self.__any_device.connect_signal("property-changed", self.__on_device_property_changed)

def close(self) -> None:
self.selector.destroy()
super().close()
self._manager.populate_devices()

def __list_filter_func(self, row: Gtk.ListBoxRow) -> bool:
active_id = self._adapter_combo.get_active_id()
adapter_name = adapter_path_to_name(row.adapter_path)
if active_id == "any" or active_id == adapter_name:
return True
else:
return False

def __on_manager_signal(self, _manager: Manager, object_path: ObjectPath, signal_name: str):
logging.debug(f"{object_path} {signal_name}")
match signal_name:
case "adapter-added":
adapter_name = adapter_path_to_name(object_path)
pos = int(adapter_name[-1]) + 1
self._adapter_combo.insert(pos, adapter_name, adapter_name)
self._manager.populate_devices(adapter_path=object_path)
case "adapter-removed":
for key in self._rows:
if self._rows[key].adapter_path == object_path:
row = self._rows.pop(key)
self._listbox.remove(row)

def on_row_activated(self, _treeview: Gtk.TreeView, _path: Gtk.TreePath, _view_column: Gtk.TreeViewColumn,
*_args: object) -> None:
self.response(Gtk.ResponseType.ACCEPT)
adapter_name = adapter_path_to_name(object_path)
if adapter_name == self._adapter_combo.set_active_id:
self._adapter_combo.set_active_id(adapter_name)

def on_adapter_changed(self, _devlist: DeviceList, _adapter: str) -> None:
self.selection = None
pos = int(adapter_name[-1]) + 1
self._adapter_combo.remove(pos)

def on_device_selected(self, devlist: DeviceList, device: Optional[Device], _tree_iter: Gtk.TreeIter) -> None:
assert devlist.Adapter is not None
self.selection = (devlist.Adapter.get_object_path(), device)
case "device-added":
props = Device(obj_path=object_path).get_properties()
row = DeviceRow(object_path, props["Adapter"], props["Icon"], props['Alias'], props["Paired"], props["Trusted"])
row.show_all()
self._listbox.add(row)
self._rows[object_path] = row
case "device-removed":
row = self._rows.pop(object_path)
self._listbox.remove(row)
case _:
raise ValueError(f"Unhandled signal {signal_name}")

def __on_device_property_changed(self, _device: AnyDevice, key: str, value: Any, object_path: ObjectPath) -> None:
row = self._rows.get(object_path, None)
if row is None:
return

match key:
case "Trusted":
row.trusted = value
case "Paired":
row.paired = value

def __on_row_selected(self, _listbox, row):
logging.debug(f"{row.device_path}")
device = Device(obj_path=row.device_path)
self.selection = row.adapter_path, device

def run(self) -> int:
return self.dialog.run()

def close(self) -> None:
# FIXME implement a destroy method self.selector.destroy()
self.dialog.close()
121 changes: 0 additions & 121 deletions blueman/gui/DeviceSelectorWidget.py

This file was deleted.

2 changes: 1 addition & 1 deletion blueman/gui/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SUBDIRS = \
manager

bluemandir = $(pythondir)/blueman/gui
blueman_PYTHON = Animation.py GsmSettings.py CommonUi.py DeviceList.py DeviceSelectorDialog.py DeviceSelectorList.py DeviceSelectorWidget.py GenericList.py GtkAnimation.py __init__.py Notification.py
blueman_PYTHON = Animation.py GsmSettings.py CommonUi.py DeviceList.py DeviceSelectorDialog.py DeviceSelectorList.py GenericList.py GtkAnimation.py __init__.py Notification.py

CLEANFILES = \
$(BUILT_SOURCES)
Expand Down
2 changes: 2 additions & 0 deletions data/ui/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ui_DATA = \
services-network.ui \
services-transfer.ui \
send-dialog.ui \
sendto-device-dialog.ui \
sendto-rowbox.ui \
applet-plugins-widget.ui \
gsm-settings.ui \
net-usage.ui \
Expand Down
Loading

0 comments on commit b669ef1

Please sign in to comment.