/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ /* vim:set et sts=4: */ /* bus - The Input Bus * Copyright (C) 2008-2010 Peng Huang * Copyright (C) 2008-2010 Red Hat, Inc. * * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include "ibuscomponent.h" #include "ibusinternal.h" enum { LAST_SIGNAL, }; enum { PROP_0 = 0, PROP_NAME, PROP_DESCRIPTION, PROP_VERSION, PROP_LICENSE, PROP_AUTHOR, PROP_HOMEPAGE, PROP_COMMAND_LINE, PROP_TEXTDOMAIN, }; /* IBusComponentPriv */ struct _IBusComponentPrivate { gchar *name; gchar *description; gchar *version; gchar *license; gchar *author; gchar *homepage; gchar *exec; gchar *textdomain; /* engines */ GList *engines; /* observed paths */ GList *observed_paths; }; #define IBUS_COMPONENT_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_COMPONENT, IBusComponentPrivate)) // static guint _signals[LAST_SIGNAL] = { 0 }; /* functions prototype */ static void ibus_component_set_property (IBusComponent *component, guint prop_id, const GValue *value, GParamSpec *pspec); static void ibus_component_get_property (IBusComponent *component, guint prop_id, GValue *value, GParamSpec *pspec); static void ibus_component_destroy (IBusComponent *component); static gboolean ibus_component_serialize (IBusComponent *component, GVariantBuilder *builder); static gint ibus_component_deserialize (IBusComponent *component, GVariant *variant); static gboolean ibus_component_copy (IBusComponent *dest, const IBusComponent *src); static gboolean ibus_component_parse_xml_node (IBusComponent *component, XMLNode *node, gboolean access_fs); static void ibus_component_parse_engines(IBusComponent *component, XMLNode *node); static void ibus_component_parse_observed_paths (IBusComponent *component, XMLNode *node, gboolean access_fs); G_DEFINE_TYPE (IBusComponent, ibus_component, IBUS_TYPE_SERIALIZABLE) static void ibus_component_class_init (IBusComponentClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class); IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class); g_type_class_add_private (class, sizeof (IBusComponentPrivate)); gobject_class->set_property = (GObjectSetPropertyFunc) ibus_component_set_property; gobject_class->get_property = (GObjectGetPropertyFunc) ibus_component_get_property; object_class->destroy = (IBusObjectDestroyFunc) ibus_component_destroy; serializable_class->serialize = (IBusSerializableSerializeFunc) ibus_component_serialize; serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_component_deserialize; serializable_class->copy = (IBusSerializableCopyFunc) ibus_component_copy; /* install properties */ /** * IBusComponent:name: * * The name of component */ g_object_class_install_property (gobject_class, PROP_NAME, g_param_spec_string ("name", "component name", "The name of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:description: * * The description of component */ g_object_class_install_property (gobject_class, PROP_DESCRIPTION, g_param_spec_string ("description", "component description", "The description of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:version: * * The version of component */ g_object_class_install_property (gobject_class, PROP_VERSION, g_param_spec_string ("version", "component version", "The version of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:license: * * The license of component */ g_object_class_install_property (gobject_class, PROP_LICENSE, g_param_spec_string ("license", "component license", "The license of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:author: * * The author of component */ g_object_class_install_property (gobject_class, PROP_AUTHOR, g_param_spec_string ("author", "component author", "The author of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:homepage: * * The homepage of component */ g_object_class_install_property (gobject_class, PROP_HOMEPAGE, g_param_spec_string ("homepage", "component homepage", "The homepage of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:command-line: * * The exec path of component */ g_object_class_install_property (gobject_class, PROP_COMMAND_LINE, g_param_spec_string ("command-line", "component command-line", "The command line of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); /** * IBusComponent:textdomain: * * The textdomain of component */ g_object_class_install_property (gobject_class, PROP_TEXTDOMAIN, g_param_spec_string ("textdomain", "component textdomain", "The textdomain path of component", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void ibus_component_init (IBusComponent *component) { component->priv = IBUS_COMPONENT_GET_PRIVATE (component); } static void ibus_component_destroy (IBusComponent *component) { GList *p; g_free (component->priv->name); g_free (component->priv->description); g_free (component->priv->version); g_free (component->priv->license); g_free (component->priv->author); g_free (component->priv->homepage); g_free (component->priv->exec); g_free (component->priv->textdomain); component->priv->name = NULL; component->priv->description = NULL; component->priv->version = NULL; component->priv->license = NULL; component->priv->author = NULL; component->priv->homepage = NULL; component->priv->exec = NULL; component->priv->textdomain = NULL; g_list_free_full (component->priv->observed_paths, g_object_unref); component->priv->observed_paths = NULL; for (p = component->priv->engines; p != NULL; p = p->next) { g_object_steal_data ((GObject *)p->data, "component"); ibus_object_destroy ((IBusObject *)p->data); g_object_unref (p->data); } g_list_free (component->priv->engines); component->priv->engines = NULL; IBUS_OBJECT_CLASS (ibus_component_parent_class)->destroy (IBUS_OBJECT (component)); } static void ibus_component_set_property (IBusComponent *component, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_NAME: g_assert (component->priv->name == NULL); component->priv->name = g_value_dup_string (value); break; case PROP_DESCRIPTION: g_assert (component->priv->description == NULL); component->priv->description = g_value_dup_string (value); break; case PROP_VERSION: g_assert (component->priv->version == NULL); component->priv->version = g_value_dup_string (value); break; case PROP_LICENSE: g_assert (component->priv->license == NULL); component->priv->license = g_value_dup_string (value); break; case PROP_AUTHOR: g_assert (component->priv->author == NULL); component->priv->author = g_value_dup_string (value); break; case PROP_HOMEPAGE: g_assert (component->priv->homepage == NULL); component->priv->homepage = g_value_dup_string (value); break; case PROP_COMMAND_LINE: g_assert (component->priv->exec == NULL); component->priv->exec = g_value_dup_string (value); break; case PROP_TEXTDOMAIN: g_assert (component->priv->textdomain == NULL); component->priv->textdomain = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec); } } static void ibus_component_get_property (IBusComponent *component, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_NAME: g_value_set_string (value, ibus_component_get_name (component)); break; case PROP_DESCRIPTION: g_value_set_string (value, ibus_component_get_description (component)); break; case PROP_VERSION: g_value_set_string (value, ibus_component_get_version (component)); break; case PROP_LICENSE: g_value_set_string (value, ibus_component_get_license (component)); break; case PROP_AUTHOR: g_value_set_string (value, ibus_component_get_author (component)); break; case PROP_HOMEPAGE: g_value_set_string (value, ibus_component_get_homepage (component)); break; case PROP_COMMAND_LINE: g_value_set_string (value, ibus_component_get_exec (component)); break; case PROP_TEXTDOMAIN: g_value_set_string (value, ibus_component_get_textdomain (component)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec); } } static gboolean ibus_component_serialize (IBusComponent *component, GVariantBuilder *builder) { gboolean retval; retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->serialize ((IBusSerializable *)component, builder); g_return_val_if_fail (retval, FALSE); g_variant_builder_add (builder, "s", component->priv->name); g_variant_builder_add (builder, "s", component->priv->description); g_variant_builder_add (builder, "s", component->priv->version); g_variant_builder_add (builder, "s", component->priv->license); g_variant_builder_add (builder, "s", component->priv->author); g_variant_builder_add (builder, "s", component->priv->homepage); g_variant_builder_add (builder, "s", component->priv->exec); g_variant_builder_add (builder, "s", component->priv->textdomain); GList *p; GVariantBuilder *array; /* serialize observed paths */ array = g_variant_builder_new (G_VARIANT_TYPE ("av")); for (p = component->priv->observed_paths; p != NULL; p = p->next) { g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data)); } g_variant_builder_add (builder, "av", array); g_variant_builder_unref (array); /* serialize engine desc list */ array = g_variant_builder_new (G_VARIANT_TYPE ("av")); for (p = component->priv->engines; p != NULL; p = p->next) { g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data)); } g_variant_builder_add (builder, "av", array); g_variant_builder_unref (array); return TRUE; } static gint ibus_component_deserialize (IBusComponent *component, GVariant *variant) { gboolean retval; retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->deserialize ((IBusSerializable *)component, variant); g_return_val_if_fail (retval, 0); ibus_g_variant_get_child_string (variant, retval++, &component->priv->name); ibus_g_variant_get_child_string (variant, retval++, &component->priv->description); ibus_g_variant_get_child_string (variant, retval++, &component->priv->version); ibus_g_variant_get_child_string (variant, retval++, &component->priv->license); ibus_g_variant_get_child_string (variant, retval++, &component->priv->author); ibus_g_variant_get_child_string (variant, retval++, &component->priv->homepage); ibus_g_variant_get_child_string (variant, retval++, &component->priv->exec); ibus_g_variant_get_child_string (variant, retval++, &component->priv->textdomain); GVariant *var; GVariantIter *iter = NULL; g_variant_get_child (variant, retval++, "av", &iter); while (g_variant_iter_loop (iter, "v", &var)) { component->priv->observed_paths = g_list_append (component->priv->observed_paths, IBUS_OBSERVED_PATH (ibus_serializable_deserialize (var))); } g_variant_iter_free (iter); g_variant_get_child (variant, retval++, "av", &iter); while (g_variant_iter_loop (iter, "v", &var)) { ibus_component_add_engine (component, IBUS_ENGINE_DESC (ibus_serializable_deserialize (var))); } g_variant_iter_free (iter); return retval; } static gboolean ibus_component_copy (IBusComponent *dest, const IBusComponent *src) { gboolean retval; retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->copy ((IBusSerializable *)dest, (IBusSerializable *)src); g_return_val_if_fail (retval, FALSE); dest->priv->name = g_strdup (src->priv->name); dest->priv->description = g_strdup (src->priv->description); dest->priv->version = g_strdup (src->priv->version); dest->priv->license = g_strdup (src->priv->license); dest->priv->author = g_strdup (src->priv->author); dest->priv->homepage = g_strdup (src->priv->homepage); dest->priv->exec = g_strdup (src->priv->exec); dest->priv->textdomain = g_strdup (src->priv->textdomain); dest->priv->observed_paths = g_list_copy (src->priv->observed_paths); g_list_foreach (dest->priv->observed_paths, (GFunc) g_object_ref, NULL); dest->priv->engines = g_list_copy (src->priv->engines); g_list_foreach (dest->priv->engines, (GFunc) g_object_ref, NULL); return TRUE; } #define g_string_append_indent(string, indent) \ { \ gint i; \ for (i = 0; i < (indent); i++) { \ g_string_append (string, " "); \ } \ } void ibus_component_output (IBusComponent *component, GString *output, gint indent) { g_assert (IBUS_IS_COMPONENT (component)); GList *p; g_string_append_indent (output, indent); g_string_append (output, "\n"); #define OUTPUT_ENTRY(field, element) \ { \ gchar *escape_text = \ g_markup_escape_text (component->priv->field ? \ component->priv->field : "", -1); \ g_string_append_indent (output, indent + 1); \ g_string_append_printf (output, "<"element">%s\n", \ escape_text); \ g_free (escape_text); \ } #define OUTPUT_ENTRY_1(name) OUTPUT_ENTRY(name, #name) OUTPUT_ENTRY_1 (name); OUTPUT_ENTRY_1 (description); OUTPUT_ENTRY_1 (version); OUTPUT_ENTRY_1 (license); OUTPUT_ENTRY_1 (author); OUTPUT_ENTRY_1 (homepage); OUTPUT_ENTRY_1 (exec); OUTPUT_ENTRY_1 (textdomain); #undef OUTPUT_ENTRY #undef OUTPUT_ENTRY_1 if (component->priv->observed_paths) { g_string_append_indent (output, indent + 1); g_string_append (output, "\n"); for (p = component->priv->observed_paths; p != NULL; p = p->next ) { IBusObservedPath *path = (IBusObservedPath *) p->data; g_string_append_indent (output, indent + 2); g_string_append_printf (output, "%s\n", path->mtime, path->path); } g_string_append_indent (output, indent + 1); g_string_append (output, "\n"); } ibus_component_output_engines (component, output, indent + 1); g_string_append_indent (output, indent); g_string_append (output, "\n"); } void ibus_component_output_engines (IBusComponent *component, GString *output, gint indent) { g_assert (IBUS_IS_COMPONENT (component)); g_assert (output); GList *p; g_string_append_indent (output, indent); g_string_append (output, "\n"); for (p = component->priv->engines; p != NULL; p = p->next) { ibus_engine_desc_output ((IBusEngineDesc *)p->data, output, indent + 2); } g_string_append_indent (output, indent); g_string_append (output, "\n"); } static gboolean ibus_component_parse_xml_node (IBusComponent *component, XMLNode *node, gboolean access_fs) { g_assert (component); g_assert (node); if (G_UNLIKELY (g_strcmp0 (node->name, "component") != 0)) { return FALSE; } GList *p; for (p = node->sub_nodes; p != NULL; p = p->next) { XMLNode *sub_node = (XMLNode *)p->data; #define PARSE_ENTRY(field_name, element_name) \ if (g_strcmp0 (sub_node->name, element_name) == 0) { \ if (component->priv->field_name != NULL) { \ g_free (component->priv->field_name); \ } \ component->priv->field_name = g_strdup (sub_node->text); \ continue; \ } #define PARSE_ENTRY_1(name) PARSE_ENTRY (name, #name) PARSE_ENTRY_1 (name); PARSE_ENTRY_1 (description); PARSE_ENTRY_1 (version); PARSE_ENTRY_1 (license); PARSE_ENTRY_1 (author); PARSE_ENTRY_1 (homepage); PARSE_ENTRY_1 (exec); PARSE_ENTRY_1 (textdomain); #undef PARSE_ENTRY #undef PARSE_ENTRY_1 if (g_strcmp0 (sub_node->name, "engines") == 0) { ibus_component_parse_engines (component, sub_node); continue; } if (g_strcmp0 (sub_node->name, "observed-paths") == 0) { ibus_component_parse_observed_paths (component, sub_node, access_fs); continue; } g_warning (" element contains invalidate element <%s>", sub_node->name); } return TRUE; } static void ibus_component_parse_engines (IBusComponent *component, XMLNode *node) { g_assert (IBUS_IS_COMPONENT (component)); g_assert (node); gchar *exec = NULL; gchar **p; XMLNode *engines_node = NULL; if (g_strcmp0 (node->name, "engines") != 0) { return; } for (p = node->attributes; *p != NULL; p += 2) { if (g_strcmp0 (*p, "exec") == 0) { exec = *(p + 1); break; } } if (exec != NULL) { gchar *output = NULL; if (g_spawn_command_line_sync (exec, &output, NULL, NULL, NULL)) { engines_node = ibus_xml_parse_buffer (output); g_free (output); if (engines_node) { if (g_strcmp0 (engines_node->name, "engines") == 0) { node = engines_node; } } } } GList *pl; for (pl = node->sub_nodes; pl != NULL; pl = pl->next) { IBusEngineDesc *engine; engine = ibus_engine_desc_new_from_xml_node ((XMLNode *)pl->data); if (G_UNLIKELY (engine == NULL)) continue; ibus_component_add_engine (component, engine); } if (engines_node) { ibus_xml_free (engines_node); } } static void ibus_component_parse_observed_paths (IBusComponent *component, XMLNode *node, gboolean access_fs) { g_assert (IBUS_IS_COMPONENT (component)); g_assert (node); if (g_strcmp0 (node->name, "observed-paths") != 0) { return; } GList *p; for (p = node->sub_nodes; p != NULL; p = p->next) { IBusObservedPath *path; path = ibus_observed_path_new_from_xml_node ((XMLNode *)p->data, access_fs); g_object_ref_sink (path); component->priv->observed_paths = g_list_append (component->priv->observed_paths, path); if (access_fs && path->is_dir && path->is_exist) { component->priv->observed_paths = g_list_concat (component->priv->observed_paths, ibus_observed_path_traverse (path, TRUE)); } } } #define IBUS_COMPONENT_GET_PROPERTY(property, return_type) \ return_type \ ibus_component_get_ ## property (IBusComponent *component) \ { \ return component->priv->property; \ } IBUS_COMPONENT_GET_PROPERTY (name, const gchar *) IBUS_COMPONENT_GET_PROPERTY (description, const gchar *) IBUS_COMPONENT_GET_PROPERTY (version, const gchar *) IBUS_COMPONENT_GET_PROPERTY (license, const gchar *) IBUS_COMPONENT_GET_PROPERTY (author, const gchar *) IBUS_COMPONENT_GET_PROPERTY (homepage, const gchar *) IBUS_COMPONENT_GET_PROPERTY (exec, const gchar *) IBUS_COMPONENT_GET_PROPERTY (textdomain, const gchar *) #undef IBUS_COMPONENT_GET_PROPERTY IBusComponent * ibus_component_new (const gchar *name, const gchar *description, const gchar *version, const gchar *license, const gchar *author, const gchar *homepage, const gchar *command_line, const gchar *textdomain) { return ibus_component_new_varargs ("name", name, "description", description, "version", version, "license", license, "author", author, "homepage", homepage, "command-line", command_line, "textdomain", textdomain, NULL); } IBusComponent * ibus_component_new_varargs (const gchar *first_property_name, ...) { va_list var_args; IBusComponent *component; IBusComponentPrivate *priv; g_assert (first_property_name); va_start (var_args, first_property_name); component = (IBusComponent *) g_object_new_valist (IBUS_TYPE_COMPONENT, first_property_name, var_args); va_end (var_args); priv = IBUS_COMPONENT_GET_PRIVATE (component); /* name is required. Other properties are set in class_init by default. */ g_assert (priv->name); return component; } IBusComponent * ibus_component_new_from_xml_node (XMLNode *node) { g_assert (node); IBusComponent *component; component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL); if (!ibus_component_parse_xml_node (component, node, FALSE)) { g_object_unref (component); component = NULL; } return component; } IBusComponent * ibus_component_new_from_file (const gchar *filename) { g_assert (filename); XMLNode *node; struct stat buf; IBusComponent *component; gboolean retval; if (g_stat (filename, &buf) != 0) { g_warning ("Can not get stat of file %s", filename); return NULL; } node = ibus_xml_parse_file (filename); if (!node) { return NULL; } component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL); retval = ibus_component_parse_xml_node (component, node, TRUE); ibus_xml_free (node); if (!retval) { g_object_unref (component); component = NULL; } else { IBusObservedPath *path; path = ibus_observed_path_new (filename, TRUE); component->priv->observed_paths = g_list_prepend(component->priv->observed_paths, path); } return component; } void ibus_component_add_observed_path (IBusComponent *component, const gchar *path, gboolean access_fs) { IBusObservedPath *p; p = ibus_observed_path_new (path, access_fs); g_object_ref_sink (p); component->priv->observed_paths = g_list_append (component->priv->observed_paths, p); if (access_fs && p->is_dir && p->is_exist) { component->priv->observed_paths = g_list_concat (component->priv->observed_paths, ibus_observed_path_traverse (p, TRUE)); } } void ibus_component_add_engine (IBusComponent *component, IBusEngineDesc *engine) { g_assert (IBUS_IS_COMPONENT (component)); g_assert (IBUS_IS_ENGINE_DESC (engine)); g_object_ref_sink (engine); component->priv->engines = g_list_append (component->priv->engines, engine); } GList * ibus_component_get_engines (IBusComponent *component) { return g_list_copy (component->priv->engines); } gboolean ibus_component_check_modification (IBusComponent *component) { g_assert (IBUS_IS_COMPONENT (component)); GList *p; for (p = component->priv->observed_paths; p != NULL; p = p->next) { if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data)) return TRUE; } return FALSE; } GList * ibus_component_get_observed_paths (IBusComponent *component) { g_assert (IBUS_IS_COMPONENT (component)); return g_list_copy (component->priv->observed_paths); }