|
9 | 9 | import gi |
10 | 10 | gi.require_version('Gtk', '3.0') |
11 | 11 | gi.require_version('GnomeBluetooth', '1.0') |
12 | | -from gi.repository import Gtk, GnomeBluetooth, Gio |
| 12 | +from gi.repository import Gtk, GnomeBluetooth, Gio, GLib |
| 13 | + |
| 14 | +APPLICATION_ID = 'com.linuxmint.blueberry' |
13 | 15 |
|
14 | 16 | BLUETOOTH_RFKILL_ERR = "rfkill-err" |
15 | 17 | BLUETOOTH_DISABLED_PAGE = "disabled-page" |
|
22 | 24 |
|
23 | 25 | setproctitle.setproctitle("blueberry") |
24 | 26 |
|
| 27 | +# similar to g_warning by default, can specify any log level though |
| 28 | +def log(text, log_level=GLib.LogLevelFlags.LEVEL_WARNING): |
| 29 | + variant = GLib.Variant("a{sv}", { "MESSAGE": GLib.Variant("s", text) }) |
| 30 | + GLib.log_variant(APPLICATION_ID, log_level, variant) |
| 31 | + |
| 32 | +# searches a widget and its children for a specific buildable id or widget type |
| 33 | +# note: stops at first match |
| 34 | +def find_widget(parent, name="", widgetClass=None): |
| 35 | + p_name = Gtk.Buildable.get_name(parent) |
| 36 | + if p_name and name and p_name.strip() == name.strip(): |
| 37 | + return parent |
| 38 | + |
| 39 | + if widgetClass and isinstance(parent, widgetClass): |
| 40 | + return parent |
| 41 | + |
| 42 | + if hasattr(parent, "get_children"): |
| 43 | + children = parent.get_children() |
| 44 | + for child in children: |
| 45 | + res = find_widget(child, name, widgetClass) |
| 46 | + if res is not None: |
| 47 | + return res |
| 48 | + |
| 49 | + return None |
| 50 | + |
| 51 | +# We attempt to override the widget style to replace |
| 52 | +# the explanation label text and stop the spinner. |
| 53 | +# gnome_bluetooth_settings_widget doesn't give explicit access |
| 54 | +# to its label via gi so we recurse its child widgets to try |
| 55 | +# to find the parts we want to modify. |
| 56 | + |
| 57 | +# if the override fails for any reason it is disabled. update |
| 58 | +# signals in the main class are only connected if a test call |
| 59 | +# to this succeeds the first time |
| 60 | +override_failed = False |
| 61 | + |
| 62 | +def apply_widget_override(widget, adapter_name, obex_enabled): |
| 63 | + global override_failed |
| 64 | + if override_failed: |
| 65 | + return False |
| 66 | + |
| 67 | + try: |
| 68 | + # not finding the label is fatal as it's our main purpose here |
| 69 | + label = find_widget(widget, "explanation-label") |
| 70 | + if label is None: |
| 71 | + raise LookupError("unable to find label to override") |
| 72 | + |
| 73 | + # not finding the spinner is non-fatal |
| 74 | + spinner = find_widget(widget, widgetClass=Gtk.Spinner) |
| 75 | + if spinner and spinner.props.active: |
| 76 | + spinner.stop() |
| 77 | + |
| 78 | + if adapter_name is not None: |
| 79 | + if obex_enabled: |
| 80 | + text = _("Visible as %s and available for Bluetooth file transfers.") |
| 81 | + else: |
| 82 | + text = _("Visible as %s.") |
| 83 | + text = "%s\n" % text |
| 84 | + label.set_markup(text % "\"%s\"" % adapter_name) |
| 85 | + else: |
| 86 | + label.set_label("") |
| 87 | + |
| 88 | + except Exception as e: |
| 89 | + log("apply_widget_override failed: {}".format(e)) |
| 90 | + override_failed = True |
| 91 | + return False |
| 92 | + |
| 93 | + return True |
| 94 | + |
| 95 | + |
25 | 96 | class Blueberry(Gtk.Application): |
26 | 97 | ''' Create the UI ''' |
27 | 98 | def __init__(self): |
28 | | - |
29 | | - Gtk.Application.__init__(self, application_id='com.linuxmint.blueberry', flags=Gio.ApplicationFlags.FLAGS_NONE) |
| 99 | + Gtk.Application.__init__(self, application_id=APPLICATION_ID, flags=Gio.ApplicationFlags.FLAGS_NONE) |
30 | 100 | self.window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL) |
31 | 101 | self.detect_desktop_environment() |
32 | 102 | self.connect("activate", self.on_activate) |
@@ -70,7 +140,7 @@ def detect_desktop_environment(self): |
70 | 140 | self.configuration_tools = {"sound": "pavucontrol", "keyboard": "lxinput", "mouse": "lxinput"} |
71 | 141 | else: |
72 | 142 | self.de = "Unknown" |
73 | | - print("Warning: DE could not be detected!") |
| 143 | + log("DE could not be detected!") |
74 | 144 | self.configuration_tools = {} |
75 | 145 | if os.path.exists("/usr/bin/pavucontrol"): |
76 | 146 | self.configuration_tools["sound"] = "pavucontrol" |
@@ -188,16 +258,20 @@ def create_window(self): |
188 | 258 | self.add_window(self.window) |
189 | 259 | self.window.show_all() |
190 | 260 |
|
191 | | - self.client = GnomeBluetooth.Client() |
192 | | - self.model = self.client.get_model() |
193 | | - self.model.connect('row-changed', self.update_status) |
194 | | - self.model.connect('row-deleted', self.update_status) |
195 | | - self.model.connect('row-inserted', self.update_status) |
196 | | - self.update_status() |
| 261 | + # attempt to apply overrides and if we fail don't setup update hooks |
| 262 | + name = self.get_default_adapter_name() |
| 263 | + obex_enabled = self.settings.get_boolean("obex-enabled") |
| 264 | + if apply_widget_override(self.lib_widget, name, obex_enabled): |
| 265 | + self.client = GnomeBluetooth.Client() |
| 266 | + self.model = self.client.get_model() |
| 267 | + self.model.connect('row-changed', self.update_status) |
| 268 | + self.model.connect('row-deleted', self.update_status) |
| 269 | + self.model.connect('row-inserted', self.update_status) |
| 270 | + self.update_status() |
197 | 271 |
|
198 | 272 | def panel_changed(self, widget, panel): |
199 | 273 | if not panel in self.configuration_tools: |
200 | | - print("Warning, no configuration tool known for panel '%s'" % panel) |
| 274 | + log("No configuration tool known for panel '{}'".format(panel)) |
201 | 275 | else: |
202 | 276 | os.system("%s &" % self.configuration_tools[panel]) |
203 | 277 |
|
@@ -241,36 +315,16 @@ def get_default_adapter_name(self): |
241 | 315 | name = line.replace("Alias: ", "").replace(" [rw]", "").replace(" [ro]", "") |
242 | 316 | break |
243 | 317 | except Exception as cause: |
244 | | - print ("Could not retrieve the BT adapter name with 'bt-adapter -i': %s" % cause) |
| 318 | + log("Could not retrieve the BT adapter name with 'bt-adapter -i': {}".format(cause)) |
245 | 319 | return name |
246 | 320 |
|
247 | 321 | def update_status(self, path=None, iter=None, data=None): |
248 | | - try: |
249 | | - # In version 3.18, gnome_bluetooth_settings_widget |
250 | | - # doesn't give explicit access to its label via gi |
251 | | - # but it's a composite widget and its hierarchy is: |
252 | | - # scrolledwindow -> viewport -> vbox -> explanation-label |
253 | | - scrolledwindow = self.lib_widget.get_children()[0] |
254 | | - scrolledwindow.set_shadow_type(Gtk.ShadowType.NONE) |
255 | | - viewport = scrolledwindow.get_children()[0] |
256 | | - vbox = viewport.get_children()[0] |
257 | | - spinner = vbox.get_children()[1].get_children()[0].get_children()[1] |
258 | | - if spinner.props.active: |
259 | | - spinner.stop() |
260 | | - explanation_label = vbox.get_children()[0] |
261 | | - name = self.get_default_adapter_name() |
262 | | - if name is not None: |
263 | | - if self.settings.get_boolean('obex-enabled'): |
264 | | - text = _("Visible as %s and available for Bluetooth file transfers.") |
265 | | - else: |
266 | | - text = _("Visible as %s.") |
267 | | - text = "%s\n" % text |
268 | | - explanation_label.set_markup(text % "\"%s\"" % name) |
269 | | - else: |
270 | | - explanation_label.set_label("") |
271 | | - except Exception as e: |
272 | | - print(e) |
273 | | - return None |
| 322 | + if override_failed: |
| 323 | + return |
| 324 | + |
| 325 | + name = self.get_default_adapter_name() |
| 326 | + obex_enabled = self.settings.get_boolean("obex-enabled") |
| 327 | + apply_widget_override(self.lib_widget, name, obex_enabled) |
274 | 328 |
|
275 | 329 | def update_ui_callback(self): |
276 | 330 | powered = False |
|
0 commit comments