Skip to content
Open
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
9 changes: 4 additions & 5 deletions blueman/Functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,14 @@ def create_menuitem(
pixbuf: GdkPixbuf.Pixbuf | None = None,
surface: cairo.Surface | None = None,
) -> Gtk.ImageMenuItem:
image = Gtk.Image(pixel_size=16)
if icon_name:
image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
image = Gtk.Image(icon_name=icon_name, icon_size=Gtk.IconSize.MENU, pixel_size=16)
elif surface:
image.set_from_surface(surface)
image = Gtk.Image(surface=surface, icon_size=Gtk.IconSize.MENU, pixel_size=16)
elif pixbuf:
image.set_from_pixbuf(pixbuf)
image = Gtk.Image(pixbuf=pixbuf, icon_size=Gtk.IconSize.MENU, pixel_size=16)
else:
raise ValueError("At least provide one of, icon name, surface or pixbuf")
image = None

item = Gtk.ImageMenuItem(label=text, image=image, use_underline=True)
child = item.get_child()
Expand Down
3 changes: 2 additions & 1 deletion blueman/Service.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ def priority(self) -> int:
@property
@abstractmethod
def available(self) -> bool:
...
# Most services fail if device is not paired or blocked.
return self.device["Paired"] and not self.device["Blocked"]

@property
@abstractmethod
Expand Down
173 changes: 83 additions & 90 deletions blueman/gui/manager/ManagerDeviceMenu.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,26 @@ def show_generic_connect_calc(self, device_uuids: Iterable[str]) -> bool:
# LE devices do not appear to expose certain properties like uuids until connect to at least once.
return not device_uuids

def _add_seperator(self) -> None:
sep_item = Gtk.SeparatorMenuItem(visible=True)
self.append(sep_item)

def _add_menu_item(self, label: str, icon_name: str | None = None, sensitive: bool = True,
tooltip: str | None = None) -> Gtk.ImageMenuItem:
item = create_menuitem(text=label, icon_name=icon_name)
item.set_sensitive(sensitive)
if tooltip is not None:
item.set_tooltip_text(tooltip)
self.append(item)
return item

def generate(self) -> None:
self.clear()

if not self.is_popup or self.props.visible:
selected = self.Blueman.List.selected()
if not selected:
return
row = self.Blueman.List.get(selected, "alias", "paired", "connected", "trusted", "objpush", "device",
"blocked")
else:
(x, y) = self.Blueman.List.get_pointer()
posdata = self.Blueman.List.get_path_at_pos(x, y)
Expand All @@ -277,39 +288,43 @@ def generate(self) -> None:

tree_iter = self.Blueman.List.filter.get_iter(path)
assert tree_iter is not None
child_iter = self.Blueman.List.filter.convert_iter_to_child_iter(tree_iter)
assert child_iter is not None

row = self.Blueman.List.get(child_iter, "alias", "paired", "connected", "trusted", "objpush", "device",
"blocked")
selected = self.Blueman.List.filter.convert_iter_to_child_iter(tree_iter)
assert selected is not None

row = self.Blueman.List.get(selected, "alias", "paired", "connected", "trusted", "objpush", "device", "blocked")
self.SelectedDevice = row["device"]

op = self.get_op(self.SelectedDevice)

if op is not None:
item: Gtk.MenuItem = create_menuitem(op, "network-transmit-receive-symbolic")
item.props.sensitive = False
item.show()
self.append(item)
self._add_menu_item(label=op, icon_name="network-transmit-receive-symbolic", sensitive=False)
return

show_generic_connect = self.show_generic_connect_calc(self.SelectedDevice['UUIDs'])

powered = Adapter(obj_path=self.SelectedDevice["Adapter"])["Powered"]
blocked = self.SelectedDevice["Blocked"]

if not row["connected"] and show_generic_connect and powered:
connect_item = create_menuitem(_("<b>_Connect</b>"), "bluetooth-symbolic")
if blocked:
tooltip = _("Not available (Blocked)")
else:
tooltip = _("Connects auto connect profiles A2DP source, A2DP sink, and HID")

connect_item = self._add_menu_item(
label=_("<b>_Connect</b>"),
icon_name="bluetooth-symbolic",
tooltip=tooltip,
sensitive=not blocked
)
connect_item.connect("activate", lambda _item: self.connect_service(self.SelectedDevice))
connect_item.props.tooltip_text = _("Connects auto connect profiles A2DP source, A2DP sink, and HID")
connect_item.show()
self.append(connect_item)
elif row["connected"] and show_generic_connect:
connect_item = create_menuitem(_("<b>_Disconnect</b>"), "bluetooth-disabled-symbolic")
connect_item.props.tooltip_text = _("Forcefully disconnect the device")
connect_item = self._add_menu_item(
label=_("<b>_Disconnect</b>"),
icon_name="bluetooth-disabled-symbolic",
tooltip=_("Forcefully disconnect the device")
)
connect_item.connect("activate", lambda _item: self.disconnect_service(self.SelectedDevice))
connect_item.show()
self.append(connect_item)

logging.debug(row["alias"])

Expand All @@ -321,7 +336,7 @@ def generate(self) -> None:
autoconnect_items = [item for item in items if item.group == DeviceMenuItem.Group.AUTOCONNECT]
action_items = [i for i in items if i.group == DeviceMenuItem.Group.ACTIONS]

if connect_items:
if connect_items and not blocked:
self.append(self._create_header(_("<b>Connect To:</b>")))
for it in sorted(connect_items, key=attrgetter("position")):
self.append(it.item)
Expand All @@ -331,111 +346,89 @@ def generate(self) -> None:
for it in sorted(disconnect_items, key=attrgetter("position")):
self.append(it.item)

config = AutoConnectConfig()
auto_connect_config = AutoConnectConfig()
generic_service = ServiceUUID("00000000-0000-0000-0000-000000000000")
object_path = self.SelectedDevice.get_object_path()
btaddress: BtAddress = self.SelectedDevice["Address"]
generic_autoconnect = (object_path, str(generic_service)) in set(config["services"])
generic_autoconnect = (object_path, str(generic_service)) in set(auto_connect_config["services"])

if row["connected"] or generic_autoconnect or autoconnect_items:
self.append(self._create_header(_("<b>Auto-connect:</b>")))

if row["connected"] or generic_autoconnect:
item = Gtk.CheckMenuItem(label=generic_service.name)
config.bind_to_menuitem(item, (btaddress, str(generic_service)))
item.show()
self.append(item)
auto_connect_item = Gtk.CheckMenuItem(label=generic_service.name)
auto_connect_config.bind_to_menuitem(auto_connect_item, (btaddress, str(generic_service)))
auto_connect_item.show()
self.append(auto_connect_item)

for it in sorted(autoconnect_items, key=attrgetter("position")):
self.append(it.item)

if (powered and show_generic_connect) or connect_items or disconnect_items or autoconnect_items:
item = Gtk.SeparatorMenuItem()
item.show()
self.append(item)
self._add_seperator()

for it in sorted(action_items, key=attrgetter("position")):
self.append(it.item)

if powered:
send_item = create_menuitem(_("Send a _File…"), "blueman-send-symbolic")
send_item.props.sensitive = False
self.append(send_item)
send_item.show()

if row["objpush"]:
send_item.connect("activate", lambda x: self.Blueman.send(self.SelectedDevice))
send_item.props.sensitive = True

item = Gtk.SeparatorMenuItem()
item.show()
self.append(item)

item = create_menuitem(_("_Pair"), "blueman-pair-symbolic")
item.props.tooltip_text = _("Create pairing with the device")
self.append(item)
item.show()
if not row["paired"]:
item.connect("activate", lambda x: self.Blueman.bond(self.SelectedDevice))
else:
item.props.sensitive = False

if not row["trusted"]:
item = create_menuitem(_("_Trust"), "blueman-trust-symbolic")
item.connect("activate", lambda x: self.Blueman.toggle_trust(self.SelectedDevice))
self.append(item)
item.show()
else:
item = create_menuitem(_("_Untrust"), "blueman-untrust-symbolic")
self.append(item)
item.connect("activate", lambda x: self.Blueman.toggle_trust(self.SelectedDevice))
item.show()
item.props.tooltip_text = _("Mark/Unmark this device as trusted")

if not row["blocked"]:
item = create_menuitem(_("_Block"), "blueman-block-symbolic")
item.connect("activate", lambda x: self.Blueman.toggle_blocked(self.SelectedDevice))
self.append(item)
item.show()
else:
item = create_menuitem(_("_Unblock"), "blueman-block-symbolic")
self.append(item)
item.connect("activate", lambda x: self.Blueman.toggle_blocked(self.SelectedDevice))
item.show()
item.props.tooltip_text = _("Block/Unblock this device")
if powered and row["objpush"] and not blocked:
send_item = self._add_menu_item(
label=_("Send a _File…"),
icon_name="blueman-send-symbolic"
)
send_item.connect("activate", lambda _: self.Blueman.send(self.SelectedDevice))

self._add_seperator()

self._add_menu_item(
label=_("_Pair"),
icon_name="blueman-pair-symbolic",
tooltip=_("Create pairing with the device") if not blocked else _("Not available (Blocked)"),
sensitive=False if blocked else not row["paired"]
)

trust_item = self._add_menu_item(
label=_("_Untrust") if row["trusted"] else _("_Trust"),
icon_name="blueman-untrust-symbolic" if row["trusted"] else "blueman-trust-symbolic",
tooltip=_("Mark/Unmark this device as trusted"),
)
trust_item.connect("activate", lambda _: self.Blueman.toggle_trust(self.SelectedDevice))

block_item = self._add_menu_item(
label=_("_Unblock") if row["blocked"] else _("_Block"),
icon_name="blueman-block-symbolic",
tooltip=_("Block/Unblock this device")
)
block_item.connect("activate", lambda _: self.Blueman.toggle_blocked(self.SelectedDevice))

def on_rename(_item: Gtk.MenuItem, device: Device) -> None:
def on_response(dialog: Gtk.Dialog, response_id: int) -> None:
def on_response(rename_dialog: Gtk.Dialog, response_id: int) -> None:
if response_id == Gtk.ResponseType.ACCEPT:
assert isinstance(alias_entry, Gtk.Entry) # https://github.com/python/mypy/issues/2608
device.set('Alias', alias_entry.get_text())
elif response_id == 1:
device.set('Alias', '')
dialog.destroy()
rename_dialog.destroy()

builder = Builder("rename-device.ui")
dialog = builder.get_widget("dialog", Gtk.Dialog)
dialog.set_transient_for(self.Blueman.window)
dialog.props.icon_name = "blueman"

alias_entry = builder.get_widget("alias_entry", Gtk.Entry)
alias_entry.set_text(device['Alias'])
dialog.connect("response", on_response)
dialog.present()

item = Gtk.MenuItem.new_with_mnemonic(_("R_ename device…"))
item.connect('activate', on_rename, self.SelectedDevice)
self.append(item)
item.show()
rename_item = self._add_menu_item(label=_("R_ename device…"))
rename_item.connect('activate', on_rename, self.SelectedDevice)

item = Gtk.SeparatorMenuItem()
item.show()
self.append(item)
self._add_seperator()

item = create_menuitem(_("_Remove…"), "list-remove-symbolic")
item.connect("activate", lambda x: self.Blueman.remove(self.SelectedDevice))
self.append(item)
item.show()
item.props.tooltip_text = _("Remove this device from the known devices list")
remove_item = self._add_menu_item(
label=_("_Remove…"),
icon_name="list-remove-symbolic",
tooltip=_("Remove this device from the known devices list")
)
remove_item.connect("activate", lambda _: self.Blueman.remove(self.SelectedDevice))

@staticmethod
def _create_header(text: str) -> Gtk.MenuItem:
Expand Down
2 changes: 1 addition & 1 deletion blueman/plugins/manager/Notes.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def on_request_menu_items(
device: Device,
powered: bool,
) -> list[DeviceMenuItem]:
if not powered:
if not powered or device["Blocked"]:
return []

item = create_menuitem(_("Send _note"), "dialog-information-symbolic")
Expand Down
4 changes: 1 addition & 3 deletions blueman/services/meta/NetworkService.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def __init__(self, device: Device, uuid: str):

@property
def available(self) -> bool:
# This interface is only available after pairing
paired: bool = self.device["Paired"]
return paired
return super().available

@property
def connectable(self) -> bool:
Expand Down
4 changes: 1 addition & 3 deletions blueman/services/meta/SerialService.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ def __init__(self, device: Device, uuid: str) -> None:

@property
def available(self) -> bool:
# It will ask to pair anyway so not make it available
paired: bool = self.device["Paired"]
return paired
return super().available

@property
def connectable(self) -> bool:
Expand Down
1 change: 1 addition & 0 deletions data/ui/rename-device.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<property name="border-width">5</property>
<property name="can-focus">False</property>
<property name="decorated">False</property>
<property name="icon-name">blueman</property>
<property name="modal">True</property>
<property name="resizable">False</property>
<property name="title" translatable="yes">Rename device</property>
Expand Down
6 changes: 6 additions & 0 deletions stubs/gi/repository/Gtk.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7045,13 +7045,19 @@ class Image(Misc):

class _Props(Misc._Props):
icon_name: typing.Optional[str]
pixbuf: GdkPixbuf.Pixbuf
surface: cairo.Surface
icon_size: int
pixel_size: int

props: _Props

def __init__(self,
*,
icon_name: typing.Optional[str] = None,
pixbuf: GdkPixbuf.Pixbuf | None = None,
surface: cairo.Surface | None = None,
icon_size: int = 4,
pixel_size: int = -1,
# Widget
halign: Align = Align.FILL,
Expand Down