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