Skip to content

Commit 069df70

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 069df70

File tree

2 files changed

+167
-71
lines changed

2 files changed

+167
-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: 160 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,17 @@ 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+
changed_key = "enabled-applets" if self.type == "applet" else "enabled-desklets"
107+
self.gsettings.connect("changed::" + changed_key, lambda *args: self.on_enabled_xlets_changed(changed_key, *args))
108+
self.g_directories = []
104109
self.custom_modules = {}
105110

106111
self.load_xlet_data()
@@ -128,7 +133,7 @@ def _on_proxy_ready (self, obj, result, data=None):
128133
proxy = None
129134

130135
if proxy:
131-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], True)
136+
self.highlight_xlet(self.selected_instance, True)
132137

133138
def load_xlet_data (self):
134139
self.xlet_dir = "/usr/share/cinnamon/%ss/%s" % (self.type, self.uuid)
@@ -242,18 +247,22 @@ def check_sizing(widget, data=None):
242247
self.next_button.connect("clicked", self.next_instance)
243248

244249
def load_instances(self):
245-
self.instance_info = []
246250
path = Path(os.path.join(settings_dir, self.uuid))
247251
old_path = Path("%s/.cinnamon/configs/%s" % (home, self.uuid))
248-
instances = 0
252+
for p in path, old_path:
253+
if not p.exists(): continue
254+
self.g_directories.append(Gio.File.new_for_path(str(p)))
255+
249256
new_items = os.listdir(path) if path.exists() else []
250257
old_items = os.listdir(old_path) if old_path.exists() else []
251258
dir_items = sorted(new_items + old_items)
259+
252260
try:
253261
multi_instance = int(self.xlet_meta["max-instances"]) != 1
254262
except (KeyError, ValueError):
255263
multi_instance = False
256264

265+
enabled = [x.split(":") for x in self.gsettings.get_strv('enabled-%ss' % self.type)]
257266
for item in dir_items:
258267
# ignore anything that isn't json
259268
if item[-5:] != ".json":
@@ -271,66 +280,82 @@ def load_instances(self):
271280
continue # multi-instance should have file names of the form [instance-id].json
272281

273282
instance_exists = False
274-
enabled = self.gsettings.get_strv('enabled-%ss' % self.type)
275283
for definition in enabled:
276-
if self.uuid in definition and instance_id in definition.split(':'):
284+
if self.uuid in definition and instance_id in definition:
277285
instance_exists = True
278286
break
279287

280288
if not instance_exists:
281289
continue
282290

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

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

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)
344+
if self.selected_instance is None:
345+
self.selected_instance = info
346+
if "stack" in info:
347+
self.stack_switcher.set_stack(info["stack"])
323348

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"])
349+
def has_multiple_instances(self):
350+
return len(self.instance_info) > 1
328351

329-
instances += 1
352+
def hide_prev_next_buttons(self):
353+
self.prev_button.hide()
354+
self.next_button.hide()
330355

331-
if instances < 2:
332-
self.prev_button.set_no_show_all(True)
333-
self.next_button.set_no_show_all(True)
356+
def show_prev_next_buttons(self):
357+
self.prev_button.show()
358+
self.next_button.show()
334359

335360
def build_with_layout(self, settings_map, info, box, first_key):
336361
layout = first_key
@@ -460,26 +485,95 @@ def set_instance(self, info):
460485
else:
461486
info["stack"].set_visible_child(children[0])
462487
if proxy:
463-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False)
464-
proxy.highlightXlet('(ssb)', self.uuid, info["id"], True)
488+
old_info = self.selected_instance
489+
new_info = info
490+
self.highlight_xlet(old_info, False)
491+
self.highlight_xlet(new_info, True)
465492
self.selected_instance = info
466493

494+
def highlight_xlet(self, info, highlighted):
495+
try:
496+
proxy.highlightXlet('(ssb)', self.uuid, info["id"], highlighted)
497+
except:
498+
return
499+
467500
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])
501+
self.get_next_instance(False)
471502

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

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

484578
def backup(self, *args):
485579
dialog = Gtk.FileChooserDialog(_("Select or enter file to export to"),
@@ -531,8 +625,7 @@ def reload_xlet(self, *args):
531625

532626
def quit(self, *args):
533627
if proxy:
534-
proxy.highlightXlet('(ssb)', self.uuid, self.selected_instance["id"], False)
535-
628+
self.highlight_xlet(self.selected_instance, False)
536629
self.window.destroy()
537630
Gtk.main_quit()
538631

0 commit comments

Comments
 (0)