Ejemplo n.º 1
0
    def save(self):
        """ Save this theme. """

        domdoc = minidom.Document()
        try:
            theme_element = domdoc.createElement("theme")
            theme_element.setAttribute("name", self._name)
            theme_element.setAttribute("format", str(self.THEME_FORMAT))
            domdoc.appendChild(theme_element)

            for name, _type, _default in self.attributes:
                if name == "color_scheme_basename":
                    element = domdoc.createElement("color_scheme")
                    text = domdoc.createTextNode(self.color_scheme_basename)
                    element.appendChild(text)
                    theme_element.appendChild(element)
                elif name == "key_label_overrides":
                    overrides_element = \
                            domdoc.createElement("key_label_overrides")
                    theme_element.appendChild(overrides_element)
                    tuples = self.key_label_overrides
                    for key_id, values in list(tuples.items()):
                        element = domdoc.createElement("key")
                        element.setAttribute("id", key_id)
                        element.setAttribute("label", values[0])
                        element.setAttribute("group", values[1])
                        overrides_element.appendChild(element)
                else:
                    value = getattr(self, name)
                    if _type == "s":
                        pass
                    elif _type == "i":
                        value = str(value)
                    elif _type == "d":
                        value = str(round(float(value), 2))
                    elif _type == "ad":
                        value = ", ".join(str(d) for d in value)
                    else:
                        assert (False)  # attribute of unknown type

                    element = domdoc.createElement(name)
                    text = domdoc.createTextNode(value)
                    element.appendChild(text)
                    theme_element.appendChild(element)

            pretty_xml = toprettyxml(domdoc)

            with open_utf8(self._filename, "w") as _file:
                if sys.version_info.major >= 3:
                    _file.write(pretty_xml)
                else:
                    _file.write(pretty_xml.encode("UTF-8"))

        except Exception as xxx_todo_changeme2:
            (ex) = xxx_todo_changeme2
            raise Exceptions.ThemeFileError(_("Error saving ") +
                                            self._filename,
                                            chained_exception=ex)
        finally:
            domdoc.unlink()
Ejemplo n.º 2
0
 def _load_svg_keys(self, filename):
     filename = os.path.join(self._root_layout_dir, filename)
     try:
         with open_utf8(filename) as svg_file:
             svg_dom = minidom.parse(svg_file).documentElement
             svg_nodes = self._parse_svg(svg_dom)
             svg_nodes = {node.id: node for node in svg_nodes}
     except Exceptions.LayoutFileError as ex:
         raise Exceptions.LayoutFileError(
             "error loading '{}'".format(filename), chained_exception=(ex))
     return svg_nodes
Ejemplo n.º 3
0
    def _load_svg_keys(self, filename):
        filename = os.path.join(self._root_layout_dir, filename)
        try:
            with open_utf8(filename) as svg_file:
                svg_dom = minidom.parse(svg_file).documentElement
                svg_keys = self._parse_svg(svg_dom)

        except Exception as xxx_todo_changeme1:
            (exception) = xxx_todo_changeme1
            raise Exceptions.LayoutFileError(_("Error loading ") + filename,
                                             chained_exception=exception)

        return svg_keys
Ejemplo n.º 4
0
    def _parse_key_template(self, node, parent):
        """
        Templates are partially define layout items. Later non-template
        items inherit attributes of templates with matching id.
        """
        attributes = dict(list(node.attributes.items()))
        id = attributes.get("id")
        if not id:
            raise Exceptions.LayoutFileError(
                "'id' attribute required for template '{} {}' "
                "in layout '{}'" \
                .format(tag,
                        str(list(attributes.values())),
                        self._layout_filename))

        parent.update_templates({(id, RectKey): attributes})
Ejemplo n.º 5
0
    def _parse_svg(self, node):
        svg_nodes = []
        for child in node.childNodes:
            if child.nodeType == minidom.Node.ELEMENT_NODE:
                tag = child.tagName
                if tag in ("rect", "path", "g"):
                    svg_node = SVGNode()
                    id = child.attributes["id"].value
                    svg_node.id = id

                    if tag == "rect":
                        svg_node.bounds = \
                                   Rect(float(child.attributes['x'].value),
                                        float(child.attributes['y'].value),
                                        float(child.attributes['width'].value),
                                        float(child.attributes['height'].value))

                    elif tag == "path":
                        data = child.attributes['d'].value

                        try:
                            svg_node.path = KeyPath.from_svg_path(data)
                        except ValueError as ex:
                            raise Exceptions.LayoutFileError(
                                "while reading geometry with id '{}'".format(
                                    id),
                                chained_exception=(ex))

                        svg_node.bounds = svg_node.path.get_bounds()

                    elif tag == "g":  # group
                        svg_node.children = self._parse_svg(child)

                    svg_nodes.append(svg_node)

                svg_nodes.extend(self._parse_svg(child))

        return svg_nodes
Ejemplo n.º 6
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))
Ejemplo n.º 7
0
    def _init_key(self, key, attributes):
        # Re-parse the id to distinguish between the short key_id
        # and the optional longer theme_id.
        full_id = attributes["id"]
        theme_id = attributes.get("theme_id")
        svg_id = attributes.get("svg_id")
        key.set_id(full_id, theme_id, svg_id)

        if "_" in key.get_id():
            _logger.warning("underscore in key id '{}', please use dashes" \
                            .format(key.get_id()))

        value = attributes.get("modifier")
        if value:
            try:
                key.modifier = modifiers[value]
            except KeyError as ex:
                (strerror) = ex
                raise Exceptions.LayoutFileError("Unrecognized modifier %s in" \
                    "definition of %s" (strerror, full_id))

        value = attributes.get("action")
        if value:
            try:
                key.action = KeyCommon.actions[value]
            except KeyError as ex:
                (strerror) = ex
                raise Exceptions.LayoutFileError("Unrecognized key action {} in" \
                    "definition of {}".format(strerror, full_id))

        if "char" in attributes:
            key.code = attributes["char"]
            key.type = KeyCommon.CHAR_TYPE
        elif "keysym" in attributes:
            value = attributes["keysym"]
            key.type = KeyCommon.KEYSYM_TYPE
            if value[1] == "x":  #Deals for when keysym is hex
                key.code = int(value, 16)
            else:
                key.code = int(value, 10)
        elif "keypress_name" in attributes:
            key.code = attributes["keypress_name"]
            key.type = KeyCommon.KEYPRESS_NAME_TYPE
        elif "macro" in attributes:
            key.code = attributes["macro"]
            key.type = KeyCommon.MACRO_TYPE
        elif "script" in attributes:
            key.code = attributes["script"]
            key.type = KeyCommon.SCRIPT_TYPE
        elif "keycode" in attributes:
            key.code = int(attributes["keycode"])
            key.type = KeyCommon.KEYCODE_TYPE
        elif "button" in attributes:
            key.code = key.id[:]
            key.type = KeyCommon.BUTTON_TYPE
        elif key.modifier:
            key.code = None
            key.type = KeyCommon.LEGACY_MODIFIER_TYPE
        else:
            # key without action: just draw it, do nothing on click
            key.action = None
            key.action_type = None

        # get the size group of the key
        if "group" in attributes:
            group_name = attributes["group"]
        else:
            group_name = "_default"

        # get the optional image filename
        if "image" in attributes:
            if not key.image_filenames: key.image_filenames = {}
            key.image_filenames[ImageSlot.NORMAL] = attributes["image"].split(
                ";")[0]
        if "image_active" in attributes:
            if not key.image_filenames: key.image_filenames = {}
            key.image_filenames[ImageSlot.ACTIVE] = attributes["image_active"]

        # get labels
        labels = self._parse_key_labels(attributes, key)

        # Replace label and size group with overrides from
        # theme and/or system defaults.
        label_overrides = config.theme_settings.key_label_overrides
        override = label_overrides.get(key.id)
        if override:
            olabel, ogroup = override
            if olabel:
                labels = {0: olabel[:]}
                if ogroup:
                    group_name = ogroup[:]

        key.labels = labels
        key.group = group_name

        # optionally  override the theme's default key_style
        if "key_style" in attributes:
            key.style = attributes["key_style"]

        # select what gets drawn, different from "visible" flag as this
        # doesn't affect the layout.
        if "show" in attributes:
            if attributes["show"].lower() == 'false':
                key.show_face = False
                key.show_border = False
        if "show_face" in attributes:
            if attributes["show_face"].lower() == 'false':
                key.show_face = False
        if "show_border" in attributes:
            if attributes["show_border"].lower() == 'false':
                key.show_border = False

        # show_active: allow to display key in latched or locked state
        # legacy show_active behavior was hard-coded for layer0
        if self._format < LayoutLoaderSVG.LAYOUT_FORMAT_3_2:
            if key.id == "layer0":
                key.show_active = False
        if "show_active" in attributes:
            if attributes["show_active"].lower() == 'false':
                key.show_active = False

        if "label_x_align" in attributes:
            key.label_x_align = float(attributes["label_x_align"])
        if "label_y_align" in attributes:
            key.label_y_align = float(attributes["label_y_align"])

        if "label_margin" in attributes:
            values = attributes["label_margin"].replace(" ", "").split(",")
            margin = [float(x) if x else key.label_margin[i] \
                      for i, x in enumerate(values[:2])]
            margin += margin[:1] * (2 - len(margin))
            if margin:
                key.label_margin = margin

        if "sticky" in attributes:
            sticky = attributes["sticky"].lower()
            if sticky == "true":
                key.sticky = True
            elif sticky == "false":
                key.sticky = False
            else:
                raise Exceptions.LayoutFileError(
                    "Invalid value '{}' for 'sticky' attribute of key '{}'" \
                    .format(sticky, key.id))
        else:
            key.sticky = False

        # legacy sticky key behavior was hard-coded for CAPS
        if self._format < LayoutLoaderSVG.LAYOUT_FORMAT_2_2:
            if key.id == "CAPS":
                key.sticky_behavior = StickyBehavior.LOCK_ONLY

        value = attributes.get("sticky_behavior")
        if value:
            try:
                key.sticky_behavior = StickyBehavior.from_string(value)
            except KeyError as ex:
                (strerror) = ex
                raise Exceptions.LayoutFileError("Unrecognized sticky behavior {} in" \
                    "definition of {}".format(strerror, full_id))

        if "tooltip" in attributes:
            key.tooltip = attributes["tooltip"]

        if "popup_id" in attributes:
            key.popup_id = attributes["popup_id"]

        if "chamfer_size" in attributes:
            key.chamfer_size = float(attributes["chamfer_size"])

        key.color_scheme = self._color_scheme