/* * Copyright (C) 2002-2006 Sergey V. Udaltsov * * This library 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 library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include "config.h" #include "xklavier_private.h" static GObjectClass *parent_class = NULL; static xmlXPathCompExprPtr models_xpath; static xmlXPathCompExprPtr layouts_xpath; static xmlXPathCompExprPtr option_groups_xpath; static GRegex **xml_encode_regexen = NULL; static GRegex **xml_decode_regexen = NULL; static const char *xml_decode_regexen_str[] = { "<", ">", "&" }; static const char *xml_encode_regexen_str[] = { "<", ">", "&" }; /* gettext domain for translations */ #define XKB_DOMAIN "xkeyboard-config" enum { PROP_0, PROP_ENGINE }; typedef struct { gchar **patterns; XklTwoConfigItemsProcessFunc func; gpointer data; gboolean country_matched; gboolean language_matched; const XklConfigItem *layout_item; } SearchParamType; static gboolean xkl_xml_find_config_item_child(xmlNodePtr iptr, xmlNodePtr * ptr) { /* * Walking through the 1st level children of iptr * looking for the configItem */ if (iptr->type != XML_ELEMENT_NODE) return FALSE; *ptr = iptr->children; while (*ptr != NULL) { switch ((*ptr)->type) { case XML_ELEMENT_NODE: return !g_ascii_strcasecmp ((char *) (*ptr)->name, "configItem"); case XML_TEXT_NODE: case XML_COMMENT_NODE: (*ptr) = (*ptr)->next; continue; default: return FALSE; } break; } return FALSE; } static xmlNodePtr xkl_find_element(xmlNodePtr ptr, const gchar * tag_name) { xmlNodePtr found_element = NULL; /* Look through all siblings, trying to find a node with proper name */ while (ptr != NULL) { char *node_name = (char *) ptr->name; if (ptr->type != XML_TEXT_NODE) { if (!g_ascii_strcasecmp(node_name, tag_name)) { found_element = ptr; break; } } ptr = ptr->next; } return found_element; } static gboolean xkl_item_populate_optional_array(XklConfigItem * item, xmlNodePtr ptr, const gchar list_tag[], const gchar element_tag[], const gchar property_name[]) { xmlNodePtr top_list_element = xkl_find_element(ptr, list_tag), element_ptr; gint n_elements, idx; gchar **elements = NULL; if (top_list_element == NULL || top_list_element->children == NULL) return FALSE; n_elements = 0; /* First, count countries */ element_ptr = top_list_element->children; while (NULL != (element_ptr = xkl_find_element(element_ptr, element_tag))) { n_elements++; element_ptr = element_ptr->next; } if (n_elements == 0) return FALSE; elements = g_new0(gchar *, n_elements + 1); /* Then, actually, populate the list */ element_ptr = top_list_element->children; for (idx = 0; NULL != (element_ptr = xkl_find_element(element_ptr, element_tag)); element_ptr = element_ptr->next, idx++) { elements[idx] = g_strdup((const char *) element_ptr-> children->content); } g_object_set_data_full(G_OBJECT(item), property_name, elements, (GDestroyNotify) g_strfreev); return TRUE; } #include "libxml/parserInternals.h" gboolean xkl_read_config_item(XklConfigRegistry * config, gint doc_index, xmlNodePtr iptr, XklConfigItem * item) { xmlNodePtr name_element, ptr; xmlNodePtr desc_element = NULL, short_desc_element = NULL, vendor_element = NULL; gchar *vendor = NULL, *translated = NULL, *escaped = NULL, *unescaped = NULL; gint i; *item->name = 0; *item->short_description = 0; *item->description = 0; g_object_set_data(G_OBJECT(item), XCI_PROP_VENDOR, NULL); g_object_set_data(G_OBJECT(item), XCI_PROP_COUNTRY_LIST, NULL); g_object_set_data(G_OBJECT(item), XCI_PROP_LANGUAGE_LIST, NULL); if (!xkl_xml_find_config_item_child(iptr, &ptr)) return FALSE; if (doc_index > 0) g_object_set_data(G_OBJECT(item), XCI_PROP_EXTRA_ITEM, GINT_TO_POINTER(TRUE)); ptr = ptr->children; if (ptr->type == XML_TEXT_NODE) ptr = ptr->next; name_element = ptr; ptr = ptr->next; short_desc_element = xkl_find_element(ptr, XML_TAG_SHORT_DESCR); desc_element = xkl_find_element(ptr, XML_TAG_DESCR); vendor_element = xkl_find_element(ptr, XML_TAG_VENDOR); if (name_element != NULL && name_element->children != NULL) strncat(item->name, (char *) name_element->children->content, XKL_MAX_CI_NAME_LENGTH - 1); if (short_desc_element != NULL && short_desc_element->children != NULL) { strncat(item->short_description, dgettext(XKB_DOMAIN, (const char *) short_desc_element->children->content), XKL_MAX_CI_SHORT_DESC_LENGTH - 1); } if (desc_element != NULL && desc_element->children != NULL) { /* Convert all xml-related characters to XML form, otherwise dgettext won't find the translation * The conversion is not using libxml2, because there are no handy functions in API */ translated = g_strdup((gchar *) desc_element->children->content); for (i = sizeof(xml_encode_regexen_str) / sizeof(xml_encode_regexen_str[0]); --i >= 0;) { escaped = g_regex_replace(xml_encode_regexen[i], translated, -1, 0, xml_decode_regexen_str[i], 0, NULL); g_free(translated); translated = escaped; } escaped = translated; /* Do the translation! */ translated = g_strdup(dgettext(XKB_DOMAIN, (const char *) escaped)); g_free(escaped); /* Convert all XML entities back to normal form */ for (i = sizeof(xml_decode_regexen_str) / sizeof(xml_decode_regexen_str[0]); --i >= 0;) { unescaped = g_regex_replace(xml_decode_regexen[i], translated, -1, 0, xml_encode_regexen_str[i], 0, NULL); g_free(translated); translated = unescaped; } strncat(item->description, translated, XKL_MAX_CI_DESC_LENGTH - 1); g_free(translated); } if (vendor_element != NULL && vendor_element->children != NULL) { vendor = g_strdup((const char *) vendor_element->children-> content); g_object_set_data_full(G_OBJECT(item), XCI_PROP_VENDOR, vendor, g_free); } xkl_item_populate_optional_array(item, ptr, XML_TAG_COUNTRY_LIST, XML_TAG_ISO3166ID, XCI_PROP_COUNTRY_LIST); xkl_item_populate_optional_array(item, ptr, XML_TAG_LANGUAGE_LIST, XML_TAG_ISO639ID, XCI_PROP_LANGUAGE_LIST); return TRUE; } static void xkl_config_registry_foreach_in_nodeset(XklConfigRegistry * config, GSList ** processed_ids, gint doc_index, xmlNodeSetPtr nodes, XklConfigItemProcessFunc func, gpointer data) { gint i; if (nodes != NULL) { xmlNodePtr *pnode = nodes->nodeTab; XklConfigItem *ci = xkl_config_item_new(); for (i = nodes->nodeNr; --i >= 0;) { if (xkl_read_config_item (config, doc_index, *pnode, ci)) { if (g_slist_find_custom (*processed_ids, ci->name, (GCompareFunc) g_ascii_strcasecmp) == NULL) { func(config, ci, data); *processed_ids = g_slist_append(*processed_ids, g_strdup (ci->name)); } } pnode++; } g_object_unref(G_OBJECT(ci)); } } void xkl_config_registry_foreach_in_xpath(XklConfigRegistry * config, xmlXPathCompExprPtr xpath_comp_expr, XklConfigItemProcessFunc func, gpointer data) { xmlXPathObjectPtr xpath_obj; gint di; GSList *processed_ids = NULL; if (!xkl_config_registry_is_initialized(config)) return; for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) { xmlXPathContextPtr xmlctxt = xkl_config_registry_priv(config, xpath_contexts[di]); if (xmlctxt == NULL) continue; xpath_obj = xmlXPathCompiledEval(xpath_comp_expr, xmlctxt); if (xpath_obj == NULL) continue; xkl_config_registry_foreach_in_nodeset(config, &processed_ids, di, xpath_obj-> nodesetval, func, data); xmlXPathFreeObject(xpath_obj); } g_slist_foreach(processed_ids, (GFunc) g_free, NULL); g_slist_free(processed_ids); } void xkl_config_registry_foreach_in_xpath_with_param(XklConfigRegistry * config, const gchar * format, const gchar * value, XklConfigItemProcessFunc func, gpointer data) { char xpath_expr[1024]; xmlXPathObjectPtr xpath_obj; gint di; GSList *processed_ids = NULL; if (!xkl_config_registry_is_initialized(config)) return; g_snprintf(xpath_expr, sizeof xpath_expr, format, value); for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) { xmlXPathContextPtr xmlctxt = xkl_config_registry_priv(config, xpath_contexts[di]); if (xmlctxt == NULL) continue; xpath_obj = xmlXPathEval((unsigned char *) xpath_expr, xmlctxt); if (xpath_obj == NULL) continue; xkl_config_registry_foreach_in_nodeset(config, &processed_ids, di, xpath_obj-> nodesetval, func, data); xmlXPathFreeObject(xpath_obj); } g_slist_foreach(processed_ids, (GFunc) g_free, NULL); g_slist_free(processed_ids); } static gboolean xkl_config_registry_find_object(XklConfigRegistry * config, const gchar * format, const gchar * arg1, XklConfigItem * pitem /* in/out */ , xmlNodePtr * pnode /* out */ ) { xmlXPathObjectPtr xpath_obj; xmlNodeSetPtr nodes; gboolean rv = FALSE; gchar xpath_expr[1024]; gint di; if (!xkl_config_registry_is_initialized(config)) return FALSE; g_snprintf(xpath_expr, sizeof xpath_expr, format, arg1, pitem->name); for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) { xmlXPathContextPtr xmlctxt = xkl_config_registry_priv(config, xpath_contexts[di]); if (xmlctxt == NULL) continue; xpath_obj = xmlXPathEval((unsigned char *) xpath_expr, xmlctxt); if (xpath_obj == NULL) continue; nodes = xpath_obj->nodesetval; if (nodes != NULL && nodes->nodeTab != NULL && nodes->nodeNr > 0) { rv = xkl_read_config_item(config, di, nodes->nodeTab[0], pitem); if (pnode != NULL) { *pnode = *nodes->nodeTab; } } xmlXPathFreeObject(xpath_obj); } return rv; } gchar * xkl_config_rec_merge_layouts(const XklConfigRec * data) { return xkl_strings_concat_comma_separated(data->layouts); } gchar * xkl_config_rec_merge_variants(const XklConfigRec * data) { return xkl_strings_concat_comma_separated(data->variants); } gchar * xkl_config_rec_merge_options(const XklConfigRec * data) { return xkl_strings_concat_comma_separated(data->options); } gchar * xkl_strings_concat_comma_separated(gchar ** array) { if (array) { return g_strjoinv(",", array); } else { return g_strdup(""); } } void xkl_config_rec_split_layouts(XklConfigRec * data, const gchar * merged) { xkl_strings_split_comma_separated(&data->layouts, merged); } void xkl_config_rec_split_variants(XklConfigRec * data, const gchar * merged) { xkl_strings_split_comma_separated(&data->variants, merged); } void xkl_config_rec_split_options(XklConfigRec * data, const gchar * merged) { xkl_strings_split_comma_separated(&data->options, merged); } void xkl_strings_split_comma_separated(gchar *** array, const gchar * merged) { *array = g_strsplit(merged, ",", 0); } gchar * xkl_engine_get_ruleset_name(XklEngine * engine, const gchar default_ruleset[]) { static gchar rules_set_name[1024] = ""; if (!rules_set_name[0]) { /* first call */ gchar *rf = NULL; if (!xkl_config_rec_get_from_root_window_property (NULL, xkl_engine_priv(engine, base_config_atom), &rf, engine) || (rf == NULL)) { g_strlcpy(rules_set_name, default_ruleset, sizeof rules_set_name); xkl_debug(100, "Using default rules set: [%s]\n", rules_set_name); return rules_set_name; } g_strlcpy(rules_set_name, rf, sizeof rules_set_name); g_free(rf); } xkl_debug(100, "Rules set: [%s]\n", rules_set_name); return rules_set_name; } XklConfigRegistry * xkl_config_registry_get_instance(XklEngine * engine) { XklConfigRegistry *config; if (!engine) { xkl_debug(10, "xkl_config_registry_get_instance : engine is NULL ?\n"); return NULL; } config = XKL_CONFIG_REGISTRY(g_object_new (xkl_config_registry_get_type(), "engine", engine, NULL)); return config; } /* We process descriptions as "leaf" elements - this is ok for base.xml*/ gboolean xkl_config_registry_load_from_file(XklConfigRegistry * config, const gchar * file_name, gint docidx) { xmlParserCtxtPtr ctxt = xmlNewParserCtxt(); xmlDocPtr doc; xkl_debug(100, "Loading XML registry from file %s\n", file_name); xmlSAX2InitDefaultSAXHandler(ctxt->sax, TRUE); doc = xkl_config_registry_priv(config, docs[docidx]) = xmlCtxtReadFile(ctxt, file_name, NULL, XML_PARSE_NOBLANKS); xmlFreeParserCtxt(ctxt); if (doc == NULL) { xkl_config_registry_priv(config, xpath_contexts[docidx]) = NULL; xkl_last_error_message = "Could not parse primary XKB configuration registry"; return FALSE; } xkl_config_registry_priv(config, xpath_contexts[docidx]) = xmlXPathNewContext(doc); return TRUE; } gboolean xkl_config_registry_load_helper(XklConfigRegistry * config, const char default_ruleset[], const char base_dir[], gboolean if_extras_needed) { struct stat stat_buf; gchar file_name[MAXPATHLEN] = ""; XklEngine *engine = xkl_config_registry_get_engine(config); gchar *rf = xkl_engine_get_ruleset_name(engine, default_ruleset); if (rf == NULL || rf[0] == '\0') return FALSE; g_snprintf(file_name, sizeof file_name, "%s/%s.xml", base_dir, rf); if (stat(file_name, &stat_buf) != 0) { xkl_debug(0, "Missing registry file %s\n", file_name); xkl_last_error_message = "Missing registry file"; return FALSE; } if (!xkl_config_registry_load_from_file(config, file_name, 0)) return FALSE; if (!if_extras_needed) return TRUE; g_snprintf(file_name, sizeof file_name, "%s/%s.extras.xml", base_dir, rf); /* no extras - ok, no problem */ if (stat(file_name, &stat_buf) != 0) return TRUE; return xkl_config_registry_load_from_file(config, file_name, 1); } void xkl_config_registry_free(XklConfigRegistry * config) { if (xkl_config_registry_is_initialized(config)) { gint di; for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) { xmlXPathContextPtr xmlctxt = xkl_config_registry_priv(config, xpath_contexts[di]); if (xmlctxt == NULL) continue; xmlXPathFreeContext(xmlctxt); xmlFreeDoc(xkl_config_registry_priv (config, docs[di])); xkl_config_registry_priv(config, xpath_contexts[di]) = NULL; xkl_config_registry_priv(config, docs[di]) = NULL; } } } void xkl_config_registry_foreach_model(XklConfigRegistry * config, XklConfigItemProcessFunc func, gpointer data) { xkl_config_registry_foreach_in_xpath(config, models_xpath, func, data); } void xkl_config_registry_foreach_layout(XklConfigRegistry * config, XklConfigItemProcessFunc func, gpointer data) { xkl_config_registry_foreach_in_xpath(config, layouts_xpath, func, data); } void xkl_config_registry_foreach_layout_variant(XklConfigRegistry * config, const gchar * layout_name, XklConfigItemProcessFunc func, gpointer data) { xkl_config_registry_foreach_in_xpath_with_param(config, XKBCR_VARIANT_PATH "[../../configItem/name = '%s']", layout_name, func, data); } void xkl_config_registry_foreach_option_group(XklConfigRegistry * config, XklConfigItemProcessFunc func, gpointer data) { xmlXPathObjectPtr xpath_obj; gint di, j; GSList *processed_ids = NULL; if (!xkl_config_registry_is_initialized(config)) return; for (di = 0; di < XKL_NUMBER_OF_REGISTRY_DOCS; di++) { xmlNodeSetPtr nodes; xmlNodePtr *pnode; XklConfigItem *ci; xmlXPathContextPtr xmlctxt = xkl_config_registry_priv(config, xpath_contexts[di]); if (xmlctxt == NULL) continue; xpath_obj = xmlXPathCompiledEval(option_groups_xpath, xmlctxt); if (xpath_obj == NULL) continue; nodes = xpath_obj->nodesetval; pnode = nodes->nodeTab; ci = xkl_config_item_new(); for (j = nodes->nodeNr; --j >= 0;) { if (xkl_read_config_item(config, di, *pnode, ci)) { if (g_slist_find_custom (processed_ids, ci->name, (GCompareFunc) g_ascii_strcasecmp) == NULL) { gboolean allow_multisel = TRUE; xmlChar *sallow_multisel = xmlGetProp(*pnode, (unsigned char *) XCI_PROP_ALLOW_MULTIPLE_SELECTION); if (sallow_multisel != NULL) { allow_multisel = !g_ascii_strcasecmp ("true", (char *) sallow_multisel); xmlFree(sallow_multisel); g_object_set_data(G_OBJECT (ci), XCI_PROP_ALLOW_MULTIPLE_SELECTION, GINT_TO_POINTER (allow_multisel)); } func(config, ci, data); processed_ids = g_slist_append(processed_ids, g_strdup (ci->name)); } } pnode++; } g_object_unref(G_OBJECT(ci)); xmlXPathFreeObject(xpath_obj); } g_slist_foreach(processed_ids, (GFunc) g_free, NULL); g_slist_free(processed_ids); } void xkl_config_registry_foreach_option(XklConfigRegistry * config, const gchar * option_group_name, XklConfigItemProcessFunc func, gpointer data) { xkl_config_registry_foreach_in_xpath_with_param(config, XKBCR_OPTION_PATH "[../configItem/name = '%s']", option_group_name, func, data); } static gboolean search_all(const gchar * haystack, gchar ** needles) { /* match anything */ if (!needles || !*needles) return TRUE; gchar *uchs = g_utf8_strup(haystack, -1); do { if (g_strstr_len(uchs, -1, *needles) == NULL) { g_free(uchs); return FALSE; } needles++; } while (*needles); g_free(uchs); return TRUE; } static gboolean if_country_matches_pattern(const XklConfigItem * item, gchar ** patterns, const gboolean check_name) { const gchar *country_desc; if (check_name) { gchar *upper_name = g_ascii_strup(item->name, -1); country_desc = xkl_get_country_name(upper_name); g_free(upper_name); xkl_debug(200, "Checking layout country: [%s]\n", country_desc); if ((country_desc != NULL) && search_all(country_desc, patterns)) return TRUE; } gchar **countries = g_object_get_data(G_OBJECT(item), XCI_PROP_COUNTRY_LIST); for (; countries && *countries; countries++) { country_desc = xkl_get_country_name(*countries); xkl_debug(200, "Checking country: [%s][%s]\n", *countries, country_desc); if ((country_desc != NULL) && search_all(country_desc, patterns)) { return TRUE; } } return FALSE; } static gboolean if_language_matches_pattern(const XklConfigItem * item, gchar ** patterns, const gboolean check_name) { const gchar *language_desc; if (check_name) { language_desc = xkl_get_language_name(item->name); xkl_debug(200, "Checking layout language: [%s]\n", language_desc); if ((language_desc != NULL) && search_all(language_desc, patterns)) return TRUE; } gchar **languages = g_object_get_data(G_OBJECT(item), XCI_PROP_LANGUAGE_LIST); for (; languages && *languages; languages++) { language_desc = xkl_get_language_name(*languages); xkl_debug(200, "Checking language: [%s][%s]\n", *languages, language_desc); if ((language_desc != NULL) && search_all(language_desc, patterns)) { return TRUE; } } return FALSE; } static void xkl_config_registry_search_by_pattern_in_variant(XklConfigRegistry * config, const XklConfigItem * item, SearchParamType * search_param) { gboolean variant_matched = FALSE; gchar *full_desc = g_strdup_printf("%s - %s", search_param-> layout_item->description, item->description); xkl_debug(200, "Variant to check: [%s][%s]\n", item->name, item->description); if (search_all(full_desc, search_param->patterns)) variant_matched = TRUE; g_free(full_desc); if (!variant_matched) { gchar **countries = g_object_get_data(G_OBJECT(item), XCI_PROP_COUNTRY_LIST); if (countries && g_strv_length(countries) > 0) { if (if_country_matches_pattern (item, search_param->patterns, FALSE)) variant_matched = TRUE; } else { if (search_param->country_matched) variant_matched = TRUE; } } if (!variant_matched) { gchar **languages = g_object_get_data(G_OBJECT(item), XCI_PROP_LANGUAGE_LIST); if (languages && g_strv_length(languages) > 0) { if (if_language_matches_pattern (item, search_param->patterns, FALSE)) variant_matched = TRUE; } else { if (search_param->language_matched) variant_matched = TRUE; } } if (variant_matched) (search_param->func) (config, search_param->layout_item, item, search_param->data); } static void xkl_config_registry_search_by_pattern_in_layout(XklConfigRegistry * config, const XklConfigItem * item, SearchParamType * search_param) { gchar *upper_name = g_ascii_strup(item->name, -1); xkl_debug(200, "Layout to check: [%s][%s]\n", item->name, item->description); search_param->country_matched = search_param->language_matched = FALSE; if (if_country_matches_pattern(item, search_param->patterns, TRUE)) search_param->country_matched = TRUE; else if (if_language_matches_pattern (item, search_param->patterns, TRUE)) search_param->language_matched = TRUE; else if (search_all(item->description, search_param->patterns)) search_param->language_matched = TRUE; if (search_param->country_matched || search_param->language_matched) (search_param->func) (config, item, NULL, search_param->data); search_param->layout_item = item; xkl_config_registry_foreach_layout_variant(config, item->name, (XklConfigItemProcessFunc) xkl_config_registry_search_by_pattern_in_variant, search_param); g_free(upper_name); } void xkl_config_registry_search_by_pattern(XklConfigRegistry * config, const gchar * pattern, XklTwoConfigItemsProcessFunc func, gpointer data) { xkl_debug(200, "Searching by pattern: [%s]\n", pattern); gchar *upattern = pattern ? g_utf8_strup(pattern, -1) : NULL; gchar **patterns = pattern ? g_strsplit(upattern, " ", -1) : NULL; SearchParamType search_param = { patterns, func, data }; xkl_config_registry_foreach_layout(config, (XklConfigItemProcessFunc) xkl_config_registry_search_by_pattern_in_layout, &search_param); g_strfreev(patterns); g_free(upattern); } gboolean xkl_config_registry_find_model(XklConfigRegistry * config, XklConfigItem * pitem /* in/out */ ) { return xkl_config_registry_find_object(config, XKBCR_MODEL_PATH "[configItem/name = '%s%s']", "", pitem, NULL); } gboolean xkl_config_registry_find_layout(XklConfigRegistry * config, XklConfigItem * pitem /* in/out */ ) { return xkl_config_registry_find_object(config, XKBCR_LAYOUT_PATH "[configItem/name = '%s%s']", "", pitem, NULL); } gboolean xkl_config_registry_find_variant(XklConfigRegistry * config, const char *layout_name, XklConfigItem * pitem /* in/out */ ) { return xkl_config_registry_find_object(config, XKBCR_VARIANT_PATH "[../../configItem/name = '%s' and configItem/name = '%s']", layout_name, pitem, NULL); } gboolean xkl_config_registry_find_option_group(XklConfigRegistry * config, XklConfigItem * pitem /* in/out */ ) { xmlNodePtr node = NULL; gboolean rv = xkl_config_registry_find_object(config, XKBCR_GROUP_PATH "[configItem/name = '%s%s']", "", pitem, &node); if (rv) { xmlChar *val = xmlGetProp(node, (unsigned char *) XCI_PROP_ALLOW_MULTIPLE_SELECTION); if (val != NULL) { gboolean allow_multisel = !g_ascii_strcasecmp("true", (char *) val); g_object_set_data(G_OBJECT(pitem), XCI_PROP_ALLOW_MULTIPLE_SELECTION, GINT_TO_POINTER(allow_multisel)); xmlFree(val); } } return rv; } gboolean xkl_config_registry_find_option(XklConfigRegistry * config, const char *option_group_name, XklConfigItem * pitem /* in/out */ ) { return xkl_config_registry_find_object(config, XKBCR_OPTION_PATH "[../configItem/name = '%s' and configItem/name = '%s']", option_group_name, pitem, NULL); } /* * Calling through vtable */ gboolean xkl_config_rec_activate(const XklConfigRec * data, XklEngine * engine) { xkl_engine_ensure_vtable_inited(engine); return xkl_engine_vcall(engine, activate_config_rec) (engine, data); } gboolean xkl_config_registry_load(XklConfigRegistry * config, gboolean if_extras_needed) { XklEngine *engine; xkl_config_registry_free(config); engine = xkl_config_registry_get_engine(config); xkl_engine_ensure_vtable_inited(engine); return xkl_engine_vcall(engine, load_config_registry) (config, if_extras_needed); } gboolean xkl_config_rec_write_to_file(XklEngine * engine, const gchar * file_name, const XklConfigRec * data, const gboolean binary) { if ((!binary && !(xkl_engine_priv(engine, features) & XKLF_CAN_OUTPUT_CONFIG_AS_ASCII)) || (binary && !(xkl_engine_priv(engine, features) & XKLF_CAN_OUTPUT_CONFIG_AS_BINARY))) { xkl_last_error_message = "Function not supported at backend"; return FALSE; } xkl_engine_ensure_vtable_inited(engine); return xkl_engine_vcall(engine, write_config_rec_to_file) (engine, file_name, data, binary); } void xkl_config_rec_dump(FILE * file, XklConfigRec * data) { int j; fprintf(file, " model: [%s]\n", data->model); fprintf(file, " layouts:\n"); #define OUTPUT_ARRZ(arrz) \ { \ gchar **p = data->arrz; \ fprintf( file, " " #arrz ":\n" ); \ if ( p != NULL ) \ for( j = 0; *p != NULL; ) \ fprintf( file, " %d: [%s]\n", j++, *p++ ); \ } OUTPUT_ARRZ(layouts); OUTPUT_ARRZ(variants); OUTPUT_ARRZ(options); } G_DEFINE_TYPE(XklConfigRegistry, xkl_config_registry, G_TYPE_OBJECT) static GObject * xkl_config_registry_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_properties) { GObject *obj; XklConfigRegistry *config; XklEngine *engine; { /* Invoke parent constructor. */ g_type_class_peek(XKL_TYPE_CONFIG_REGISTRY); obj = parent_class->constructor(type, n_construct_properties, construct_properties); } config = XKL_CONFIG_REGISTRY(obj); engine = XKL_ENGINE(g_value_peek_pointer (construct_properties[0].value)); xkl_config_registry_get_engine(config) = engine; xkl_engine_ensure_vtable_inited(engine); xkl_engine_vcall(engine, init_config_registry) (config); return obj; } static void xkl_config_registry_init(XklConfigRegistry * config) { config->priv = g_new0(XklConfigRegistryPrivate, 1); } static void xkl_config_registry_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { } static void xkl_config_registry_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { XklConfigRegistry *config = XKL_CONFIG_REGISTRY(object); switch (property_id) { case PROP_ENGINE: g_value_set_pointer(value, xkl_config_registry_get_engine (config)); break; } } static void xkl_config_registry_finalize(GObject * obj) { XklConfigRegistry *config = (XklConfigRegistry *) obj; xkl_config_registry_free(config); g_free(config->priv); G_OBJECT_CLASS(parent_class)->finalize(obj); } /* * This function is actually NEVER called. * It is 'extern' just to avoid the compilation warnings * TODO: add class cleanup */ extern void xkl_config_registry_class_term(XklConfigRegistryClass * klass) { gint i; if (models_xpath != NULL) { xmlXPathFreeCompExpr(models_xpath); models_xpath = NULL; } if (layouts_xpath != NULL) { xmlXPathFreeCompExpr(layouts_xpath); layouts_xpath = NULL; } if (option_groups_xpath != NULL) { xmlXPathFreeCompExpr(option_groups_xpath); option_groups_xpath = NULL; } if (xml_encode_regexen != NULL) { for (i = sizeof(xml_encode_regexen_str) / sizeof(xml_encode_regexen_str[0]); --i >= 0;) { g_regex_unref(xml_encode_regexen[i]); } g_free(xml_encode_regexen); xml_encode_regexen = NULL; } if (xml_decode_regexen != NULL) { for (i = sizeof(xml_decode_regexen_str) / sizeof(xml_decode_regexen_str[0]); --i >= 0;) { g_regex_unref(xml_decode_regexen[i]); } g_free(xml_decode_regexen); xml_decode_regexen = NULL; } } static void xkl_config_registry_class_init(XklConfigRegistryClass * klass) { GObjectClass *object_class; GParamSpec *engine_param_spec; gint i; object_class = (GObjectClass *) klass; parent_class = g_type_class_peek_parent(object_class); object_class->constructor = xkl_config_registry_constructor; object_class->finalize = xkl_config_registry_finalize; object_class->set_property = xkl_config_registry_set_property; object_class->get_property = xkl_config_registry_get_property; bind_textdomain_codeset(XKB_DOMAIN, "UTF-8"); engine_param_spec = g_param_spec_object("engine", "Engine", "XklEngine", XKL_TYPE_ENGINE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); g_object_class_install_property(object_class, PROP_ENGINE, engine_param_spec); /* static stuff initialized */ xmlXPathInit(); models_xpath = xmlXPathCompile((unsigned char *) XKBCR_MODEL_PATH); layouts_xpath = xmlXPathCompile((unsigned char *) XKBCR_LAYOUT_PATH); option_groups_xpath = xmlXPathCompile((unsigned char *) XKBCR_GROUP_PATH); xml_encode_regexen = g_new0(GRegex *, sizeof(xml_encode_regexen_str) / sizeof(xml_encode_regexen_str[0])); xml_decode_regexen = g_new0(GRegex *, sizeof(xml_decode_regexen_str) / sizeof(xml_decode_regexen_str[0])); for (i = sizeof(xml_encode_regexen_str) / sizeof(xml_encode_regexen_str[0]); --i >= 0;) { xml_encode_regexen[i] = g_regex_new(xml_encode_regexen_str[i], 0, 0, NULL); xml_decode_regexen[i] = g_regex_new(xml_decode_regexen_str[i], 0, 0, NULL); } }