Skip to content

Commit 52f37b2

Browse files
committed
Added check to exit early if no existing instances of xlet are found.
Added logic to handle adding and removing xlet while xlet-settings is open. - Arrows will be hidden if number of instances is less than 2 - Arrows will be shown if number of instances is greater than 1 - Next instance will be selected if currently selected xlet is removed - xlet-settings will exit if last instance of xlet is removed. - Corresponding setting pages are created/removed when xlets are created/removed
1 parent 1201332 commit 52f37b2

File tree

2 files changed

+170
-71
lines changed

2 files changed

+170
-71
lines changed

files/usr/share/cinnamon/cinnamon-settings/bin/JsonSettingsWidgets.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def set_object_value(self, info, value):
134134
def check_settings(self, *args):
135135
old_settings = self.settings
136136
self.settings = self.get_settings()
137-
137+
if self.settings is None: return
138138
for key in self.bindings:
139139
new_value = self.settings[key]["value"]
140140
if new_value != old_settings[key]["value"]:
@@ -148,9 +148,12 @@ def check_settings(self, *args):
148148
callback(key, new_value)
149149

150150
def get_settings(self):
151-
file = open(self.filepath)
152-
raw_data = file.read()
153-
file.close()
151+
try:
152+
file = open(self.filepath)
153+
raw_data = file.read()
154+
file.close()
155+
except FileNotFoundError:
156+
return
154157
try:
155158
settings = json.loads(raw_data, object_pairs_hook=collections.OrderedDict)
156159
except:

files/usr/share/cinnamon/cinnamon-settings/xlet-settings.py

Lines changed: 163 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,21 @@ def __init__(self, args):
9595
self.type = args.type
9696
self.uuid = args.uuid
9797
self.tab = 0
98+
self.instance_info = []
9899
self.instance_id = str(args.id)
99100
if args.tab is not None:
100101
self.tab = int(args.tab)
101102

102103
self.selected_instance = None
103104
self.gsettings = Gio.Settings.new("org.cinnamon")
105+
self.monitors = {}
106+
self.g_directories = []
104107
self.custom_modules = {}
108+
if self.type == "applet": changed_key = "enabled-applets"
109+
elif self.type == "desklet": changed_key = "enabled-desklets"
110+
else: changed_key = None
111+
if changed_key:
112+
self.gsettings.connect("changed::" + changed_key, lambda *args: self.on_enabled_xlets_changed(changed_key, *args))
105113

106114
self.load_xlet_data()
107115
self.build_window()
@@ -128,7 +136,7 @@ def _on_proxy_ready (self, obj, result, data=None):
128136
proxy = None
129137

130138
if proxy:
131-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], True)
139+
self.highlight_xlet(self.selected_instance, True)
132140

133141
def load_xlet_data (self):
134142
self.xlet_dir = "/usr/share/cinnamon/%ss/%s" % (self.type, self.uuid)
@@ -242,18 +250,22 @@ def check_sizing(widget, data=None):
242250
self.next_button.connect("clicked", self.next_instance)
243251

244252
def load_instances(self):
245-
self.instance_info = []
246253
path = Path(os.path.join(settings_dir, self.uuid))
247254
old_path = Path("%s/.cinnamon/configs/%s" % (home, self.uuid))
248-
instances = 0
255+
for p in path, old_path:
256+
if not p.exists(): continue
257+
self.g_directories.append(Gio.File.new_for_path(str(p)))
258+
249259
new_items = os.listdir(path) if path.exists() else []
250260
old_items = os.listdir(old_path) if old_path.exists() else []
251261
dir_items = sorted(new_items + old_items)
262+
252263
try:
253264
multi_instance = int(self.xlet_meta["max-instances"]) != 1
254265
except (KeyError, ValueError):
255266
multi_instance = False
256267

268+
enabled = [x.split(":") for x in self.gsettings.get_strv('enabled-%ss' % self.type)]
257269
for item in dir_items:
258270
# ignore anything that isn't json
259271
if item[-5:] != ".json":
@@ -271,66 +283,82 @@ def load_instances(self):
271283
continue # multi-instance should have file names of the form [instance-id].json
272284

273285
instance_exists = False
274-
enabled = self.gsettings.get_strv('enabled-%ss' % self.type)
275286
for definition in enabled:
276-
if self.uuid in definition and instance_id in definition.split(':'):
287+
if self.uuid in definition and instance_id in definition:
277288
instance_exists = True
278289
break
279290

280291
if not instance_exists:
281292
continue
282293

283-
settings = JSONSettingsHandler(os.path.join(path if item in new_items else old_path, item), self.notify_dbus)
284-
settings.instance_id = instance_id
285-
instance_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
286-
self.instance_stack.add_named(instance_box, instance_id)
287-
288-
info = {"settings": settings, "id": instance_id}
289-
self.instance_info.append(info)
294+
config_path = os.path.join(path if item in new_items else old_path, item)
295+
self.create_settings_page(config_path)
296+
297+
if not self.instance_info:
298+
print(f"No instances were found for {self.uuid}. Exiting...")
299+
sys.exit()
300+
301+
self.next_button.set_no_show_all(True)
302+
self.prev_button.set_no_show_all(True)
303+
self.show_prev_next_buttons() if self.has_multiple_instances() else self.hide_prev_next_buttons()
304+
305+
def create_settings_page(self, config_path):
306+
instance_id = os.path.basename(config_path)[:-5]
307+
if self.instance_stack.get_child_by_name(instance_id) is not None: return
308+
settings = JSONSettingsHandler(config_path, self.notify_dbus)
309+
settings.instance_id = instance_id
310+
instance_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
311+
self.instance_stack.add_named(instance_box, instance_id)
312+
info = {"settings": settings, "id": instance_id}
313+
self.instance_info.append(info)
314+
settings_map = settings.get_settings()
315+
first_key = next(iter(settings_map.values()))
290316

291-
settings_map = settings.get_settings()
292-
first_key = next(iter(settings_map.values()))
317+
try:
318+
for setting in settings_map:
319+
if setting == "__md5__":
320+
continue
321+
for key in settings_map[setting]:
322+
if key in ("description", "tooltip", "units"):
323+
try:
324+
settings_map[setting][key] = translate(self.uuid, settings_map[setting][key])
325+
except (KeyError, ValueError):
326+
traceback.print_exc()
327+
elif key in "options":
328+
new_opt_data = collections.OrderedDict()
329+
opt_data = settings_map[setting][key]
330+
for option in opt_data:
331+
if opt_data[option] == "custom":
332+
continue
333+
new_opt_data[translate(self.uuid, option)] = opt_data[option]
334+
settings_map[setting][key] = new_opt_data
335+
elif key in "columns":
336+
columns_data = settings_map[setting][key]
337+
for column in columns_data:
338+
column["title"] = translate(self.uuid, column["title"])
339+
finally:
340+
# if a layout is not explicitly defined, generate the settings
341+
# widgets based on the order they occur
342+
if first_key["type"] == "layout":
343+
self.build_with_layout(settings_map, info, instance_box, first_key)
344+
else:
345+
self.build_from_order(settings_map, info, instance_box, first_key)
293346

294-
try:
295-
for setting in settings_map:
296-
if setting == "__md5__":
297-
continue
298-
for key in settings_map[setting]:
299-
if key in ("description", "tooltip", "units"):
300-
try:
301-
settings_map[setting][key] = translate(self.uuid, settings_map[setting][key])
302-
except (KeyError, ValueError):
303-
traceback.print_exc()
304-
elif key in "options":
305-
new_opt_data = collections.OrderedDict()
306-
opt_data = settings_map[setting][key]
307-
for option in opt_data:
308-
if opt_data[option] == "custom":
309-
continue
310-
new_opt_data[translate(self.uuid, option)] = opt_data[option]
311-
settings_map[setting][key] = new_opt_data
312-
elif key in "columns":
313-
columns_data = settings_map[setting][key]
314-
for column in columns_data:
315-
column["title"] = translate(self.uuid, column["title"])
316-
finally:
317-
# if a layout is not explicitly defined, generate the settings
318-
# widgets based on the order they occur
319-
if first_key["type"] == "layout":
320-
self.build_with_layout(settings_map, info, instance_box, first_key)
321-
else:
322-
self.build_from_order(settings_map, info, instance_box, first_key)
347+
if self.selected_instance is None:
348+
self.selected_instance = info
349+
if "stack" in info:
350+
self.stack_switcher.set_stack(info["stack"])
323351

324-
if self.selected_instance is None:
325-
self.selected_instance = info
326-
if "stack" in info:
327-
self.stack_switcher.set_stack(info["stack"])
352+
def has_multiple_instances(self):
353+
return len(self.instance_info) > 1
328354

329-
instances += 1
355+
def hide_prev_next_buttons(self):
356+
self.prev_button.hide()
357+
self.next_button.hide()
330358

331-
if instances < 2:
332-
self.prev_button.set_no_show_all(True)
333-
self.next_button.set_no_show_all(True)
359+
def show_prev_next_buttons(self):
360+
self.prev_button.show()
361+
self.next_button.show()
334362

335363
def build_with_layout(self, settings_map, info, box, first_key):
336364
layout = first_key
@@ -460,26 +488,95 @@ def set_instance(self, info):
460488
else:
461489
info["stack"].set_visible_child(children[0])
462490
if proxy:
463-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False)
464-
proxy.highlightXlet('(ssb)', self.uuid, info["id"], True)
491+
old_info = self.selected_instance
492+
new_info = info
493+
self.highlight_xlet(old_info, False)
494+
self.highlight_xlet(new_info, True)
465495
self.selected_instance = info
466496

497+
def highlight_xlet(self, info, highlighted):
498+
try:
499+
proxy.highlightXlet('(ssb)', self.uuid, info["id"], highlighted)
500+
except:
501+
return
502+
467503
def previous_instance(self, *args):
468-
self.instance_stack.set_transition_type(Gtk.StackTransitionType.OVER_RIGHT)
469-
index = self.instance_info.index(self.selected_instance)
470-
self.set_instance(self.instance_info[index-1])
504+
self.get_next_instance(False)
471505

472506
def next_instance(self, *args):
473-
self.instance_stack.set_transition_type(Gtk.StackTransitionType.OVER_LEFT)
474-
index = self.instance_info.index(self.selected_instance)
475-
if index == len(self.instance_info) - 1:
476-
index = 0
477-
else:
478-
index +=1
479-
self.set_instance(self.instance_info[index])
507+
self.get_next_instance()
508+
509+
def get_next_instance(self, positive_direction = True):
510+
transition = Gtk.StackTransitionType.OVER_LEFT if positive_direction else Gtk.StackTransitionType.OVER_RIGHT
511+
self.instance_stack.set_transition_type(transition)
512+
step = 1 if positive_direction else -1
513+
instances_length = len(self.instance_info)
514+
start = self.instance_info.index(self.selected_instance)
515+
nextIndex = (start + step) % instances_length
516+
self.set_instance(self.instance_info[nextIndex])
517+
518+
def on_enabled_xlets_changed(self, key, *args):
519+
"""
520+
Args:
521+
key ("enabled-applets"|"enabled-desklets")
522+
"""
523+
current_ids = {info["id"] for info in self.instance_info}
524+
new_ids = set()
525+
for definition in self.gsettings.get_strv(key):
526+
definition = definition.split(":")
527+
uuid, instance_id = (definition[-2], definition[-1]) if key == "enabled-applets"\
528+
else (definition[0], definition[1])
529+
if uuid != self.uuid: continue
530+
new_ids.add(instance_id)
531+
added_ids = new_ids - current_ids
532+
533+
removed_indices = []
534+
selected_removed_index = -1
535+
for i, info in enumerate(self.instance_info):
536+
if info["id"] in new_ids: continue
537+
removed_indices.append(i)
538+
if info == self.selected_instance: selected_removed_index = i
539+
540+
if len(current_ids) + len(added_ids) == len(removed_indices):
541+
self.quit()
542+
return
543+
544+
for id in added_ids:
545+
for dir in self.g_directories:
546+
file = dir.get_child(id + ".json")
547+
if file.query_exists(None):
548+
self.create_new_settings_page(file.get_path())
549+
continue
550+
# Config files have not been added yet, need to monitor directories
551+
monitor = dir.monitor_directory(Gio.FileMonitorFlags.NONE, None)
552+
monitor.connect("changed", self.on_config_file_added)
553+
self.monitors.setdefault(id, []).append(monitor)
554+
555+
if (selected_removed_index != -1):
556+
self.get_next_instance()
557+
558+
for index in sorted(removed_indices, reverse=True):
559+
self.monitors.get(self.instance_info[index]["id"], []).clear()
560+
self.instance_stack.remove(self.instance_stack.get_child_by_name(self.instance_info[index]["id"]))
561+
self.instance_info.pop(index)
480562

481-
# def unpack_args(self, args):
482-
# args = {}
563+
if not self.has_multiple_instances(): self.hide_prev_next_buttons()
564+
565+
def on_config_file_added(self, *args):
566+
file, event_type = args[1], args[-1]
567+
instance = file.get_basename()[:-5]
568+
if event_type != Gio.FileMonitorEvent.CHANGES_DONE_HINT : return
569+
if instance not in self.monitors: return
570+
for monitor in self.monitors[instance]: monitor.cancel()
571+
del self.monitors[instance]
572+
self.create_new_settings_page(file.get_path())
573+
574+
575+
def create_new_settings_page(self, path):
576+
self.create_settings_page(path)
577+
self.window.show_all()
578+
if self.has_multiple_instances(): self.show_prev_next_buttons()
579+
self.highlight_xlet(self.selected_instance, True)
483580

484581
def backup(self, *args):
485582
dialog = Gtk.FileChooserDialog(_("Select or enter file to export to"),
@@ -531,8 +628,7 @@ def reload_xlet(self, *args):
531628

532629
def quit(self, *args):
533630
if proxy:
534-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False)
535-
631+
self.highlight_xlet(self.selected_instance, False)
536632
self.window.destroy()
537633
Gtk.main_quit()
538634

0 commit comments

Comments
 (0)