#!/usr/bin/env python # # Copyright (c) 2013 Stefano Karapetsas # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. # # Authors: Stefano Karapetsas import glob import optparse import os import sys # to read MateConf files import xml.etree.ElementTree as ElementTree import gi # to show dialogs gi.require_version("Gtk", "2.0") from gi.repository import Gtk # to read convert files from gi.repository import GLib # to write GSettings from gi.repository import Gio # command options parser = optparse.OptionParser() parser.add_option("-d", "--debug", dest="debug", action="store_true", help="enable debug messages") parser.add_option("-f", "--force", dest="force", action="store_true", help="force migration of settings") parser.add_option("-l", "--linuxmint", dest="linuxmint", action="store_true", help="force detection of linuxmint") (options, args) = parser.parse_args() # print a message only if debug option is enabled def debug_message(*argv): args = [] for arg in argv: args.append(str(arg)) if options.debug: print " ".join(args) # get a mateconf string list def mateconf_get_string_list(root, key): for child in root: if child.attrib['name'] == key: if child.attrib["type"] == "list": if child.attrib["ltype"] == "string": if len(list(child)) > 0: strv = [] for ch in child: for li in ch: strv.append(li.text) return strv else: return [] return None def gsettings_id(id): return id.replace("_", "-") mateconf_user_path = os.path.dirname(os.path.join(GLib.get_home_dir(), ".mateconf/")) if not os.path.exists(mateconf_user_path): #FIXME save key in GSettings debug_message(mateconf_user_path, "not found!") sys.exit(0) #FIXME check GSettings key dialog = Gtk.MessageDialog (None, Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO, "Migrate settings from MATE 1.4?") res = dialog.run() if res != Gtk.ResponseType.YES: dialog.destroy() sys.exit(0) dialog.destroy() # load .convert files list from MateConf folder convert_files = glob.glob("/usr/share/MateConf/gsettings/*") no_background = False for convert_file in convert_files: # load .convert file keyfile = GLib.KeyFile.new() GLib.KeyFile.load_from_file(keyfile, convert_file, 0) groups = GLib.KeyFile.get_groups(keyfile) # iter GSettings schemas for group in groups[0]: # each group is a GSettings schema gsettings_schema = group keys = GLib.KeyFile.get_keys(keyfile, group); settings = Gio.Settings.new(gsettings_schema) # delay settings to save them only once settings.delay() # iter GSettings keys for gsettings_key in keys[0]: # get mateconf xml file, mateconf_fullkey = GLib.KeyFile.get_string(keyfile, group, gsettings_key) mateconf_path = os.path.dirname(os.path.join(mateconf_user_path, mateconf_fullkey[1:])) mateconf_file = os.path.join(mateconf_path, "%mateconf.xml") mateconf_key = os.path.basename(mateconf_fullkey) # if user has custom mateconf if os.path.exists(mateconf_file): # MateConf file could be empty try: tree = ElementTree.parse(mateconf_file) root = tree.getroot() except: continue for child in root: # if the key exists, the user has a custom value if child.attrib['name'] == mateconf_key: # boolean if child.attrib["type"] == "bool": if child.attrib["value"] == "true": debug_message(gsettings_schema, gsettings_key, True) settings.set_boolean(gsettings_key, True) else: debug_message(gsettings_schema, gsettings_key, False) settings.set_boolean(gsettings_key, False) # int elif child.attrib["type"] == "int": debug_message(gsettings_schema, gsettings_key, int(child.attrib["value"])) settings.set_int(gsettings_key, int(child.attrib["value"])) # string elif child.attrib["type"] == "string": value = child[0].text # hack for background if gsettings_schema == "org.mate.background" and gsettings_key == "picture-filename": # check if the background exists before apply it if not os.path.exists(value): if "/pixmaps/backgrounds/" in value: value = value.replace("/pixmaps/backgrounds/", "/backgrounds/") if not os.path.exists(value): no_background = True continue else: no_background = True continue if no_background and gsettings_schema == "org.mate.background": continue if value != None: debug_message(gsettings_schema, gsettings_key, value) settings.set_string(gsettings_key, value) # list elif child.attrib["type"] == "list": # list of strings if child.attrib["ltype"] == "string": if len(list(child)) > 0: strv = [] for ch in child: for li in ch: strv.append(li.text) debug_message(gsettings_schema, gsettings_key, strv) settings.set_strv(gsettings_key, strv) else: debug_message(gsettings_schema, gsettings_key, "[]") settings.set_strv(gsettings_key, []) else: debug_message("Unknown list type", child.attrib["ltype"]) else: debug_message("Unknown type", child.attrib["type"]) settings.apply() # ugly hack to migrate panel layout is_linuxmint = os.path.exists("/etc/linuxmint/info") or options.linuxmint mateconf_panel_file = os.path.join(mateconf_user_path, "apps/panel/general", "%mateconf.xml") if os.path.exists(mateconf_panel_file): # load list of panels tree = ElementTree.parse(mateconf_panel_file) root = tree.getroot() # old lists toplevels = mateconf_get_string_list (root, "toplevel_id_list") objects = mateconf_get_string_list (root, "object_id_list") applets = mateconf_get_string_list (root, "applet_id_list") panel_settings = Gio.Settings.new("org.mate.panel") panel_settings.delay() # new lists (applets are together with objects in 1.6) new_toplevels = [] new_objects = [] new_applets_without_toplevel = [] new_bottom_toplevel = None new_top_toplevel = None # import objects for obj in objects: debug_message("object", obj) mateconf_obj_file = os.path.join(mateconf_user_path, "apps/panel/objects", obj, "%mateconf.xml") if not os.path.exists(mateconf_obj_file): debug_message ("object", obj, "mateconf file not found!") continue try: obj_tree = ElementTree.parse(mateconf_obj_file) obj_root = obj_tree.getroot() except: debug_message ("object", obj, "mateconf file invalid!") continue obj_id = gsettings_id(obj) obj_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % obj_id) new_objects.append(obj_id) for child in obj_root: # toplevel-id if child.attrib["name"] == "toplevel_id": obj_settings.set_string("toplevel-id", gsettings_id(child[0].text)) # object-type elif child.attrib["name"] == "object_type": obj_type = child[0].text # changed types in 1.6 if obj_type == "launcher-object": obj_type = "launcher" elif obj_type == "action-applet": obj_type = "action" elif obj_type == "drawer-object": obj_type = "drawer" obj_settings.set_string("object-type", obj_type) # other settings elif "type" in child.attrib: name = gsettings_id(child.attrib["name"]) # boolean if child.attrib["type"] == "bool": if child.attrib["value"] == "true": obj_settings.set_boolean(name, True) else: obj_settings.set_boolean(name, False) # int elif child.attrib["type"] == "int": obj_settings.set_int(name, int(child.attrib["value"])) # string elif child.attrib["type"] == "string": obj_settings.set_string(name, child[0].text) # import applets for app in applets: debug_message("applet", app) mateconf_app_file = os.path.join(mateconf_user_path, "apps/panel/applets", app, "%mateconf.xml") if not os.path.exists(mateconf_app_file): debug_message ("applet", app, "mateconf file not found!") try: app_tree = ElementTree.parse(mateconf_app_file) app_root = app_tree.getroot() except: debug_message ("applet", app, "mateconf file invalid!") # empty mateconf file, try to create an applet # FIXME: we should add default applets with their values # this means use default 1.4 layout, except for linuxmint # (that should be the only distribution with a custom panel layout) applet_iid = None right_stick = False position = 0 # if you add an applet here, add also the same iid couple later if "showdesktop" in app: applet_iid = "WnckletFactory::ShowDesktopApplet" elif "notification" in app: applet_iid = "NotificationAreaAppletFactory::NotificationArea" right_stick = True position = 10 elif "clock" in app: applet_iid = "ClockAppletFactory::ClockApplet" right_stick = True elif "windowlist" in app: applet_iid = "WnckletFactory::WindowListApplet" position = 10 elif "mintmenu" in app: applet_iid = "MintMenuAppletFactory::MintMenuApplet" if applet_iid != None: debug_message ("applet", app, "try to create as '%s'" % applet_iid) app_id = gsettings_id(app) app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id) new_objects.append(app_id) new_applets_without_toplevel.append(app_id) app_settings.set_string("object-type", "applet") app_settings.set_string("applet-iid", applet_iid) app_settings.set_boolean("panel-right-stick", right_stick) app_settings.set_int("position", position) continue app_id = gsettings_id(app) app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id) new_objects.append(app_id) applet_iid = None toplevel_id = None app_type = None for child in app_root: # toplevel-id if child.attrib["name"] == "toplevel_id": toplevel_id = gsettings_id(child[0].text) app_settings.set_string("toplevel-id", toplevel_id) # object-type elif child.attrib["name"] == "object_type": app_type = child[0].text if app_type == "external-applet": app_type = "applet" elif app_type == "matecomponent-applet": app_type = "applet" app_settings.set_string("object-type", app_type) # applet-iid elif child.attrib["name"] == "applet_iid": applet_iid = child[0].text # changed iids in 1.6 if applet_iid == "OAFIID:SensorsApplet": applet_iid = "SensorsAppletFactory::SensorsApplet" elif applet_iid == "OAFIID:MATE_NetspeedApplet": applet_iid = "NetspeedAppletFactory::NetspeedApplet" elif applet_iid == "OAFIID:MATE_DictionaryApplet": applet_iid = "DictionaryAppletFactory::DictionaryApplet" elif applet_iid == "OAFIID:Invest_Applet": applet_iid = "InvestAppletFactory::InvestApplet" elif applet_iid == "OAFIID:TimerApplet": # not yet ready in 1.6 :( applet_iid = "TimerAppletFactory::TimerApplet" debug_message ("applet", app, "is '%s'" % applet_iid) app_settings.set_string("applet-iid", applet_iid) # other settings elif "type" in child.attrib: name = gsettings_id(child.attrib["name"]) # boolean if child.attrib["type"] == "bool": if child.attrib["value"] == "true": app_settings.set_boolean(name, True) else: app_settings.set_boolean(name, False) # int elif child.attrib["type"] == "int": app_settings.set_int(name, int(child.attrib["value"])) # string elif child.attrib["type"] == "string": app_settings.set_string(name, child[0].text) # applet-iid could be missing if it is a default applet, but the # user changes some values of it (for example, position) if applet_iid == None: if "showdesktop" in app: applet_iid = "WnckletFactory::ShowDesktopApplet" elif "notification" in app: applet_iid = "NotificationAreaAppletFactory::NotificationArea" elif "clock" in app: applet_iid = "ClockAppletFactory::ClockApplet" elif "windowlist" in app: applet_iid = "WnckletFactory::WindowListApplet" elif "mintmenu" in app: applet_iid = "MintMenuAppletFactory::MintMenuApplet" if applet_iid != None: debug_message ("applet", app, "id was missing: '%s'" % applet_iid) app_settings.set_string("applet-iid", applet_iid) if toplevel_id == None: new_applets_without_toplevel.append(app_id) if app_type == None: app_settings.set_string("object-type", "applet") # import panels for toplevel in toplevels: debug_message("toplevel", toplevel) mateconf_toplevel_file = os.path.join(mateconf_user_path, "apps/panel/toplevels", toplevel, "%mateconf.xml") if not os.path.exists(mateconf_toplevel_file): debug_message ("toplevel", toplevel, "mateconf file not found!") try: toplevel_tree = ElementTree.parse(mateconf_toplevel_file) toplevel_root = toplevel_tree.getroot() except: # empty mateconf file, it's a default panel debug_message ("toplevel", toplevel, "mateconf file invalid, creating as default!") toplevel_id = gsettings_id(toplevel) toplevel_settings = Gio.Settings.new_with_path("org.mate.panel.toplevel", "/org/mate/panel/toplevels/%s/" % toplevel_id) if is_linuxmint: toplevel_settings.set_string("orientation", "bottom") else: toplevel_settings.set_string("orientation", "top") new_toplevels.append(toplevel_id) continue toplevel_id = gsettings_id(toplevel) toplevel_settings = Gio.Settings.new_with_path("org.mate.panel.toplevel", "/org/mate/panel/toplevels/%s/" % toplevel_id) new_toplevels.append(toplevel_id) for child in toplevel_root: if "type" in child.attrib: name = gsettings_id(child.attrib["name"]) if name == "orientation": if child[0].text == "top": new_top_toplevel = toplevel_id elif child[0].text == "bottom": new_bottom_toplevel = toplevel_id # boolean if child.attrib["type"] == "bool": if child.attrib["value"] == "true": toplevel_settings.set_boolean(name, True) else: toplevel_settings.set_boolean(name, False) # int elif child.attrib["type"] == "int": toplevel_settings.set_int(name, int(child.attrib["value"])) # string elif child.attrib["type"] == "string": toplevel_settings.set_string(name, child[0].text) # set created applets to first toplevel for app_id in new_applets_without_toplevel: app_settings = Gio.Settings.new_with_path("org.mate.panel.object", "/org/mate/panel/objects/%s/" % app_id) if is_linuxmint and (new_bottom_toplevel != None): app_settings.set_string("toplevel-id", new_bottom_toplevel) else: app_settings.set_string("toplevel-id", new_toplevels[0]) # set new toplevels and objects panel_settings.set_strv("toplevel-id-list", new_toplevels) panel_settings.set_strv("object-id-list", new_objects) # apply settings! panel_settings.apply() # success! dialog = Gtk.MessageDialog (None, Gtk.DialogFlags.MODAL, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, "MATE 1.4 settings migration completed!") dialog.run() dialog.destroy()