1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2008-2010 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
22 #include <glib/gstdio.h>
23 #include "ibuscomponent.h"
41 /* IBusComponentPriv */
42 struct _IBusComponentPrivate {
56 GList *observed_paths;
59 #define IBUS_COMPONENT_GET_PRIVATE(o) \
60 (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_COMPONENT, IBusComponentPrivate))
62 // static guint _signals[LAST_SIGNAL] = { 0 };
64 /* functions prototype */
65 static void ibus_component_set_property (IBusComponent *component,
69 static void ibus_component_get_property (IBusComponent *component,
73 static void ibus_component_destroy (IBusComponent *component);
74 static gboolean ibus_component_serialize (IBusComponent *component,
75 GVariantBuilder *builder);
76 static gint ibus_component_deserialize (IBusComponent *component,
78 static gboolean ibus_component_copy (IBusComponent *dest,
79 const IBusComponent *src);
80 static gboolean ibus_component_parse_xml_node
81 (IBusComponent *component,
85 static void ibus_component_parse_engines(IBusComponent *component,
87 static void ibus_component_parse_observed_paths
88 (IBusComponent *component,
92 G_DEFINE_TYPE (IBusComponent, ibus_component, IBUS_TYPE_SERIALIZABLE)
95 ibus_component_class_init (IBusComponentClass *class)
97 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
98 IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
99 IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
101 g_type_class_add_private (class, sizeof (IBusComponentPrivate));
103 gobject_class->set_property = (GObjectSetPropertyFunc) ibus_component_set_property;
104 gobject_class->get_property = (GObjectGetPropertyFunc) ibus_component_get_property;
105 object_class->destroy = (IBusObjectDestroyFunc) ibus_component_destroy;
107 serializable_class->serialize = (IBusSerializableSerializeFunc) ibus_component_serialize;
108 serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_component_deserialize;
109 serializable_class->copy = (IBusSerializableCopyFunc) ibus_component_copy;
111 /* install properties */
113 * IBusComponent:name:
115 * The name of component
117 g_object_class_install_property (gobject_class,
119 g_param_spec_string ("name",
121 "The name of component",
123 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
126 * IBusComponent:description:
128 * The description of component
130 g_object_class_install_property (gobject_class,
132 g_param_spec_string ("description",
133 "component description",
134 "The description of component",
136 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
139 * IBusComponent:version:
141 * The version of component
143 g_object_class_install_property (gobject_class,
145 g_param_spec_string ("version",
147 "The version of component",
149 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
152 * IBusComponent:license:
154 * The license of component
156 g_object_class_install_property (gobject_class,
158 g_param_spec_string ("license",
160 "The license of component",
162 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
165 * IBusComponent:author:
167 * The author of component
169 g_object_class_install_property (gobject_class,
171 g_param_spec_string ("author",
173 "The author of component",
175 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
178 * IBusComponent:homepage:
180 * The homepage of component
182 g_object_class_install_property (gobject_class,
184 g_param_spec_string ("homepage",
185 "component homepage",
186 "The homepage of component",
188 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
191 * IBusComponent:command-line:
193 * The exec path of component
195 g_object_class_install_property (gobject_class,
197 g_param_spec_string ("command-line",
198 "component command-line",
199 "The command line of component",
201 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
204 * IBusComponent:textdomain:
206 * The textdomain of component
208 g_object_class_install_property (gobject_class,
210 g_param_spec_string ("textdomain",
211 "component textdomain",
212 "The textdomain path of component",
214 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
219 ibus_component_init (IBusComponent *component)
221 component->priv = IBUS_COMPONENT_GET_PRIVATE (component);
223 /* FIXME: Is it necessary? */
225 component->priv->engines = NULL;
226 component->priv->observed_paths = NULL;
228 component->priv->name = NULL;
229 component->priv->description = NULL;
230 component->priv->version = NULL;
231 component->priv->license = NULL;
232 component->priv->author = NULL;
233 component->priv->homepage = NULL;
234 component->priv->exec = NULL;
235 component->priv->textdomain = NULL;
240 ibus_component_destroy (IBusComponent *component)
244 g_free (component->priv->name);
245 g_free (component->priv->description);
246 g_free (component->priv->version);
247 g_free (component->priv->license);
248 g_free (component->priv->author);
249 g_free (component->priv->homepage);
250 g_free (component->priv->exec);
251 g_free (component->priv->textdomain);
253 component->priv->name = NULL;
254 component->priv->description = NULL;
255 component->priv->version = NULL;
256 component->priv->license = NULL;
257 component->priv->author = NULL;
258 component->priv->homepage = NULL;
259 component->priv->exec = NULL;
260 component->priv->textdomain = NULL;
262 g_list_free_full (component->priv->observed_paths, g_object_unref);
263 component->priv->observed_paths = NULL;
265 for (p = component->priv->engines; p != NULL; p = p->next) {
266 g_object_steal_data ((GObject *)p->data, "component");
267 ibus_object_destroy ((IBusObject *)p->data);
268 g_object_unref (p->data);
270 g_list_free (component->priv->engines);
271 component->priv->engines = NULL;
273 IBUS_OBJECT_CLASS (ibus_component_parent_class)->destroy (IBUS_OBJECT (component));
277 ibus_component_set_property (IBusComponent *component,
284 g_assert (component->priv->name == NULL);
285 component->priv->name = g_value_dup_string (value);
287 case PROP_DESCRIPTION:
288 g_assert (component->priv->description == NULL);
289 component->priv->description = g_value_dup_string (value);
292 g_assert (component->priv->version == NULL);
293 component->priv->version = g_value_dup_string (value);
296 g_assert (component->priv->license == NULL);
297 component->priv->license = g_value_dup_string (value);
300 g_assert (component->priv->author == NULL);
301 component->priv->author = g_value_dup_string (value);
304 g_assert (component->priv->homepage == NULL);
305 component->priv->homepage = g_value_dup_string (value);
307 case PROP_COMMAND_LINE:
308 g_assert (component->priv->exec == NULL);
309 component->priv->exec = g_value_dup_string (value);
311 case PROP_TEXTDOMAIN:
312 g_assert (component->priv->textdomain == NULL);
313 component->priv->textdomain = g_value_dup_string (value);
316 G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
321 ibus_component_get_property (IBusComponent *component,
328 g_value_set_string (value, ibus_component_get_name (component));
330 case PROP_DESCRIPTION:
331 g_value_set_string (value, ibus_component_get_description (component));
334 g_value_set_string (value, ibus_component_get_version (component));
337 g_value_set_string (value, ibus_component_get_license (component));
340 g_value_set_string (value, ibus_component_get_author (component));
343 g_value_set_string (value, ibus_component_get_homepage (component));
345 case PROP_COMMAND_LINE:
346 g_value_set_string (value, ibus_component_get_exec (component));
348 case PROP_TEXTDOMAIN:
349 g_value_set_string (value, ibus_component_get_textdomain (component));
352 G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
357 ibus_component_serialize (IBusComponent *component,
358 GVariantBuilder *builder)
362 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->serialize ((IBusSerializable *)component, builder);
363 g_return_val_if_fail (retval, FALSE);
365 g_variant_builder_add (builder, "s", component->priv->name);
366 g_variant_builder_add (builder, "s", component->priv->description);
367 g_variant_builder_add (builder, "s", component->priv->version);
368 g_variant_builder_add (builder, "s", component->priv->license);
369 g_variant_builder_add (builder, "s", component->priv->author);
370 g_variant_builder_add (builder, "s", component->priv->homepage);
371 g_variant_builder_add (builder, "s", component->priv->exec);
372 g_variant_builder_add (builder, "s", component->priv->textdomain);
375 GVariantBuilder *array;
376 /* serialize observed paths */
377 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
378 for (p = component->priv->observed_paths; p != NULL; p = p->next) {
379 g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
381 g_variant_builder_add (builder, "av", array);
383 /* serialize engine desc list */
384 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
385 for (p = component->priv->engines; p != NULL; p = p->next) {
386 g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
388 g_variant_builder_add (builder, "av", array);
394 ibus_component_deserialize (IBusComponent *component,
399 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->deserialize ((IBusSerializable *)component, variant);
400 g_return_val_if_fail (retval, 0);
402 g_variant_get_child (variant, retval++, "s", &component->priv->name);
403 g_variant_get_child (variant, retval++, "s", &component->priv->description);
404 g_variant_get_child (variant, retval++, "s", &component->priv->version);
405 g_variant_get_child (variant, retval++, "s", &component->priv->license);
406 g_variant_get_child (variant, retval++, "s", &component->priv->author);
407 g_variant_get_child (variant, retval++, "s", &component->priv->homepage);
408 g_variant_get_child (variant, retval++, "s", &component->priv->exec);
409 g_variant_get_child (variant, retval++, "s", &component->priv->textdomain);
412 GVariantIter *iter = NULL;
413 g_variant_get_child (variant, retval++, "av", &iter);
414 while (g_variant_iter_loop (iter, "v", &var)) {
415 component->priv->observed_paths = g_list_append (component->priv->observed_paths,
416 IBUS_OBSERVED_PATH (ibus_serializable_deserialize (var)));
418 g_variant_iter_free (iter);
420 g_variant_get_child (variant, retval++, "av", &iter);
421 while (g_variant_iter_loop (iter, "v", &var)) {
422 ibus_component_add_engine (component,
423 IBUS_ENGINE_DESC (ibus_serializable_deserialize (var)));
425 g_variant_iter_free (iter);
431 ibus_component_copy (IBusComponent *dest,
432 const IBusComponent *src)
436 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->copy ((IBusSerializable *)dest,
437 (IBusSerializable *)src);
438 g_return_val_if_fail (retval, FALSE);
440 dest->priv->name = g_strdup (src->priv->name);
441 dest->priv->description = g_strdup (src->priv->description);
442 dest->priv->version = g_strdup (src->priv->version);
443 dest->priv->license = g_strdup (src->priv->license);
444 dest->priv->author = g_strdup (src->priv->author);
445 dest->priv->homepage = g_strdup (src->priv->homepage);
446 dest->priv->exec = g_strdup (src->priv->exec);
447 dest->priv->textdomain = g_strdup (src->priv->textdomain);
449 dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
450 g_list_foreach (dest->priv->observed_paths, (GFunc) g_object_ref, NULL);
452 dest->priv->engines = g_list_copy (src->priv->engines);
453 g_list_foreach (dest->priv->engines, (GFunc) g_object_ref, NULL);
459 #define g_string_append_indent(string, indent) \
462 for (i = 0; i < (indent); i++) { \
463 g_string_append (string, " "); \
468 ibus_component_output (IBusComponent *component,
472 g_assert (IBUS_IS_COMPONENT (component));
475 g_string_append_indent (output, indent);
476 g_string_append (output, "<component>\n");
478 #define OUTPUT_ENTRY(field, element) \
480 gchar *escape_text = \
481 g_markup_escape_text (component->priv->field ? \
482 component->priv->field : "", -1); \
483 g_string_append_indent (output, indent + 1); \
484 g_string_append_printf (output, "<"element">%s</"element">\n", \
486 g_free (escape_text); \
488 #define OUTPUT_ENTRY_1(name) OUTPUT_ENTRY(name, #name)
489 OUTPUT_ENTRY_1 (name);
490 OUTPUT_ENTRY_1 (description);
491 OUTPUT_ENTRY_1 (version);
492 OUTPUT_ENTRY_1 (license);
493 OUTPUT_ENTRY_1 (author);
494 OUTPUT_ENTRY_1 (homepage);
495 OUTPUT_ENTRY_1 (exec);
496 OUTPUT_ENTRY_1 (textdomain);
498 #undef OUTPUT_ENTRY_1
500 if (component->priv->observed_paths) {
501 g_string_append_indent (output, indent + 1);
502 g_string_append (output, "<observed-paths>\n");
504 for (p = component->priv->observed_paths; p != NULL; p = p->next ) {
505 IBusObservedPath *path = (IBusObservedPath *) p->data;
507 g_string_append_indent (output, indent + 2);
508 g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
513 g_string_append_indent (output, indent + 1);
514 g_string_append (output, "</observed-paths>\n");
517 ibus_component_output_engines (component, output, indent + 1);
519 g_string_append_indent (output, indent);
520 g_string_append (output, "</component>\n");
524 ibus_component_output_engines (IBusComponent *component,
528 g_assert (IBUS_IS_COMPONENT (component));
533 g_string_append_indent (output, indent);
534 g_string_append (output, "<engines>\n");
536 for (p = component->priv->engines; p != NULL; p = p->next) {
537 ibus_engine_desc_output ((IBusEngineDesc *)p->data, output, indent + 2);
540 g_string_append_indent (output, indent);
541 g_string_append (output, "</engines>\n");
545 ibus_component_parse_xml_node (IBusComponent *component,
549 g_assert (component);
552 if (G_UNLIKELY (g_strcmp0 (node->name, "component") != 0)) {
557 for (p = node->sub_nodes; p != NULL; p = p->next) {
558 XMLNode *sub_node = (XMLNode *)p->data;
560 #define PARSE_ENTRY(field_name, element_name) \
561 if (g_strcmp0 (sub_node->name, element_name) == 0) { \
562 if (component->priv->field_name != NULL) { \
563 g_free (component->priv->field_name); \
565 component->priv->field_name = g_strdup (sub_node->text); \
568 #define PARSE_ENTRY_1(name) PARSE_ENTRY (name, #name)
569 PARSE_ENTRY_1 (name);
570 PARSE_ENTRY_1 (description);
571 PARSE_ENTRY_1 (version);
572 PARSE_ENTRY_1 (license);
573 PARSE_ENTRY_1 (author);
574 PARSE_ENTRY_1 (homepage);
575 PARSE_ENTRY_1 (exec);
576 PARSE_ENTRY_1 (textdomain);
580 if (g_strcmp0 (sub_node->name, "engines") == 0) {
581 ibus_component_parse_engines (component, sub_node);
585 if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
586 ibus_component_parse_observed_paths (component, sub_node, access_fs);
590 g_warning ("<component> element contains invalidate element <%s>", sub_node->name);
598 ibus_component_parse_engines (IBusComponent *component,
601 g_assert (IBUS_IS_COMPONENT (component));
606 XMLNode *engines_node = NULL;
608 if (g_strcmp0 (node->name, "engines") != 0) {
612 for (p = node->attributes; *p != NULL; p += 2) {
613 if (g_strcmp0 (*p, "exec") == 0) {
620 gchar *output = NULL;
621 if (g_spawn_command_line_sync (exec, &output, NULL, NULL, NULL)) {
622 engines_node = ibus_xml_parse_buffer (output);
626 if (g_strcmp0 (engines_node->name, "engines") == 0) {
634 for (pl = node->sub_nodes; pl != NULL; pl = pl->next) {
635 IBusEngineDesc *engine;
636 engine = ibus_engine_desc_new_from_xml_node ((XMLNode *)pl->data);
638 if (G_UNLIKELY (engine == NULL))
640 ibus_component_add_engine (component, engine);
644 ibus_xml_free (engines_node);
649 ibus_component_parse_observed_paths (IBusComponent *component,
653 g_assert (IBUS_IS_COMPONENT (component));
656 if (g_strcmp0 (node->name, "observed-paths") != 0) {
661 for (p = node->sub_nodes; p != NULL; p = p->next) {
662 IBusObservedPath *path;
664 path = ibus_observed_path_new_from_xml_node ((XMLNode *)p->data, access_fs);
665 g_object_ref_sink (path);
666 component->priv->observed_paths = g_list_append (component->priv->observed_paths, path);
668 if (access_fs && path->is_dir && path->is_exist) {
669 component->priv->observed_paths =
670 g_list_concat(component->priv->observed_paths,
671 ibus_observed_path_traverse(path));
676 #define IBUS_COMPONENT_GET_PROPERTY(property, return_type) \
678 ibus_component_get_ ## property (IBusComponent *component) \
680 return component->priv->property; \
683 IBUS_COMPONENT_GET_PROPERTY (name, const gchar *)
684 IBUS_COMPONENT_GET_PROPERTY (description, const gchar *)
685 IBUS_COMPONENT_GET_PROPERTY (version, const gchar *)
686 IBUS_COMPONENT_GET_PROPERTY (license, const gchar *)
687 IBUS_COMPONENT_GET_PROPERTY (author, const gchar *)
688 IBUS_COMPONENT_GET_PROPERTY (homepage, const gchar *)
689 IBUS_COMPONENT_GET_PROPERTY (exec, const gchar *)
690 IBUS_COMPONENT_GET_PROPERTY (textdomain, const gchar *)
691 #undef IBUS_COMPONENT_GET_PROPERTY
694 ibus_component_new (const gchar *name,
695 const gchar *description,
696 const gchar *version,
697 const gchar *license,
699 const gchar *homepage,
700 const gchar *command_line,
701 const gchar *textdomain)
703 return ibus_component_new_varargs ("name", name,
704 "description", description,
708 "homepage", homepage,
709 "command-line", command_line,
710 "textdomain", textdomain,
716 ibus_component_new_varargs (const gchar *first_property_name, ...)
719 IBusComponent *component;
720 IBusComponentPrivate *priv;
722 g_assert (first_property_name);
724 va_start (var_args, first_property_name);
725 component = (IBusComponent *) g_object_new_valist (IBUS_TYPE_COMPONENT,
730 priv = IBUS_COMPONENT_GET_PRIVATE (component);
732 /* name is required. Other properties are set in class_init by default. */
733 g_assert (priv->name);
740 ibus_component_new_from_xml_node (XMLNode *node)
744 IBusComponent *component;
746 component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
747 if (!ibus_component_parse_xml_node (component, node, FALSE)) {
748 g_object_unref (component);
756 ibus_component_new_from_file (const gchar *filename)
762 IBusComponent *component;
765 if (g_stat (filename, &buf) != 0) {
766 g_warning ("Can not get stat of file %s", filename);
770 node = ibus_xml_parse_file (filename);
776 component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
777 retval = ibus_component_parse_xml_node (component, node, TRUE);
778 ibus_xml_free (node);
781 g_object_unref (component);
785 IBusObservedPath *path;
786 path = ibus_observed_path_new (filename, TRUE);
787 component->priv->observed_paths =
788 g_list_prepend(component->priv->observed_paths, path);
795 ibus_component_add_observed_path (IBusComponent *component,
801 p = ibus_observed_path_new (path, access_fs);
802 g_object_ref_sink (p);
803 component->priv->observed_paths =
804 g_list_append (component->priv->observed_paths, p);
806 if (access_fs && p->is_dir && p->is_exist) {
807 component->priv->observed_paths =
808 g_list_concat(component->priv->observed_paths,
809 ibus_observed_path_traverse(p));
814 ibus_component_add_engine (IBusComponent *component,
815 IBusEngineDesc *engine)
817 g_assert (IBUS_IS_COMPONENT (component));
818 g_assert (IBUS_IS_ENGINE_DESC (engine));
820 g_object_ref_sink (engine);
821 component->priv->engines =
822 g_list_append (component->priv->engines, engine);
826 ibus_component_get_engines (IBusComponent *component)
828 return g_list_copy (component->priv->engines);
832 ibus_component_check_modification (IBusComponent *component)
834 g_assert (IBUS_IS_COMPONENT (component));
838 for (p = component->priv->observed_paths; p != NULL; p = p->next) {
839 if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data))