6d21b66d679190e189c00b6483b181d467b7c0ed
[platform/upstream/ibus.git] / src / ibuscomponent.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* bus - The Input Bus
4  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2008-2010 Red Hat, Inc.
6  *
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.
11  *
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.
16  *
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.
21  */
22 #include <glib/gstdio.h>
23 #include "ibuscomponent.h"
24
25 enum {
26     LAST_SIGNAL,
27 };
28
29 enum {
30     PROP_0 = 0,
31     PROP_NAME,
32     PROP_DESCRIPTION,
33     PROP_VERSION,
34     PROP_LICENSE,
35     PROP_AUTHOR,
36     PROP_HOMEPAGE,
37     PROP_COMMAND_LINE,
38     PROP_TEXTDOMAIN,
39 };
40
41 /* IBusComponentPriv */
42 struct _IBusComponentPrivate {
43     gchar *name;
44     gchar *description;
45     gchar *version;
46     gchar *license;
47     gchar *author;
48     gchar *homepage;
49     gchar *exec;
50     gchar *textdomain;
51
52     /* engines */
53     GList *engines;
54
55     /* observed paths */
56     GList *observed_paths;
57 };
58
59 #define IBUS_COMPONENT_GET_PRIVATE(o)  \
60    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_COMPONENT, IBusComponentPrivate))
61
62 // static guint            _signals[LAST_SIGNAL] = { 0 };
63
64 /* functions prototype */
65 static void         ibus_component_set_property (IBusComponent          *component,
66                                                  guint                   prop_id,
67                                                  const GValue           *value,
68                                                  GParamSpec             *pspec);
69 static void         ibus_component_get_property (IBusComponent          *component,
70                                                  guint                   prop_id,
71                                                  GValue                 *value,
72                                                  GParamSpec             *pspec);
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,
77                                                  GVariant               *variant);
78 static gboolean     ibus_component_copy         (IBusComponent          *dest,
79                                                  const IBusComponent    *src);
80 static gboolean     ibus_component_parse_xml_node
81                                                 (IBusComponent          *component,
82                                                  XMLNode                *node,
83                                                  gboolean                access_fs);
84
85 static void         ibus_component_parse_engines(IBusComponent          *component,
86                                                  XMLNode                *node);
87 static void         ibus_component_parse_observed_paths
88                                                 (IBusComponent          *component,
89                                                  XMLNode                *node,
90                                                  gboolean                access_fs);
91
92 G_DEFINE_TYPE (IBusComponent, ibus_component, IBUS_TYPE_SERIALIZABLE)
93
94 static void
95 ibus_component_class_init (IBusComponentClass *class)
96 {
97     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
98     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
99     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
100
101     g_type_class_add_private (class, sizeof (IBusComponentPrivate));
102
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;
106
107     serializable_class->serialize   = (IBusSerializableSerializeFunc) ibus_component_serialize;
108     serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_component_deserialize;
109     serializable_class->copy        = (IBusSerializableCopyFunc) ibus_component_copy;
110
111     /* install properties */
112     /**
113      * IBusComponent:name:
114      *
115      * The name of component
116      */
117     g_object_class_install_property (gobject_class,
118                     PROP_NAME,
119                     g_param_spec_string ("name",
120                         "component name",
121                         "The name of component",
122                         NULL,
123                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
124
125     /**
126      * IBusComponent:description:
127      *
128      * The description of component
129      */
130     g_object_class_install_property (gobject_class,
131                     PROP_DESCRIPTION,
132                     g_param_spec_string ("description",
133                         "component description",
134                         "The description of component",
135                         NULL,
136                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
137
138     /**
139      * IBusComponent:version:
140      *
141      * The version of component
142      */
143     g_object_class_install_property (gobject_class,
144                     PROP_VERSION,
145                     g_param_spec_string ("version",
146                         "component version",
147                         "The version of component",
148                         NULL,
149                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
150
151     /**
152      * IBusComponent:license:
153      *
154      * The license of component
155      */
156     g_object_class_install_property (gobject_class,
157                     PROP_LICENSE,
158                     g_param_spec_string ("license",
159                         "component license",
160                         "The license of component",
161                         NULL,
162                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
163
164     /**
165      * IBusComponent:author:
166      *
167      * The author of component
168      */
169     g_object_class_install_property (gobject_class,
170                     PROP_AUTHOR,
171                     g_param_spec_string ("author",
172                         "component author",
173                         "The author of component",
174                         NULL,
175                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
176
177     /**
178      * IBusComponent:homepage:
179      *
180      * The homepage of component
181      */
182     g_object_class_install_property (gobject_class,
183                     PROP_HOMEPAGE,
184                     g_param_spec_string ("homepage",
185                         "component homepage",
186                         "The homepage of component",
187                         NULL,
188                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
189
190     /**
191      * IBusComponent:command-line:
192      *
193      * The exec path of component
194      */
195     g_object_class_install_property (gobject_class,
196                     PROP_COMMAND_LINE,
197                     g_param_spec_string ("command-line",
198                         "component command-line",
199                         "The command line of component",
200                         NULL,
201                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
202
203     /**
204      * IBusComponent:textdomain:
205      *
206      * The textdomain of component
207      */
208     g_object_class_install_property (gobject_class,
209                     PROP_TEXTDOMAIN,
210                     g_param_spec_string ("textdomain",
211                         "component textdomain",
212                         "The textdomain path of component",
213                         NULL,
214                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
215 }
216
217
218 static void
219 ibus_component_init (IBusComponent *component)
220 {
221     component->priv = IBUS_COMPONENT_GET_PRIVATE (component);
222
223     /* FIXME: Is it necessary? */
224 #if 0
225     component->priv->engines = NULL;
226     component->priv->observed_paths = NULL;
227
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;
236 #endif
237 }
238
239 static void
240 ibus_component_destroy (IBusComponent *component)
241 {
242     GList *p;
243
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);
252
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;
261
262     g_list_free_full (component->priv->observed_paths, g_object_unref);
263     component->priv->observed_paths = NULL;
264
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);
269     }
270     g_list_free (component->priv->engines);
271     component->priv->engines = NULL;
272
273     IBUS_OBJECT_CLASS (ibus_component_parent_class)->destroy (IBUS_OBJECT (component));
274 }
275
276 static void
277 ibus_component_set_property (IBusComponent *component,
278                              guint          prop_id,
279                              const GValue  *value,
280                              GParamSpec    *pspec)
281 {
282     switch (prop_id) {
283     case PROP_NAME:
284         g_assert (component->priv->name == NULL);
285         component->priv->name = g_value_dup_string (value);
286         break;
287     case PROP_DESCRIPTION:
288         g_assert (component->priv->description == NULL);
289         component->priv->description = g_value_dup_string (value);
290         break;
291     case PROP_VERSION:
292         g_assert (component->priv->version == NULL);
293         component->priv->version = g_value_dup_string (value);
294         break;
295     case PROP_LICENSE:
296         g_assert (component->priv->license == NULL);
297         component->priv->license = g_value_dup_string (value);
298         break;
299     case PROP_AUTHOR:
300         g_assert (component->priv->author == NULL);
301         component->priv->author = g_value_dup_string (value);
302         break;
303     case PROP_HOMEPAGE:
304         g_assert (component->priv->homepage == NULL);
305         component->priv->homepage = g_value_dup_string (value);
306         break;
307     case PROP_COMMAND_LINE:
308         g_assert (component->priv->exec == NULL);
309         component->priv->exec = g_value_dup_string (value);
310         break;
311     case PROP_TEXTDOMAIN:
312         g_assert (component->priv->textdomain == NULL);
313         component->priv->textdomain = g_value_dup_string (value);
314         break;
315     default:
316         G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
317     }
318 }
319
320 static void
321 ibus_component_get_property (IBusComponent *component,
322                              guint          prop_id,
323                              GValue        *value,
324                              GParamSpec    *pspec)
325 {
326     switch (prop_id) {
327     case PROP_NAME:
328         g_value_set_string (value, ibus_component_get_name (component));
329         break;
330     case PROP_DESCRIPTION:
331         g_value_set_string (value, ibus_component_get_description (component));
332         break;
333     case PROP_VERSION:
334         g_value_set_string (value, ibus_component_get_version (component));
335         break;
336     case PROP_LICENSE:
337         g_value_set_string (value, ibus_component_get_license (component));
338         break;
339     case PROP_AUTHOR:
340         g_value_set_string (value, ibus_component_get_author (component));
341         break;
342     case PROP_HOMEPAGE:
343         g_value_set_string (value, ibus_component_get_homepage (component));
344         break;
345     case PROP_COMMAND_LINE:
346         g_value_set_string (value, ibus_component_get_exec (component));
347         break;
348     case PROP_TEXTDOMAIN:
349         g_value_set_string (value, ibus_component_get_textdomain (component));
350         break;
351     default:
352         G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
353     }
354 }
355
356 static gboolean
357 ibus_component_serialize (IBusComponent   *component,
358                           GVariantBuilder *builder)
359 {
360     gboolean retval;
361
362     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->serialize ((IBusSerializable *)component, builder);
363     g_return_val_if_fail (retval, FALSE);
364
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);
373
374     GList *p;
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));
380     }
381     g_variant_builder_add (builder, "av", array);
382
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));
387     }
388     g_variant_builder_add (builder, "av", array);
389
390     return TRUE;
391 }
392
393 static gint
394 ibus_component_deserialize (IBusComponent   *component,
395                             GVariant        *variant)
396 {
397     gboolean retval;
398
399     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->deserialize ((IBusSerializable *)component, variant);
400     g_return_val_if_fail (retval, 0);
401
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);
410
411     GVariant *var;
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)));
417     }
418     g_variant_iter_free (iter);
419
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)));
424     }
425     g_variant_iter_free (iter);
426
427     return retval;
428 }
429
430 static gboolean
431 ibus_component_copy (IBusComponent       *dest,
432                      const IBusComponent *src)
433 {
434     gboolean retval;
435
436     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->copy ((IBusSerializable *)dest,
437                                  (IBusSerializable *)src);
438     g_return_val_if_fail (retval, FALSE);
439
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);
448
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);
451
452     dest->priv->engines = g_list_copy (src->priv->engines);
453     g_list_foreach (dest->priv->engines, (GFunc) g_object_ref, NULL);
454
455     return TRUE;
456 }
457
458
459 #define g_string_append_indent(string, indent)  \
460     {                                           \
461         gint i;                                 \
462         for (i = 0; i < (indent); i++) {        \
463             g_string_append (string, "    ");   \
464         }                                       \
465     }
466
467 void
468 ibus_component_output (IBusComponent *component,
469                       GString      *output,
470                       gint          indent)
471 {
472     g_assert (IBUS_IS_COMPONENT (component));
473     GList *p;
474
475     g_string_append_indent (output, indent);
476     g_string_append (output, "<component>\n");
477
478 #define OUTPUT_ENTRY(field, element)                                        \
479     {                                                                       \
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",      \
485                                 escape_text);                               \
486         g_free (escape_text);                                               \
487     }
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);
497 #undef OUTPUT_ENTRY
498 #undef OUTPUT_ENTRY_1
499
500     if (component->priv->observed_paths) {
501         g_string_append_indent (output, indent + 1);
502         g_string_append (output, "<observed-paths>\n");
503
504         for (p = component->priv->observed_paths; p != NULL; p = p->next ) {
505             IBusObservedPath *path = (IBusObservedPath *) p->data;
506
507             g_string_append_indent (output, indent + 2);
508             g_string_append_printf (output, "<path mtime=\"%ld\" >%s</path>\n",
509                                     path->mtime,
510                                     path->path);
511         }
512
513         g_string_append_indent (output, indent + 1);
514         g_string_append (output, "</observed-paths>\n");
515     }
516
517     ibus_component_output_engines (component, output, indent + 1);
518
519     g_string_append_indent (output, indent);
520     g_string_append (output, "</component>\n");
521 }
522
523 void
524 ibus_component_output_engines (IBusComponent  *component,
525                                GString        *output,
526                                gint            indent)
527 {
528     g_assert (IBUS_IS_COMPONENT (component));
529     g_assert (output);
530
531     GList *p;
532
533     g_string_append_indent (output, indent);
534     g_string_append (output, "<engines>\n");
535
536     for (p = component->priv->engines; p != NULL; p = p->next) {
537         ibus_engine_desc_output ((IBusEngineDesc *)p->data, output, indent + 2);
538     }
539
540     g_string_append_indent (output, indent);
541     g_string_append (output, "</engines>\n");
542 }
543
544 static gboolean
545 ibus_component_parse_xml_node (IBusComponent   *component,
546                               XMLNode          *node,
547                               gboolean          access_fs)
548 {
549     g_assert (component);
550     g_assert (node);
551
552     if (G_UNLIKELY (g_strcmp0 (node->name, "component") != 0)) {
553         return FALSE;
554     }
555
556     GList *p;
557     for (p = node->sub_nodes; p != NULL; p = p->next) {
558         XMLNode *sub_node = (XMLNode *)p->data;
559
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);                   \
564             }                                                           \
565             component->priv->field_name = g_strdup (sub_node->text);    \
566             continue;                                                   \
567         }
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);
577 #undef PARSE_ENTRY
578 #undef PARSE_ENTRY_1
579
580         if (g_strcmp0 (sub_node->name, "engines") == 0) {
581             ibus_component_parse_engines (component, sub_node);
582             continue;
583         }
584
585         if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
586             ibus_component_parse_observed_paths (component, sub_node, access_fs);
587             continue;
588         }
589
590         g_warning ("<component> element contains invalidate element <%s>", sub_node->name);
591     }
592
593     return TRUE;
594 }
595
596
597 static void
598 ibus_component_parse_engines (IBusComponent *component,
599                               XMLNode       *node)
600 {
601     g_assert (IBUS_IS_COMPONENT (component));
602     g_assert (node);
603
604     gchar *exec = NULL;
605     gchar **p;
606     XMLNode *engines_node = NULL;
607
608     if (g_strcmp0 (node->name, "engines") != 0) {
609         return;
610     }
611
612     for (p = node->attributes; *p != NULL; p += 2) {
613         if (g_strcmp0 (*p, "exec") == 0) {
614             exec = *(p + 1);
615             break;
616         }
617     }
618
619     if (exec != NULL) {
620         gchar *output = NULL;
621         if (g_spawn_command_line_sync (exec, &output, NULL, NULL, NULL)) {
622             engines_node = ibus_xml_parse_buffer (output);
623             g_free (output);
624
625             if (engines_node) {
626                 if (g_strcmp0 (engines_node->name, "engines") == 0) {
627                     node = engines_node;
628                 }
629             }
630         }
631     }
632
633     GList *pl;
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);
637
638         if (G_UNLIKELY (engine == NULL))
639             continue;
640         ibus_component_add_engine (component, engine);
641     }
642
643     if (engines_node) {
644         ibus_xml_free (engines_node);
645     }
646 }
647
648 static void
649 ibus_component_parse_observed_paths (IBusComponent    *component,
650                                     XMLNode         *node,
651                                     gboolean         access_fs)
652 {
653     g_assert (IBUS_IS_COMPONENT (component));
654     g_assert (node);
655
656     if (g_strcmp0 (node->name, "observed-paths") != 0) {
657         return;
658     }
659
660     GList *p;
661     for (p = node->sub_nodes; p != NULL; p = p->next) {
662         IBusObservedPath *path;
663
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);
667
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));
672         }
673     }
674 }
675
676 #define IBUS_COMPONENT_GET_PROPERTY(property, return_type)  \
677 return_type                                                 \
678 ibus_component_get_ ## property (IBusComponent *component)  \
679 {                                                           \
680     return component->priv->property;                       \
681 }
682
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
692
693 IBusComponent *
694 ibus_component_new (const gchar *name,
695                     const gchar *description,
696                     const gchar *version,
697                     const gchar *license,
698                     const gchar *author,
699                     const gchar *homepage,
700                     const gchar *command_line,
701                     const gchar *textdomain)
702 {
703     return ibus_component_new_varargs ("name", name,
704                                        "description", description,
705                                        "version", version,
706                                        "license", license,
707                                        "author", author,
708                                        "homepage", homepage,
709                                        "command-line", command_line,
710                                        "textdomain", textdomain,
711                                        NULL);
712 }
713
714
715 IBusComponent *
716 ibus_component_new_varargs (const gchar *first_property_name, ...)
717 {
718     va_list var_args;
719     IBusComponent *component;
720     IBusComponentPrivate *priv;
721
722     g_assert (first_property_name);
723
724     va_start (var_args, first_property_name);
725     component = (IBusComponent *) g_object_new_valist (IBUS_TYPE_COMPONENT,
726                                                        first_property_name,
727                                                        var_args);
728     va_end (var_args);
729
730     priv = IBUS_COMPONENT_GET_PRIVATE (component);
731
732     /* name is required. Other properties are set in class_init by default. */
733     g_assert (priv->name);
734
735     return component;
736 }
737
738
739 IBusComponent *
740 ibus_component_new_from_xml_node (XMLNode  *node)
741 {
742     g_assert (node);
743
744     IBusComponent *component;
745
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);
749         component = NULL;
750     }
751
752     return component;
753 }
754
755 IBusComponent *
756 ibus_component_new_from_file (const gchar *filename)
757 {
758     g_assert (filename);
759
760     XMLNode *node;
761     struct stat buf;
762     IBusComponent *component;
763     gboolean retval;
764
765     if (g_stat (filename, &buf) != 0) {
766         g_warning ("Can not get stat of file %s", filename);
767         return NULL;
768     }
769
770     node = ibus_xml_parse_file (filename);
771
772     if (!node) {
773         return NULL;
774     }
775
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);
779
780     if (!retval) {
781         g_object_unref (component);
782         component = NULL;
783     }
784     else {
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);
789     }
790
791     return component;
792 }
793
794 void
795 ibus_component_add_observed_path (IBusComponent *component,
796                                   const gchar   *path,
797                                   gboolean       access_fs)
798 {
799     IBusObservedPath *p;
800
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);
805
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));
810     }
811 }
812
813 void
814 ibus_component_add_engine (IBusComponent  *component,
815                            IBusEngineDesc *engine)
816 {
817     g_assert (IBUS_IS_COMPONENT (component));
818     g_assert (IBUS_IS_ENGINE_DESC (engine));
819
820     g_object_ref_sink (engine);
821     component->priv->engines =
822             g_list_append (component->priv->engines, engine);
823 }
824
825 GList *
826 ibus_component_get_engines (IBusComponent *component)
827 {
828     return g_list_copy (component->priv->engines);
829 }
830
831 gboolean
832 ibus_component_check_modification (IBusComponent *component)
833 {
834     g_assert (IBUS_IS_COMPONENT (component));
835
836     GList *p;
837
838     for (p = component->priv->observed_paths; p != NULL; p = p->next) {
839         if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data))
840             return TRUE;
841     }
842     return FALSE;
843 }