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.
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.
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.
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.
22 #include "ibushotkey.h"
23 #include "ibusmarshalers.h"
24 #include "ibuskeysyms.h"
25 #include "ibusinternal.h"
26 #include "ibusshare.h"
28 #define IBUS_HOTKEY_PROFILE_GET_PRIVATE(o) \
29 (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_HOTKEY_PROFILE, IBusHotkeyProfilePrivate))
36 typedef struct _IBusHotkey IBusHotkey;
37 typedef struct _IBusHotkeyEvent IBusHotkeyEvent;
38 typedef struct _IBusHotkeyProfilePrivate IBusHotkeyProfilePrivate;
45 struct _IBusHotkeyEvent {
50 struct _IBusHotkeyProfilePrivate {
58 /* functions prototype */
59 static IBusHotkey *ibus_hotkey_new (guint keyval,
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,
70 static gboolean ibus_hotkey_profile_copy (IBusHotkeyProfile *dest,
71 const IBusHotkeyProfile*src);
72 static void ibus_hotkey_profile_trigger (IBusHotkeyProfile *profile,
76 // Normalize modifiers by setting necessary modifier bits according to keyval.
77 static guint normalize_modifiers (guint keyval,
79 static gboolean is_modifier (guint keyval);
81 static IBusSerializableClass *parent_class = NULL;
83 static guint profile_signals[LAST_SIGNAL] = { 0 };
86 ibus_hotkey_get_type (void)
88 static GType type = 0;
91 type = g_boxed_type_register_static ("IBusHotkey",
92 (GBoxedCopyFunc) ibus_hotkey_copy,
93 (GBoxedFreeFunc) ibus_hotkey_free);
100 ibus_hotkey_new (guint keyval,
103 IBusHotkey *hotkey = g_slice_new (IBusHotkey);
105 hotkey->keyval = keyval;
106 hotkey->modifiers = modifiers;
112 ibus_hotkey_free (IBusHotkey *hotkey)
114 g_slice_free (IBusHotkey, hotkey);
119 ibus_hotkey_copy (const IBusHotkey *src)
121 return ibus_hotkey_new (src->keyval, src->modifiers);
125 ibus_hotkey_cmp_with_data (IBusHotkey *hotkey1,
131 if (hotkey1 == hotkey2)
134 retval = hotkey1->keyval - hotkey2->keyval;
136 retval = hotkey1->modifiers - hotkey2->modifiers;
144 ibus_hotkey_profile_get_type (void)
146 static GType type = 0;
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),
157 (GInstanceInitFunc) ibus_hotkey_profile_init,
161 type = g_type_register_static (IBUS_TYPE_SERIALIZABLE,
171 ibus_hotkey_profile_class_init (IBusHotkeyProfileClass *class)
173 IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class);
174 IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
176 parent_class = (IBusSerializableClass *) g_type_class_peek_parent (class);
178 g_type_class_add_private (class, sizeof (IBusHotkeyProfilePrivate));
180 object_class->destroy = (IBusObjectDestroyFunc) ibus_hotkey_profile_destroy;
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;
186 class->trigger = ibus_hotkey_profile_trigger;
188 /* install signals */
190 * IBusHotkeyProfile::trigger:
191 * @profile: An IBusHotkeyProfile.
192 * @event: An event in GQuark.
193 * @user_data: User data for callback.
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.
198 * <note><para>The last parameter, user_data is not actually a valid parameter. It is displayed because of GtkDoc bug.</para></note>
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),
206 _ibus_marshal_VOID__UINT_POINTER,
214 ibus_hotkey_profile_init (IBusHotkeyProfile *profile)
216 IBusHotkeyProfilePrivate *priv;
217 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
219 priv->hotkeys = g_tree_new_full ((GCompareDataFunc) ibus_hotkey_cmp_with_data,
221 (GDestroyNotify) ibus_hotkey_free,
223 priv->events = g_array_new (TRUE, TRUE, sizeof (IBusHotkeyEvent));
225 priv->mask = IBUS_SHIFT_MASK |
234 ibus_hotkey_profile_destroy (IBusHotkeyProfile *profile)
236 IBusHotkeyProfilePrivate *priv;
237 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
243 p = (IBusHotkeyEvent *)g_array_free (priv->events, FALSE);
246 for (i = 0; p[i].event != 0; i++) {
247 g_list_free (p[i].hotkeys);
253 g_tree_destroy (priv->hotkeys);
254 priv->hotkeys = NULL;
257 IBUS_OBJECT_CLASS (parent_class)->destroy ((IBusObject *)profile);
261 ibus_hotkey_profile_serialize (IBusHotkeyProfile *profile,
262 GVariantBuilder *builder)
266 retval = parent_class->serialize ((IBusSerializable *) profile, builder);
267 g_return_val_if_fail (retval, FALSE);
273 ibus_hotkey_profile_deserialize (IBusHotkeyProfile *profile,
278 retval = parent_class->deserialize ((IBusSerializable *) profile, variant);
279 g_return_val_if_fail (retval, 0);
285 ibus_hotkey_profile_copy (IBusHotkeyProfile *dest,
286 const IBusHotkeyProfile *src)
290 retval = parent_class->copy ((IBusSerializable *)dest,
291 (IBusSerializable *)src);
292 g_return_val_if_fail (retval, FALSE);
294 g_return_val_if_fail (IBUS_IS_HOTKEY_PROFILE (dest), FALSE);
295 g_return_val_if_fail (IBUS_IS_HOTKEY_PROFILE (src), FALSE);
301 ibus_hotkey_profile_new (void)
303 IBusHotkeyProfile *profile = g_object_new (IBUS_TYPE_HOTKEY_PROFILE, NULL);
309 ibus_hotkey_profile_trigger (IBusHotkeyProfile *profile,
313 // g_debug ("%s is triggerred", g_quark_to_string (event));
317 normalize_modifiers (guint 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;
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;
345 is_modifier (guint 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:
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:
367 ibus_hotkey_profile_add_hotkey (IBusHotkeyProfile *profile,
372 IBusHotkeyProfilePrivate *priv;
373 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
375 IBusHotkey *hotkey = ibus_hotkey_new (keyval,
376 normalize_modifiers (keyval, modifiers & priv->mask));
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);
384 g_tree_insert (priv->hotkeys, (gpointer) hotkey, GUINT_TO_POINTER (event));
386 IBusHotkeyEvent *p = NULL;
388 for ( i = 0; i < priv->events->len; i++) {
389 p = &g_array_index (priv->events, IBusHotkeyEvent, i);
390 if (p->event == event)
394 if (i >= priv->events->len) {
395 g_array_set_size (priv->events, i + 1);
396 p = &g_array_index (priv->events, IBusHotkeyEvent, i);
400 p->hotkeys = g_list_append (p->hotkeys, hotkey);
407 ibus_hotkey_profile_add_hotkey_from_string (IBusHotkeyProfile *profile,
414 if (ibus_key_event_from_string (str, &keyval, &modifiers) == FALSE) {
418 return ibus_hotkey_profile_add_hotkey (profile, keyval, modifiers, event);
422 ibus_hotkey_profile_remove_hotkey (IBusHotkeyProfile *profile,
426 IBusHotkeyProfilePrivate *priv;
427 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
429 modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
431 IBusHotkey hotkey = {
433 .modifiers = modifiers
440 retval = g_tree_lookup_extended (priv->hotkeys,
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)
456 g_assert (p2->event == event);
458 p2->hotkeys = g_list_remove (p2->hotkeys, p1);
459 if (p2->hotkeys == NULL) {
460 g_array_remove_index_fast (priv->events, i);
463 g_tree_remove (priv->hotkeys, p1);
469 ibus_hotkey_profile_remove_hotkey_by_event (IBusHotkeyProfile *profile,
472 IBusHotkeyProfilePrivate *priv;
473 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
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)
483 if (p == NULL || p->event != event)
487 for (list = p->hotkeys; list != NULL; list = list->next) {
488 g_tree_remove (priv->hotkeys, (IBusHotkey *)list->data);
491 g_list_free (p->hotkeys);
492 g_array_remove_index_fast (priv->events, i);
498 ibus_hotkey_profile_filter_key_event (IBusHotkeyProfile *profile,
502 guint prev_modifiers,
505 IBusHotkeyProfilePrivate *priv;
506 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
508 modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
509 prev_modifiers = normalize_modifiers (prev_keyval, prev_modifiers & priv->mask);
511 IBusHotkey hotkey = {
513 .modifiers = modifiers,
516 if (modifiers & IBUS_RELEASE_MASK) {
517 /* previous key event must be a press key event */
518 if (prev_modifiers & IBUS_RELEASE_MASK)
521 /* modifiers should be same except the release bit */
522 if (modifiers != (prev_modifiers | IBUS_RELEASE_MASK))
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
533 if ((keyval != prev_keyval) &&
534 (is_modifier (keyval) == FALSE ||
535 is_modifier (prev_keyval) == FALSE))
539 GQuark event = (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey));
542 g_signal_emit (profile, profile_signals[TRIGGER], event, user_data);
549 ibus_hotkey_profile_lookup_hotkey (IBusHotkeyProfile *profile,
553 IBusHotkeyProfilePrivate *priv;
554 priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile);
556 modifiers = normalize_modifiers (keyval, modifiers & priv->mask);
558 IBusHotkey hotkey = {
560 .modifiers = modifiers,
563 return (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey));