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) 2020 Takao Fujiwara <takao.fujiwara1@gmail.com>
6  * Copyright (C) 2008-2020 Red Hat, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 #include <glib/gstdio.h>
24 #include "ibuscomponent.h"
25 #include "ibusinternal.h"
26 
27 enum {
28     LAST_SIGNAL,
29 };
30 
31 enum {
32     PROP_0 = 0,
33     PROP_NAME,
34     PROP_DESCRIPTION,
35     PROP_VERSION,
36     PROP_LICENSE,
37     PROP_AUTHOR,
38     PROP_HOMEPAGE,
39     PROP_COMMAND_LINE,
40     PROP_TEXTDOMAIN,
41 };
42 
43 /* IBusComponentPriv */
44 struct _IBusComponentPrivate {
45     gchar *name;
46     gchar *description;
47     gchar *version;
48     gchar *license;
49     gchar *author;
50     gchar *homepage;
51     gchar *exec;
52     gchar *textdomain;
53 
54     /* engines */
55     GList *engines;
56 
57     /* observed paths */
58     GList *observed_paths;
59 };
60 
61 #define IBUS_COMPONENT_GET_PRIVATE(o)  \
62    ((IBusComponentPrivate *)ibus_component_get_instance_private (o))
63 
64 // static guint            _signals[LAST_SIGNAL] = { 0 };
65 
66 /* functions prototype */
67 static void         ibus_component_set_property (IBusComponent          *component,
68                                                  guint                   prop_id,
69                                                  const GValue           *value,
70                                                  GParamSpec             *pspec);
71 static void         ibus_component_get_property (IBusComponent          *component,
72                                                  guint                   prop_id,
73                                                  GValue                 *value,
74                                                  GParamSpec             *pspec);
75 static void         ibus_component_destroy      (IBusComponent          *component);
76 static gboolean     ibus_component_serialize    (IBusComponent          *component,
77                                                  GVariantBuilder        *builder);
78 static gint         ibus_component_deserialize  (IBusComponent          *component,
79                                                  GVariant               *variant);
80 static gboolean     ibus_component_copy         (IBusComponent          *dest,
81                                                  const IBusComponent    *src);
82 static gboolean     ibus_component_parse_xml_node
83                                                 (IBusComponent          *component,
84                                                  XMLNode                *node,
85                                                  gboolean                access_fs);
86 
87 static void         ibus_component_parse_engines(IBusComponent          *component,
88                                                  XMLNode                *node);
89 static void         ibus_component_parse_observed_paths
90                                                 (IBusComponent          *component,
91                                                  XMLNode                *node,
92                                                  gboolean                access_fs);
93 
G_DEFINE_TYPE_WITH_PRIVATE(IBusComponent,ibus_component,IBUS_TYPE_SERIALIZABLE)94 G_DEFINE_TYPE_WITH_PRIVATE (IBusComponent,
95                             ibus_component,
96                             IBUS_TYPE_SERIALIZABLE)
97 
98 static void
99 ibus_component_class_init (IBusComponentClass *class)
100 {
101     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
102     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
103     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
104 
105     gobject_class->set_property = (GObjectSetPropertyFunc) ibus_component_set_property;
106     gobject_class->get_property = (GObjectGetPropertyFunc) ibus_component_get_property;
107     object_class->destroy = (IBusObjectDestroyFunc) ibus_component_destroy;
108 
109     serializable_class->serialize   = (IBusSerializableSerializeFunc) ibus_component_serialize;
110     serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_component_deserialize;
111     serializable_class->copy        = (IBusSerializableCopyFunc) ibus_component_copy;
112 
113     /* install properties */
114     /**
115      * IBusComponent:name:
116      *
117      * The name of component
118      */
119     g_object_class_install_property (gobject_class,
120                     PROP_NAME,
121                     g_param_spec_string ("name",
122                         "component name",
123                         "The name of component",
124                         NULL,
125                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
126 
127     /**
128      * IBusComponent:description:
129      *
130      * The description of component
131      */
132     g_object_class_install_property (gobject_class,
133                     PROP_DESCRIPTION,
134                     g_param_spec_string ("description",
135                         "component description",
136                         "The description of component",
137                         NULL,
138                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
139 
140     /**
141      * IBusComponent:version:
142      *
143      * The version of component
144      */
145     g_object_class_install_property (gobject_class,
146                     PROP_VERSION,
147                     g_param_spec_string ("version",
148                         "component version",
149                         "The version of component",
150                         NULL,
151                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
152 
153     /**
154      * IBusComponent:license:
155      *
156      * The license of component
157      */
158     g_object_class_install_property (gobject_class,
159                     PROP_LICENSE,
160                     g_param_spec_string ("license",
161                         "component license",
162                         "The license of component",
163                         NULL,
164                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
165 
166     /**
167      * IBusComponent:author:
168      *
169      * The author of component
170      */
171     g_object_class_install_property (gobject_class,
172                     PROP_AUTHOR,
173                     g_param_spec_string ("author",
174                         "component author",
175                         "The author of component",
176                         NULL,
177                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
178 
179     /**
180      * IBusComponent:homepage:
181      *
182      * The homepage of component
183      */
184     g_object_class_install_property (gobject_class,
185                     PROP_HOMEPAGE,
186                     g_param_spec_string ("homepage",
187                         "component homepage",
188                         "The homepage of component",
189                         NULL,
190                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
191 
192     /**
193      * IBusComponent:command-line:
194      *
195      * The exec path of component
196      */
197     g_object_class_install_property (gobject_class,
198                     PROP_COMMAND_LINE,
199                     g_param_spec_string ("command-line",
200                         "component command-line",
201                         "The command line of component",
202                         NULL,
203                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
204 
205     /**
206      * IBusComponent:textdomain:
207      *
208      * The textdomain of component
209      */
210     g_object_class_install_property (gobject_class,
211                     PROP_TEXTDOMAIN,
212                     g_param_spec_string ("textdomain",
213                         "component textdomain",
214                         "The textdomain path of component",
215                         NULL,
216                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
217 }
218 
219 
220 static void
ibus_component_init(IBusComponent * component)221 ibus_component_init (IBusComponent *component)
222 {
223     component->priv = IBUS_COMPONENT_GET_PRIVATE (component);
224 }
225 
226 static void
ibus_component_destroy(IBusComponent * component)227 ibus_component_destroy (IBusComponent *component)
228 {
229     GList *p;
230 
231     g_free (component->priv->name);
232     g_free (component->priv->description);
233     g_free (component->priv->version);
234     g_free (component->priv->license);
235     g_free (component->priv->author);
236     g_free (component->priv->homepage);
237     g_free (component->priv->exec);
238     g_free (component->priv->textdomain);
239 
240     component->priv->name = NULL;
241     component->priv->description = NULL;
242     component->priv->version = NULL;
243     component->priv->license = NULL;
244     component->priv->author = NULL;
245     component->priv->homepage = NULL;
246     component->priv->exec = NULL;
247     component->priv->textdomain = NULL;
248 
249     g_list_free_full (component->priv->observed_paths, g_object_unref);
250     component->priv->observed_paths = NULL;
251 
252     for (p = component->priv->engines; p != NULL; p = p->next) {
253         g_object_steal_data ((GObject *)p->data, "component");
254         ibus_object_destroy ((IBusObject *)p->data);
255         g_object_unref (p->data);
256     }
257     g_list_free (component->priv->engines);
258     component->priv->engines = NULL;
259 
260     IBUS_OBJECT_CLASS (ibus_component_parent_class)->destroy (IBUS_OBJECT (component));
261 }
262 
263 static void
ibus_component_set_property(IBusComponent * component,guint prop_id,const GValue * value,GParamSpec * pspec)264 ibus_component_set_property (IBusComponent *component,
265                              guint          prop_id,
266                              const GValue  *value,
267                              GParamSpec    *pspec)
268 {
269     switch (prop_id) {
270     case PROP_NAME:
271         g_assert (component->priv->name == NULL);
272         component->priv->name = g_value_dup_string (value);
273         break;
274     case PROP_DESCRIPTION:
275         g_assert (component->priv->description == NULL);
276         component->priv->description = g_value_dup_string (value);
277         break;
278     case PROP_VERSION:
279         g_assert (component->priv->version == NULL);
280         component->priv->version = g_value_dup_string (value);
281         break;
282     case PROP_LICENSE:
283         g_assert (component->priv->license == NULL);
284         component->priv->license = g_value_dup_string (value);
285         break;
286     case PROP_AUTHOR:
287         g_assert (component->priv->author == NULL);
288         component->priv->author = g_value_dup_string (value);
289         break;
290     case PROP_HOMEPAGE:
291         g_assert (component->priv->homepage == NULL);
292         component->priv->homepage = g_value_dup_string (value);
293         break;
294     case PROP_COMMAND_LINE:
295         g_assert (component->priv->exec == NULL);
296         component->priv->exec = g_value_dup_string (value);
297         break;
298     case PROP_TEXTDOMAIN:
299         g_assert (component->priv->textdomain == NULL);
300         component->priv->textdomain = g_value_dup_string (value);
301         break;
302     default:
303         G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
304     }
305 }
306 
307 static void
ibus_component_get_property(IBusComponent * component,guint prop_id,GValue * value,GParamSpec * pspec)308 ibus_component_get_property (IBusComponent *component,
309                              guint          prop_id,
310                              GValue        *value,
311                              GParamSpec    *pspec)
312 {
313     switch (prop_id) {
314     case PROP_NAME:
315         g_value_set_string (value, ibus_component_get_name (component));
316         break;
317     case PROP_DESCRIPTION:
318         g_value_set_string (value, ibus_component_get_description (component));
319         break;
320     case PROP_VERSION:
321         g_value_set_string (value, ibus_component_get_version (component));
322         break;
323     case PROP_LICENSE:
324         g_value_set_string (value, ibus_component_get_license (component));
325         break;
326     case PROP_AUTHOR:
327         g_value_set_string (value, ibus_component_get_author (component));
328         break;
329     case PROP_HOMEPAGE:
330         g_value_set_string (value, ibus_component_get_homepage (component));
331         break;
332     case PROP_COMMAND_LINE:
333         g_value_set_string (value, ibus_component_get_exec (component));
334         break;
335     case PROP_TEXTDOMAIN:
336         g_value_set_string (value, ibus_component_get_textdomain (component));
337         break;
338     default:
339         G_OBJECT_WARN_INVALID_PROPERTY_ID (component, prop_id, pspec);
340     }
341 }
342 
343 static gboolean
ibus_component_serialize(IBusComponent * component,GVariantBuilder * builder)344 ibus_component_serialize (IBusComponent   *component,
345                           GVariantBuilder *builder)
346 {
347     gboolean retval;
348 
349     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->serialize ((IBusSerializable *)component, builder);
350     g_return_val_if_fail (retval, FALSE);
351 
352     g_variant_builder_add (builder, "s", component->priv->name);
353     g_variant_builder_add (builder, "s", component->priv->description);
354     g_variant_builder_add (builder, "s", component->priv->version);
355     g_variant_builder_add (builder, "s", component->priv->license);
356     g_variant_builder_add (builder, "s", component->priv->author);
357     g_variant_builder_add (builder, "s", component->priv->homepage);
358     g_variant_builder_add (builder, "s", component->priv->exec);
359     g_variant_builder_add (builder, "s", component->priv->textdomain);
360 
361     GList *p;
362     GVariantBuilder *array;
363     /* serialize observed paths */
364     array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
365     for (p = component->priv->observed_paths; p != NULL; p = p->next) {
366         g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
367     }
368     g_variant_builder_add (builder, "av", array);
369     g_variant_builder_unref (array);
370 
371     /* serialize engine desc list */
372     array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
373     for (p = component->priv->engines; p != NULL; p = p->next) {
374         g_variant_builder_add (array, "v", ibus_serializable_serialize ((IBusSerializable *)p->data));
375     }
376     g_variant_builder_add (builder, "av", array);
377     g_variant_builder_unref (array);
378 
379     return TRUE;
380 }
381 
382 static gint
ibus_component_deserialize(IBusComponent * component,GVariant * variant)383 ibus_component_deserialize (IBusComponent   *component,
384                             GVariant        *variant)
385 {
386     gboolean retval;
387 
388     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->deserialize ((IBusSerializable *)component, variant);
389     g_return_val_if_fail (retval, 0);
390 
391     ibus_g_variant_get_child_string (variant, retval++,
392                                      &component->priv->name);
393     ibus_g_variant_get_child_string (variant, retval++,
394                                      &component->priv->description);
395     ibus_g_variant_get_child_string (variant, retval++,
396                                      &component->priv->version);
397     ibus_g_variant_get_child_string (variant, retval++,
398                                      &component->priv->license);
399     ibus_g_variant_get_child_string (variant, retval++,
400                                      &component->priv->author);
401     ibus_g_variant_get_child_string (variant, retval++,
402                                      &component->priv->homepage);
403     ibus_g_variant_get_child_string (variant, retval++,
404                                      &component->priv->exec);
405     ibus_g_variant_get_child_string (variant, retval++,
406                                      &component->priv->textdomain);
407 
408     GVariant *var;
409     GVariantIter *iter = NULL;
410     g_variant_get_child (variant, retval++, "av", &iter);
411     while (g_variant_iter_loop (iter, "v", &var)) {
412         component->priv->observed_paths = g_list_append (component->priv->observed_paths,
413                         IBUS_OBSERVED_PATH (ibus_serializable_deserialize (var)));
414     }
415     g_variant_iter_free (iter);
416 
417     g_variant_get_child (variant, retval++, "av", &iter);
418     while (g_variant_iter_loop (iter, "v", &var)) {
419         ibus_component_add_engine (component,
420                                    IBUS_ENGINE_DESC (ibus_serializable_deserialize (var)));
421     }
422     g_variant_iter_free (iter);
423 
424     return retval;
425 }
426 
427 static gboolean
ibus_component_copy(IBusComponent * dest,const IBusComponent * src)428 ibus_component_copy (IBusComponent       *dest,
429                      const IBusComponent *src)
430 {
431     gboolean retval;
432 
433     retval = IBUS_SERIALIZABLE_CLASS (ibus_component_parent_class)->copy ((IBusSerializable *)dest,
434                                  (IBusSerializable *)src);
435     g_return_val_if_fail (retval, FALSE);
436 
437     dest->priv->name          = g_strdup (src->priv->name);
438     dest->priv->description   = g_strdup (src->priv->description);
439     dest->priv->version       = g_strdup (src->priv->version);
440     dest->priv->license       = g_strdup (src->priv->license);
441     dest->priv->author        = g_strdup (src->priv->author);
442     dest->priv->homepage      = g_strdup (src->priv->homepage);
443     dest->priv->exec          = g_strdup (src->priv->exec);
444     dest->priv->textdomain    = g_strdup (src->priv->textdomain);
445 
446     dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
447     g_list_foreach (dest->priv->observed_paths, (GFunc) g_object_ref, NULL);
448 
449     dest->priv->engines = g_list_copy (src->priv->engines);
450     g_list_foreach (dest->priv->engines, (GFunc) g_object_ref, NULL);
451 
452     return TRUE;
453 }
454 
455 
456 #define g_string_append_indent(string, indent)  \
457     {                                           \
458         gint i;                                 \
459         for (i = 0; i < (indent); i++) {        \
460             g_string_append (string, "    ");   \
461         }                                       \
462     }
463 
464 void
ibus_component_output(IBusComponent * component,GString * output,gint indent)465 ibus_component_output (IBusComponent *component,
466                       GString      *output,
467                       gint          indent)
468 {
469     g_assert (IBUS_IS_COMPONENT (component));
470     GList *p;
471 
472     g_string_append_indent (output, indent);
473     g_string_append (output, "<component>\n");
474 
475 #define OUTPUT_ENTRY(field, element)                                        \
476     {                                                                       \
477         gchar *escape_text =                                                \
478             g_markup_escape_text (component->priv->field ?                  \
479                                   component->priv->field : "", -1);         \
480         g_string_append_indent (output, indent + 1);                        \
481         g_string_append_printf (output, "<"element">%s</"element">\n",      \
482                                 escape_text);                               \
483         g_free (escape_text);                                               \
484     }
485 #define OUTPUT_ENTRY_1(name) OUTPUT_ENTRY(name, #name)
486     OUTPUT_ENTRY_1 (name);
487     OUTPUT_ENTRY_1 (description);
488     OUTPUT_ENTRY_1 (version);
489     OUTPUT_ENTRY_1 (license);
490     OUTPUT_ENTRY_1 (author);
491     OUTPUT_ENTRY_1 (homepage);
492     OUTPUT_ENTRY_1 (exec);
493     OUTPUT_ENTRY_1 (textdomain);
494 #undef OUTPUT_ENTRY
495 #undef OUTPUT_ENTRY_1
496 
497     if (component->priv->observed_paths) {
498         g_string_append_indent (output, indent + 1);
499         g_string_append (output, "<observed-paths>\n");
500 
501         for (p = component->priv->observed_paths; p != NULL; p = p->next ) {
502             IBusObservedPath *path = (IBusObservedPath *) p->data;
503             ibus_observed_path_output (path, output, indent + 2);
504         }
505 
506         g_string_append_indent (output, indent + 1);
507         g_string_append (output, "</observed-paths>\n");
508     }
509 
510     ibus_component_output_engines (component, output, indent + 1);
511 
512     g_string_append_indent (output, indent);
513     g_string_append (output, "</component>\n");
514 }
515 
516 void
ibus_component_output_engines(IBusComponent * component,GString * output,gint indent)517 ibus_component_output_engines (IBusComponent  *component,
518                                GString        *output,
519                                gint            indent)
520 {
521     g_assert (IBUS_IS_COMPONENT (component));
522     g_assert (output);
523 
524     GList *p;
525 
526     g_string_append_indent (output, indent);
527     g_string_append (output, "<engines>\n");
528 
529     for (p = component->priv->engines; p != NULL; p = p->next) {
530         ibus_engine_desc_output ((IBusEngineDesc *)p->data, output, indent + 2);
531     }
532 
533     g_string_append_indent (output, indent);
534     g_string_append (output, "</engines>\n");
535 }
536 
537 static gboolean
ibus_component_parse_xml_node(IBusComponent * component,XMLNode * node,gboolean access_fs)538 ibus_component_parse_xml_node (IBusComponent   *component,
539                               XMLNode          *node,
540                               gboolean          access_fs)
541 {
542     g_assert (component);
543     g_assert (node);
544 
545     if (G_UNLIKELY (g_strcmp0 (node->name, "component") != 0)) {
546         return FALSE;
547     }
548 
549     GList *p;
550     for (p = node->sub_nodes; p != NULL; p = p->next) {
551         XMLNode *sub_node = (XMLNode *)p->data;
552 
553 #define PARSE_ENTRY(field_name, element_name)                           \
554         if (g_strcmp0 (sub_node->name, element_name) == 0) {            \
555             if (component->priv->field_name != NULL) {                  \
556                 g_free (component->priv->field_name);                   \
557             }                                                           \
558             component->priv->field_name = g_strdup (sub_node->text);    \
559             continue;                                                   \
560         }
561 #define PARSE_ENTRY_1(name) PARSE_ENTRY (name, #name)
562         PARSE_ENTRY_1 (name);
563         PARSE_ENTRY_1 (description);
564         PARSE_ENTRY_1 (version);
565         PARSE_ENTRY_1 (license);
566         PARSE_ENTRY_1 (author);
567         PARSE_ENTRY_1 (homepage);
568         PARSE_ENTRY_1 (exec);
569         PARSE_ENTRY_1 (textdomain);
570 #undef PARSE_ENTRY
571 #undef PARSE_ENTRY_1
572 
573         if (g_strcmp0 (sub_node->name, "engines") == 0) {
574             ibus_component_parse_engines (component, sub_node);
575             continue;
576         }
577 
578         if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
579             ibus_component_parse_observed_paths (component, sub_node, access_fs);
580             continue;
581         }
582 
583         g_warning ("<component> element contains invalidate element <%s>", sub_node->name);
584     }
585 
586     return TRUE;
587 }
588 
589 
590 static void
ibus_component_parse_engines(IBusComponent * component,XMLNode * node)591 ibus_component_parse_engines (IBusComponent *component,
592                               XMLNode       *node)
593 {
594     g_assert (IBUS_IS_COMPONENT (component));
595     g_assert (node);
596 
597     gchar *exec = NULL;
598     gchar **p;
599     XMLNode *engines_node = NULL;
600 
601     if (g_strcmp0 (node->name, "engines") != 0) {
602         return;
603     }
604 
605     for (p = node->attributes; *p != NULL; p += 2) {
606         if (g_strcmp0 (*p, "exec") == 0) {
607             exec = *(p + 1);
608             break;
609         }
610     }
611 
612     if (exec != NULL) {
613         gchar *output = NULL;
614         if (g_spawn_command_line_sync (exec, &output, NULL, NULL, NULL)) {
615             engines_node = ibus_xml_parse_buffer (output);
616             g_free (output);
617 
618             if (engines_node) {
619                 if (g_strcmp0 (engines_node->name, "engines") == 0) {
620                     node = engines_node;
621                 }
622             }
623         }
624     }
625 
626     GList *pl;
627     for (pl = node->sub_nodes; pl != NULL; pl = pl->next) {
628         IBusEngineDesc *engine;
629         engine = ibus_engine_desc_new_from_xml_node ((XMLNode *)pl->data);
630 
631         if (G_UNLIKELY (engine == NULL))
632             continue;
633         ibus_component_add_engine (component, engine);
634     }
635 
636     if (engines_node) {
637         ibus_xml_free (engines_node);
638     }
639 }
640 
641 static void
ibus_component_parse_observed_paths(IBusComponent * component,XMLNode * node,gboolean access_fs)642 ibus_component_parse_observed_paths (IBusComponent *component,
643                                      XMLNode       *node,
644                                      gboolean       access_fs)
645 {
646     g_assert (IBUS_IS_COMPONENT (component));
647     g_assert (node);
648 
649     if (g_strcmp0 (node->name, "observed-paths") != 0) {
650         return;
651     }
652 
653     GList *p;
654     for (p = node->sub_nodes; p != NULL; p = p->next) {
655         IBusObservedPath *path;
656 
657         path = ibus_observed_path_new_from_xml_node ((XMLNode *)p->data, access_fs);
658         g_object_ref_sink (path);
659         component->priv->observed_paths = g_list_append (component->priv->observed_paths, path);
660 
661         if (access_fs && path->is_dir && path->is_exist) {
662             component->priv->observed_paths =
663                     g_list_concat (component->priv->observed_paths,
664                                    ibus_observed_path_traverse (path, TRUE));
665         }
666     }
667 }
668 
669 #define IBUS_COMPONENT_GET_PROPERTY(property, return_type)  \
670 return_type                                                 \
671 ibus_component_get_ ## property (IBusComponent *component)  \
672 {                                                           \
673     return component->priv->property;                       \
674 }
675 
IBUS_COMPONENT_GET_PROPERTY(name,const gchar *)676 IBUS_COMPONENT_GET_PROPERTY (name, const gchar *)
677 IBUS_COMPONENT_GET_PROPERTY (description, const gchar *)
678 IBUS_COMPONENT_GET_PROPERTY (version, const gchar *)
679 IBUS_COMPONENT_GET_PROPERTY (license, const gchar *)
680 IBUS_COMPONENT_GET_PROPERTY (author, const gchar *)
681 IBUS_COMPONENT_GET_PROPERTY (homepage, const gchar *)
682 IBUS_COMPONENT_GET_PROPERTY (exec, const gchar *)
683 IBUS_COMPONENT_GET_PROPERTY (textdomain, const gchar *)
684 #undef IBUS_COMPONENT_GET_PROPERTY
685 
686 IBusComponent *
687 ibus_component_new (const gchar *name,
688                     const gchar *description,
689                     const gchar *version,
690                     const gchar *license,
691                     const gchar *author,
692                     const gchar *homepage,
693                     const gchar *command_line,
694                     const gchar *textdomain)
695 {
696     return ibus_component_new_varargs ("name", name,
697                                        "description", description,
698                                        "version", version,
699                                        "license", license,
700                                        "author", author,
701                                        "homepage", homepage,
702                                        "command-line", command_line,
703                                        "textdomain", textdomain,
704                                        NULL);
705 }
706 
707 
708 IBusComponent *
ibus_component_new_varargs(const gchar * first_property_name,...)709 ibus_component_new_varargs (const gchar *first_property_name, ...)
710 {
711     va_list var_args;
712     IBusComponent *component;
713     IBusComponentPrivate *priv;
714 
715     g_assert (first_property_name);
716 
717     va_start (var_args, first_property_name);
718     component = (IBusComponent *) g_object_new_valist (IBUS_TYPE_COMPONENT,
719                                                        first_property_name,
720                                                        var_args);
721     va_end (var_args);
722 
723     priv = IBUS_COMPONENT_GET_PRIVATE (component);
724 
725     /* name is required. Other properties are set in class_init by default. */
726     g_assert (priv->name);
727 
728     return component;
729 }
730 
731 
732 IBusComponent *
ibus_component_new_from_xml_node(XMLNode * node)733 ibus_component_new_from_xml_node (XMLNode  *node)
734 {
735     g_assert (node);
736 
737     IBusComponent *component;
738 
739     component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
740     if (!ibus_component_parse_xml_node (component, node, FALSE)) {
741         g_object_unref (component);
742         component = NULL;
743     }
744 
745     return component;
746 }
747 
748 IBusComponent *
ibus_component_new_from_file(const gchar * filename)749 ibus_component_new_from_file (const gchar *filename)
750 {
751     g_assert (filename);
752 
753     XMLNode *node;
754     struct stat buf;
755     IBusComponent *component;
756     gboolean retval;
757 
758     if (g_stat (filename, &buf) != 0) {
759         g_warning ("Can not get stat of file %s", filename);
760         return NULL;
761     }
762 
763     node = ibus_xml_parse_file (filename);
764 
765     if (!node) {
766         return NULL;
767     }
768 
769     component = (IBusComponent *)g_object_new (IBUS_TYPE_COMPONENT, NULL);
770     retval = ibus_component_parse_xml_node (component, node, TRUE);
771     ibus_xml_free (node);
772 
773     if (!retval) {
774         g_object_unref (component);
775         component = NULL;
776     }
777     else {
778         IBusObservedPath *path;
779         path = ibus_observed_path_new (filename, TRUE);
780         component->priv->observed_paths =
781                 g_list_prepend(component->priv->observed_paths, path);
782     }
783 
784     return component;
785 }
786 
787 void
ibus_component_add_observed_path(IBusComponent * component,const gchar * path,gboolean access_fs)788 ibus_component_add_observed_path (IBusComponent *component,
789                                   const gchar   *path,
790                                   gboolean       access_fs)
791 {
792     IBusObservedPath *p;
793 
794     p = ibus_observed_path_new (path, access_fs);
795     g_object_ref_sink (p);
796     component->priv->observed_paths =
797             g_list_append (component->priv->observed_paths, p);
798 
799     if (access_fs && p->is_dir && p->is_exist) {
800         component->priv->observed_paths =
801                 g_list_concat (component->priv->observed_paths,
802                                ibus_observed_path_traverse (p, TRUE));
803     }
804 }
805 
806 void
ibus_component_add_engine(IBusComponent * component,IBusEngineDesc * engine)807 ibus_component_add_engine (IBusComponent  *component,
808                            IBusEngineDesc *engine)
809 {
810     g_assert (IBUS_IS_COMPONENT (component));
811     g_assert (IBUS_IS_ENGINE_DESC (engine));
812 
813     g_object_ref_sink (engine);
814     component->priv->engines =
815             g_list_append (component->priv->engines, engine);
816 }
817 
818 GList *
ibus_component_get_engines(IBusComponent * component)819 ibus_component_get_engines (IBusComponent *component)
820 {
821     return g_list_copy (component->priv->engines);
822 }
823 
824 gboolean
ibus_component_check_modification(IBusComponent * component)825 ibus_component_check_modification (IBusComponent *component)
826 {
827     g_assert (IBUS_IS_COMPONENT (component));
828 
829     GList *p;
830 
831     for (p = component->priv->observed_paths; p != NULL; p = p->next) {
832         if (ibus_observed_path_check_modification ((IBusObservedPath *)p->data))
833             return TRUE;
834     }
835     return FALSE;
836 }
837 
838 GList *
ibus_component_get_observed_paths(IBusComponent * component)839 ibus_component_get_observed_paths (IBusComponent *component)
840 {
841     g_assert (IBUS_IS_COMPONENT (component));
842     return g_list_copy (component->priv->observed_paths);
843 }
844