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) 2018-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
5  * Copyright (C) 2018-2019 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.1 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 Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <glib.h>
27 #include <glib/gstdio.h>
28 #include "ibusinternal.h"
29 #include "ibuserror.h"
30 #include "ibusunicode.h"
31 
32 #define IBUS_UNICODE_DATA_MAGIC "IBusUnicodeData"
33 #define IBUS_UNICODE_BLOCK_MAGIC "IBusUnicodeBlock"
34 #define IBUS_UNICODE_DATA_VERSION (1)
35 #define IBUS_UNICODE_DESERIALIZE_SIGNALL_STR \
36         "deserialize-unicode"
37 
38 enum {
39     PROP_0 = 0,
40     PROP_CODE,
41     PROP_NAME,
42     PROP_ALIAS,
43     PROP_BLOCK_NAME,
44     PROP_START,
45     PROP_END
46 };
47 
48 struct _IBusUnicodeDataPrivate {
49     gunichar    code;
50     gchar      *name;
51     gchar      *alias;
52     gchar      *block_name;
53 };
54 
55 struct _IBusUnicodeBlockPrivate {
56     gunichar    start;
57     gunichar    end;
58     gchar      *name;
59 };
60 
61 typedef struct {
62     IBusUnicodeDataLoadAsyncFinish callback;
63     gpointer                       user_data;
64 } IBusUnicodeDataLoadData;
65 
66 #define IBUS_UNICODE_DATA_GET_PRIVATE(o)  \
67    ((IBusUnicodeDataPrivate *)ibus_unicode_data_get_instance_private (o))
68 #define IBUS_UNICODE_BLOCK_GET_PRIVATE(o)  \
69    ((IBusUnicodeBlockPrivate *)ibus_unicode_block_get_instance_private (o))
70 
71 /* functions prototype */
72 static void      ibus_unicode_data_set_property (IBusUnicodeData      *unicode,
73                                                  guint                 prop_id,
74                                                  const GValue         *value,
75                                                  GParamSpec           *pspec);
76 static void      ibus_unicode_data_get_property (IBusUnicodeData      *unicode,
77                                                  guint                 prop_id,
78                                                  GValue               *value,
79                                                  GParamSpec           *pspec);
80 static void      ibus_unicode_data_destroy      (IBusUnicodeData      *unicode);
81 static gboolean  ibus_unicode_data_serialize    (IBusUnicodeData      *unicode,
82                                                  GVariantBuilder      *builder);
83 static gint      ibus_unicode_data_deserialize  (IBusUnicodeData      *unicode,
84                                                  GVariant             *variant);
85 static gboolean  ibus_unicode_data_copy         (IBusUnicodeData      *dest,
86                                                 const IBusUnicodeData *src);
87 static void      ibus_unicode_block_set_property
88                                                 (IBusUnicodeBlock     *block,
89                                                  guint                 prop_id,
90                                                  const GValue         *value,
91                                                  GParamSpec           *pspec);
92 static void      ibus_unicode_block_get_property
93                                                 (IBusUnicodeBlock     *block,
94                                                  guint                 prop_id,
95                                                  GValue               *value,
96                                                  GParamSpec           *pspec);
97 static void      ibus_unicode_block_destroy     (IBusUnicodeBlock     *block);
98 static gboolean  ibus_unicode_block_serialize   (IBusUnicodeBlock     *block,
99                                                  GVariantBuilder      *builder);
100 static gint      ibus_unicode_block_deserialize (IBusUnicodeBlock     *block,
101                                                  GVariant             *variant);
102 static gboolean  ibus_unicode_block_copy        (IBusUnicodeBlock     *dest,
103                                                 const IBusUnicodeBlock *src);
104 
G_DEFINE_TYPE_WITH_PRIVATE(IBusUnicodeData,ibus_unicode_data,IBUS_TYPE_SERIALIZABLE)105 G_DEFINE_TYPE_WITH_PRIVATE (IBusUnicodeData,
106                             ibus_unicode_data,
107                             IBUS_TYPE_SERIALIZABLE)
108 G_DEFINE_TYPE_WITH_PRIVATE (IBusUnicodeBlock,
109                             ibus_unicode_block,
110                             IBUS_TYPE_SERIALIZABLE)
111 
112 static void
113 ibus_unicode_data_class_init (IBusUnicodeDataClass *class)
114 {
115     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
116     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
117     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
118 
119     object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_data_destroy;
120     gobject_class->set_property =
121             (GObjectSetPropertyFunc) ibus_unicode_data_set_property;
122     gobject_class->get_property =
123             (GObjectGetPropertyFunc) ibus_unicode_data_get_property;
124     serializable_class->serialize   =
125             (IBusSerializableSerializeFunc) ibus_unicode_data_serialize;
126     serializable_class->deserialize =
127             (IBusSerializableDeserializeFunc) ibus_unicode_data_deserialize;
128     serializable_class->copy        =
129             (IBusSerializableCopyFunc) ibus_unicode_data_copy;
130 
131     /* install properties */
132     /**
133      * IBusUnicodeData:code:
134      *
135      * The Uniode code point
136      */
137     g_object_class_install_property (gobject_class,
138                     PROP_CODE,
139                     g_param_spec_unichar ("code",
140                         "code point",
141                         "The Unicode code point",
142                         0,
143                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
144 
145 
146     /**
147      * IBusUnicodeData:name:
148      *
149      * The Uniode name
150      */
151     g_object_class_install_property (gobject_class,
152                     PROP_NAME,
153                     g_param_spec_string ("name",
154                         "name",
155                         "The Unicode name",
156                         "",
157                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
158 
159     /**
160      * IBusUnicodeData:alias:
161      *
162      * The Uniode alias name
163      */
164     g_object_class_install_property (gobject_class,
165                     PROP_ALIAS,
166                     g_param_spec_string ("alias",
167                         "alias name",
168                         "The Unicode alias name",
169                         "",
170                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
171 
172     /**
173      * IBusUnicodeData:block-name:
174      *
175      * The Uniode block name
176      */
177     g_object_class_install_property (gobject_class,
178                     PROP_BLOCK_NAME,
179                     g_param_spec_string ("block-name",
180                         "block name",
181                         "The Unicode block name",
182                         "",
183                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
184 }
185 
186 static void
ibus_unicode_data_init(IBusUnicodeData * unicode)187 ibus_unicode_data_init (IBusUnicodeData *unicode)
188 {
189     unicode->priv = IBUS_UNICODE_DATA_GET_PRIVATE (unicode);
190 }
191 
192 static void
ibus_unicode_data_destroy(IBusUnicodeData * unicode)193 ibus_unicode_data_destroy (IBusUnicodeData *unicode)
194 {
195     g_clear_pointer (&unicode->priv->name, g_free);
196     g_clear_pointer (&unicode->priv->alias, g_free);
197     g_clear_pointer (&unicode->priv->block_name, g_free);
198 
199     IBUS_OBJECT_CLASS (ibus_unicode_data_parent_class)->
200             destroy (IBUS_OBJECT (unicode));
201 }
202 
203 static void
ibus_unicode_data_set_property(IBusUnicodeData * unicode,guint prop_id,const GValue * value,GParamSpec * pspec)204 ibus_unicode_data_set_property (IBusUnicodeData *unicode,
205                                 guint            prop_id,
206                                 const GValue    *value,
207                                 GParamSpec      *pspec)
208 {
209     switch (prop_id) {
210     case PROP_CODE:
211         g_assert (unicode->priv->code == 0);
212         unicode->priv->code = g_value_get_uint (value);
213         break;
214     case PROP_NAME:
215         g_assert (unicode->priv->name == NULL);
216         unicode->priv->name = g_value_dup_string (value);
217         break;
218     case PROP_ALIAS:
219         g_assert (unicode->priv->alias == NULL);
220         unicode->priv->alias = g_value_dup_string (value);
221         break;
222     case PROP_BLOCK_NAME:
223         g_free (unicode->priv->block_name);
224         unicode->priv->block_name = g_value_dup_string (value);
225         break;
226     default:
227         G_OBJECT_WARN_INVALID_PROPERTY_ID (unicode, prop_id, pspec);
228     }
229 }
230 
231 static void
ibus_unicode_data_get_property(IBusUnicodeData * unicode,guint prop_id,GValue * value,GParamSpec * pspec)232 ibus_unicode_data_get_property (IBusUnicodeData *unicode,
233                                 guint            prop_id,
234                                 GValue          *value,
235                                 GParamSpec      *pspec)
236 {
237     switch (prop_id) {
238     case PROP_CODE:
239         g_value_set_uint (value, ibus_unicode_data_get_code (unicode));
240         break;
241     case PROP_NAME:
242         g_value_set_string (value, ibus_unicode_data_get_name (unicode));
243         break;
244     case PROP_ALIAS:
245         g_value_set_string (value, ibus_unicode_data_get_alias (unicode));
246         break;
247     case PROP_BLOCK_NAME:
248         g_value_set_string (value, ibus_unicode_data_get_block_name (unicode));
249         break;
250     default:
251         G_OBJECT_WARN_INVALID_PROPERTY_ID (unicode, prop_id, pspec);
252     }
253 }
254 
255 static gboolean
ibus_unicode_data_serialize(IBusUnicodeData * unicode,GVariantBuilder * builder)256 ibus_unicode_data_serialize (IBusUnicodeData   *unicode,
257                              GVariantBuilder   *builder)
258 {
259     gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
260             serialize ((IBusSerializable *)unicode, builder);
261     g_return_val_if_fail (retval, FALSE);
262 
263 #define NOTNULL(s) ((s) != NULL ? (s) : "")
264     /* If you will add a new property, you can append it at the end and
265      * you should not change the serialized order of name, longname,
266      * description, ... because the order is also used in other applications
267      * likes ibus-qt. */
268     g_variant_builder_add (builder, "u", unicode->priv->code);
269     g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->name));
270     g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->alias));
271     /* Use IBusUnicodeBlock for memory usage.
272     g_variant_builder_add (builder, "s", NOTNULL (unicode->priv->block_name));
273      */
274 #undef NOTNULL
275     return TRUE;
276 }
277 
278 static gint
ibus_unicode_data_deserialize(IBusUnicodeData * unicode,GVariant * variant)279 ibus_unicode_data_deserialize (IBusUnicodeData *unicode,
280                                GVariant        *variant)
281 {
282     gint retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
283             deserialize ((IBusSerializable *)unicode, variant);
284     g_return_val_if_fail (retval, 0);
285 
286     /* If you will add a new property, you can append it at the end and
287      * you should not change the serialized order of name, longname,
288      * description, ... because the order is also used in other applications
289      * likes ibus-qt. */
290     g_variant_get_child (variant, retval++, "u", &unicode->priv->code);
291     ibus_g_variant_get_child_string (variant, retval++,
292                                      &unicode->priv->name);
293     ibus_g_variant_get_child_string (variant, retval++,
294                                      &unicode->priv->alias);
295     /* Use IBusUnicodeBlock for memory usage.
296     ibus_g_variant_get_child_string (variant, retval++,
297                                      &unicode->priv->block_name);
298      */
299     return retval;
300 }
301 
302 static gboolean
ibus_unicode_data_copy(IBusUnicodeData * dest,const IBusUnicodeData * src)303 ibus_unicode_data_copy (IBusUnicodeData       *dest,
304                         const IBusUnicodeData *src)
305 {
306     gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_data_parent_class)->
307             copy ((IBusSerializable *)dest,
308                   (IBusSerializable *)src);
309     g_return_val_if_fail (retval, FALSE);
310 
311     dest->priv->code             = src->priv->code;
312     dest->priv->name             = g_strdup (src->priv->name);
313     dest->priv->alias            = g_strdup (src->priv->alias);
314     dest->priv->block_name       = g_strdup (src->priv->block_name);
315     return TRUE;
316 }
317 
318 IBusUnicodeData *
ibus_unicode_data_new(const gchar * first_property_name,...)319 ibus_unicode_data_new (const gchar *first_property_name, ...)
320 {
321     va_list var_args;
322     IBusUnicodeData *unicode;
323 
324     g_assert (first_property_name != NULL);
325     va_start (var_args, first_property_name);
326     unicode = (IBusUnicodeData *) g_object_new_valist (IBUS_TYPE_UNICODE_DATA,
327                                                        first_property_name,
328                                                        var_args);
329     va_end (var_args);
330     /* code is required. Other properties are set in class_init by default. */
331     g_assert (unicode->priv->name != NULL);
332     g_assert (unicode->priv->alias != NULL);
333     g_assert (unicode->priv->block_name != NULL);
334     return unicode;
335 }
336 
337 gunichar
ibus_unicode_data_get_code(IBusUnicodeData * unicode)338 ibus_unicode_data_get_code (IBusUnicodeData *unicode)
339 {
340     g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), G_MAXUINT32);
341 
342     return unicode->priv->code;
343 }
344 
345 const gchar *
ibus_unicode_data_get_name(IBusUnicodeData * unicode)346 ibus_unicode_data_get_name (IBusUnicodeData *unicode)
347 {
348     g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
349 
350     return unicode->priv->name;
351 }
352 
353 const gchar *
ibus_unicode_data_get_alias(IBusUnicodeData * unicode)354 ibus_unicode_data_get_alias (IBusUnicodeData *unicode)
355 {
356     g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
357 
358     return unicode->priv->alias;
359 }
360 
361 const gchar *
ibus_unicode_data_get_block_name(IBusUnicodeData * unicode)362 ibus_unicode_data_get_block_name (IBusUnicodeData *unicode)
363 {
364     g_return_val_if_fail (IBUS_IS_UNICODE_DATA (unicode), "");
365 
366     return unicode->priv->block_name;
367 }
368 
369 void
ibus_unicode_data_set_block_name(IBusUnicodeData * unicode,const gchar * block_name)370 ibus_unicode_data_set_block_name (IBusUnicodeData *unicode,
371                                   const gchar     *block_name)
372 {
373     g_return_if_fail (IBUS_IS_UNICODE_DATA (unicode));
374 
375     g_free (unicode->priv->block_name);
376     unicode->priv->block_name = g_strdup (block_name);
377 }
378 
379 static void
variant_foreach_add_unicode(IBusUnicodeData * unicode,GVariantBuilder * builder)380 variant_foreach_add_unicode (IBusUnicodeData *unicode,
381                              GVariantBuilder *builder)
382 {
383     g_variant_builder_add (
384             builder, "v",
385             ibus_serializable_serialize (IBUS_SERIALIZABLE (unicode)));
386 }
387 
388 static GVariant *
ibus_unicode_data_list_serialize(GSList * list)389 ibus_unicode_data_list_serialize (GSList *list)
390 {
391     GVariantBuilder builder;
392 
393     g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
394     g_slist_foreach (list,  (GFunc) variant_foreach_add_unicode, &builder);
395     return g_variant_builder_end (&builder);
396 }
397 
398 static GSList *
ibus_unicode_data_list_deserialize(GVariant * variant,GObject * source_object)399 ibus_unicode_data_list_deserialize (GVariant *variant,
400                                     GObject  *source_object)
401 {
402     GSList *list = NULL;
403     GVariantIter iter;
404     GVariant *unicode_variant = NULL;
405     gsize i, size;
406     gboolean has_signal = FALSE;
407 
408     if (G_IS_OBJECT (source_object)) {
409         has_signal = g_signal_lookup (
410                 IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
411                 G_OBJECT_TYPE (source_object));
412         if (!has_signal) {
413             const gchar *type_name =
414                     g_type_name (G_OBJECT_TYPE (source_object));
415             g_warning ("GObject %s does not have the signal \"%s\"",
416                        type_name ? type_name : "(null)",
417                        IBUS_UNICODE_DESERIALIZE_SIGNALL_STR);
418         }
419     }
420     g_variant_iter_init (&iter, variant);
421     size = g_variant_iter_n_children (&iter);
422     i = 0;
423     while (g_variant_iter_loop (&iter, "v", &unicode_variant)) {
424         IBusUnicodeData *data =
425                 IBUS_UNICODE_DATA (ibus_serializable_deserialize (
426                         unicode_variant));
427         list = g_slist_append (list, data);
428         g_clear_pointer (&unicode_variant, g_variant_unref);
429         if (has_signal && (i == 0 || ((i + 1) % 100) == 0)) {
430             g_signal_emit_by_name (source_object,
431                                    IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
432                                    i + 1, size);
433         }
434         i++;
435     }
436     if (has_signal && (i != 1 && (i % 100) != 0)) {
437         g_signal_emit_by_name (source_object,
438                                IBUS_UNICODE_DESERIALIZE_SIGNALL_STR,
439                                i, size);
440     }
441 
442     return list;
443 }
444 
445 void
ibus_unicode_data_save(const gchar * path,GSList * list)446 ibus_unicode_data_save (const gchar *path,
447                         GSList      *list)
448 {
449     GVariant *variant;
450     const gchar *header = IBUS_UNICODE_DATA_MAGIC;
451     const guint16 version = IBUS_UNICODE_DATA_VERSION;
452     const gchar *contents;
453     gsize length;
454     gchar *dir;
455     GStatBuf buf = { 0, };
456     GError *error = NULL;
457 
458     g_return_if_fail (path != NULL);
459     g_return_if_fail (list != NULL);
460     if (list->data == NULL) {
461         g_warning ("Failed to save IBus Unicode data: Need a list data.");
462         return;
463     }
464 
465     variant = g_variant_new ("(sqv)",
466                              header,
467                              version,
468                              ibus_unicode_data_list_serialize (list));
469 
470     contents =  g_variant_get_data (variant);
471     length =  g_variant_get_size (variant);
472 
473     dir = g_path_get_dirname (path);
474     if (g_strcmp0 (dir, ".") != 0 && g_stat (dir, &buf) != 0) {
475         g_mkdir_with_parents (dir, 0777);
476     }
477     g_free (dir);
478     if (!g_file_set_contents (path, contents, length, &error)) {
479         g_warning ("Failed to save Unicode dict %s: %s", path, error->message);
480         g_error_free (error);
481     }
482 
483     g_variant_unref (variant);
484 }
485 
486 static GSList *
ibus_unicode_data_load_with_error(const gchar * path,GObject * source_object,GError ** error)487 ibus_unicode_data_load_with_error (const gchar *path,
488                                    GObject     *source_object,
489                                    GError     **error)
490 {
491     gchar *contents = NULL;
492     gsize length = 0;
493     GVariant *variant_table = NULL;
494     GVariant *variant = NULL;
495     const gchar *header = NULL;
496     guint16 version = 0;
497     GSList *retval = NULL;
498 
499     if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
500         g_set_error (error,
501                      IBUS_ERROR,
502                      IBUS_ERROR_FAILED,
503                      "Unicode dict does not exist: %s", path);
504         goto out_load_cache;
505     }
506 
507     if (!g_file_get_contents (path, &contents, &length, error)) {
508         goto out_load_cache;
509     }
510 
511     variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sq)"),
512                                              contents,
513                                              length,
514                                              FALSE,
515                                              NULL,
516                                              NULL);
517 
518     if (variant_table == NULL) {
519         g_set_error (error,
520                      IBUS_ERROR,
521                      IBUS_ERROR_FAILED,
522                      "cache table is broken.");
523         goto out_load_cache;
524     }
525 
526     g_variant_get (variant_table, "(&sq)", &header, &version);
527 
528     if (g_strcmp0 (header, IBUS_UNICODE_DATA_MAGIC) != 0) {
529         g_set_error (error,
530                      IBUS_ERROR,
531                      IBUS_ERROR_FAILED,
532                      "cache is not IBusUnicodeData.");
533         goto out_load_cache;
534     }
535 
536     if (version > IBUS_UNICODE_DATA_VERSION) {
537         g_set_error (error,
538                      IBUS_ERROR,
539                      IBUS_ERROR_FAILED,
540                      "cache version is different: %u != %u",
541                      version, IBUS_UNICODE_DATA_VERSION);
542         goto out_load_cache;
543     }
544 
545     version = 0;
546     header = NULL;
547     g_variant_unref (variant_table);
548 
549     variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sqv)"),
550                                              contents,
551                                              length,
552                                              FALSE,
553                                              NULL,
554                                              NULL);
555 
556     if (variant_table == NULL) {
557         g_set_error (error,
558                      IBUS_ERROR,
559                      IBUS_ERROR_FAILED,
560                      "cache table is broken.");
561         goto out_load_cache;
562     }
563 
564     g_variant_get (variant_table, "(&sqv)",
565                    NULL,
566                    NULL,
567                    &variant);
568 
569     if (variant == NULL) {
570         g_set_error (error,
571                      IBUS_ERROR,
572                      IBUS_ERROR_FAILED,
573                      "cache dict is broken.");
574         goto out_load_cache;
575     }
576 
577     retval = ibus_unicode_data_list_deserialize (variant, source_object);
578 
579 out_load_cache:
580     if (variant)
581         g_variant_unref (variant);
582     if (variant_table)
583         g_variant_unref (variant_table);
584     g_free (contents);
585 
586     return retval;
587 }
588 
589 GSList *
ibus_unicode_data_load(const gchar * path,GObject * source_object)590 ibus_unicode_data_load (const gchar *path,
591                         GObject     *source_object)
592 {
593     GError *error = NULL;
594     GSList *retval = ibus_unicode_data_load_with_error (path,
595                                                         source_object,
596                                                         &error);
597 
598     if (retval == NULL) {
599         g_warning ("%s", error->message);
600         g_error_free (error);
601     }
602 
603     return retval;
604 }
605 
606 static void
ibus_unicode_data_load_async_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)607 ibus_unicode_data_load_async_thread (GTask        *task,
608                                      gpointer      source_object,
609                                      gpointer      task_data,
610                                      GCancellable *cancellable)
611 {
612     GSList *retval;
613     gchar *path = (gchar *)task_data;
614     GError *error = NULL;
615 
616     g_assert (path != NULL);
617 
618     retval = ibus_unicode_data_load_with_error (path, source_object, &error);
619     g_free (path);
620     if (retval == NULL)
621         g_task_return_error (task, error);
622     else
623         g_task_return_pointer (task, retval, NULL);
624     g_object_unref (task);
625 }
626 
627 static void
ibus_unicode_data_load_async_done(GObject * source_object,GAsyncResult * res,gpointer user_data)628 ibus_unicode_data_load_async_done (GObject *source_object,
629                                    GAsyncResult *res,
630                                    gpointer user_data)
631 {
632     IBusUnicodeDataLoadData *data = (IBusUnicodeDataLoadData*)user_data;
633     GSList *list;
634     GError *error = NULL;
635     g_assert (data != NULL);
636     list = g_task_propagate_pointer (G_TASK (res), &error);
637     if (error) {
638         g_warning ("%s", error->message);
639         g_error_free (error);
640         data->callback (NULL, data->user_data);
641     } else {
642         data->callback (list, data->user_data);
643     }
644     g_slice_free (IBusUnicodeDataLoadData, data);
645 }
646 
647 void
ibus_unicode_data_load_async(const gchar * path,GObject * source_object,GCancellable * cancellable,IBusUnicodeDataLoadAsyncFinish callback,gpointer user_data)648 ibus_unicode_data_load_async (const gchar        *path,
649                               GObject            *source_object,
650                               GCancellable       *cancellable,
651                               IBusUnicodeDataLoadAsyncFinish
652                                                   callback,
653                               gpointer            user_data)
654 {
655     GTask *task;
656     IBusUnicodeDataLoadData *data;
657 
658     g_return_if_fail (path != NULL);
659 
660     data = g_slice_new0 (IBusUnicodeDataLoadData);
661     data->callback = callback;
662     data->user_data = user_data;
663     task = g_task_new (source_object,
664                        cancellable,
665                        ibus_unicode_data_load_async_done,
666                        data);
667     g_task_set_source_tag (task, ibus_unicode_data_load_async);
668     g_task_set_task_data (task, g_strdup (path), NULL);
669     g_task_run_in_thread (task, ibus_unicode_data_load_async_thread);
670 }
671 
672 static void
ibus_unicode_block_class_init(IBusUnicodeBlockClass * class)673 ibus_unicode_block_class_init (IBusUnicodeBlockClass *class)
674 {
675     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
676     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
677     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
678 
679     object_class->destroy = (IBusObjectDestroyFunc) ibus_unicode_block_destroy;
680     gobject_class->set_property =
681             (GObjectSetPropertyFunc) ibus_unicode_block_set_property;
682     gobject_class->get_property =
683             (GObjectGetPropertyFunc) ibus_unicode_block_get_property;
684     serializable_class->serialize   =
685             (IBusSerializableSerializeFunc) ibus_unicode_block_serialize;
686     serializable_class->deserialize =
687             (IBusSerializableDeserializeFunc) ibus_unicode_block_deserialize;
688     serializable_class->copy        =
689             (IBusSerializableCopyFunc) ibus_unicode_block_copy;
690 
691     /* install properties */
692     /**
693      * IBusUnicodeBlock:start:
694      *
695      * The Uniode start code point
696      */
697     g_object_class_install_property (gobject_class,
698                     PROP_START,
699                     /* Cannot use g_param_spec_unichar() for the Unicode
700                      * boundary values because the function checks
701                      * if the value is a valid Unicode besides MAXUINT.
702                      */
703                     g_param_spec_uint ("start",
704                         "start code point",
705                         "The Unicode start code point",
706                         0,
707                         G_MAXUINT,
708                         0,
709                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
710 
711 
712     /**
713      * IBusUnicodeBlock:end:
714      *
715      * The Uniode end code point
716      */
717     g_object_class_install_property (gobject_class,
718                     PROP_END,
719                     /* Cannot use g_param_spec_unichar() for the Unicode
720                      * boundary values because the function checks
721                      * if the value is a valid Unicode besides MAXUINT.
722                      */
723                     g_param_spec_uint ("end",
724                         "end code point",
725                         "The Unicode end code point",
726                         0,
727                         G_MAXUINT,
728                         0,
729                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
730 
731 
732     /**
733      * IBusUnicodeBlock:name:
734      *
735      * The Uniode block name
736      */
737     g_object_class_install_property (gobject_class,
738                     PROP_NAME,
739                     g_param_spec_string ("name",
740                         "name",
741                         "The Unicode name",
742                         "",
743                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
744 }
745 
746 static void
ibus_unicode_block_init(IBusUnicodeBlock * block)747 ibus_unicode_block_init (IBusUnicodeBlock *block)
748 {
749     block->priv = IBUS_UNICODE_BLOCK_GET_PRIVATE (block);
750 }
751 
752 static void
ibus_unicode_block_destroy(IBusUnicodeBlock * block)753 ibus_unicode_block_destroy (IBusUnicodeBlock *block)
754 {
755     g_clear_pointer (&block->priv->name, g_free);
756 
757     IBUS_OBJECT_CLASS (ibus_unicode_data_parent_class)->
758             destroy (IBUS_OBJECT (block));
759 }
760 
761 static void
ibus_unicode_block_set_property(IBusUnicodeBlock * block,guint prop_id,const GValue * value,GParamSpec * pspec)762 ibus_unicode_block_set_property (IBusUnicodeBlock *block,
763                                  guint             prop_id,
764                                  const GValue     *value,
765                                  GParamSpec       *pspec)
766 {
767     switch (prop_id) {
768     case PROP_START:
769         g_assert (block->priv->start == 0);
770         block->priv->start = g_value_get_uint (value);
771         break;
772     case PROP_END:
773         g_assert (block->priv->end == 0);
774         block->priv->end = g_value_get_uint (value);
775         break;
776     case PROP_NAME:
777         g_assert (block->priv->name == NULL);
778         block->priv->name = g_value_dup_string (value);
779         break;
780     default:
781         G_OBJECT_WARN_INVALID_PROPERTY_ID (block, prop_id, pspec);
782     }
783 }
784 
785 static void
ibus_unicode_block_get_property(IBusUnicodeBlock * block,guint prop_id,GValue * value,GParamSpec * pspec)786 ibus_unicode_block_get_property (IBusUnicodeBlock *block,
787                                  guint             prop_id,
788                                  GValue           *value,
789                                  GParamSpec       *pspec)
790 {
791     switch (prop_id) {
792     case PROP_START:
793         g_value_set_uint (value, ibus_unicode_block_get_start (block));
794         break;
795     case PROP_END:
796         g_value_set_uint (value, ibus_unicode_block_get_end (block));
797         break;
798     case PROP_NAME:
799         g_value_set_string (value, ibus_unicode_block_get_name (block));
800         break;
801     default:
802         G_OBJECT_WARN_INVALID_PROPERTY_ID (block, prop_id, pspec);
803     }
804 }
805 
806 static gboolean
ibus_unicode_block_serialize(IBusUnicodeBlock * block,GVariantBuilder * builder)807 ibus_unicode_block_serialize (IBusUnicodeBlock *block,
808                               GVariantBuilder  *builder)
809 {
810     gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
811             serialize ((IBusSerializable *)block, builder);
812     g_return_val_if_fail (retval, FALSE);
813 
814 #define NOTNULL(s) ((s) != NULL ? (s) : "")
815     /* If you will add a new property, you can append it at the end and
816      * you should not change the serialized order of name, longname,
817      * description, ... because the order is also used in other applications
818      * likes ibus-qt. */
819     g_variant_builder_add (builder, "u", block->priv->start);
820     g_variant_builder_add (builder, "u", block->priv->end);
821     g_variant_builder_add (builder, "s", NOTNULL (block->priv->name));
822 #undef NOTNULL
823     return TRUE;
824 }
825 
826 static gint
ibus_unicode_block_deserialize(IBusUnicodeBlock * block,GVariant * variant)827 ibus_unicode_block_deserialize (IBusUnicodeBlock *block,
828                                 GVariant        *variant)
829 {
830     gint retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
831             deserialize ((IBusSerializable *)block, variant);
832     g_return_val_if_fail (retval, 0);
833 
834     /* If you will add a new property, you can append it at the end and
835      * you should not change the serialized order of name, longname,
836      * description, ... because the order is also used in other applications
837      * likes ibus-qt. */
838     g_variant_get_child (variant, retval++, "u", &block->priv->start);
839     g_variant_get_child (variant, retval++, "u", &block->priv->end);
840     ibus_g_variant_get_child_string (variant, retval++,
841                                      &block->priv->name);
842     return retval;
843 }
844 
845 static gboolean
ibus_unicode_block_copy(IBusUnicodeBlock * dest,const IBusUnicodeBlock * src)846 ibus_unicode_block_copy (IBusUnicodeBlock       *dest,
847                          const IBusUnicodeBlock *src)
848 {
849     gboolean retval = IBUS_SERIALIZABLE_CLASS (ibus_unicode_block_parent_class)->
850             copy ((IBusSerializable *)dest,
851                   (IBusSerializable *)src);
852     g_return_val_if_fail (retval, FALSE);
853 
854     dest->priv->start            = src->priv->start;
855     dest->priv->end              = src->priv->end;
856     dest->priv->name             = g_strdup (src->priv->name);
857     return TRUE;
858 }
859 
860 IBusUnicodeBlock *
ibus_unicode_block_new(const gchar * first_property_name,...)861 ibus_unicode_block_new (const gchar *first_property_name, ...)
862 {
863     va_list var_args;
864     IBusUnicodeBlock *block;
865 
866     g_assert (first_property_name != NULL);
867     va_start (var_args, first_property_name);
868     block = (IBusUnicodeBlock *) g_object_new_valist (IBUS_TYPE_UNICODE_BLOCK,
869                                                      first_property_name,
870                                                      var_args);
871     va_end (var_args);
872     /* end is required. Other properties are set in class_init by default. */
873     g_assert (block->priv->start != block->priv->end);
874     g_assert (block->priv->name != NULL);
875     return block;
876 }
877 
878 gunichar
ibus_unicode_block_get_start(IBusUnicodeBlock * block)879 ibus_unicode_block_get_start (IBusUnicodeBlock *block)
880 {
881     g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), G_MAXUINT32);
882 
883     return block->priv->start;
884 }
885 
886 gunichar
ibus_unicode_block_get_end(IBusUnicodeBlock * block)887 ibus_unicode_block_get_end (IBusUnicodeBlock *block)
888 {
889     g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), G_MAXUINT32);
890 
891     return block->priv->end;
892 }
893 
894 const gchar *
ibus_unicode_block_get_name(IBusUnicodeBlock * block)895 ibus_unicode_block_get_name (IBusUnicodeBlock *block)
896 {
897     g_return_val_if_fail (IBUS_IS_UNICODE_BLOCK (block), "");
898 
899     return block->priv->name;
900 }
901 
902 static void
variant_foreach_add_block(IBusUnicodeBlock * block,GVariantBuilder * builder)903 variant_foreach_add_block (IBusUnicodeBlock *block,
904                            GVariantBuilder *builder)
905 {
906     g_variant_builder_add (
907             builder, "v",
908             ibus_serializable_serialize (IBUS_SERIALIZABLE (block)));
909 }
910 
911 static GVariant *
ibus_unicode_block_list_serialize(GSList * list)912 ibus_unicode_block_list_serialize (GSList *list)
913 {
914     GVariantBuilder builder;
915 
916     g_variant_builder_init (&builder, G_VARIANT_TYPE ("av"));
917     g_slist_foreach (list,  (GFunc) variant_foreach_add_block, &builder);
918     return g_variant_builder_end (&builder);
919 }
920 
921 static GSList *
ibus_unicode_block_list_deserialize(GVariant * variant)922 ibus_unicode_block_list_deserialize (GVariant *variant)
923 {
924     GSList *list = NULL;
925     GVariantIter iter;
926     GVariant *unicode_variant = NULL;
927 
928     g_variant_iter_init (&iter, variant);
929     while (g_variant_iter_loop (&iter, "v", &unicode_variant)) {
930         IBusUnicodeBlock *data =
931                 IBUS_UNICODE_BLOCK (ibus_serializable_deserialize (
932                         unicode_variant));
933         list = g_slist_append (list, data);
934         g_clear_pointer (&unicode_variant, g_variant_unref);
935     }
936 
937     return list;
938 }
939 
940 void
ibus_unicode_block_save(const gchar * path,GSList * list)941 ibus_unicode_block_save (const gchar *path,
942                          GSList      *list)
943 {
944     GVariant *variant;
945     const gchar *header = IBUS_UNICODE_BLOCK_MAGIC;
946     const guint16 version = IBUS_UNICODE_DATA_VERSION;
947     const gchar *contents;
948     gsize length;
949     gchar *dir;
950     GStatBuf buf = { 0, };
951     GError *error = NULL;
952 
953     g_return_if_fail (path != NULL);
954     g_return_if_fail (list != NULL);
955     if (list->data == NULL) {
956         g_warning ("Failed to save IBus Unicode block: Need a list data.");
957         return;
958     }
959 
960     variant = g_variant_new ("(sqv)",
961                              header,
962                              version,
963                              ibus_unicode_block_list_serialize (list));
964 
965     contents =  g_variant_get_data (variant);
966     length =  g_variant_get_size (variant);
967 
968     dir = g_path_get_dirname (path);
969     if (g_strcmp0 (dir, ".") != 0 && g_stat (dir, &buf) != 0) {
970         g_mkdir_with_parents (dir, 0777);
971     }
972     g_free (dir);
973     if (!g_file_set_contents (path, contents, length, &error)) {
974         g_warning ("Failed to save Unicode dict %s: %s", path, error->message);
975         g_error_free (error);
976     }
977 
978     g_variant_unref (variant);
979 }
980 
981 GSList *
ibus_unicode_block_load(const gchar * path)982 ibus_unicode_block_load (const gchar *path)
983 {
984     gchar *contents = NULL;
985     gsize length = 0;
986     GError *error = NULL;
987     GVariant *variant_table = NULL;
988     GVariant *variant = NULL;
989     const gchar *header = NULL;
990     guint16 version = 0;
991     GSList *retval = NULL;
992 
993     if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
994         g_warning ("Unicode dict does not exist: %s", path);
995         goto out_load_cache;
996     }
997 
998     if (!g_file_get_contents (path, &contents, &length, &error)) {
999         g_warning ("Failed to get dict content %s: %s", path, error->message);
1000         g_error_free (error);
1001         goto out_load_cache;
1002     }
1003 
1004     variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sq)"),
1005                                              contents,
1006                                              length,
1007                                              FALSE,
1008                                              NULL,
1009                                              NULL);
1010 
1011     if (variant_table == NULL) {
1012         g_warning ("cache table is broken.");
1013         goto out_load_cache;
1014     }
1015 
1016     g_variant_get (variant_table, "(&sq)", &header, &version);
1017 
1018     if (g_strcmp0 (header, IBUS_UNICODE_BLOCK_MAGIC) != 0) {
1019         g_warning ("cache is not IBusUnicodeBlock.");
1020         goto out_load_cache;
1021     }
1022 
1023     if (version > IBUS_UNICODE_DATA_VERSION) {
1024         g_warning ("cache version is different: %u != %u",
1025                    version, IBUS_UNICODE_DATA_VERSION);
1026         goto out_load_cache;
1027     }
1028 
1029     version = 0;
1030     header = NULL;
1031     g_variant_unref (variant_table);
1032 
1033     variant_table = g_variant_new_from_data (G_VARIANT_TYPE ("(sqv)"),
1034                                              contents,
1035                                              length,
1036                                              FALSE,
1037                                              NULL,
1038                                              NULL);
1039 
1040     if (variant_table == NULL) {
1041         g_warning ("cache table is broken.");
1042         goto out_load_cache;
1043     }
1044 
1045     g_variant_get (variant_table, "(&sqv)",
1046                    NULL,
1047                    NULL,
1048                    &variant);
1049 
1050     if (variant == NULL) {
1051         g_warning ("cache dict is broken.");
1052         goto out_load_cache;
1053     }
1054 
1055     retval = ibus_unicode_block_list_deserialize (variant);
1056 
1057 out_load_cache:
1058     if (variant)
1059         g_variant_unref (variant);
1060     if (variant_table)
1061         g_variant_unref (variant_table);
1062     g_free (contents);
1063 
1064     return retval;
1065 }
1066 
1067