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);
225 ibus_component_destroy (IBusComponent *component)
229 g_free (component->priv->name);
230 g_free (component->priv->description);
231 g_free (component->priv->version);
232 g_free (component->priv->license);
233 g_free (component->priv->author);
234 g_free (component->priv->homepage);
235 g_free (component->priv->exec);
236 g_free (component->priv->textdomain);
238 component->priv->name = NULL;
239 component->priv->description = NULL;
240 component->priv->version = NULL;
241 component->priv->license = NULL;
242 component->priv->author = NULL;
243 component->priv->homepage = NULL;
244 component->priv->exec = NULL;
245 component->priv->textdomain = NULL;
247 g_list_free_full (component->priv->observed_paths, g_object_unref);
248 component->priv->observed_paths = NULL;
250 for (p = component->priv->engines; p != NULL; p = p->next) {
251 g_object_steal_data ((GObject *)p->data, "component");
252 ibus_object_destroy ((IBusObject *)p->data);
253 g_object_unref (p->data);
255 g_list_free (component->priv->engines);
256 component->priv->engines = NULL;
258 IBUS_OBJECT_CLASS (ibus_component_parent_class)->destroy (IBUS_OBJECT (component));
262 ibus_component_set_property (IBusComponent *component,
269 g_assert (component->priv->name == NULL);
270 component->priv->name = g_value_dup_string (value);
272 case PROP_DESCRIPTION:
273 g_assert (component->priv->description == NULL);
274 component->priv->description = g_value_dup_string (value);
277 g_assert (component->priv->version == NULL);
278 component->priv->version = g_value_dup_string (value);
281 g_assert (component->priv->license == NULL);
282 component->priv->license = g_value_dup_string (value);
285 g_assert (component->priv->author == NULL);
286 component->priv->author = g_value_dup_string (value);
289 g_assert (component->priv->homepage == NULL);
290 component->priv->homepage = g_value_dup_string (value);
292 case PROP_COMMAND_LINE:
293 g_assert (component->priv->exec == NULL);
294 component->priv->exec = g_value_dup_string (value);
296 case PROP_TEXTDOMAIN:
297 g_assert (component->priv->textdomain == NULL);
298 component->priv->textdomain = g_value_dup_string (value);
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
306 ibus_component_get_property (IBusComponent *component,
313 g_value_set_string (value, ibus_component_get_name (component));
315 case PROP_DESCRIPTION:
316 g_value_set_string (value, ibus_component_get_description (component));
319 g_value_set_string (value, ibus_component_get_version (component));
322 g_value_set_string (value, ibus_component_get_license (component));
325 g_value_set_string (value, ibus_component_get_author (component));
328 g_value_set_string (value, ibus_component_get_homepage (component));
330 case PROP_COMMAND_LINE:
331 g_value_set_string (value, ibus_component_get_exec (component));
333 case PROP_TEXTDOMAIN:
334 g_value_set_string (value, ibus_component_get_textdomain (component));
337 G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
342 ibus_component_serialize (IBusComponent *component,
343 GVariantBuilder *builder)
347 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->serialize ((IBusSerializable *)component, builder);
348 g_return_val_if_fail (retval, FALSE);
350 g_variant_builder_add (builder, "s", component->priv->name);
351 g_variant_builder_add (builder, "s", component->priv->description);
352 g_variant_builder_add (builder, "s", component->priv->version);
353 g_variant_builder_add (builder, "s", component->priv->license);
354 g_variant_builder_add (builder, "s", component->priv->author);
355 g_variant_builder_add (builder, "s", component->priv->homepage);
356 g_variant_builder_add (builder, "s", component->priv->exec);
357 g_variant_builder_add (builder, "s", component->priv->textdomain);
360 GVariantBuilder *array;
361 /* serialize observed paths */
362 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
363 for (p = component->priv->observed_paths; p != NULL; p = p->next) {
364 g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
366 g_variant_builder_add (builder, "av", array);
368 /* serialize engine desc list */
369 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
370 for (p = component->priv->engines; p != NULL; p = p->next) {
371 g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
373 g_variant_builder_add (builder, "av", array);
379 ibus_component_deserialize (IBusComponent *component,
384 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->deserialize ((IBusSerializable *)component, variant);
385 g_return_val_if_fail (retval, 0);
387 g_variant_get_child (variant, retval++, "s", &component->priv->name);
388 g_variant_get_child (variant, retval++, "s", &component->priv->description);
389 g_variant_get_child (variant, retval++, "s", &component->priv->version);
390 g_variant_get_child (variant, retval++, "s", &component->priv->license);
391 g_variant_get_child (variant, retval++, "s", &component->priv->author);
392 g_variant_get_child (variant, retval++, "s", &component->priv->homepage);
393 g_variant_get_child (variant, retval++, "s", &component->priv->exec);
394 g_variant_get_child (variant, retval++, "s", &component->priv->textdomain);
397 GVariantIter *iter = NULL;
398 g_variant_get_child (variant, retval++, "av", &iter);
399 while (g_variant_iter_loop (iter, "v", &var)) {
400 component->priv->observed_paths = g_list_append (component->priv->observed_paths,
401 IBUS_OBSERVED_PATH (ibus_serializable_deserialize (var)));
403 g_variant_iter_free (iter);
405 g_variant_get_child (variant, retval++, "av", &iter);
406 while (g_variant_iter_loop (iter, "v", &var)) {
407 ibus_component_add_engine (component,
408 IBUS_ENGINE_DESC (ibus_serializable_deserialize (var)));
410 g_variant_iter_free (iter);
416 ibus_component_copy (IBusComponent *dest,
417 const IBusComponent *src)
421 retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->copy ((IBusSerializable *)dest,
422 (IBusSerializable *)src);
423 g_return_val_if_fail (retval, FALSE);
425 dest->priv->name = g_strdup (src->priv->name);
426 dest->priv->description = g_strdup (src->priv->description);
427 dest->priv->version = g_strdup (src->priv->version);
428 dest->priv->license = g_strdup (src->priv->license);
429 dest->priv->author = g_strdup (src->priv->author);
430 dest->priv->homepage = g_strdup (src->priv->homepage);
431 dest->priv->exec = g_strdup (src->priv->exec);
432 dest->priv->textdomain = g_strdup (src->priv->textdomain);
434 dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
435 g_list_foreach (dest->priv->observed_paths, (GFunc) g_object_ref, NULL);
437 dest->priv->engines = g_list_copy (src->priv->engines);
438 g_list_foreach (dest->priv->engines, (GFunc) g_object_ref, NULL);
444 #define g_string_append_indent(string, indent) \
447 for (i = 0; i < (indent); i++) { \
448 g_string_append (string, " "); \
453 ibus_component_output (IBusComponent *component,
457 g_assert (IBUS_IS_COMPONENT (component));
460 g_string_append_indent (output, indent);
461 g_string_append (output, "<component>\n");
463 #define OUTPUT_ENTRY(field, element) \
465 gchar *escape_text = \
466 g_markup_escape_text (component->priv->field ? \
467 component->priv->field : "", -1); \
468 g_string_append_indent (output, indent + 1); \
469 g_string_append_printf (output, "<"element">%s</"element">\n", \
471 g_free (escape_text); \
473 #define OUTPUT_ENTRY_1(name) OUTPUT_ENTRY(name, #name)
474 OUTPUT_ENTRY_1 (name);
475 OUTPUT_ENTRY_1 (description);
476 OUTPUT_ENTRY_1 (version);
477 OUTPUT_ENTRY_1 (license);
478 OUTPUT_ENTRY_1 (author);
479 OUTPUT_ENTRY_1 (homepage);
480 OUTPUT_ENTRY_1 (exec);
481 OUTPUT_ENTRY_1 (textdomain);
483 #undef OUTPUT_ENTRY_1
485 if (component->priv->observed_paths) {
486 g_string_append_indent (output, indent + 1);
487 g_string_append (output, "<observed-paths>\n");
489 for (p = component->priv->observed_paths; p != NULL; p = p->next ) {
490 IBusObservedPath *path = (IBusObservedPath *) p->data;
492 g_string_append_indent (output, indent + 2);
493 g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
498 g_string_append_indent (output, indent + 1);
499 g_string_append (output, "</observed-paths>\n");
502 ibus_component_output_engines (component, output, indent + 1);
504 g_string_append_indent (output, indent);
505 g_string_append (output, "</component>\n");
509 ibus_component_output_engines (IBusComponent *component,
513 g_assert (IBUS_IS_COMPONENT (component));
518 g_string_append_indent (output, indent);
519 g_string_append (output, "<engines>\n");
521 for (p = component->priv->engines; p != NULL; p = p->next) {
522 ibus_engine_desc_output ((IBusEngineDesc *)p->data, output, indent + 2);
525 g_string_append_indent (output, indent);
526 g_string_append (output, "</engines>\n");
530 ibus_component_parse_xml_node (IBusComponent *component,
534 g_assert (component);
537 if (G_UNLIKELY (g_strcmp0 (node->name, "component") != 0)) {
542 for (p = node->sub_nodes; p != NULL; p = p->next) {
543 XMLNode *sub_node = (XMLNode *)p->data;
545 #define PARSE_ENTRY(field_name, element_name) \
546 if (g_strcmp0 (sub_node->name, element_name) == 0) { \
547 if (component->priv->field_name != NULL) { \
548 g_free (component->priv->field_name); \
550 component->priv->field_name = g_strdup (sub_node->text); \
553 #define PARSE_ENTRY_1(name) PARSE_ENTRY (name, #name)
554 PARSE_ENTRY_1 (name);
555 PARSE_ENTRY_1 (description);
556 PARSE_ENTRY_1 (version);
557 PARSE_ENTRY_1 (license);
558 PARSE_ENTRY_1 (author);
559 PARSE_ENTRY_1 (homepage);
560 PARSE_ENTRY_1 (exec);
561 PARSE_ENTRY_1 (textdomain);
565 if (g_strcmp0 (sub_node->name, "engines") == 0) {
566 ibus_component_parse_engines (component, sub_node);
570 if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
571 ibus_component_parse_observed_paths (component, sub_node, access_fs);
575 g_warning ("<component> element contains invalidate element <%s>", sub_node->name);
583 ibus_component_parse_engines (IBusComponent *component,
586 g_assert (IBUS_IS_COMPONENT (component));
591 XMLNode *engines_node = NULL;
593 if (g_strcmp0 (node->name, "engines") != 0) {
597 for (p = node->attributes; *p != NULL; p += 2) {
598 if (g_strcmp0 (*p, "exec") == 0) {
605 gchar *output = NULL;
606 if (g_spawn_command_line_sync (exec, &output, NULL, NULL, NULL)) {
607 engines_node = ibus_xml_parse_buffer (output);
611 if (g_strcmp0 (engines_node->name, "engines") == 0) {
619 for (pl = node->sub_nodes; pl != NULL; pl = pl->next) {
620 IBusEngineDesc *engine;
621 engine = ibus_engine_desc_new_from_xml_node ((XMLNode *)pl->data);
623 if (G_UNLIKELY (engine == NULL))
625 ibus_component_add_engine (component, engine);
629 ibus_xml_free (engines_node);
634 ibus_component_parse_observed_paths (IBusComponent *component,
638 g_assert (IBUS_IS_COMPONENT (component));
641 if (g_strcmp0 (node->name, "observed-paths") != 0) {
646 for (p = node->sub_nodes; p != NULL; p = p->next) {
647 IBusObservedPath *path;
649 path = ibus_observed_path_new_from_xml_node ((XMLNode *)p->data, access_fs);
650 g_object_ref_sink (path);
651 component->priv->observed_paths = g_list_append (component->priv->observed_paths, path);
653 if (access_fs && path->is_dir && path->is_exist) {
654 component->priv->observed_paths =
655 g_list_concat (component->priv->observed_paths,
656 ibus_observed_path_traverse (path, TRUE));
661 #define IBUS_COMPONENT_GET_PROPERTY(property, return_type) \
663 ibus_component_get_ ## property (IBusComponent *component) \
665 return component->priv->property; \
668 IBUS_COMPONENT_GET_PROPERTY (name, const gchar *)
669 IBUS_COMPONENT_GET_PROPERTY (description, const gchar *)
670 IBUS_COMPONENT_GET_PROPERTY (version, const gchar *)
671 IBUS_COMPONENT_GET_PROPERTY (license, const gchar *)
672 IBUS_COMPONENT_GET_PROPERTY (author, const gchar *)
673 IBUS_COMPONENT_GET_PROPERTY (homepage, const gchar *)
674 IBUS_COMPONENT_GET_PROPERTY (exec, const gchar *)
675 IBUS_COMPONENT_GET_PROPERTY (textdomain, const gchar *)
676 #undef IBUS_COMPONENT_GET_PROPERTY
679 ibus_component_new (const gchar *name,
680 const gchar *description,
681 const gchar *version,
682 const gchar *license,
684 const gchar *homepage,
685 const gchar *command_line,
686 const gchar *textdomain)
688 return ibus_component_new_varargs ("name", name,
689 "description", description,
693 "homepage", homepage,
694 "command-line", command_line,
695 "textdomain", textdomain,
701 ibus_component_new_varargs (const gchar *first_property_name, ...)
704 IBusComponent *component;
705 IBusComponentPrivate *priv;
707 g_assert (first_property_name);
709 va_start (var_args, first_property_name);
710 component = (IBusComponent *) g_object_new_valist (IBUS_TYPE_COMPONENT,
715 priv = IBUS_COMPONENT_GET_PRIVATE (component);
717 /* name is required. Other properties are set in class_init by default. */
718 g_assert (priv->name);
725 ibus_component_new_from_xml_node (XMLNode *node)
729 IBusComponent *component;
731 component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
732 if (!ibus_component_parse_xml_node (component, node, FALSE)) {
733 g_object_unref (component);
741 ibus_component_new_from_file (const gchar *filename)
747 IBusComponent *component;
750 if (g_stat (filename, &buf) != 0) {
751 g_warning ("Can not get stat of file %s", filename);
755 node = ibus_xml_parse_file (filename);
761 component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
762 retval = ibus_component_parse_xml_node (component, node, TRUE);
763 ibus_xml_free (node);
766 g_object_unref (component);
770 IBusObservedPath *path;
771 path = ibus_observed_path_new (filename, TRUE);
772 component->priv->observed_paths =
773 g_list_prepend(component->priv->observed_paths, path);
780 ibus_component_add_observed_path (IBusComponent *component,
786 p = ibus_observed_path_new (path, access_fs);
787 g_object_ref_sink (p);
788 component->priv->observed_paths =
789 g_list_append (component->priv->observed_paths, p);
791 if (access_fs && p->is_dir && p->is_exist) {
792 component->priv->observed_paths =
793 g_list_concat (component->priv->observed_paths,
794 ibus_observed_path_traverse (p, TRUE));
799 ibus_component_add_engine (IBusComponent *component,
800 IBusEngineDesc *engine)
802 g_assert (IBUS_IS_COMPONENT (component));
803 g_assert (IBUS_IS_ENGINE_DESC (engine));
805 g_object_ref_sink (engine);
806 component->priv->engines =
807 g_list_append (component->priv->engines, engine);
811 ibus_component_get_engines (IBusComponent *component)
813 return g_list_copy (component->priv->engines);
817 ibus_component_check_modification (IBusComponent *component)
819 g_assert (IBUS_IS_COMPONENT (component));
823 for (p = component->priv->observed_paths; p != NULL; p = p->next) {
824 if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data))
831 ibus_component_get_observed_paths (IBusComponent *component)
833 g_assert (IBUS_IS_COMPONENT (component));
834 return g_list_copy (component->priv->observed_paths);