Fix make rpm errors due to ibus-dconf and pygobject override
[platform/upstream/ibus.git] / src / ibushotkey.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* IBus - The Input Bus
4  * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2008-2010 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #include "ibushotkey.h"
23 #include "ibusmarshalers.h"
24 #include "ibuskeysyms.h"
25 #include "ibusinternal.h"
26 #include "ibusshare.h"
27
28 #define IBUS_HOTKEY_PROFILE_GET_PRIVATE(o)  \
29    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_HOTKEY_PROFILE, IBusHotkeyProfilePrivate))
30
31 enum {
32     TRIGGER,
33     LAST_SIGNAL,
34 };
35
36 typedef struct _IBusHotkey IBusHotkey;
37 typedef struct _IBusHotkeyEvent IBusHotkeyEvent;
38 typedef struct _IBusHotkeyProfilePrivate IBusHotkeyProfilePrivate;
39
40 struct _IBusHotkey {
41     guint   keyval;
42     guint   modifiers;
43 };
44
45 struct _IBusHotkeyEvent {
46     GQuark event;
47     GList *hotkeys;
48 };
49
50 struct _IBusHotkeyProfilePrivate {
51     GTree *hotkeys;
52     GArray *events;
53     guint   mask;
54 };
55
56
57
58 /* functions prototype */
59 static IBusHotkey   *ibus_hotkey_new                (guint                   keyval,
60                                                      guint                   modifiers);
61 static IBusHotkey   *ibus_hotkey_copy               (const IBusHotkey       *src);
62 static void          ibus_hotkey_free               (IBusHotkey             *hotkey);
63 static void          ibus_hotkey_profile_class_init (IBusHotkeyProfileClass *class);
64 static void          ibus_hotkey_profile_init       (IBusHotkeyProfile      *profile);
65 static void          ibus_hotkey_profile_destroy    (IBusHotkeyProfile      *profile);
66 static gboolean      ibus_hotkey_profile_serialize  (IBusHotkeyProfile      *profile,
67                                                      GVariantBuilder        *builder);
68 static gint          ibus_hotkey_profile_deserialize(IBusHotkeyProfile      *profile,
69                                                      GVariant               *variant);
70 static gboolean      ibus_hotkey_profile_copy       (IBusHotkeyProfile      *dest,
71                                                      const IBusHotkeyProfile*src);
72 static void          ibus_hotkey_profile_trigger    (IBusHotkeyProfile      *profile,
73                                                      GQuark                  event,
74                                                      gpointer                user_data);
75
76 // Normalize modifiers by setting necessary modifier bits according to keyval.
77 static guint         normalize_modifiers            (guint                   keyval,
78                                                      guint                   modifiers);
79 static gboolean      is_modifier                    (guint                   keyval);
80
81 static IBusSerializableClass *parent_class = NULL;
82
83 static guint profile_signals[LAST_SIGNAL] = { 0 };
84
85 GType
86 ibus_hotkey_get_type (void)
87 {
88     static GType type = 0;
89
90     if (type == 0) {
91         type = g_boxed_type_register_static ("IBusHotkey",
92                                              (GBoxedCopyFunc) ibus_hotkey_copy,
93                                              (GBoxedFreeFunc) ibus_hotkey_free);
94     }
95
96     return type;
97 }
98
99 static IBusHotkey *
100 ibus_hotkey_new (guint keyval,
101                  guint modifiers)
102 {
103     IBusHotkey *hotkey = g_slice_new (IBusHotkey);
104
105     hotkey->keyval = keyval;
106     hotkey->modifiers = modifiers;
107
108     return hotkey;
109 }
110
111 static void
112 ibus_hotkey_free (IBusHotkey *hotkey)
113 {
114     g_slice_free (IBusHotkey, hotkey);
115 }
116
117
118 static IBusHotkey *
119 ibus_hotkey_copy (const IBusHotkey *src)
120 {
121     return ibus_hotkey_new (src->keyval, src->modifiers);
122 }
123
124 static gint
125 ibus_hotkey_cmp_with_data (IBusHotkey *hotkey1,
126                            IBusHotkey *hotkey2,
127                            gpointer    user_data)
128 {
129     gint retval;
130
131     if (hotkey1 == hotkey2)
132         return 0;
133
134     retval = hotkey1->keyval - hotkey2->keyval;
135     if (retval == 0)
136         retval = hotkey1->modifiers - hotkey2->modifiers;
137
138     return retval;
139 }
140
141
142
143 GType
144 ibus_hotkey_profile_get_type (void)
145 {
146     static GType type = 0;
147
148     static const GTypeInfo type_info = {
149         sizeof (IBusHotkeyProfileClass),
150         (GBaseInitFunc)     NULL,
151         (GBaseFinalizeFunc) NULL,
152         (GClassInitFunc)    ibus_hotkey_profile_class_init,
153         NULL,               /* class finialize */
154         NULL,               /* class data */
155         sizeof (IBusHotkeyProfile),
156         0,
157         (GInstanceInitFunc) ibus_hotkey_profile_init,
158     };
159
160     if (type == 0) {
161         type = g_type_register_static (IBUS_TYPE_SERIALIZABLE,
162                                        "IBusHotkeyProfile",
163                                        &type_info,
164                                        0);
165     }
166
167     return type;
168 }
169
170 static void
171 ibus_hotkey_profile_class_init (IBusHotkeyProfileClass *class)
172 {
173     IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
174     IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
175
176     parent_class = (IBusSerializableClass *) g_type_class_peek_parent (class);
177
178     g_type_class_add_private (class, sizeof (IBusHotkeyProfilePrivate));
179
180     object_class->destroy = (IBusObjectDestroyFunc) ibus_hotkey_profile_destroy;
181
182     serializable_class->serialize   = (IBusSerializableSerializeFunc) ibus_hotkey_profile_serialize;
183     serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_hotkey_profile_deserialize;
184     serializable_class->copy        = (IBusSerializableCopyFunc) ibus_hotkey_profile_copy;
185
186     class->trigger = ibus_hotkey_profile_trigger;
187
188     /* install signals */
189     /**
190      * IBusHotkeyProfile::trigger:
191      * @profile: An IBusHotkeyProfile.
192      * @event: An event in GQuark.
193      * @user_data: User data for callback.
194      *
195      * Emitted when a hotkey is pressed and the hotkey is in profile.
196      * Implement the member function trigger() in extended class to receive this signal.
197      *
198      * <note><para>The last parameter, user_data is not actually a valid parameter. It is displayed because of GtkDoc bug.</para></note>
199      */
200     profile_signals[TRIGGER] =
201         g_signal_new (I_("trigger"),
202             G_TYPE_FROM_CLASS (class),
203             G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
204             G_STRUCT_OFFSET (IBusHotkeyProfileClass, trigger),
205             NULL, NULL,
206             _ibus_marshal_VOID__UINT_POINTER,
207             G_TYPE_NONE,
208             2,
209             G_TYPE_UINT,
210             G_TYPE_POINTER);
211 }
212
213 static void
214 ibus_hotkey_profile_init (IBusHotkeyProfile *profile)
215 {
216     IBusHotkeyProfilePrivate *priv;
217     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
218
219     priv->hotkeys = g_tree_new_full ((GCompareDataFunc) ibus_hotkey_cmp_with_data,
220                                      NULL,
221                                      (GDestroyNotify) ibus_hotkey_free,
222                                      NULL);
223     priv->events = g_array_new (TRUE, TRUE, sizeof (IBusHotkeyEvent));
224
225     priv->mask = IBUS_SHIFT_MASK |
226                  IBUS_CONTROL_MASK |
227                  IBUS_MOD1_MASK |
228                  IBUS_SUPER_MASK |
229                  IBUS_HYPER_MASK |
230                  IBUS_RELEASE_MASK;
231 }
232
233 static void
234 ibus_hotkey_profile_destroy (IBusHotkeyProfile *profile)
235 {
236     IBusHotkeyProfilePrivate *priv;
237     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
238
239     /* free events */
240     if (priv->events) {
241         IBusHotkeyEvent *p;
242         gint i;
243         p = (IBusHotkeyEvent *)g_array_free (priv->events, FALSE);
244         priv->events = NULL;
245
246         for (i = 0; p[i].event != 0; i++) {
247             g_list_free (p[i].hotkeys);
248         }
249         g_free (p);
250     }
251
252     if (priv->hotkeys) {
253         g_tree_destroy (priv->hotkeys);
254         priv->hotkeys = NULL;
255     }
256
257     IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)profile);
258 }
259
260 static gboolean
261 ibus_hotkey_profile_serialize (IBusHotkeyProfile *profile,
262                                GVariantBuilder   *builder)
263 {
264     gboolean retval;
265
266     retval = parent_class->serialize ((IBusSerializable *) profile, builder);
267     g_return_val_if_fail (retval, FALSE);
268
269     return TRUE;
270 }
271
272 static gint
273 ibus_hotkey_profile_deserialize (IBusHotkeyProfile *profile,
274                                  GVariant          *variant)
275 {
276     gint retval;
277
278     retval = parent_class->deserialize ((IBusSerializable *) profile, variant);
279     g_return_val_if_fail (retval, 0);
280
281     return retval;
282 }
283
284 static gboolean
285 ibus_hotkey_profile_copy (IBusHotkeyProfile       *dest,
286                           const IBusHotkeyProfile *src)
287 {
288     gboolean retval;
289
290     retval = parent_class->copy ((IBusSerializable *)dest,
291                                  (IBusSerializable *)src);
292     g_return_val_if_fail (retval, FALSE);
293
294     g_return_val_if_fail (IBUS_IS_HOTKEY_PROFILE (dest), FALSE);
295     g_return_val_if_fail (IBUS_IS_HOTKEY_PROFILE (src), FALSE);
296
297     return TRUE;
298 }
299
300 IBusHotkeyProfile *
301 ibus_hotkey_profile_new (void)
302 {
303     IBusHotkeyProfile *profile = g_object_new (IBUS_TYPE_HOTKEY_PROFILE, NULL);
304
305     return profile;
306 }
307
308 static void
309 ibus_hotkey_profile_trigger (IBusHotkeyProfile *profile,
310                              GQuark             event,
311                              gpointer           user_data)
312 {
313     // g_debug ("%s is triggerred", g_quark_to_string (event));
314 }
315
316 static guint
317 normalize_modifiers (guint keyval,
318                      guint modifiers)
319 {
320     switch(keyval) {
321     case IBUS_KEY_Control_L:
322     case IBUS_KEY_Control_R:
323         return modifiers | IBUS_CONTROL_MASK;
324     case IBUS_KEY_Shift_L:
325     case IBUS_KEY_Shift_R:
326         return modifiers | IBUS_SHIFT_MASK;
327     case IBUS_KEY_Alt_L:
328     case IBUS_KEY_Alt_R:
329     // Chrome OS does not have Meta key. Instead, shift+alt generates Meta keyval.
330     case IBUS_KEY_Meta_L:
331     case IBUS_KEY_Meta_R:
332         return modifiers | IBUS_MOD1_MASK;
333     case IBUS_KEY_Super_L:
334     case IBUS_KEY_Super_R:
335         return modifiers | IBUS_SUPER_MASK;
336     case IBUS_KEY_Hyper_L:
337     case IBUS_KEY_Hyper_R:
338         return modifiers | IBUS_HYPER_MASK;
339     default:
340         return modifiers;
341     }
342 }
343
344 static gboolean
345 is_modifier (guint keyval)
346 {
347     switch(keyval) {
348     case IBUS_KEY_Control_L:
349     case IBUS_KEY_Control_R:
350     case IBUS_KEY_Shift_L:
351     case IBUS_KEY_Shift_R:
352     case IBUS_KEY_Alt_L:
353     case IBUS_KEY_Alt_R:
354     case IBUS_KEY_Meta_L:
355     case IBUS_KEY_Meta_R:
356     case IBUS_KEY_Super_L:
357     case IBUS_KEY_Super_R:
358     case IBUS_KEY_Hyper_L:
359     case IBUS_KEY_Hyper_R:
360         return TRUE;
361     default:
362         return FALSE;
363     }
364 }
365
366 gboolean
367 ibus_hotkey_profile_add_hotkey (IBusHotkeyProfile *profile,
368                                 guint              keyval,
369                                 guint              modifiers,
370                                 GQuark             event)
371 {
372     IBusHotkeyProfilePrivate *priv;
373     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
374
375     IBusHotkey *hotkey = ibus_hotkey_new (keyval,
376                                           normalize_modifiers (keyval, modifiers & priv->mask));
377
378     /* has the same hotkey in profile */
379     if (g_tree_lookup (priv->hotkeys, hotkey) != NULL) {
380         ibus_hotkey_free (hotkey);
381         g_return_val_if_reached (FALSE);
382     }
383
384     g_tree_insert (priv->hotkeys, (gpointer) hotkey, GUINT_TO_POINTER (event));
385
386     IBusHotkeyEvent *p = NULL;
387     gint i;
388     for ( i = 0; i < priv->events->len; i++) {
389         p = &g_array_index (priv->events, IBusHotkeyEvent, i);
390         if (p->event == event)
391             break;
392     }
393
394     if (i >= priv->events->len) {
395         g_array_set_size (priv->events, i + 1);
396         p = &g_array_index (priv->events, IBusHotkeyEvent, i);
397         p->event = event;
398     }
399
400     p->hotkeys = g_list_append (p->hotkeys, hotkey);
401
402     return TRUE;
403 }
404
405
406 gboolean
407 ibus_hotkey_profile_add_hotkey_from_string (IBusHotkeyProfile *profile,
408                                             const gchar       *str,
409                                             GQuark             event)
410 {
411     guint keyval;
412     guint modifiers;
413
414     if (ibus_key_event_from_string (str, &keyval, &modifiers) == FALSE) {
415         return FALSE;
416     }
417
418     return ibus_hotkey_profile_add_hotkey (profile, keyval, modifiers, event);
419 }
420
421 gboolean
422 ibus_hotkey_profile_remove_hotkey (IBusHotkeyProfile *profile,
423                                    guint              keyval,
424                                    guint              modifiers)
425 {
426     IBusHotkeyProfilePrivate *priv;
427     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
428
429     modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
430
431     IBusHotkey hotkey = {
432         .keyval = keyval,
433         .modifiers = modifiers
434     };
435
436     IBusHotkey *p1;
437     GQuark event;
438     gboolean retval;
439
440     retval = g_tree_lookup_extended (priv->hotkeys,
441                                      &hotkey,
442                                      (gpointer)&p1,
443                                      (gpointer)&event);
444
445     if (!retval)
446         return FALSE;
447
448     gint i;
449     IBusHotkeyEvent *p2 = NULL;
450     for ( i = 0; i < priv->events->len; i++) {
451         p2 = &g_array_index (priv->events, IBusHotkeyEvent, i);
452         if (p2->event == event)
453             break;
454     }
455
456     g_assert (p2->event == event);
457
458     p2->hotkeys = g_list_remove (p2->hotkeys, p1);
459     if (p2->hotkeys == NULL) {
460         g_array_remove_index_fast (priv->events, i);
461     }
462
463     g_tree_remove (priv->hotkeys, p1);
464
465     return TRUE;
466 }
467
468 gboolean
469 ibus_hotkey_profile_remove_hotkey_by_event (IBusHotkeyProfile *profile,
470                                             GQuark             event)
471 {
472     IBusHotkeyProfilePrivate *priv;
473     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
474
475     gint i;
476     IBusHotkeyEvent *p = NULL;
477     for ( i = 0; i < priv->events->len; i++) {
478         p = &g_array_index (priv->events, IBusHotkeyEvent, i);
479         if (p->event == event)
480             break;
481     }
482
483     if (p == NULL || p->event != event)
484         return FALSE;
485
486     GList *list;
487     for (list = p->hotkeys; list != NULL; list = list->next) {
488         g_tree_remove (priv->hotkeys, (IBusHotkey *)list->data);
489     }
490
491     g_list_free (p->hotkeys);
492     g_array_remove_index_fast (priv->events, i);
493
494     return TRUE;
495 }
496
497 GQuark
498 ibus_hotkey_profile_filter_key_event (IBusHotkeyProfile *profile,
499                                       guint              keyval,
500                                       guint              modifiers,
501                                       guint              prev_keyval,
502                                       guint              prev_modifiers,
503                                       gpointer           user_data)
504 {
505     IBusHotkeyProfilePrivate *priv;
506     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
507
508     modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
509     prev_modifiers = normalize_modifiers (prev_keyval, prev_modifiers & priv->mask);
510
511     IBusHotkey hotkey = {
512         .keyval = keyval,
513         .modifiers = modifiers,
514     };
515
516     if (modifiers & IBUS_RELEASE_MASK) {
517         /* previous key event must be a press key event */
518         if (prev_modifiers & IBUS_RELEASE_MASK)
519             return 0;
520
521         /* modifiers should be same except the release bit */
522         if (modifiers != (prev_modifiers | IBUS_RELEASE_MASK))
523             return 0;
524
525         /* If it is release key event,
526          * we need check if keyval is equal to the prev keyval.
527          * If keyval is not equal to the prev keyval,
528          * but both keyvals are modifier keys,
529          * we will still search it in hotkeys.
530          * It is for matching some key sequences like:
531          * Shift Down, Alt Down, Shift Up => Shift+Alt+Release - Shift hotkey
532          **/
533         if ((keyval != prev_keyval) &&
534             (is_modifier (keyval) == FALSE ||
535              is_modifier (prev_keyval) == FALSE))
536             return 0;
537     }
538
539     GQuark event = (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey));
540
541     if (event != 0) {
542         g_signal_emit (profile, profile_signals[TRIGGER], event, user_data);
543     }
544
545     return event;
546 }
547
548 GQuark
549 ibus_hotkey_profile_lookup_hotkey (IBusHotkeyProfile *profile,
550                                    guint              keyval,
551                                    guint              modifiers)
552 {
553     IBusHotkeyProfilePrivate *priv;
554     priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
555
556     modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
557
558     IBusHotkey hotkey = {
559         .keyval = keyval,
560         .modifiers = modifiers,
561     };
562
563     return (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey));
564 }