From e2793f52bf3da7a22321f053f3ba8154026fd6fa Mon Sep 17 00:00:00 2001 From: James Su Date: Tue, 11 May 2010 13:41:59 -0700 Subject: [PATCH] Support engine specific hotkey. BUG=http://crosbug.com/2543 TEST=none With this CL, each engine can specify one or more special activation hotkeys. This CL doesn't support customizing global hotkeys per engine. I'd still prefer to customize the global hotkeys based on current locale rather than input method engine. Add hotkeys property to IBusEngineDesc, so that each engine can specify their own special hotkeys. This is useful for input methods that have dedicated hotkeys, such as Japanese input methods, which has a dedicated hotkey key: Kana. Review URL: http://codereview.chromium.org/1702015 --- bus/ibusimpl.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++----- bus/ibusimpl.h | 3 + ibus/component.py | 4 +- ibus/enginedesc.py | 13 +++- src/ibusenginedesc.c | 35 +++++++++- src/ibusenginedesc.h | 30 ++++++++- src/ibushotkey.c | 16 +++++ src/ibushotkey.h | 14 +++- 8 files changed, 270 insertions(+), 28 deletions(-) diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c index c14831c..d63bf12 100644 --- a/bus/ibusimpl.c +++ b/bus/ibusimpl.c @@ -90,6 +90,10 @@ static void bus_ibus_impl_global_engine_changed static void _factory_destroy_cb (BusFactoryProxy *factory, BusIBusImpl *ibus); +static void bus_ibus_impl_set_context_engine_from_desc + (BusIBusImpl *ibus, + BusInputContext *context, + IBusEngineDesc *engine_desc); static void bus_ibus_impl_set_context_engine(BusIBusImpl *ibus, BusInputContext *context, BusEngineProxy *engine); @@ -103,6 +107,8 @@ static gchar *bus_ibus_impl_load_global_previous_engine_name_from_config (BusIBusImpl *ibus); static void bus_ibus_impl_save_global_previous_engine_name_to_config (BusIBusImpl *ibus); +static void bus_ibus_impl_update_engines_hotkey_profile + (BusIBusImpl *ibus); G_DEFINE_TYPE(BusIBusImpl, bus_ibus_impl, IBUS_TYPE_SERVICE) @@ -250,6 +256,8 @@ bus_ibus_impl_set_preload_engines (BusIBusImpl *ibus, ibus_component_start (component, g_verbose); } } + + bus_ibus_impl_update_engines_hotkey_profile (ibus); } static void @@ -616,6 +624,9 @@ bus_ibus_impl_init (BusIBusImpl *ibus) ibus->global_engine = NULL; ibus->global_previous_engine_name = NULL; + ibus->engines_hotkey_profile = NULL; + ibus->hotkey_to_engines_map = NULL; + bus_ibus_impl_reload_config (ibus); g_signal_connect (BUS_DEFAULT_DBUS, @@ -692,6 +703,16 @@ bus_ibus_impl_destroy (BusIBusImpl *ibus) g_free (ibus->global_previous_engine_name); + if (ibus->engines_hotkey_profile != NULL) { + g_object_unref (ibus->engines_hotkey_profile); + ibus->engines_hotkey_profile = NULL; + } + + if (ibus->hotkey_to_engines_map) { + g_hash_table_unref (ibus->hotkey_to_engines_map); + ibus->hotkey_to_engines_map = NULL; + } + bus_server_quit (BUS_DEFAULT_SERVER); ibus_object_destroy ((IBusObject *) BUS_DEFAULT_SERVER); IBUS_OBJECT_CLASS(bus_ibus_impl_parent_class)->destroy (IBUS_OBJECT (ibus)); @@ -901,12 +922,7 @@ _context_request_engine_cb (BusInputContext *context, engine_desc = _find_engine_desc_by_name (ibus, engine_name); } - if (engine_desc != NULL) { - engine = bus_ibus_impl_create_engine (engine_desc); - if (engine != NULL) { - bus_ibus_impl_set_context_engine (ibus, context, engine); - } - } + bus_ibus_impl_set_context_engine_from_desc (ibus, context, engine_desc); } static void @@ -949,12 +965,7 @@ bus_ibus_impl_context_request_next_engine_in_menu (BusIBusImpl *ibus, } } - if (next_desc != NULL) { - engine = bus_ibus_impl_create_engine (next_desc); - if (engine != NULL) { - bus_ibus_impl_set_context_engine (ibus, context, engine); - } - } + bus_ibus_impl_set_context_engine_from_desc (ibus, context, next_desc); } static void @@ -1023,6 +1034,19 @@ bus_ibus_impl_set_global_engine (BusIBusImpl *ibus, } static void +bus_ibus_impl_set_context_engine_from_desc (BusIBusImpl *ibus, + BusInputContext *context, + IBusEngineDesc *engine_desc) +{ + if (engine_desc != NULL) { + BusEngineProxy *engine = bus_ibus_impl_create_engine (engine_desc); + if (engine != NULL) { + bus_ibus_impl_set_context_engine (ibus, context, engine); + } + } +} + +static void bus_ibus_impl_set_context_engine (BusIBusImpl *ibus, BusInputContext *context, BusEngineProxy *engine) { @@ -1295,6 +1319,8 @@ _factory_destroy_cb (BusFactoryProxy *factory, } g_object_unref (factory); + + bus_ibus_impl_update_engines_hotkey_profile (ibus); } static void @@ -1354,6 +1380,8 @@ _ibus_register_component (BusIBusImpl *ibus, ibus->register_engine_list = g_list_concat (ibus->register_engine_list, engines); g_object_unref (component); + bus_ibus_impl_update_engines_hotkey_profile (ibus); + reply = ibus_message_new_method_return (message); return reply; } @@ -1800,18 +1828,22 @@ bus_ibus_impl_filter_keyboard_shortcuts (BusIBusImpl *ibus, static GQuark next = 0; static GQuark previous = 0; + GQuark event; + GList *engine_list; + if (trigger == 0) { trigger = g_quark_from_static_string ("trigger"); next = g_quark_from_static_string ("next-engine-in-menu"); previous = g_quark_from_static_string ("previous-engine"); } - GQuark event = ibus_hotkey_profile_filter_key_event (ibus->hotkey_profile, - keyval, - modifiers, - prev_keyval, - prev_modifiers, - 0); + /* Try global hotkeys first. */ + event = ibus_hotkey_profile_filter_key_event (ibus->hotkey_profile, + keyval, + modifiers, + prev_keyval, + prev_modifiers, + 0); if (event == trigger) { gboolean enabled = bus_input_context_is_enabled (context); @@ -1841,6 +1873,50 @@ bus_ibus_impl_filter_keyboard_shortcuts (BusIBusImpl *ibus, } return TRUE; } + + if (!ibus->engines_hotkey_profile || !ibus->hotkey_to_engines_map) { + return FALSE; + } + + /* Then try engines hotkeys. */ + event = ibus_hotkey_profile_filter_key_event (ibus->engines_hotkey_profile, + keyval, + modifiers, + prev_keyval, + prev_modifiers, + 0); + if (event == 0) { + return FALSE; + } + + engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map, + GUINT_TO_POINTER (event)); + if (engine_list) { + BusEngineProxy *current_engine = bus_input_context_get_engine (context); + IBusEngineDesc *current_engine_desc = + (current_engine ? bus_engine_proxy_get_desc (current_engine) : NULL); + IBusEngineDesc *new_engine_desc = (IBusEngineDesc *) engine_list->data; + + g_assert (new_engine_desc); + + /* Find out what engine we should switch to. If the current engine has + * the same hotkey, then we should switch to the next engine with the + * same hotkey in the list. Otherwise, we just switch to the first + * engine in the list. */ + GList *p = engine_list; + for (; p->next != NULL; p = p->next) { + if (current_engine_desc == (IBusEngineDesc *) p->data) { + new_engine_desc = (IBusEngineDesc *) p->next->data; + break; + } + } + + if (current_engine_desc != new_engine_desc) { + bus_ibus_impl_set_context_engine_from_desc (ibus, context, new_engine_desc); + return TRUE; + } + } + return FALSE; } @@ -1907,3 +1983,74 @@ bus_ibus_impl_save_global_previous_engine_name_to_config (BusIBusImpl *ibus) g_value_unset (&value); } } + +static void +_add_engine_hotkey (IBusEngineDesc *engine, BusIBusImpl *ibus) +{ + gchar **hotkey_list; + gchar **p; + gchar *hotkey; + GList *engine_list; + + GQuark event; + guint keyval; + guint modifiers; + + if (!engine || !engine->hotkeys || !*engine->hotkeys) { + return; + } + + hotkey_list = g_strsplit_set (engine->hotkeys, ";,", 0); + + for (p = hotkey_list; p && *p; ++p) { + hotkey = g_strstrip (*p); + if (!*hotkey || !ibus_key_event_from_string (hotkey, &keyval, &modifiers)) { + continue; + } + + /* If the hotkey already exists, we won't need to add it again. */ + event = ibus_hotkey_profile_lookup_hotkey (ibus->engines_hotkey_profile, + keyval, modifiers); + if (event == 0) { + event = g_quark_from_string (hotkey); + ibus_hotkey_profile_add_hotkey (ibus->engines_hotkey_profile, + keyval, modifiers, event); + } + + engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map, + GUINT_TO_POINTER (event)); + + /* As we will rebuild the engines hotkey map whenever an engine was + * added or removed, we don't need to hold a reference of the engine + * here. */ + engine_list = g_list_append (engine_list, engine); + + /* We need to steal the value before adding it back, otherwise it will + * be destroyed. */ + g_hash_table_steal (ibus->hotkey_to_engines_map, GUINT_TO_POINTER (event)); + + g_hash_table_insert (ibus->hotkey_to_engines_map, + GUINT_TO_POINTER (event), engine_list); + } + + g_strfreev (hotkey_list); +} + +static void +bus_ibus_impl_update_engines_hotkey_profile (BusIBusImpl *ibus) +{ + if (ibus->engines_hotkey_profile) { + g_object_unref (ibus->engines_hotkey_profile); + } + + if (ibus->hotkey_to_engines_map) { + g_hash_table_unref (ibus->hotkey_to_engines_map); + } + + ibus->engines_hotkey_profile = ibus_hotkey_profile_new(); + ibus->hotkey_to_engines_map = + g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_list_free); + + g_list_foreach (ibus->register_engine_list, (GFunc) _add_engine_hotkey, ibus); + g_list_foreach (ibus->engine_list, (GFunc) _add_engine_hotkey, ibus); +} diff --git a/bus/ibusimpl.h b/bus/ibusimpl.h index 7eda871..860f695 100644 --- a/bus/ibusimpl.h +++ b/bus/ibusimpl.h @@ -88,6 +88,9 @@ struct _BusIBusImpl { gboolean use_global_engine; BusEngineProxy *global_engine; gchar *global_previous_engine_name; + + IBusHotkeyProfile *engines_hotkey_profile; + GHashTable *hotkey_to_engines_map; }; struct _BusIBusImplClass { diff --git a/ibus/component.py b/ibus/component.py index 47db730..a662b70 100644 --- a/ibus/component.py +++ b/ibus/component.py @@ -90,8 +90,8 @@ class Component(Serializable): def add_observed_path(self, path): self.__observed_paths.append(ObservedPath(path)) - def add_engine(self, name="", longname="", description="", language="", license="", author="", icon="", layout=""): - engine = EngineDesc(name, longname, description, language, license, author, icon, layout) + def add_engine(self, name="", longname="", description="", language="", license="", author="", icon="", layout="", hotkeys=""): + engine = EngineDesc(name, longname, description, language, license, author, icon, layout, hotkeys) self.__engines.append(engine) def serialize(self, struct): diff --git a/ibus/enginedesc.py b/ibus/enginedesc.py index 3193932..d8ba28b 100644 --- a/ibus/enginedesc.py +++ b/ibus/enginedesc.py @@ -31,7 +31,7 @@ from serializable import * class EngineDesc(Serializable): __gtype_name__ = "PYIBusEngineDesc" __NAME__ = "IBusEngineDesc" - def __init__(self, name="", longname="", description="", language="", license="", author="", icon="", layout="", rank=0): + def __init__(self, name="", longname="", description="", language="", license="", author="", icon="", layout="", hotkeys="", rank=0): super(EngineDesc, self).__init__() self.__name = name self.__longname = longname @@ -41,7 +41,8 @@ class EngineDesc(Serializable): self.__author = author self.__icon = icon self.__layout = layout - self.__rank = rank; + self.__hotkeys = hotkeys + self.__rank = rank def get_name(self): return self.__name @@ -67,6 +68,9 @@ class EngineDesc(Serializable): def get_layout(self): return self.__layout + def get_hotkeys(self): + return self.__hotkeys + def get_rank(self): return self.__rank @@ -78,6 +82,7 @@ class EngineDesc(Serializable): author = property(get_author) icon = property(get_icon) layout = property(get_layout) + hotkeys = property(get_hotkeys) rank = property(get_rank) def serialize(self, struct): @@ -90,6 +95,7 @@ class EngineDesc(Serializable): struct.append(dbus.String(self.__author)) struct.append(dbus.String(self.__icon)) struct.append(dbus.String(self.__layout)) + struct.append(dbus.String(self.__hotkeys)) struct.append(dbus.UInt32(self.__rank)) def deserialize(self, struct): @@ -102,10 +108,11 @@ class EngineDesc(Serializable): self.__author = struct.pop(0) self.__icon = struct.pop(0) self.__layout = struct.pop(0) + self.__hotkeys = struct.pop(0) self.__rank = struct.pop(0) def test(): - engine = EngineDesc("Hello", "", "", "", "", "", "", "") + engine = EngineDesc("Hello", "", "", "", "", "", "", "", "") value = serialize_object(engine) engine = deserialize_object(value) diff --git a/src/ibusenginedesc.c b/src/ibusenginedesc.c index 5563cc0..e9a6248 100644 --- a/src/ibusenginedesc.c +++ b/src/ibusenginedesc.c @@ -64,7 +64,7 @@ ibus_engine_desc_class_init (IBusEngineDescClass *klass) serializable_class->deserialize = (IBusSerializableDeserializeFunc) ibus_engine_desc_deserialize; serializable_class->copy = (IBusSerializableCopyFunc) ibus_engine_desc_copy; - g_string_append (serializable_class->signature, "ssssssssu"); + g_string_append (serializable_class->signature, "sssssssssu"); } static void @@ -79,6 +79,7 @@ ibus_engine_desc_init (IBusEngineDesc *desc) desc->author = NULL; desc->icon = NULL; desc->layout = NULL; + desc->hotkeys = NULL; desc->rank = 0; } @@ -93,6 +94,7 @@ ibus_engine_desc_destroy (IBusEngineDesc *desc) g_free (desc->author); g_free (desc->icon); g_free (desc->layout); + g_free (desc->hotkeys); IBUS_OBJECT_CLASS (ibus_engine_desc_parent_class)->destroy (IBUS_OBJECT (desc)); } @@ -130,6 +132,9 @@ ibus_engine_desc_serialize (IBusEngineDesc *desc, retval = ibus_message_iter_append (iter, G_TYPE_STRING, &desc->layout); g_return_val_if_fail (retval, FALSE); + retval = ibus_message_iter_append (iter, G_TYPE_STRING, &desc->hotkeys); + g_return_val_if_fail (retval, FALSE); + retval = ibus_message_iter_append (iter, G_TYPE_UINT, &desc->rank); g_return_val_if_fail (retval, FALSE); @@ -186,6 +191,11 @@ ibus_engine_desc_deserialize (IBusEngineDesc *desc, ibus_message_iter_next (iter); desc->layout = g_strdup (str); + retval = ibus_message_iter_get (iter, G_TYPE_STRING, &str); + g_return_val_if_fail (retval, FALSE); + ibus_message_iter_next (iter); + desc->hotkeys = g_strdup (str); + retval = ibus_message_iter_get (iter, G_TYPE_UINT, &desc->rank); g_return_val_if_fail (retval, FALSE); ibus_message_iter_next (iter); @@ -212,6 +222,7 @@ ibus_engine_desc_copy (IBusEngineDesc *dest, dest->author = g_strdup (src->author); dest->icon = g_strdup (src->icon); dest->layout = g_strdup (src->layout); + dest->hotkeys = g_strdup (src->hotkeys); return TRUE; } @@ -248,6 +259,7 @@ ibus_engine_desc_output (IBusEngineDesc *desc, OUTPUT_ENTRY_1(author); OUTPUT_ENTRY_1(icon); OUTPUT_ENTRY_1(layout); + OUTPUT_ENTRY_1(hotkeys); g_string_append_indent (output, indent + 1); g_string_append_printf (output, "%u\n", desc->rank); #undef OUTPUT_ENTRY @@ -279,8 +291,9 @@ ibus_engine_desc_parse_xml_node (IBusEngineDesc *desc, PARSE_ENTRY_1(author); PARSE_ENTRY_1(icon); PARSE_ENTRY_1(layout); + PARSE_ENTRY_1(hotkeys); #undef PARSE_ENTRY -#undef PARSE_ENTRY1 +#undef PARSE_ENTRY_1 if (g_strcmp0 (sub_node->name , "rank") == 0) { desc->rank = atoi (sub_node->text); continue; @@ -300,6 +313,21 @@ ibus_engine_desc_new (const gchar *name, const gchar *icon, const gchar *layout) { + return ibus_engine_desc_new2 (name, longname, description, language, + license, author, icon, layout, ""); +} + +IBusEngineDesc * +ibus_engine_desc_new2 (const gchar *name, + const gchar *longname, + const gchar *description, + const gchar *language, + const gchar *license, + const gchar *author, + const gchar *icon, + const gchar *layout, + const gchar *hotkeys) +{ g_assert (name); g_assert (longname); g_assert (description); @@ -308,6 +336,7 @@ ibus_engine_desc_new (const gchar *name, g_assert (author); g_assert (icon); g_assert (layout); + g_assert (hotkeys); IBusEngineDesc *desc; desc = (IBusEngineDesc *)g_object_new (IBUS_TYPE_ENGINE_DESC, NULL); @@ -320,6 +349,7 @@ ibus_engine_desc_new (const gchar *name, desc->author = g_strdup (author); desc->icon = g_strdup (icon); desc->layout = g_strdup (layout); + desc->hotkeys = g_strdup (hotkeys); return desc; } @@ -344,4 +374,3 @@ ibus_engine_desc_new_from_xml_node (XMLNode *node) return desc; } - diff --git a/src/ibusenginedesc.h b/src/ibusenginedesc.h index df77400..59d7edf 100644 --- a/src/ibusenginedesc.h +++ b/src/ibusenginedesc.h @@ -77,6 +77,8 @@ typedef struct _BusComponent BusComponent; * @author: Author of the input method engine. * @icon: Icon file of this engine. * @layout: Keyboard layout + * @hotkeys: One or more hotkeys for switching to this engine, separated by + * semi-colon. * @rank: Preference rank among engines, the highest ranked IME will put in * the front. * @@ -95,6 +97,7 @@ struct _IBusEngineDesc { gchar *author; gchar *icon; gchar *layout; + gchar *hotkeys; guint rank; }; @@ -127,6 +130,32 @@ IBusEngineDesc *ibus_engine_desc_new (const gchar *name, const gchar *author, const gchar *icon, const gchar *layout); + +/** + * ibus_engine_desc_new2: + * @name: Name of the engine. + * @longname: Long name of the input method engine. + * @description: Input method engine description. + * @language: Language (e.g. zh, jp) supported by this input method engine. + * @license: License of the input method engine. + * @author: Author of the input method engine. + * @icon: Icon file of this engine. + * @layout: Keyboard layout + * @hotkeys: Hotkeys for switching to this engine. + * @returns: A newly allocated IBusEngineDesc. + * + * New a IBusEngineDesc. + */ +IBusEngineDesc *ibus_engine_desc_new2 (const gchar *name, + const gchar *longname, + const gchar *description, + const gchar *language, + const gchar *license, + const gchar *author, + const gchar *icon, + const gchar *layout, + const gchar *hotkeys); + /** * ibus_engine_desc_new_from_xml_node: * @node: An XML node @@ -154,4 +183,3 @@ void ibus_engine_desc_output (IBusEngineDesc *info, gint indent); G_END_DECLS #endif - diff --git a/src/ibushotkey.c b/src/ibushotkey.c index 5076c34..a91013e 100644 --- a/src/ibushotkey.c +++ b/src/ibushotkey.c @@ -503,3 +503,19 @@ ibus_hotkey_profile_filter_key_event (IBusHotkeyProfile *profile, return event; } + +GQuark +ibus_hotkey_profile_lookup_hotkey (IBusHotkeyProfile *profile, + guint keyval, + guint modifiers) +{ + IBusHotkeyProfilePrivate *priv; + priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile); + + IBusHotkey hotkey = { + .keyval = keyval, + .modifiers = modifiers & priv->mask, + }; + + return (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey)); +} diff --git a/src/ibushotkey.h b/src/ibushotkey.h index 4b63121..4014d46 100644 --- a/src/ibushotkey.h +++ b/src/ibushotkey.h @@ -160,6 +160,18 @@ GQuark ibus_hotkey_profile_filter_key_event guint prev_modifiers, gpointer user_data); +/** + * ibus_hotkey_profile_lookup_hotkey: + * @profile: An IBusHotkeyProfile. + * @keyval: Keycode of the hotkey. + * @modifiers: Modifiers of the hotkey. + * @returns: The event associated to the hotkey or 0 if the hotkey is not in the + * profile. + */ +GQuark ibus_hotkey_profile_lookup_hotkey + (IBusHotkeyProfile *profile, + guint keyval, + guint modifiers); + G_END_DECLS #endif - -- 2.7.4