You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

303 lines
13 KiB

  1. #!/usr/bin/env python
  2. import sys
  3. try:
  4. import os
  5. import glob
  6. import gettext
  7. import json
  8. import collections
  9. import XletSettingsWidgets
  10. import dbus
  11. from SettingsWidgets import SectionBg
  12. from gi.repository import Gio, Gtk, GObject, GdkPixbuf
  13. except Exception, detail:
  14. print detail
  15. sys.exit(1)
  16. home = os.path.expanduser("~")
  17. class XletSetting:
  18. def __init__(self, uuid, parent, _type):
  19. self.parent = parent
  20. self.type = _type
  21. self.current_id = None
  22. self.builder = Gtk.Builder()
  23. self.builder.add_from_file("/usr/lib/cinnamon-settings/bin/xlet-settings.ui")
  24. self.content = self.builder.get_object("content")
  25. self.back_to_list_button = self.builder.get_object("back_to_list")
  26. self.highlight_button = self.builder.get_object("highlight_button")
  27. self.more_button = self.builder.get_object("more_button")
  28. self.remove_button = self.builder.get_object("remove_xlet")
  29. self.uuid = uuid
  30. self.content.connect("hide", self.on_hide)
  31. self.applet_meta = {}
  32. self.applet_settings = collections.OrderedDict()
  33. self.setting_factories = collections.OrderedDict()
  34. self.load_applet_data (self.uuid)
  35. if "icon" in self.applet_meta:
  36. image = Gtk.Image().new_from_icon_name(self.applet_meta["icon"], Gtk.IconSize.BUTTON)
  37. self.back_to_list_button.set_image(image)
  38. self.back_to_list_button.get_property('image').set_padding(5, 0)
  39. self.back_to_list_button.set_label(self.applet_meta["name"])
  40. self.back_to_list_button.set_tooltip_text(_("Back to list"))
  41. self.more_button.set_tooltip_text(_("More actions..."))
  42. self.remove_button.set_tooltip_text(_("Remove the current instance of this %s") % self.type)
  43. self.highlight_button.set_tooltip_text(_("Momentarily highlight the %s on your desktop") % self.type)
  44. if len(self.applet_settings.keys()) > 1:
  45. self.build_notebook()
  46. else:
  47. self.build_single()
  48. self.back_to_list_button.connect("clicked", self.on_back_to_list_button_clicked)
  49. if self.type != "extension":
  50. self.highlight_button.connect("clicked", self.on_highlight_button_clicked)
  51. self.highlight_button.show()
  52. else:
  53. self.highlight_button.hide()
  54. self.more_button.connect("clicked", self.on_more_button_clicked)
  55. self.remove_button.connect("clicked", self.on_remove_button_clicked)
  56. def show (self):
  57. self.content.show_all()
  58. try:
  59. self.back_to_list_button.get_property('image').show()
  60. except:
  61. pass
  62. def on_hide (self, widget):
  63. self.content.hide()
  64. self.content.destroy()
  65. self.applet_meta = None
  66. self.applet_settings = None
  67. for _id in self.setting_factories.keys():
  68. self.setting_factories[_id].pause_monitor()
  69. self.setting_factories = None
  70. def load_applet_data (self, uuid):
  71. found = self.get_meta_data_for_applet("/usr/share/cinnamon/%ss/%s" % (self.type, uuid))
  72. if not found:
  73. found = self.get_meta_data_for_applet("%s/.local/share/cinnamon/%ss/%s" % (home, self.type, uuid))
  74. if not found:
  75. print("Could not find %s metadata - are you sure it's installed correctly?" % self.type)
  76. return
  77. found = self.get_settings_for_applet("%s/.cinnamon/configs/%s" % (home, uuid))
  78. if not found:
  79. print("Could not find any instance settings data for this %s - are you sure it is loaded, and supports settings?" % self.type)
  80. def get_meta_data_for_applet(self, path):
  81. if os.path.exists(path) and os.path.isdir(path):
  82. if os.path.exists("%s/metadata.json" % path):
  83. raw_data = open("%s/metadata.json" % path).read()
  84. self.applet_meta = json.loads(raw_data.decode('utf-8'))
  85. return True
  86. return False
  87. def get_settings_for_applet(self, path):
  88. if "max-instances" in self.applet_meta:
  89. self.multi_instance = int(self.applet_meta["max-instances"]) > 1
  90. else:
  91. self.multi_instance = False
  92. if os.path.exists(path) and os.path.isdir(path):
  93. instances = os.listdir(path)
  94. if len(instances) != 0:
  95. for instance in instances:
  96. raw_data = open("%s/%s" % (path, instance)).read()
  97. try:
  98. js = json.loads(raw_data.decode('utf-8'), object_pairs_hook=collections.OrderedDict)
  99. except:
  100. raise Exception("Failed to parse settings JSON data for %s %s" % (self.type, self.uuid))
  101. instance_id = instance.split(".json")[0]
  102. self.applet_settings[instance_id] = js
  103. self.setting_factories[instance_id] = XletSettingsWidgets.Factory("%s/%s" % (path, instance), instance_id, self.multi_instance, self.uuid)
  104. return True
  105. else:
  106. raise Exception("Could not find any active setting files for %s %s" % (self.type, self.uuid))
  107. return False
  108. def build_single(self):
  109. self.nb = None
  110. self.view = SectionBg()
  111. self.content_box = Gtk.VBox()
  112. self.view.add(self.content_box)
  113. self.content_box.set_border_width(5)
  114. for instance_key in self.applet_settings.keys():
  115. for setting_key in self.applet_settings[instance_key].keys():
  116. if setting_key == "__md5__" or self.applet_settings[instance_key][setting_key]["type"] == "generic":
  117. continue
  118. self.setting_factories[instance_key].create(setting_key,
  119. self.applet_settings[instance_key][setting_key]["type"],
  120. self.uuid)
  121. widgets = self.setting_factories[instance_key].widgets
  122. for widget_key in widgets.keys():
  123. if widgets[widget_key].get_indented():
  124. indent = XletSettingsWidgets.IndentedHBox()
  125. indent.add_fill(widgets[widget_key])
  126. self.content_box.pack_start(indent, False, False, 2)
  127. else:
  128. self.content_box.pack_start(widgets[widget_key], False, False, 2)
  129. if len(widgets[widget_key].dependents) > 0:
  130. widgets[widget_key].update_dependents()
  131. self.content.pack_start(self.view, True, True, 2)
  132. self.current_id = instance_key
  133. def build_notebook(self):
  134. self.nb = Gtk.Notebook()
  135. i = 0
  136. target_instance = -1
  137. target_page = -1
  138. if len(sys.argv) > 3:
  139. target_instance = sys.argv[3]
  140. for instance_key in self.applet_settings.keys():
  141. view = Gtk.ScrolledWindow()
  142. content_box = Gtk.VBox()
  143. view.add_with_viewport(content_box)
  144. content_box.set_border_width(5)
  145. for setting_key in self.applet_settings[instance_key].keys():
  146. if setting_key == "__md5__" or self.applet_settings[instance_key][setting_key]["type"] == "generic":
  147. continue
  148. self.setting_factories[instance_key].create(setting_key,
  149. self.applet_settings[instance_key][setting_key]["type"],
  150. self.uuid)
  151. widgets = self.setting_factories[instance_key].widgets
  152. for widget_key in widgets.keys():
  153. if widgets[widget_key].get_indented():
  154. indent = XletSettingsWidgets.IndentedHBox()
  155. indent.add_fill(widgets[widget_key])
  156. content_box.pack_start(indent, False, False, 2)
  157. else:
  158. content_box.pack_start(widgets[widget_key], False, False, 2)
  159. if len(widgets[widget_key].dependents) > 0:
  160. widgets[widget_key].update_dependents()
  161. view.show()
  162. self.nb.append_page(view, Gtk.Label.new(_("Instance %d") % (i + 1)))
  163. view.key = instance_key
  164. if view.key == target_instance:
  165. target_page = i
  166. i += 1
  167. self.content.pack_start(self.nb, True, True, 2)
  168. self.nb.set_scrollable(True)
  169. if target_page != -1:
  170. self.nb.set_current_page(target_page)
  171. self.nb.connect("switch-page", self.on_page_changed)
  172. def on_page_changed(self, nb, page, num):
  173. self.current_id = page.key
  174. def on_highlight_button_clicked(self, widget):
  175. session_bus = dbus.SessionBus()
  176. cinnamon_dbus = session_bus.get_object("org.Cinnamon", "/org/Cinnamon")
  177. highlight_applet = cinnamon_dbus.get_dbus_method('highlightApplet', 'org.Cinnamon')
  178. highlight_applet(self.current_id, self.multi_instance)
  179. def on_back_to_list_button_clicked(self, widget):
  180. self.parent._close_configure(self)
  181. def on_remove_button_clicked(self, widget):
  182. settings = Gio.Settings.new("org.cinnamon")
  183. if self.type == "applet":
  184. enabled_xlets = settings.get_strv("enabled-applets")
  185. elif self.type == "desklet":
  186. enabled_xlets = settings.get_strv("enabled-desklets")
  187. elif self.type == "extension":
  188. enabled_xlets = settings.get_strv("enabled-extensions")
  189. else:
  190. return
  191. new_enabled = []
  192. for xlet in enabled_xlets:
  193. if self.uuid not in xlet:
  194. new_enabled.append(xlet)
  195. elif self.multi_instance and self.current_id not in xlet:
  196. new_enabled.append(xlet)
  197. if self.nb is None or (self.nb is not None and self.nb.get_n_pages() == 1):
  198. self.parent._close_configure(self)
  199. else:
  200. current_index = self.nb.get_current_page()
  201. tab = self.nb.get_nth_page(current_index)
  202. self.setting_factories[self.current_id].pause_monitor()
  203. self.nb.remove_page(current_index)
  204. tab.destroy()
  205. self.nb.set_current_page(0)
  206. if self.type == "applet":
  207. settings.set_strv("enabled-applets", new_enabled)
  208. elif self.type == "desklet":
  209. settings.set_strv("enabled-desklets", new_enabled)
  210. elif self.type == "extension":
  211. settings.set_strv("enabled-extensions", new_enabled)
  212. def on_more_button_clicked(self, widget):
  213. popup = Gtk.Menu()
  214. popup.attach_to_widget(widget, None)
  215. reset_option = Gtk.MenuItem(_("Reset to defaults"))
  216. popup.append(reset_option)
  217. reset_option.connect("activate", self.on_reset_defaults)
  218. reset_option.show()
  219. import_option = Gtk.MenuItem(_("Import from a file"))
  220. popup.append(import_option)
  221. import_option.connect("activate", self.on_import)
  222. import_option.show()
  223. export_option = Gtk.MenuItem(_("Export to a file"))
  224. popup.append(export_option)
  225. export_option.connect("activate", self.on_export)
  226. export_option.show()
  227. popup.popup(None, None, None, None, 0, 0)
  228. def on_reset_defaults(self, popup):
  229. self.setting_factories[self.current_id].reset_to_defaults()
  230. def on_import(self, popup):
  231. dialog = Gtk.FileChooserDialog(_("Select a JSON file to import"),
  232. None,
  233. Gtk.FileChooserAction.OPEN,
  234. (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  235. Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
  236. filter_text = Gtk.FileFilter()
  237. filter_text.add_pattern("*.json")
  238. filter_text.set_name(_("JSON files"))
  239. dialog.add_filter(filter_text)
  240. response = dialog.run()
  241. if response == Gtk.ResponseType.OK:
  242. filename = dialog.get_filename()
  243. self.setting_factories[self.current_id].load_from_file(filename)
  244. dialog.destroy()
  245. def on_export(self, popup):
  246. dialog = Gtk.FileChooserDialog(_("Select or enter file to export to"),
  247. None,
  248. Gtk.FileChooserAction.SAVE,
  249. (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
  250. Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
  251. dialog.set_do_overwrite_confirmation(True)
  252. filter_text = Gtk.FileFilter()
  253. filter_text.add_pattern("*.json")
  254. filter_text.set_name(_("JSON files"))
  255. dialog.add_filter(filter_text)
  256. response = dialog.run()
  257. if response == Gtk.ResponseType.ACCEPT:
  258. filename = dialog.get_filename()
  259. if ".json" not in filename:
  260. filename = filename + ".json"
  261. self.setting_factories[self.current_id].export_to_file(filename)
  262. dialog.destroy()