Exemplo n.º 1
0
    def _load_layout(self, layout_filename, color_scheme_filename):
        self.layout_dir = os.path.dirname(layout_filename)
        self.svg_cache = {}
        layout = None

        if color_scheme_filename:
            self.color_scheme = ColorScheme.load(color_scheme_filename)

        f = open(layout_filename)
        try:
            dom = minidom.parse(f).documentElement

            # check layout format
            format = self.LAYOUT_FORMAT_LEGACY
            if dom.hasAttribute("format"):
               format = Version.from_string(dom.attributes["format"].value)

            if format >= self.LAYOUT_FORMAT_LAYOUT_TREE:
                items = self._parse_dom_node(dom)
            else:
                _logger.warning(_("Loading legacy layout format '{}'. "
                            "Please consider upgrading to current format '{}'"
                            ).format(format, self.LAYOUT_FORMAT))
                items = self._parse_legacy_layout(dom)

            if items:
                layout = items[0]
        finally:
            f.close()

        self.svg_cache = {} # Free the memory
        return layout
Exemplo n.º 2
0
    def load(filename, is_system=False):
        """ Load a color scheme and return it as a new instance. """

        color_scheme = None

        f = open_utf8(filename)
        try:
            dom = minidom.parse(f).documentElement
            name = dom.attributes["name"].value

            # check layout format
            format = ColorScheme.COLOR_SCHEME_FORMAT_LEGACY
            if dom.hasAttribute("format"):
                format = Version.from_string(dom.attributes["format"].value)

            if format >= ColorScheme.COLOR_SCHEME_FORMAT_TREE:  # tree format?
                items = ColorScheme._parse_dom_node(dom, None, {})
            else:
                _logger.warning(_format( \
                    "Loading legacy color scheme format '{old_format}', "
                    "please consider upgrading to current format "
                    "'{new_format}': '{filename}'",
                    old_format = format,
                    new_format = ColorScheme.COLOR_SCHEME_FORMAT,
                    filename = filename))

                items = ColorScheme._parse_legacy_color_scheme(dom)

            if not items is None:
                root = Root()
                root.set_items(items)

                color_scheme = ColorScheme()
                color_scheme.name = name
                color_scheme._filename = filename
                color_scheme.is_system = is_system
                color_scheme._root = root
                #print(root.dumps())
        except xml.parsers.expat.ExpatError as ex:
            _logger.error(
                _format(
                    "Error loading color scheme '{filename}'. "
                    "{exception}: {cause}",
                    filename=filename,
                    exception=type(ex).__name__,
                    cause=unicode_str(ex)))
        finally:
            f.close()

        return color_scheme
Exemplo n.º 3
0
    def load(filename, is_system=False):
        """ Load a color scheme and return it as a new instance. """

        color_scheme = None

        f = open_utf8(filename)
        try:
            dom = minidom.parse(f).documentElement
            name = dom.attributes["name"].value

            # check layout format
            format = ColorScheme.COLOR_SCHEME_FORMAT_LEGACY
            if dom.hasAttribute("format"):
               format = Version.from_string(dom.attributes["format"].value)

            if format >= ColorScheme.COLOR_SCHEME_FORMAT_TREE:   # tree format?
                items = ColorScheme._parse_dom_node(dom, None, {})
            else:
                _logger.warning(_format( \
                    "Loading legacy color scheme format '{old_format}', "
                    "please consider upgrading to current format "
                    "'{new_format}': '{filename}'",
                    old_format = format,
                    new_format = ColorScheme.COLOR_SCHEME_FORMAT,
                    filename = filename))

                items = ColorScheme._parse_legacy_color_scheme(dom)

            if  not items is None:
                root = Root()
                root.set_items(items)

                color_scheme = ColorScheme()
                color_scheme.name = name
                color_scheme._filename = filename
                color_scheme.is_system = is_system
                color_scheme._root = root
                #print(root.dumps())
        except xml.parsers.expat.ExpatError as ex:
            _logger.error(_format("Error loading color scheme '{filename}'. "
                                  "{exception}: {cause}",
                                  filename = filename,
                                  exception = type(ex).__name__,
                                  cause = unicode_str(ex)))
        finally:
            f.close()

        return color_scheme
Exemplo n.º 4
0
    def _load_layout(self, layout_filename, parent_item=None):
        self._svg_cache = {}
        layout = None

        try:
            f = open_utf8(layout_filename)
        except FileNotFoundError as ex:
            _logger.warning("Failed to open '{}': {}".format(
                layout_filename, unicode_str(ex)))
            return None

        # make sure unlink is called
        with minidom.parse(f).documentElement as dom:

            # check layout format, no format version means legacy layout
            format = self.LAYOUT_FORMAT_LEGACY
            if dom.hasAttribute("format"):
                format = Version.from_string(dom.attributes["format"].value)
            self._format = format

            root = LayoutPanel()  # root, representing the 'keyboard' tag
            root.set_id("__root__")  # id for debug prints

            # Init included root with the parent item's svg filename.
            # -> Allows to skip specifying svg filenames in includes.
            if parent_item:
                root.filename = parent_item.filename

            if format >= self.LAYOUT_FORMAT_LAYOUT_TREE:
                self._parse_dom_node(dom, root)
                layout = root
            else:
                _logger.warning(
                    _format(
                        "Loading legacy layout, format '{}'. "
                        "Please consider upgrading to current format '{}'",
                        format, self.LAYOUT_FORMAT))
                items = self._parse_legacy_layout(dom)
                if items:
                    root.set_items(items)
                    layout = root

        f.close()

        self._svg_cache = {}  # Free the memory
        return layout
Exemplo n.º 5
0
    def load(filename, is_system=False):
        """ Load a color scheme and return it as a new object. """

        color_scheme = None

        f = open(filename)
        try:
            dom = minidom.parse(f).documentElement
            name = dom.attributes["name"].value

            # check layout format
            format = ColorScheme.COLOR_SCHEME_FORMAT_LEGACY
            if dom.hasAttribute("format"):
               format = Version.from_string(dom.attributes["format"].value)

            if format >= ColorScheme.COLOR_SCHEME_FORMAT_TREE:   # tree format?
                items = ColorScheme._parse_dom_node(dom, None, {})
            else:
                _logger.warning( \
                  _("Loading legacy color scheme format '{old_format}', "
                    "please consider upgrading to current format " \
                    "'{new_format}': '{filename}'") \
                .format(old_format = format, 
                        new_format = ColorScheme.COLOR_SCHEME_FORMAT,
                        filename = filename))

                items = ColorScheme._parse_legacy_color_scheme(dom)

            if  not items is None:
                root = Root()
                root.set_items(items)

                color_scheme = ColorScheme()
                color_scheme.name = name
                color_scheme.filename = filename
                color_scheme.is_system = is_system
                color_scheme.root = root
                #print(root.dumps())
        finally:
            f.close()

        return color_scheme
Exemplo n.º 6
0
    def _load_layout(self, layout_filename, parent_item = None):
        self._svg_cache = {}
        layout = None

        f = open_utf8(layout_filename)

        # make sure unlink is called
        with minidom.parse(f).documentElement as dom:

            # check layout format, no format version means legacy layout
            format = self.LAYOUT_FORMAT_LEGACY
            if dom.hasAttribute("format"):
               format = Version.from_string(dom.attributes["format"].value)
            self._format = format

            root = LayoutPanel() # root, representing the 'keyboard' tag
            root.set_id("__root__") # id for debug prints

            # Init included root with the parent item's svg filename.
            # -> Allows to skip specifying svg filenames in includes.
            if parent_item:
                root.filename = parent_item.filename

            if format >= self.LAYOUT_FORMAT_LAYOUT_TREE:
                self._parse_dom_node(dom, root)
                layout = root
            else:
                _logger.warning(_format("Loading legacy layout, format '{}'. "
                            "Please consider upgrading to current format '{}'",
                            format, self.LAYOUT_FORMAT))
                items = self._parse_legacy_layout(dom)
                if items:
                    root.set_items(items)
                    layout = root

        f.close()

        self._svg_cache = {} # Free the memory
        return layout
Exemplo n.º 7
0
    def copy_layout(src_filename, dst_filename):
        src_dir = os.path.dirname(src_filename)
        dst_dir, name_ext = os.path.split(dst_filename)
        dst_basename, ext = os.path.splitext(name_ext)
        _logger.info(_("copying layout '{}' to '{}'") \
                     .format(src_filename, dst_filename))

        domdoc = None
        svg_filenames = {}
        fallback_layers = {}

        with open(src_filename) as f:
            domdoc = minidom.parse(f)
            keyboard_node = domdoc.documentElement

            # check layout format
            format = KeyboardSVG.LAYOUT_FORMAT_LEGACY
            if keyboard_node.hasAttribute("format"):
               format = Version.from_string(keyboard_node.attributes["format"].value)
            keyboard_node.attributes["id"] = dst_basename

            if format < KeyboardSVG.LAYOUT_FORMAT_LAYOUT_TREE:
                raise Exceptions.LayoutFileError( \
                    _("copy_layouts failed, unsupported layout format '{}'.") \
                    .format(format))
            else:
                # replace the basename of all svg filenames
                for node in KeyboardSVG._iter_dom_nodes(keyboard_node):
                    if KeyboardSVG.is_layout_node(node):
                        if node.hasAttribute("filename"):
                            filename = node.attributes["filename"].value

                            # Create a replacement layer name for the unlikely
                            # case  that the svg-filename doesn't contain a
                            # layer section (as in path/basename-layer.ext).
                            fallback_layer_name = fallback_layers.get(filename,
                                         "Layer" + str(len(fallback_layers)))
                            fallback_layers[filename] = fallback_layer_name

                            # replace the basename of this filename
                            new_filename = KeyboardSVG._replace_basename( \
                                 filename, dst_basename, fallback_layer_name)

                            node.attributes["filename"].value = new_filename
                            svg_filenames[filename] = new_filename

        if domdoc:
            # write the new layout file
            with open(dst_filename, "w") as f:
                xml = toprettyxml(domdoc)
                f.write(xml.encode("UTF-8"))

                # copy the svg files
                for src, dst in list(svg_filenames.items()):

                    dir, name = os.path.split(src)
                    if not dir:
                        src = os.path.join(src_dir, name)
                    dir, name = os.path.split(dst)
                    if not dir:
                        dst = os.path.join(dst_dir, name)

                    _logger.info(_("copying svg file '{}' to '{}'") \
                                 .format(src, dst))
                    shutil.copyfile(src, dst)
Exemplo n.º 8
0
    def load(filename, is_system=False):
        """ Load a theme and return a new theme object. """

        result = None

        _file = open_utf8(filename)
        try:
            domdoc = minidom.parse(_file).documentElement
            try:
                theme = Theme()

                node = domdoc.attributes.get("format")
                format = Version.from_string(node.value) \
                         if node else Theme.THEME_FORMAT_INITIAL

                theme.name = domdoc.attributes["name"].value

                # "color_scheme" is the base file name of the color scheme
                text = utils.xml_get_text(domdoc, "color_scheme")
                if not text is None:
                    theme.color_scheme_basename = text

                # get key label overrides
                nodes = domdoc.getElementsByTagName("key_label_overrides")
                if nodes:
                    overrides = nodes[0]
                    tuples = {}
                    for override in overrides.getElementsByTagName("key"):
                        key_id = override.attributes["id"].value
                        node = override.attributes.get("label")
                        label = node.value if node else ""
                        node = override.attributes.get("group")
                        group = node.value if node else ""
                        tuples[key_id] = (label, group)
                    theme.key_label_overrides = tuples

                # read all other members
                for name, _type, _default in Theme.attributes:
                    if not name in [
                            "color_scheme_basename", "key_label_overrides"
                    ]:
                        value = utils.xml_get_text(domdoc, name)
                        if not value is None:

                            if _type == "i":
                                value = int(value)
                            if _type == "d":
                                value = float(value)
                            if _type == "ad":
                                value = [float(s) for s in value.split(",")]

                            # upgrade to current file format
                            if format < Theme.THEME_FORMAT_1_1:
                                # direction was    0..360, ccw
                                #        is now -180..180, cw
                                if name == "key_gradient_direction":
                                    value = -(value % 360)
                                    if value <= -180:
                                        value += 360

                            setattr(theme, name, value)

                theme._filename = filename
                theme.is_system = is_system
                theme.system_exists = is_system
                result = theme
            finally:
                domdoc.unlink()

        except (Exceptions.ThemeFileError, xml.parsers.expat.ExpatError) as ex:
            _logger.error(
                _format(
                    "Error loading theme '{filename}'. "
                    "{exception}: {cause}",
                    filename=filename,
                    exception=type(ex).__name__,
                    cause=unicode_str(ex)))
            result = None
        finally:
            _file.close()

        return result
Exemplo n.º 9
0
    def copy_layout(src_filename, dst_filename):
        src_dir = os.path.dirname(src_filename)
        dst_dir, name_ext = os.path.split(dst_filename)
        dst_basename, ext = os.path.splitext(name_ext)
        _logger.info(
            _format("copying layout '{}' to '{}'", src_filename, dst_filename))

        domdoc = None
        svg_filenames = {}
        fallback_layers = {}

        try:
            with open_utf8(src_filename) as f:
                domdoc = minidom.parse(f)
                keyboard_node = domdoc.documentElement

                # check layout format
                format = LayoutLoaderSVG.LAYOUT_FORMAT_LEGACY
                if keyboard_node.hasAttribute("format"):
                    format = Version.from_string(
                        keyboard_node.attributes["format"].value)
                keyboard_node.attributes["id"] = dst_basename

                if format < LayoutLoaderSVG.LAYOUT_FORMAT_LAYOUT_TREE:
                    raise Exceptions.LayoutFileError( \
                        _format("copy_layouts failed, unsupported layout format '{}'.",
                                format))
                else:
                    # replace the basename of all svg filenames
                    for node in LayoutLoaderSVG._iter_dom_nodes(keyboard_node):
                        if LayoutLoaderSVG.is_layout_node(node):
                            if node.hasAttribute("filename"):
                                filename = node.attributes["filename"].value

                                # Create a replacement layer name for the unlikely
                                # case  that the svg-filename doesn't contain a
                                # layer section (as in path/basename-layer.ext).
                                fallback_layer_name = fallback_layers.get(
                                    filename,
                                    "Layer" + str(len(fallback_layers)))
                                fallback_layers[filename] = fallback_layer_name

                                # replace the basename of this filename
                                new_filename = LayoutLoaderSVG._replace_basename( \
                                     filename, dst_basename, fallback_layer_name)

                                node.attributes[
                                    "filename"].value = new_filename
                                svg_filenames[filename] = new_filename

            if domdoc:
                XDGDirs.assure_user_dir_exists(config.get_user_layout_dir())

                # write the new layout file
                with open_utf8(dst_filename, "w") as f:
                    xml = toprettyxml(domdoc)
                    if sys.version_info.major == 2:  # python 2?
                        xml = xml.encode("UTF-8")
                    f.write(xml)

                    # copy the svg files
                    for src, dst in list(svg_filenames.items()):

                        dir, name = os.path.split(src)
                        if not dir:
                            src = os.path.join(src_dir, name)
                        dir, name = os.path.split(dst)
                        if not dir:
                            dst = os.path.join(dst_dir, name)

                        _logger.info(_format("copying svg file '{}' to '{}'", \
                                     src, dst))
                        shutil.copyfile(src, dst)
        except OSError as ex:
            _logger.error("copy_layout failed: " + \
                          unicode_str(ex))
        except Exceptions.LayoutFileError as ex:
            _logger.error(unicode_str(ex))
Exemplo n.º 10
0
    def load(filename, is_system=False):
        """ Load a theme and return a new theme object. """

        result = None

        _file = open(filename)
        try:
            domdoc = minidom.parse(_file).documentElement
            try:
                theme = Theme()

                node = domdoc.attributes.get("format")
                format = Version.from_string(node.value) \
                         if node else Theme.THEME_FORMAT_INITIAL

                theme.name = domdoc.attributes["name"].value

                # "color_scheme" is the base file name of the color scheme
                text = utils.xml_get_text(domdoc, "color_scheme")
                if not text is None:
                    theme.color_scheme_basename = text

                # get key label overrides
                nodes = domdoc.getElementsByTagName("key_label_overrides")
                if nodes:
                    overrides = nodes[0]
                    tuples = {}
                    for override in overrides.getElementsByTagName("key"):
                        key_id = override.attributes["id"].value
                        node = override.attributes.get("label")
                        label = node.value if node else ""
                        node = override.attributes.get("group")
                        group = node.value if node else ""
                        tuples[key_id] = (label, group)
                    theme.key_label_overrides = tuples

                # read all other members
                for name, _type, _default in Theme.attributes:
                    if not name in ["color_scheme_basename",
                                    "key_label_overrides"]:
                        value = utils.xml_get_text(domdoc, name)
                        if not value is None:

                            if _type == "i":
                                value = int(value)
                            if _type == "f":
                                value = float(value)

                            # upgrade to current file format
                            if format < Theme.THEME_FORMAT_1_1:
                                # direction was    0..360, ccw
                                #        is now -180..180, cw
                                if name == "key_gradient_direction":
                                    value = -(value % 360)
                                    if value <= -180:
                                        value += 360

                            setattr(theme, name, value)

                theme.filename = filename
                theme.is_system = is_system
                theme.system_exists = is_system
                result = theme
            except Exceptions.ThemeFileError as xxx_todo_changeme:
                (ex) = xxx_todo_changeme
                raise Exceptions.ThemeFileError(_("Error loading ")
                    + filename, chained_exception = ex)
            finally:
                domdoc.unlink()

        finally:
            _file.close()

        return result
Exemplo n.º 11
0
    def init_from_gsettings(self):
        """
        Overloaded to migrate old dconf data to a new gsettings schema
        """
        ConfigObject.init_from_gsettings(self)

        import osk
        util = osk.Util()

        def migrate_dconf_value(dconf_key, config_object, gskey):
            try:
                value = util.read_dconf_key(dconf_key)
            except (ValueError, TypeError) as e:
                value = None
                _logger.warning("migrate_dconf_value: {}".format(e))

            if not value is None:
                setattr(config_object, gskey.prop, value)
                _logger.debug("migrate_dconf_value: {key} -> {path} {gskey}, value={value}" \
                              .format(key=dconf_key,
                                      path=co.schema,
                                      gskey=gskey.key, value=value))

        def migrate_dconf_key(dconf_key, config_object, key):
            gskey = config_object.find_key(key)
            if gskey.is_default():
                migrate_dconf_value(dconf_key, config_object, gskey)

        # --- onboard 0.96 -> 0.97 ---------------------------------------------
        format = Version.from_string(self.schema_version)
        if format < SCHEMA_VERSION_0_97:

            # window rect moves from apps.onboard to
            # apps.onboard.window.landscape/portrait
            co = self.window.landscape
            if co.gskeys["x"].is_default() and \
               co.gskeys["y"].is_default() and \
               co.gskeys["width"].is_default() and \
               co.gskeys["height"].is_default():

                co.settings.delay()
                migrate_dconf_value("/apps/onboard/x", co, co.gskeys["x"])
                migrate_dconf_value("/apps/onboard/y", co, co.gskeys["y"])
                migrate_dconf_value("/apps/onboard/width", co, co.gskeys["width"])
                migrate_dconf_value("/apps/onboard/height", co, co.gskeys["height"])
                co.settings.apply()

            # icon-palette rect moves from apps.onboard.icon-palette to
            # apps.onboard.icon-palette.landscape/portrait
            co = self.icp.landscape
            if co.gskeys["x"].is_default() and \
               co.gskeys["y"].is_default() and \
               co.gskeys["width"].is_default() and \
               co.gskeys["height"].is_default():

                co.settings.delay()
                migrate_dconf_value("/apps/onboard/icon-palette/x", co, co.gskeys["x"])
                migrate_dconf_value("/apps/onboard/icon-palette/y", co, co.gskeys["y"])
                migrate_dconf_value("/apps/onboard/icon-palette/width", co, co.gskeys["width"])
                migrate_dconf_value("/apps/onboard/icon-palette/height", co, co.gskeys["height"])
                co.settings.apply()

            # move keys from root to window
            co = self.window
            migrate_dconf_key("/apps/onboard/window-decoration", co, "window-decoration")
            migrate_dconf_key("/apps/onboard/force-to-top", co, "force-to-top")
            migrate_dconf_key("/apps/onboard/transparent-background", co, "transparent-background")
            migrate_dconf_key("/apps/onboard/transparency", co, "transparency")
            migrate_dconf_key("/apps/onboard/background-transparency", co, "background-transparency")
            migrate_dconf_key("/apps/onboard/enable-inactive-transparency", co, "enable-inactive-transparency")
            migrate_dconf_key("/apps/onboard/inactive-transparency", co, "inactive-transparency")
            migrate_dconf_key("/apps/onboard/inactive-transparency-delay", co, "inactive-transparency-delay")

            # accessibility keys move from root to universal-access
            co = self.universal_access
            migrate_dconf_key("/apps/onboard/hide-click-type-window", co, "hide-click-type-window")
            migrate_dconf_key("/apps/onboard/enable-click-type-window-on-exit", co, "enable-click-type-window-on-exit")

            # move keys from root to keyboard
            co = self.keyboard
            migrate_dconf_key("/apps/onboard/show-click-buttons", co, "show-click-buttons")

            self.schema_version = SCHEMA_VERSION.to_string()
Exemplo n.º 12
0
    def load(filename, is_system=False):
        """ Load a theme and return a new theme object. """

        result = None

        _file = open_utf8(filename)
        try:
            domdoc = minidom.parse(_file).documentElement
            try:
                theme = Theme()

                node = domdoc.attributes.get("format")
                format = Version.from_string(node.value) \
                         if node else Theme.THEME_FORMAT_INITIAL

                theme.name = domdoc.attributes["name"].value

                # "color_scheme" is the base file name of the color scheme
                text = utils.xml_get_text(domdoc, "color_scheme")
                if not text is None:
                    theme.color_scheme_basename = text

                # get key label overrides
                nodes = domdoc.getElementsByTagName("key_label_overrides")
                if nodes:
                    overrides = nodes[0]
                    tuples = {}
                    for override in overrides.getElementsByTagName("key"):
                        key_id = override.attributes["id"].value
                        node = override.attributes.get("label")
                        label = node.value if node else ""
                        node = override.attributes.get("group")
                        group = node.value if node else ""
                        tuples[key_id] = (label, group)
                    theme.key_label_overrides = tuples

                # read all other members
                for name, _type, _default in Theme.attributes:
                    if not name in ["color_scheme_basename",
                                    "key_label_overrides"]:
                        value = utils.xml_get_text(domdoc, name)
                        if not value is None:

                            if _type == "i":
                                value = int(value)
                            if _type == "d":
                                value = float(value)
                            if _type == "ad":
                                value = [float(s) for s in value.split(",")]

                            # upgrade to current file format
                            if format < Theme.THEME_FORMAT_1_1:
                                # direction was    0..360, ccw
                                #        is now -180..180, cw
                                if name == "key_gradient_direction":
                                    value = -(value % 360)
                                    if value <= -180:
                                        value += 360

                            setattr(theme, name, value)

                theme._filename = filename
                theme.is_system = is_system
                theme.system_exists = is_system
                result = theme
            finally:
                domdoc.unlink()

        except (Exceptions.ThemeFileError,
                xml.parsers.expat.ExpatError) as ex:
            _logger.error(_format("Error loading theme '{filename}'. "
                                  "{exception}: {cause}",
                                  filename = filename,
                                  exception = type(ex).__name__,
                                  cause = unicode_str(ex)))
            result = None
        finally:
            _file.close()

        return result
Exemplo n.º 13
0
    def copy_layout(src_filename, dst_filename):
        src_dir = os.path.dirname(src_filename)
        dst_dir, name_ext = os.path.split(dst_filename)
        dst_basename, ext = os.path.splitext(name_ext)
        _logger.info(_format("copying layout '{}' to '{}'",
                             src_filename, dst_filename))

        domdoc = None
        svg_filenames = {}
        fallback_layers = {}

        try:
            with open_utf8(src_filename) as f:
                domdoc = minidom.parse(f)
                keyboard_node = domdoc.documentElement

                # check layout format
                format = LayoutLoaderSVG.LAYOUT_FORMAT_LEGACY
                if keyboard_node.hasAttribute("format"):
                   format = Version.from_string(keyboard_node.attributes["format"].value)
                keyboard_node.attributes["id"] = dst_basename

                if format < LayoutLoaderSVG.LAYOUT_FORMAT_LAYOUT_TREE:
                    raise Exceptions.LayoutFileError( \
                        _format("copy_layouts failed, unsupported layout format '{}'.",
                                format))
                else:
                    # replace the basename of all svg filenames
                    for node in LayoutLoaderSVG._iter_dom_nodes(keyboard_node):
                        if LayoutLoaderSVG.is_layout_node(node):
                            if node.hasAttribute("filename"):
                                filename = node.attributes["filename"].value

                                # Create a replacement layer name for the unlikely
                                # case  that the svg-filename doesn't contain a
                                # layer section (as in path/basename-layer.ext).
                                fallback_layer_name = fallback_layers.get(filename,
                                             "Layer" + str(len(fallback_layers)))
                                fallback_layers[filename] = fallback_layer_name

                                # replace the basename of this filename
                                new_filename = LayoutLoaderSVG._replace_basename( \
                                     filename, dst_basename, fallback_layer_name)

                                node.attributes["filename"].value = new_filename
                                svg_filenames[filename] = new_filename

            if domdoc:
                XDGDirs.assure_user_dir_exists(config.get_user_layout_dir())

                # write the new layout file
                with open_utf8(dst_filename, "w") as f:
                    xml = toprettyxml(domdoc)
                    if sys.version_info.major == 2:  # python 2?
                        xml = xml.encode("UTF-8")
                    f.write(xml)

                    # copy the svg files
                    for src, dst in list(svg_filenames.items()):

                        dir, name = os.path.split(src)
                        if not dir:
                            src = os.path.join(src_dir, name)
                        dir, name = os.path.split(dst)
                        if not dir:
                            dst = os.path.join(dst_dir, name)

                        _logger.info(_format("copying svg file '{}' to '{}'", \
                                     src, dst))
                        shutil.copyfile(src, dst)
        except OSError as ex:
            _logger.error("copy_layout failed: " + \
                          unicode_str(ex))
        except Exceptions.LayoutFileError as ex:
            _logger.error(unicode_str(ex))