1 /* This file is part of Maliit framework
2  *
3  * Copyright (C) 2012 One Laptop per Child Association
4  *
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the licence, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 
22 #include "maliitattributeextension.h"
23 #include "maliitattributeextensionprivate.h"
24 #include "maliitattributeextensionregistry.h"
25 #include "maliitmarshallers.h"
26 
27 /**
28  * SECTION:maliitattributeextension
29  * @short_description: attribute extensions
30  * @title: MaliitAttributeExtension
31  * @stability: Stable
32  * @include: maliit/maliitattributeextension.h
33  *
34  * #MaliitAttributeExtension class can be used by application to
35  * override some aspect of IM plugin currently used, like the looks of
36  * action key.
37  */
38 
39 struct _MaliitAttributeExtensionPrivate
40 {
41     int id;
42     gchar *filename;
43     GHashTable *attributes;
44     MaliitAttributeExtensionRegistry *registry;
45 };
46 
47 G_DEFINE_TYPE (MaliitAttributeExtension, maliit_attribute_extension, G_TYPE_OBJECT)
48 
49 enum
50 {
51     EXTENDED_ATTRIBUTE_CHANGED,
52 
53     LAST_SIGNAL
54 };
55 
56 enum
57 {
58     PROP_0,
59 
60     PROP_ID,
61     PROP_FILENAME,
62     PROP_ATTRIBUTES
63 };
64 
65 static guint signals[LAST_SIGNAL] = { 0 };
66 
67 static void
maliit_attribute_extension_finalize(GObject * object)68 maliit_attribute_extension_finalize (GObject *object)
69 {
70     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (object);
71     MaliitAttributeExtensionPrivate *priv = extension->priv;
72 
73     g_free (priv->filename);
74 
75     G_OBJECT_CLASS (maliit_attribute_extension_parent_class)->finalize (object);
76 }
77 
78 static void
maliit_attribute_extension_dispose(GObject * object)79 maliit_attribute_extension_dispose (GObject *object)
80 {
81     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (object);
82     MaliitAttributeExtensionPrivate *priv = extension->priv;
83 
84     if (priv->registry) {
85         MaliitAttributeExtensionRegistry *registry = priv->registry;
86 
87         priv->registry = NULL;
88         maliit_attribute_extension_registry_remove_extension (registry,
89                                                               extension);
90         g_object_unref (registry);
91     }
92 
93     if (priv->attributes) {
94         GHashTable *attributes = priv->attributes;
95 
96         priv->attributes = NULL;
97         g_hash_table_unref (attributes);
98     }
99 
100     G_OBJECT_CLASS (maliit_attribute_extension_parent_class)->dispose (object);
101 }
102 
103 static void
maliit_attribute_extension_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)104 maliit_attribute_extension_set_property (GObject *object,
105                                          guint prop_id,
106                                          const GValue *value,
107                                          GParamSpec *pspec)
108 {
109     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (object);
110     MaliitAttributeExtensionPrivate *priv = extension->priv;
111 
112     switch (prop_id) {
113     case PROP_ID:
114         priv->id = g_value_get_int (value);
115         break;
116     case PROP_FILENAME:
117         g_free (extension->priv->filename);
118         priv->filename = g_value_dup_string (value);
119         break;
120         /* PROP_ATTRIBUTES is read only. */
121     default:
122         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
123         break;
124     }
125 }
126 
127 static void
maliit_attribute_extension_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)128 maliit_attribute_extension_get_property (GObject *object,
129                                          guint prop_id,
130                                          GValue *value,
131                                          GParamSpec *pspec)
132 {
133     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (object);
134     MaliitAttributeExtensionPrivate *priv = extension->priv;
135 
136     switch (prop_id) {
137     case PROP_ID:
138         g_value_set_int (value, priv->id);
139         break;
140     case PROP_FILENAME:
141         g_value_set_string (value, priv->filename);
142         break;
143     case PROP_ATTRIBUTES:
144         g_value_set_boxed (value, priv->attributes);
145     default:
146         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
147         break;
148     }
149 }
150 
151 static void
maliit_attribute_extension_constructed(GObject * object)152 maliit_attribute_extension_constructed (GObject *object)
153 {
154     static int id_counter = 0;
155     MaliitAttributeExtension *extension = MALIIT_ATTRIBUTE_EXTENSION (object);
156     MaliitAttributeExtensionPrivate *priv = extension->priv;
157 
158     if (priv->id == 0) {
159         priv->id = id_counter++;
160     }
161 
162     maliit_attribute_extension_registry_add_extension (priv->registry,
163                                                        extension);
164 
165     G_OBJECT_CLASS (maliit_attribute_extension_parent_class)->constructed (object);
166 }
167 
168 static void
maliit_attribute_extension_class_init(MaliitAttributeExtensionClass * extension_class)169 maliit_attribute_extension_class_init (MaliitAttributeExtensionClass *extension_class)
170 {
171     GObjectClass *g_object_class = G_OBJECT_CLASS (extension_class);
172 
173     g_object_class->finalize = maliit_attribute_extension_finalize;
174     g_object_class->dispose = maliit_attribute_extension_dispose;
175     g_object_class->set_property = maliit_attribute_extension_set_property;
176     g_object_class->get_property = maliit_attribute_extension_get_property;
177     g_object_class->constructed = maliit_attribute_extension_constructed;
178 
179     /**
180      * MaliitAttributeExtension:id:
181      *
182      * ID of the extension.
183      */
184     g_object_class_install_property (g_object_class,
185                                      PROP_ID,
186                                      g_param_spec_int ("id",
187                                                        "ID", /* TODO: mark as translatable? */
188                                                        "ID of the extension", /* TODO: mark as translatable? */
189                                                        G_MININT,
190                                                        G_MAXINT,
191                                                        0,
192                                                        G_PARAM_READABLE |
193                                                        G_PARAM_WRITABLE |
194                                                        G_PARAM_CONSTRUCT_ONLY |
195                                                        G_PARAM_STATIC_NAME |
196                                                        G_PARAM_STATIC_BLURB |
197                                                        G_PARAM_STATIC_NICK));
198 
199     /**
200      * MaliitAttributeExtension:id:
201      *
202      * Path to file where definitions of overrides are defined.
203      */
204     g_object_class_install_property (g_object_class,
205                                      PROP_FILENAME,
206                                      g_param_spec_string ("filename",
207                                                           "Filename", /* TODO: mark as translatable? */
208                                                           "Filename of the extension", /* TODO: mark as translatable? */
209                                                           NULL,
210                                                           G_PARAM_READABLE |
211                                                           G_PARAM_WRITABLE |
212                                                           G_PARAM_CONSTRUCT_ONLY |
213                                                           G_PARAM_STATIC_NAME |
214                                                           G_PARAM_STATIC_BLURB |
215                                                           G_PARAM_STATIC_NICK));
216 
217     g_object_class_install_property (g_object_class,
218                                      PROP_ATTRIBUTES,
219                                      g_param_spec_boxed ("attributes",
220                                                          "Attributes", /* TODO: mark as translatable? */
221                                                          "Attributes overrides", /* TODO: mark as translatable? */
222                                                          G_TYPE_HASH_TABLE,
223                                                          G_PARAM_READABLE |
224                                                          G_PARAM_STATIC_NAME |
225                                                          G_PARAM_STATIC_BLURB |
226                                                          G_PARAM_STATIC_NICK));
227 
228     /**
229      * MaliitAttributeExtension::extended-attribute-changed:
230      * @extension: The #MaliitAttributeExtension emitting the signal.
231      * @key: A string specifying the target for the attribute.
232      * @value: A new value.
233      *
234      * Informs application that input method server has changed the
235      * extended attribute.
236      */
237     signals[EXTENDED_ATTRIBUTE_CHANGED] =
238         g_signal_new ("extended-attribute-changed",
239                       MALIIT_TYPE_ATTRIBUTE_EXTENSION,
240                       G_SIGNAL_RUN_FIRST,
241                       0,
242                       NULL,
243                       NULL,
244                       maliit_marshal_VOID__STRING_VARIANT,
245                       G_TYPE_NONE,
246                       2,
247                       G_TYPE_STRING,
248                       G_TYPE_VARIANT);
249 
250     g_type_class_add_private (extension_class, sizeof (MaliitAttributeExtensionPrivate));
251 }
252 
253 static void
maliit_attribute_extension_init(MaliitAttributeExtension * extension)254 maliit_attribute_extension_init (MaliitAttributeExtension *extension)
255 {
256     MaliitAttributeExtensionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (extension,
257                                                                          MALIIT_TYPE_ATTRIBUTE_EXTENSION,
258                                                                          MaliitAttributeExtensionPrivate);
259 
260     priv->id = 0;
261     priv->filename = NULL;
262     priv->attributes = g_hash_table_new_full (g_str_hash,
263                                               g_str_equal,
264                                               g_free,
265                                               (GDestroyNotify) g_variant_unref);
266     priv->registry = maliit_attribute_extension_registry_get_instance ();
267 
268     extension->priv = priv;
269 }
270 
271 /**
272  * maliit_attribute_extension_new:
273  *
274  * Creates new attribute extension, which is not associated with any file.
275  *
276  * Returns: (transfer full): The newly created
277  * #MaliitAttributeExtension.
278  */
279 MaliitAttributeExtension *
maliit_attribute_extension_new(void)280 maliit_attribute_extension_new (void)
281 {
282     return MALIIT_ATTRIBUTE_EXTENSION (g_object_new (MALIIT_TYPE_ATTRIBUTE_EXTENSION,
283                                                      NULL));
284 }
285 
286 /**
287  * maliit_attribute_extension_new_with_id: (skip)
288  * @id: An overriden id.
289  *
290  * Creates a new attribute extension with already existing id. Used
291  * internally by #MaliitSettingsManager.
292  *
293  * Returns: (transfer full): The newly created
294  * #MaliitAttributeExtension.
295  */
296 MaliitAttributeExtension *
maliit_attribute_extension_new_with_id(int id)297 maliit_attribute_extension_new_with_id (int id)
298 {
299     return MALIIT_ATTRIBUTE_EXTENSION (g_object_new (MALIIT_TYPE_ATTRIBUTE_EXTENSION,
300                                                      "id", id,
301                                                      NULL));
302 }
303 
304 /**
305  * maliit_attribute_extension_new_with_filename:
306  * @filename: (transfer none) (type filename): Filename where overrides are stored.
307  *
308  * Creates new attribute extension, which is associated with file
309  * given as @filename.
310  *
311  * Returns: (transfer full): The newly created
312  * #MaliitAttributeExtension.
313  */
314 MaliitAttributeExtension *
maliit_attribute_extension_new_with_filename(const gchar * filename)315 maliit_attribute_extension_new_with_filename (const gchar *filename)
316 {
317     return MALIIT_ATTRIBUTE_EXTENSION (g_object_new (MALIIT_TYPE_ATTRIBUTE_EXTENSION,
318                                                      "filename", filename,
319                                                      NULL));
320 }
321 
322 /**
323  * maliit_attribute_extension_get_attributes:
324  * @extension: (transfer none): The #MaliitAttributeExtension which attributes you want to get.
325  *
326  * Gets all attributes of this extension that were set previously with
327  * maliit_attribute_extension_set_attribute().
328  *
329  * Returns: (transfer none) (element-type utf8 GLib.Variant): The #GHashTable
330  * containing strings as keys and #GVariant<!-- -->s as values. Should not be
331  * freed nor modified.
332  */
333 GHashTable *
maliit_attribute_extension_get_attributes(MaliitAttributeExtension * extension)334 maliit_attribute_extension_get_attributes (MaliitAttributeExtension *extension)
335 {
336     g_return_val_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension), NULL);
337 
338     return extension->priv->attributes;
339 }
340 
341 /**
342  * maliit_attribute_extension_get_filename:
343  * @extension: (transfer none): The #MaliitAttributeExtension which filename you want to get.
344  *
345  * Gets filename of this extension that were set previously with
346  * maliit_attribute_extension_new_with_filename().
347  *
348  * Returns: (transfer none) (type filename): The string being a
349  * filename of this extension or %NULL. Returned string should not be
350  * freed nor modified.
351  */
352 const gchar *
maliit_attribute_extension_get_filename(MaliitAttributeExtension * extension)353 maliit_attribute_extension_get_filename (MaliitAttributeExtension *extension)
354 {
355     g_return_val_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension), NULL);
356 
357     return extension->priv->filename;
358 }
359 
360 /**
361  * maliit_attribute_extension_get_id:
362  * @extension: (transfer none): The #MaliitAttributeExtension which ID you want to get.
363  *
364  * Gets ID of this extension.
365  *
366  * Returns: The ID of this extension.
367  */
368 int
maliit_attribute_extension_get_id(MaliitAttributeExtension * extension)369 maliit_attribute_extension_get_id (MaliitAttributeExtension *extension)
370 {
371     g_return_val_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension), -1);
372 
373     return extension->priv->id;
374 }
375 
376 /**
377  * maliit_attribute_extension_update_attribute:
378  * @extension: (transfer none): The #MaliitAttributeExtension which attribute you want to update.
379  * @key: (transfer none): Attribute name to update.
380  * @value: (transfer none): Attribute value to update.
381  *
382  * Updates the @extension's attribute described by @key with
383  * @value. This function always emits a
384  * #MaliitAttributeExtension::extended-attribute-changed signal.
385  */
386 void
maliit_attribute_extension_update_attribute(MaliitAttributeExtension * extension,const gchar * key,GVariant * value)387 maliit_attribute_extension_update_attribute (MaliitAttributeExtension *extension,
388                                              const gchar *key,
389                                              GVariant *value)
390 {
391     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
392     g_return_if_fail (key != NULL);
393     g_return_if_fail (value != NULL);
394 
395     g_hash_table_replace (extension->priv->attributes,
396                           g_strdup (key),
397                           g_variant_ref (value));
398 
399     g_signal_emit (extension,
400                    signals[EXTENDED_ATTRIBUTE_CHANGED],
401                    0,
402                    key,
403                    value);
404 }
405 
406 /**
407  * maliit_attribute_extension_set_attribute:
408  * @extension: (transfer none): The #MaliitAttributeExtension which attribute you want to set.
409  * @key: (transfer none): Attribute name to update.
410  * @value: (transfer none): Attribute value to update.
411  *
412  * Sets an attribute in @extension described by @key to value in @value.
413  */
maliit_attribute_extension_set_attribute(MaliitAttributeExtension * extension,const gchar * key,GVariant * value)414 void maliit_attribute_extension_set_attribute (MaliitAttributeExtension *extension,
415                                                const gchar *key,
416                                                GVariant *value)
417 {
418     MaliitAttributeExtensionPrivate *priv;
419     GHashTable *attributes;
420     GVariant *orig_value;
421 
422     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
423     g_return_if_fail (key != NULL);
424     g_return_if_fail (value != NULL);
425 
426     priv = extension->priv;
427     attributes = priv->attributes;
428 
429     if (!g_hash_table_lookup_extended (attributes, key, NULL, (gpointer *)&orig_value) ||
430         !g_variant_equal (orig_value, value)) {
431 
432         g_hash_table_replace (attributes,
433                               g_strdup (key),
434                               g_variant_ref (value));
435 
436         maliit_attribute_extension_registry_extension_changed (priv->registry,
437                                                                extension,
438                                                                key,
439                                                                value);
440     }
441 }
442 
443 /**
444  * maliit_attribute_extension_attach_to_object:
445  * @extension: (transfer none): The #MaliitAttributeExtension which you want to be attached.
446  * @object: (transfer none): The #GObject to which @extension will be attached.
447  *
448  * Attaches @extension to @object, so input context can retrieve it
449  * from @object. Note that attaching extensions to non-input
450  * #GObject<!-- -->s does not have much sense.
451  */
452 void
maliit_attribute_extension_attach_to_object(MaliitAttributeExtension * extension,GObject * object)453 maliit_attribute_extension_attach_to_object (MaliitAttributeExtension *extension,
454                                              GObject *object)
455 {
456     g_return_if_fail (MALIIT_IS_ATTRIBUTE_EXTENSION (extension));
457     g_return_if_fail (G_IS_OBJECT (object));
458 
459     g_object_set_qdata_full (object, MALIIT_ATTRIBUTE_EXTENSION_DATA_QUARK,
460                              extension, g_object_unref);
461 }
462