From 5136e4136e38271da195da76e1377308dbc2ed64 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 12 Aug 2011 16:42:52 +0900 Subject: [PATCH] Add a bridge hotkey which use prev-next engines instead of on-off. --- bus/Makefile.am | 20 +- bus/ibusimpl.c | 501 ++++++++++++++++++++++++++++++++++----- bus/inputcontext.c | 39 +++ bus/inputcontext.h | 22 ++ bus/registry.c | 61 +++++ bus/registry.h | 10 + configure.ac | 31 +++ data/Makefile.am | 6 +- data/ibus.schemas.in | 287 ---------------------- data/ibus.schemas.in.in | 300 +++++++++++++++++++++++ ibus/_config.py.in | 6 + ibus/inputcontext.py | 14 +- ibus/interface/iinputcontext.py | 4 +- ibus/xkbxml.py.in | 4 + setup/enginecombobox.py | 3 + setup/enginetreeview.py | 8 +- setup/main.py | 24 ++ setup/setup.ui | 55 +++++- src/Makefile.am | 18 +- src/ibushotkey.c | 11 + src/ibushotkey.h | 11 + src/ibusutil.c | 12 + src/ibusutil.h | 14 + ui/gtk/panel.py | 271 ++++++++++++++++++++-- xkb/Makefile.am | 2 + xkb/ibus-engine-xkb-main.c | 19 ++ xkb/xkbxml.c | 10 +- 28 files changed, 1374 insertions(+), 391 deletions(-) delete mode 100644 data/ibus.schemas.in create mode 100644 data/ibus.schemas.in.in diff --git a/bus/Makefile.am b/bus/Makefile.am index 074b456..0efaa1b 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -29,15 +29,17 @@ INCLUDES = \ -I$(top_builddir)/src \ $(NULL) -AM_CFLAGS = \ - @GLIB2_CFLAGS@ \ - @GIO2_CFLAGS@ \ - @GTHREAD2_CFLAGS@ \ - -DG_LOG_DOMAIN=\"IBUS\" \ - -DPKGDATADIR=\"$(pkgdatadir)\" \ - -DLIBEXECDIR=\"$(libexecdir)\" \ - -DBINDIR=\"@bindir@\" \ - $(INCLUDES) \ +AM_CFLAGS = \ + @GLIB2_CFLAGS@ \ + @GIO2_CFLAGS@ \ + @GTHREAD2_CFLAGS@ \ + -DG_LOG_DOMAIN=\"IBUS\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DLIBEXECDIR=\"$(libexecdir)\" \ + -DBINDIR=\"@bindir@\" \ + -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY) \ + -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\" \ + $(INCLUDES) \ $(NULL) AM_LDADD = \ @GOBJECT2_LIBS@ \ diff --git a/bus/ibusimpl.c b/bus/ibusimpl.c index 853465c..00864ac 100644 --- a/bus/ibusimpl.c +++ b/bus/ibusimpl.c @@ -20,12 +20,17 @@ * Boston, MA 02111-1307, USA. */ +#ifdef HAVE_CONFIG_H +#include +#endif + #include #include #include #include #include #include +#include #include "types.h" #include "ibusimpl.h" #include "dbusimpl.h" @@ -79,6 +84,10 @@ struct _BusIBusImpl { /* engine-specific hotkeys */ IBusHotkeyProfile *engines_hotkey_profile; GHashTable *hotkey_to_engines_map; + +#if USE_BRIDGE_HOTKEY + IBusEngineDesc *prev_hotkey_engine; +#endif }; struct _BusIBusImplClass { @@ -99,6 +108,8 @@ enum { static guint _signals[LAST_SIGNAL] = { 0 }; */ +static gchar *_bridge_trigger_keys = NULL; + /* functions prototype */ static void bus_ibus_impl_destroy (BusIBusImpl *ibus); static void bus_ibus_impl_service_method_call @@ -285,6 +296,112 @@ _panel_destroy_cb (BusPanelProxy *panel, g_object_unref (panel); } +/** + * _foreach_remove_engine_hotkey: + * + * Remove the engine-specific hot key of the engine, and update ibus->engines_hotkey_profile. + */ +gboolean +_foreach_remove_engine_hotkey (gpointer key, + gpointer value, + gpointer data) +{ + GQuark event = GPOINTER_TO_UINT (value); + struct _impl_and_desc { + BusIBusImpl *ibus; + IBusEngineDesc *desc; + } *id = (struct _impl_and_desc *) data; + BusIBusImpl *ibus = id->ibus; + IBusEngineDesc *desc = id->desc; + GList *engine_list; + + g_assert (ibus != NULL); + g_assert (desc != NULL); + + if (event == 0) { + return FALSE; + } + + 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. */ + if (engine_list && g_list_find (engine_list, desc) != NULL) { + engine_list = g_list_remove (engine_list, desc); + } + + /* 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)); + + if (engine_list != NULL) { + g_hash_table_insert (ibus->hotkey_to_engines_map, + GUINT_TO_POINTER (event), engine_list); + } + + return FALSE; +} + +/** + * _add_engine_hotkey_with_hotkeys: + * + * Check the engine-specific hot key of the engine, and update ibus->engines_hotkey_profile. + */ +static void +_add_engine_hotkey_with_hotkeys (IBusEngineDesc *engine, + BusIBusImpl *ibus, + const gchar *hotkeys) +{ + gchar **hotkey_list; + gchar **p; + gchar *hotkey; + GList *engine_list; + + GQuark event; + guint keyval; + guint modifiers; + + g_assert (engine != NULL); + g_assert (hotkeys && *hotkeys); + + hotkey_list = g_strsplit_set (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 _config_set_value_done (GObject *object, GAsyncResult *res, @@ -562,6 +679,67 @@ bus_ibus_impl_set_hotkey (BusIBusImpl *i } +#if USE_BRIDGE_HOTKEY +static gboolean +use_bridge_hotkey (BusIBusImpl *ibus) +{ + GVariant *variant = NULL; + gboolean _use_bridge_hotkey = TRUE; + + g_assert (ibus != NULL); + + if (!ibus->config) { + return TRUE; + } + + variant = ibus_config_get_value (ibus->config, + "general/hotkey", + "use_bridge_hotkey"); + + if (variant != NULL) { + g_variant_get (variant, "b", &_use_bridge_hotkey); + g_variant_unref (variant); + } + + return _use_bridge_hotkey; +} + +static void +bus_ibus_impl_set_bridge_trigger_keys (BusIBusImpl *ibus, + GQuark hotkey, + GVariant *value) +{ + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + ibus_hotkey_profile_remove_hotkey_by_event (ibus->hotkey_profile, hotkey); + + if (value == NULL) { + return; + } + + GVariantIter iter; + g_variant_iter_init (&iter, value); + const gchar *str = NULL; + + g_free (_bridge_trigger_keys); + _bridge_trigger_keys = NULL; + + while (g_variant_iter_loop (&iter,"&s", &str)) { + if (str != NULL) { + gchar *tmp =NULL; + + if (_bridge_trigger_keys) { + tmp = g_strdup_printf ("%s,%s", _bridge_trigger_keys, str); + } else { + tmp = g_strdup (str); + } + g_free (_bridge_trigger_keys); + _bridge_trigger_keys = tmp; + } + } +} +#endif + /** * bus_ibus_impl_set_trigger: * @@ -573,7 +751,15 @@ bus_ibus_impl_set_trigger (BusIBusImpl * { GQuark hotkey = g_quark_from_static_string ("trigger"); if (value != NULL) { +#if USE_BRIDGE_HOTKEY + if (use_bridge_hotkey (ibus)) { + bus_ibus_impl_set_bridge_trigger_keys (ibus, hotkey, value); + } else { + bus_ibus_impl_set_hotkey (ibus, hotkey, value); + } +#else bus_ibus_impl_set_hotkey (ibus, hotkey, value); +#endif } #ifndef OS_CHROMEOS else { @@ -639,6 +825,72 @@ bus_ibus_impl_set_previous_engine (BusIB bus_ibus_impl_set_hotkey (ibus, hotkey, value); } +#if USE_BRIDGE_HOTKEY +static gint +_engine_desc_name_cmp (IBusEngineDesc *desc1, + IBusEngineDesc *desc2) +{ + return g_strcmp0 (ibus_engine_desc_get_name (desc1), + ibus_engine_desc_get_name (desc2)); +} + +static void +_set_register_engines (BusIBusImpl *ibus, + GVariant *value) +{ + GList *engine_list = NULL; + + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + engine_list = ibus->register_engine_list; + if (value != NULL && g_variant_classify (value) == G_VARIANT_CLASS_ARRAY) { + GVariantIter iter; + g_variant_iter_init (&iter, value); + const gchar *engine_name = NULL; + while (g_variant_iter_loop (&iter, "&s", &engine_name)) { + IBusEngineDesc *engine = bus_registry_find_engine_by_name (ibus->registry, engine_name); + if (engine == NULL || g_list_find (engine_list, engine) != NULL) + continue; + engine_list = g_list_append (engine_list, g_object_ref (engine)); + } + } else if (value != NULL) { + g_variant_unref (value); + } + + ibus->register_engine_list = engine_list; + + if (engine_list) { + BusComponent *component = bus_component_from_engine_desc ((IBusEngineDesc *) engine_list->data); + if (component && !bus_component_is_running (component)) { + bus_component_start (component, g_verbose); + } + } +} + +static void +_set_default_keyboard_layout_engines (BusIBusImpl *ibus) +{ + GList *engines = NULL; + GList *list; + GVariantBuilder builder; + + g_assert (BUS_IS_IBUS_IMPL (ibus)); + + engines = bus_registry_get_engines_by_name_prefix (ibus->registry, + DEFAULT_BRIDGE_ENGINE_NAME); + /* sort engines by rank */ + engines = g_list_sort (engines, (GCompareFunc) _engine_desc_name_cmp); + + g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); + for (list = engines; list != NULL; list = list->next) { + IBusEngineDesc *desc = (IBusEngineDesc *)list->data; + g_variant_builder_add (&builder, "s", ibus_engine_desc_get_name (desc)); + } + _set_register_engines (ibus, g_variant_builder_end (&builder)); + g_list_free (engines); +} +#endif + /** * bus_ibus_impl_set_preload_engines: * @@ -658,6 +910,9 @@ bus_ibus_impl_set_preload_engines (BusIB _set_preload_engines (ibus, value); } } +#if USE_BRIDGE_HOTKEY + _set_default_keyboard_layout_engines (ibus); +#endif #else _set_preload_engines (ibus, value); #endif @@ -1216,7 +1471,71 @@ _context_request_engine_cb (BusInputCont const gchar *engine_name, BusIBusImpl *ibus) { - return bus_ibus_impl_get_engine_desc (ibus, engine_name); + IBusEngineDesc *desc = bus_ibus_impl_get_engine_desc (ibus, engine_name); + struct _impl_and_desc { + BusIBusImpl *ibus; + IBusEngineDesc *desc; + } id = {ibus, desc}; + +#if USE_BRIDGE_HOTKEY + IBusEngineDesc *current_desc = NULL; + + if (!use_bridge_hotkey (ibus)) { + return desc; + } + + if (context) { + BusEngineProxy *engine = bus_input_context_get_engine (context); + if (engine != NULL) { + current_desc = bus_engine_proxy_get_desc (engine); + } + } + + if (current_desc) { + if (context) { + bus_input_context_set_prev_hotkey_engine (context, current_desc); + } else { + ibus->prev_hotkey_engine = current_desc; + } + } + + if (((current_desc == NULL && desc != NULL) || + (current_desc != NULL && desc != NULL && + g_strcmp0 (ibus_engine_desc_get_name (current_desc), + ibus_engine_desc_get_name (desc)) != 0)) && + g_ascii_strncasecmp (ibus_engine_desc_get_name (desc), + DEFAULT_BRIDGE_ENGINE_NAME, + strlen (DEFAULT_BRIDGE_ENGINE_NAME)) == 0) { + const gchar *hotkeys = NULL; + + /* If the user customized the trigger key, the trigger key is used for + * any IBus engines. */ + if (_bridge_trigger_keys != NULL && + *_bridge_trigger_keys != '\0' && + g_strcmp0 (_bridge_trigger_keys, "Control+space") != 0) { + + hotkeys = (const gchar *) _bridge_trigger_keys; + } else if (current_desc) { + hotkeys = ibus_engine_desc_get_hotkeys (current_desc); + } + + /* If engine hotkeys are not defined in the compose xml file, + * IBus trigger keys are used. */ + if (!hotkeys || !*hotkeys) { + hotkeys = (const gchar *) _bridge_trigger_keys; + } + + if (!hotkeys || !*hotkeys) { + return desc; + } + + ibus_hotkey_profile_foreach_hotkey (ibus->engines_hotkey_profile, + _foreach_remove_engine_hotkey, + &id); + _add_engine_hotkey_with_hotkeys (desc, ibus, hotkeys); + } +#endif + return desc; } /** @@ -1255,8 +1574,13 @@ bus_ibus_impl_get_engine_desc (BusIBusIm if (!desc) { if (ibus->register_engine_list) { desc = (IBusEngineDesc *) ibus->register_engine_list->data; +#if USE_BRIDGE_HOTKEY + if (engine_name == NULL) { + desc = NULL; + } +#endif } - else if (ibus->engine_list) { + if (!desc && ibus->engine_list) { desc = (IBusEngineDesc *) ibus->engine_list->data; } } @@ -1407,6 +1731,9 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, BusEngineProxy *engine = NULL; gboolean is_enabled = FALSE; +#if USE_BRIDGE_HOTKEY + IBusEngineDesc *desc = NULL; +#endif if (ibus->focused_context) { if (ibus->use_global_engine) { @@ -1336,6 +1741,9 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, engine = bus_input_context_get_engine (ibus->focused_context); if (engine) { is_enabled = bus_input_context_is_enabled (ibus->focused_context); +#if USE_BRIDGE_HOTKEY + desc = bus_input_context_get_prev_hotkey_engine (ibus->focused_context); +#endif g_object_ref (engine); bus_input_context_set_engine (ibus->focused_context, NULL); } @@ -1360,6 +1768,9 @@ bus_ibus_impl_set_focused_context (BusIBusImpl *ibus, if (is_enabled) { bus_input_context_enable (context); } +#if USE_BRIDGE_HOTKEY + bus_input_context_set_prev_hotkey_engine (ibus->focused_context, desc); +#endif g_object_unref (engine); } @@ -2267,6 +2591,9 @@ bus_ibus_impl_filter_keyboard_shortcuts GQuark event; GList *engine_list; +#if USE_BRIDGE_HOTKEY + IBusEngineDesc *prev_hotkey_engine = NULL; +#endif if (trigger == 0) { trigger = g_quark_from_static_string ("trigger"); @@ -2331,6 +2658,12 @@ bus_ibus_impl_filter_keyboard_shortcuts return FALSE; } +#if USE_BRIDGE_HOTKEY + if (!use_bridge_hotkey (ibus)) { + return FALSE; + } +#endif + /* Then try engines hotkeys. */ event = ibus_hotkey_profile_filter_key_event (ibus->engines_hotkey_profile, keyval, @@ -2352,6 +2685,24 @@ bus_ibus_impl_filter_keyboard_shortcuts g_assert (new_engine_desc); +#if USE_BRIDGE_HOTKEY + if (context) { + prev_hotkey_engine = bus_input_context_get_prev_hotkey_engine (context); + if (prev_hotkey_engine == NULL && ibus->prev_hotkey_engine) { + prev_hotkey_engine = ibus->prev_hotkey_engine; + bus_input_context_set_prev_hotkey_engine (context, + prev_hotkey_engine); + } + } + + /* If the previous engine is not included in engine_list, + * this enables a new engine instead of toggling the engines + * so should not enable the previous engine. */ + if (prev_hotkey_engine && + g_list_find (engine_list, prev_hotkey_engine) != NULL) { + new_engine_desc = prev_hotkey_engine; + } +#else /* 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 @@ -2363,8 +2714,47 @@ bus_ibus_impl_filter_keyboard_shortcuts break; } } +#endif + +#if USE_BRIDGE_HOTKEY + if (context == NULL) { + return FALSE; + } + /* This means RequestEngine signal might be done but SetEngine signal + * has not been done yet by ibus status icon. */ + if (current_engine_desc == NULL && + !bus_input_context_inited_engine (context)) { + return FALSE; + } + + if (current_engine_desc != new_engine_desc) { + if (current_engine_desc) { + if (context) { + bus_input_context_set_prev_hotkey_engine (context, + current_engine_desc); + } + } + + /* If the previous engine is not included in engine_list and + * the current engine is the defualt bridge engine, + * the current engine is also not included in engine_list. + * So the engine is added here. */ + if (current_engine_desc != NULL && + g_list_find (engine_list, current_engine_desc) == NULL && + g_ascii_strncasecmp (ibus_engine_desc_get_name (current_engine_desc), + DEFAULT_BRIDGE_ENGINE_NAME, + strlen (DEFAULT_BRIDGE_ENGINE_NAME)) == 0) { + engine_list = g_list_append (engine_list, current_engine_desc); + + 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); + } +#else if (current_engine_desc != new_engine_desc) { +#endif bus_ibus_impl_set_context_engine_from_desc (ibus, context, new_engine_desc); } @@ -2468,59 +2858,54 @@ static void _add_engine_hotkey (IBusEngineDesc *engine, BusIBusImpl *ibus) { const gchar *hotkeys; - gchar **hotkey_list; - gchar **p; - gchar *hotkey; - GList *engine_list; - - GQuark event; - guint keyval; - guint modifiers; if (!engine) { return; } - hotkeys = ibus_engine_desc_get_hotkeys (engine); - - if (!hotkeys || !*hotkeys) { +#if USE_BRIDGE_HOTKEY + if (!use_bridge_hotkey (ibus)) { return; } - hotkey_list = g_strsplit_set (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); - } + /* Do not register hotkeys for the default keymap engines + * but register hotkeys for only input-method engines + * in 'RegisterComponent' dbus method. + * The hotkeys for an activated keymap engine will be registered + * in 'SetEngine' dbus method. */ + if (g_ascii_strncasecmp (ibus_engine_desc_get_name (engine), + DEFAULT_BRIDGE_ENGINE_NAME, + strlen (DEFAULT_BRIDGE_ENGINE_NAME)) == 0) { + return; + } - engine_list = g_hash_table_lookup (ibus->hotkey_to_engines_map, - GUINT_TO_POINTER (event)); + /* If the user customized the trigger key, the trigger key is used for + * any IBus engines. */ + if (_bridge_trigger_keys != NULL && + *_bridge_trigger_keys != '\0' && + g_strcmp0 (_bridge_trigger_keys, "Control+space") != 0) { - /* 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); + hotkeys = (const gchar *) _bridge_trigger_keys; + } else { + hotkeys = ibus_engine_desc_get_hotkeys (engine); + } +#else + hotkeys = ibus_engine_desc_get_hotkeys (engine); +#endif - /* 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)); +#if USE_BRIDGE_HOTKEY + /* If engine hotkeys are not defined in the compose xml file, IBus trigger + * keys are used. */ + if (!hotkeys || !*hotkeys) { + hotkeys = (const gchar *) _bridge_trigger_keys; + } +#endif - g_hash_table_insert (ibus->hotkey_to_engines_map, - GUINT_TO_POINTER (event), engine_list); + if (!hotkeys || !*hotkeys) { + return; } - g_strfreev (hotkey_list); + _add_engine_hotkey_with_hotkeys (engine, ibus, hotkeys); } /** diff --git a/bus/inputcontext.c b/bus/inputcontext.c index 4e8cdc5..43dedc0 100644 --- a/bus/inputcontext.c +++ b/bus/inputcontext.c @@ -90,6 +90,12 @@ struct _BusInputContext { /* incompleted set engine by desc request */ SetEngineByDescData *data; + + /* if init engine */ + gboolean inited_engine; + + /* previous hotkey engine for bridge hotkey mode */ + IBusEngineDesc *prev_hotkey_engine; }; struct _BusInputContextClass { @@ -647,6 +653,11 @@ bus_input_context_destroy (BusInputContext *context) context->client = NULL; } + if (context->prev_hotkey_engine) { + g_object_unref (context->prev_hotkey_engine); + context->prev_hotkey_engine = NULL; + } + IBUS_OBJECT_CLASS (bus_input_context_parent_class)->destroy (IBUS_OBJECT (context)); } @@ -2211,6 +2222,7 @@ bus_input_context_set_engine (BusInputContext *context, } else { gint i; + context->inited_engine = TRUE; context->engine = engine; g_object_ref (context->engine); @@ -2538,3 +2550,30 @@ bus_input_context_get_client (BusInputContext *context) g_assert (BUS_IS_INPUT_CONTEXT (context)); return context->client; } + +gboolean +bus_input_context_inited_engine (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + return context->inited_engine; +} + +IBusEngineDesc * +bus_input_context_get_prev_hotkey_engine (BusInputContext *context) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + return context->prev_hotkey_engine; +} + +void +bus_input_context_set_prev_hotkey_engine (BusInputContext *context, + IBusEngineDesc *desc) +{ + g_assert (BUS_IS_INPUT_CONTEXT (context)); + g_assert (desc == NULL || IBUS_IS_ENGINE_DESC (desc)); + + if (context->prev_hotkey_engine) { + g_object_unref (context->prev_hotkey_engine); + } + context->prev_hotkey_engine = desc ? g_object_ref (desc) : NULL; +} diff --git a/bus/inputcontext.h b/bus/inputcontext.h index bc4e096..c79e033 100644 --- a/bus/inputcontext.h +++ b/bus/inputcontext.h @@ -213,5 +213,27 @@ void bus_input_context_set_capabilities (BusInputContext *con */ const gchar *bus_input_context_get_client (BusInputContext *context); +/** + * bus_input_context_inited_engine: + * @returns: context->inited_engine. + */ +gboolean bus_input_context_inited_engine + (BusInputContext *context); + +/** + * bus_input_context_get_prev_hotkey_engine: + * @returns: context->prev_hotkey_engine. + */ +IBusEngineDesc *bus_input_context_get_prev_hotkey_engine + (BusInputContext *context); + +/** + * bus_input_context_set_prev_hotkey_engine: + * @desc: Assign the desc to context->prev_hotkey_engine. + */ +void bus_input_context_set_prev_hotkey_engine + (BusInputContext *context, + IBusEngineDesc *desc); + G_END_DECLS #endif diff --git a/bus/registry.c b/bus/registry.c index 7b74781..28e2abf 100644 --- a/bus/registry.c +++ b/bus/registry.c @@ -19,6 +19,11 @@ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif + #include "registry.h" #include #include @@ -101,6 +106,9 @@ bus_registry_init (BusRegistry *registry) registry->observed_paths = NULL; registry->components = NULL; registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal); +#if USE_BRIDGE_HOTKEY + gboolean has_default_engine = FALSE; +#endif #ifdef G_THREADS_ENABLED /* If glib supports thread, we'll create a thread to monitor changes in IME @@ -145,12 +153,40 @@ bus_registry_init (BusRegistry *registry) GList *p1; for (p1 = engines; p1 != NULL; p1 = p1->next) { IBusEngineDesc *desc = (IBusEngineDesc *) p1->data; +#if USE_BRIDGE_HOTKEY + if (g_ascii_strncasecmp (ibus_engine_desc_get_name (desc), + DEFAULT_BRIDGE_ENGINE_NAME, + strlen (DEFAULT_BRIDGE_ENGINE_NAME)) == 0) { + has_default_engine = TRUE; + } +#endif g_hash_table_insert (registry->engine_table, (gpointer) ibus_engine_desc_get_name (desc), desc); } g_list_free (engines); } + +#if USE_BRIDGE_HOTKEY + if (has_default_engine == FALSE) { + bus_registry_remove_all (registry); + bus_registry_load (registry); + bus_registry_save_cache (registry); + + for (p = registry->components; p != NULL; p = p->next) { + BusComponent *comp = (BusComponent *) p->data; + GList *engines = bus_component_get_engines (comp); + GList *p1; + for (p1 = engines; p1 != NULL; p1 = p1->next) { + IBusEngineDesc *desc = (IBusEngineDesc *) p1->data; + g_hash_table_insert (registry->engine_table, + (gpointer) ibus_engine_desc_get_name (desc), + desc); + } + g_list_free (engines); + } + } +#endif } static void @@ -516,6 +552,31 @@ bus_registry_get_engines_by_language (BusRegistry *registry, return engines; } +GList * +bus_registry_get_engines_by_name_prefix (BusRegistry *registry, + const gchar *name_prefix) +{ + GList *p1, *p2; + GList *engines = NULL; + + g_assert (BUS_IS_REGISTRY (registry)); + g_assert (name_prefix); + + p1 = bus_registry_get_engines (registry); + + for (p2 = p1; p2 != NULL; p2 = p2->next) { + IBusEngineDesc *desc = (IBusEngineDesc *) p2->data; + if (g_ascii_strncasecmp (ibus_engine_desc_get_name (desc), + name_prefix, + strlen (name_prefix)) == 0) { + engines = g_list_append (engines, desc); + } + } + + g_list_free (p1); + return engines; +} + IBusEngineDesc * bus_registry_find_engine_by_name (BusRegistry *registry, const gchar *name) diff --git a/bus/registry.h b/bus/registry.h index cdabec0..721187c 100644 --- a/bus/registry.h +++ b/bus/registry.h @@ -73,6 +73,16 @@ GList *bus_registry_get_engines_by_language const gchar *language); /** + * bus_registry_get_engines_by_name_prefix: + * @name_prefix: a prefix in the name of IBusEngineDesc. + * @returns: a list of IBusEngineDesc objects which has the name prefix. + * The caller has to call g_list_free for the returned list. + */ +GList *bus_registry_get_engines_by_name_prefix + (BusRegistry *registry, + const gchar *name_prefix); + +/** * bus_registry_stop_all_components: * * Terminate all component processes. diff --git a/configure.ac b/configure.ac index 4c20ae7..21d35c3 100644 --- a/configure.ac +++ b/configure.ac @@ -462,6 +462,34 @@ else enable_surrounding_text="no (disabled, use --enable-surrounding-text to enable)" fi +# option for bridge hotkey +AC_ARG_ENABLE(bridge-hotkey, + AS_HELP_STRING([--enable-bridge-hotkey], + [Enable bridge hotkey instead of ON/OFF hotkey]), + [enable_bridge_hotkey=$enableval], + [enable_bridge_hotkey=no] +) + +if test x"$enable_bridge_hotkey" = x"yes"; then + USE_BRIDGE_HOTKEY=1 + TRIGGER_HOTKEYS="Control+space" +else + USE_BRIDGE_HOTKEY=0 + TRIGGER_HOTKEYS="Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R" + enable_bridge_hotkey="no (disabled, use --enable-bridge-hotkey to enable)" +fi +AC_SUBST(USE_BRIDGE_HOTKEY) +AC_SUBST(TRIGGER_HOTKEYS) + +# define default bridge engine name +AC_ARG_WITH(bridge-engine, + AS_HELP_STRING([--with-bridge-engine[=bridge_engine_name]], + [Set bridge engine name in IM bridge hotkey. (default: xkb:layout:default:)]), + [DEFAULT_BRIDGE_ENGINE_NAME=$with_bridge_engine], + [DEFAULT_BRIDGE_ENGINE_NAME="xkb:layout:default:"] +) +AC_SUBST(DEFAULT_BRIDGE_ENGINE_NAME) + # check iso-codes PKG_CHECK_MODULES(ISOCODES, [ iso-codes @@ -488,6 +516,7 @@ bus/Makefile util/Makefile util/IMdkit/Makefile data/Makefile +data/ibus.schemas.in data/icons/Makefile data/keymaps/Makefile data/dconf/Makefile @@ -540,5 +569,7 @@ Build options: No snooper regexes "$NO_SNOOPER_APPS" Panel icon "$IBUS_ICON_KEYBOARD" Enable surrounding-text $enable_surrounding_text + Enable bridge hotkey $enable_bridge_hotkey + Default bridge engine $DEFAULT_BRIDGE_ENGINE_NAME ]) diff --git a/data/Makefile.am b/data/Makefile.am index 99be41c..824da76 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -30,7 +30,8 @@ SUBDIRS += dconf endif schemasdir = $(GCONF_SCHEMA_FILE_DIR) -schemas_in_files = ibus.schemas.in +schemas_in_in_files = ibus.schemas.in.in +schemas_in_files = $(schemas_in_in_files:.schemas.in.in=.schemas.in) schemas_DATA = $(schemas_in_files:.schemas.in=.schemas) @INTLTOOL_SCHEMAS_RULE@ @@ -45,11 +46,12 @@ if GCONF_SCHEMAS_INSTALL endif EXTRA_DIST = \ - $(schemas_in_files) \ + $(schemas_in_in_files) \ $(NULL) DISTCLEANFILES = \ $(schemas_DATA) \ + $(schemas_in_files) \ $(NULL) -include $(top_srcdir)/git.mk diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in.in index 8979515..2a2b459 --- a/data/ibus.schemas.in +++ b/data/ibus.schemas.in.in @@ -31,7 +31,7 @@ ibus list string - [Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R] + [@TRIGGER_HOTKEYS@] Trigger shortcut keys The shortcut keys for turning input method on or off @@ -103,6 +103,19 @@ + /schemas/desktop/ibus/general/hotkey/use_bridge_hotkey + /desktop/ibus/general/hotkey/use_bridge_hotkey + ibus + bool + true + + Use bridge hotkey + Use trigger keys to toggle the prev and next IM engines + if true. Use trigger keys to toggle an enable and + disabled IM engine if false. + + + /schemas/desktop/ibus/panel/show /desktop/ibus/panel/show ibus diff --git a/ibus/_config.py.in b/ibus/_config.py.in index a830136..4c3c980 100644 --- a/ibus/_config.py.in +++ b/ibus/_config.py.in @@ -25,6 +25,8 @@ __all__ = ( "get_copyright", "get_license", "get_ICON_KEYBOARD", + "use_bridge_hotkey", + "DEFAULT_BRIDGE_ENGINE_NAME", "ISOCODES_PREFIX", "_" ) @@ -51,4 +53,8 @@ def get_ICON_KEYBOARD(): icon = 'ibus-keyboard' return icon +def use_bridge_hotkey(): + return True if @USE_BRIDGE_HOTKEY@ == 1 else False + +DEFAULT_BRIDGE_ENGINE_NAME='@DEFAULT_BRIDGE_ENGINE_NAME@' ISOCODES_PREFIX='@ISOCODES_PREFIX@' diff --git a/ibus/inputcontext.py b/ibus/inputcontext.py index ceeb56d..b3c2f65 100644 --- a/ibus/inputcontext.py +++ b/ibus/inputcontext.py @@ -28,6 +28,7 @@ import sys import gobject import dbus import dbus.lowlevel +import _config import object import common import serializable @@ -279,8 +280,19 @@ class InputContext(object.Object): except: return None + def __handle_ic_reply(self): + pass + + def __handle_ic_error(self, e): + print self.__gtype_name__, str(e) + def set_engine(self, engine): - return self.__context.SetEngine(engine.name) + return self.__context.SetEngine(engine.name, + reply_handler=self.__handle_ic_reply, + error_handler=self.__handle_ic_error) + + def set_bridge_engine(self): + return self.__context.SetEngine(_config.DEFAULT_BRIDGE_ENGINE_NAME) def introspect(self): return self.__context.Introspect() diff --git a/ibus/interface/iinputcontext.py b/ibus/interface/iinputcontext.py index 1d3cd2a..58d75e5 100644 --- a/ibus/interface/iinputcontext.py +++ b/ibus/interface/iinputcontext.py @@ -76,8 +76,8 @@ class IInputContext(dbus.service.Object): @method(out_signature="v") def GetEngine(self): pass - @method(in_signature="s") - def SetEngine(self, engine_name): pass + @async_method(in_signature="s") + def SetEngine(self, engine_name, reply_cb, error_cb): pass @method() def Destroy(self): pass diff --git a/ibus/xkbxml.py.in b/ibus/xkbxml.py.in index 9407c13..bf61810 100644 --- a/ibus/xkbxml.py.in +++ b/ibus/xkbxml.py.in @@ -33,6 +33,8 @@ import enginedesc from xml.sax.saxutils import XMLFilterBase, XMLGenerator from xml.sax._exceptions import SAXParseException from cStringIO import StringIO +from _config import DEFAULT_BRIDGE_ENGINE_NAME +from _config import get_ICON_KEYBOARD try: from glib import get_user_config_dir @@ -315,6 +317,8 @@ class XKBConfigRegistry(): engine_layout = layout icon = 'ibus-engine' + if name.startswith(DEFAULT_BRIDGE_ENGINE_NAME): + icon = get_ICON_KEYBOARD() engine = enginedesc.EngineDesc(name, longname, desc, lang, 'LGPL2.1', diff --git a/setup/enginecombobox.py b/setup/enginecombobox.py index 7383177..d35757d 100644 --- a/setup/enginecombobox.py +++ b/setup/enginecombobox.py @@ -64,6 +64,9 @@ class EngineComboBox(gtk.ComboBox): self.__model.set(iter1, 0, 0) lang = {} for e in engines: + if ibus.use_bridge_hotkey() and \ + e.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + continue l = ibus.get_language_name(e.language) if l not in lang: lang[l] = [] diff --git a/setup/enginetreeview.py b/setup/enginetreeview.py index f620361..664dc99 100644 --- a/setup/enginetreeview.py +++ b/setup/enginetreeview.py @@ -172,8 +172,12 @@ class EngineTreeView(gtk.TreeView): for e in engines: if e in self.__engines: continue - iter = self.__model.append(None) - self.__model.set(iter, 0, e) + if ibus.use_bridge_hotkey() and \ + e.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + pass + else: + iter = self.__model.append(None) + self.__model.set(iter, 0, e) self.__engines.add(e) self.__emit_changed() diff --git a/setup/main.py b/setup/main.py index 7f4a040..a22ac78 100644 --- a/setup/main.py +++ b/setup/main.py @@ -213,6 +213,25 @@ class Setup(object): self.__config.get_value("general", "use_global_engine", False)) self.__checkbutton_use_global_engine.connect("toggled", self.__checkbutton_use_global_engine_toggled_cb) + # hotkey settings + if ibus.use_bridge_hotkey(): + label = self.__builder.get_object("label_trigger_hotkey") + label.set_label(_("Toggle:")) + label.set_tooltip_text(_("The trigger shortcut keys to toggle " + "the previous and next input methods")) + self.__checkbutton_use_on_off_hotkey = \ + self.__builder.get_object("checkbutton_use_on_off_hotkey") + self.__checkbutton_use_on_off_hotkey.set_active( + not self.__config.get_value("general/hotkey", + "use_bridge_hotkey", + True)) + self.__checkbutton_use_on_off_hotkey.connect("toggled", + self.__checkbutton_use_on_off_hotkey_cb) + else: + checkbutton = self.__builder.get_object("checkbutton_use_on_off_hotkey") + checkbutton.hide() + checkbutton.set_no_show_all(True) + # init engine page preload_engine_mode = self.__config.get_value("general", "preload_engine_mode", @@ -519,6 +538,11 @@ class Setup(object): value = self.__checkbutton_use_global_engine.get_active() self.__config.set_value("general", "use_global_engine", value) + def __checkbutton_use_on_off_hotkey_cb(self, button): + value = self.__checkbutton_use_on_off_hotkey.get_active() + self.__config.set_value("general/hotkey", "use_bridge_hotkey", + not value) + def __config_value_changed_cb(self, bus, section, name, value): if section == 'general' and name == 'preload_engines': engines = self.__get_engine_descs_from_names(value) diff --git a/setup/setup.ui b/setup/setup.ui index f1e6d0b..77714a7 100644 --- a/setup/setup.ui +++ b/setup/setup.ui @@ -232,7 +232,7 @@ - + True The shortcut keys for turning input method on or off 0 @@ -962,6 +962,59 @@ You may use up/down buttons to change it.</i></small> 1 + + + True + 0 + none + + + True + 12 + + + True + 6 + 12 + + + True + vertical + 6 + + + Use trigger keys to enable or disable an input method + True + True + False + True + + + False + False + 0 + + + + + + + + + + + True + <b>Hot keys setting</b> + True + + + + + False + False + 2 + + diff --git a/src/Makefile.am b/src/Makefile.am index 6454522..319df3c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,14 +38,16 @@ INTROSPECTION_GIRS = CLEANFILES = # C preprocessor flags -AM_CPPFLAGS = \ - -DG_LOG_DOMAIN=\"IBUS\" \ - @GLIB2_CFLAGS@ \ - @GOBJECT2_CFLAGS@ \ - @GIO2_CFLAGS@ \ - -DIBUS_DATA_DIR=\"$(pkgdatadir)\" \ - -DIBUS_COMPILATION \ - -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\" \ +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN=\"IBUS\" \ + @GLIB2_CFLAGS@ \ + @GOBJECT2_CFLAGS@ \ + @GIO2_CFLAGS@ \ + -DIBUS_DATA_DIR=\"$(pkgdatadir)\" \ + -DIBUS_COMPILATION \ + -DISOCODES_PREFIX=\"$(ISOCODES_PREFIX)\" \ + -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY) \ + -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\" \ $(NULL) # ibus library diff --git a/src/ibushotkey.c b/src/ibushotkey.c index 32f8338..bef7dfc 100644 --- a/src/ibushotkey.c +++ b/src/ibushotkey.c @@ -562,3 +562,14 @@ ibus_hotkey_profile_lookup_hotkey (IBusHotkeyProfile *profile, return (GQuark) GPOINTER_TO_UINT (g_tree_lookup (priv->hotkeys, &hotkey)); } + +void +ibus_hotkey_profile_foreach_hotkey (IBusHotkeyProfile *profile, + GTraverseFunc func, + gpointer user_data) +{ + IBusHotkeyProfilePrivate *priv; + priv = IBUS_HOTKEY_PROFILE_GET_PRIVATE (profile); + + g_tree_foreach (priv->hotkeys, func, user_data); +} diff --git a/src/ibushotkey.h b/src/ibushotkey.h index 9a341f6..92ec6af 100644 --- a/src/ibushotkey.h +++ b/src/ibushotkey.h @@ -179,5 +179,16 @@ GQuark ibus_hotkey_profile_lookup_hotkey guint keyval, guint modifiers); +/** + * ibus_hotkey_profile_foreach_hotkey: + * @profile: An IBusHotkeyProfile. + * @func: (scope call): A GTraverseFunc for g_tree_traverse. + * @user_data: A gpointer for g_tree_traverse. + */ +void ibus_hotkey_profile_foreach_hotkey + (IBusHotkeyProfile *profile, + GTraverseFunc func, + gpointer user_data); + G_END_DECLS #endif diff --git a/src/ibusutil.c b/src/ibusutil.c index ddb6b9e..46dab1a 100644 --- a/src/ibusutil.c +++ b/src/ibusutil.c @@ -145,3 +145,15 @@ ibus_get_language_name(const gchar *_locale) { } return retval; } + +gboolean +ibus_use_bridge_hotkey (void) +{ + return (USE_BRIDGE_HOTKEY == 1) ? TRUE : FALSE; +} + +const gchar * +ibus_get_default_bridge_engine_name (void) +{ + return DEFAULT_BRIDGE_ENGINE_NAME; +} diff --git a/src/ibusutil.h b/src/ibusutil.h index 7cf1995..a19d16e 100644 --- a/src/ibusutil.h +++ b/src/ibusutil.h @@ -43,4 +43,18 @@ */ const gchar * ibus_get_language_name (const gchar *_locale); +/** + * ibus_bus_use_bridge_hotkey: + * @bus: An #IBusBus. + * @returns: %TRUE if @bus use bridge hotkey, %FALSE otherwise. + */ +gboolean ibus_use_bridge_hotkey (void); + +/** + * ibus_bus_get_default_bridge_engine_name: + * @bus: An #IBusBus. + * @returns: A default bridge engine name. + */ +const gchar * ibus_get_default_bridge_engine_name (void); + #endif diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py index 8804634..f7b3e50 100644 --- a/ui/gtk/panel.py +++ b/ui/gtk/panel.py @@ -67,6 +67,7 @@ class Panel(ibus.PanelBase): self.__data_dir = path.join(self.__prefix, "share", "ibus") # self.__icons_dir = path.join(self.__data_dir, "icons") self.__setup_cmd = path.join(self.__prefix, "bin", "ibus-setup") + self.__show = 0 # connect bus signal self.__config.connect("value-changed", self.__config_value_changed_cb) @@ -133,6 +134,14 @@ class Panel(ibus.PanelBase): # self.__bus.request_name(ibus.panel.IBUS_SERVICE_PANEL, 0) # init xkb + self.__default_layout = 'default' + self.__default_model = 'default' + self.__default_option = 'default' + self.__disabled_engines = None + self.__disabled_engines_id = -1 + self.__disabled_engines_prev_id = -1 + self.__disabled_engines_swapped = 0 + self.__xkblayout = ibus.XKBLayout(self.__config) use_xkb = self.__config.get_value("general", "use_system_keyboard_layout", False) if not use_xkb: @@ -142,11 +151,18 @@ class Panel(ibus.PanelBase): value = 'default' if value != 'default': self.__xkblayout.set_default_layout(value) + if value.find('(') >= 0: + self.__default_layout = value.split('(')[0] + self.__default_model = value.split('(')[1].split(')')[0] + else: + self.__default_layout = value + self.__default_model = None value = str(self.__config.get_value("general", "system_keyboard_option", '')) if value == '': value = 'default' if value != 'default': self.__xkblayout.set_default_option(value) + self.__default_option = value def set_cursor_location(self, x, y, w, h): self.__candidate_panel.set_cursor_location(x, y, w, h) @@ -233,12 +249,91 @@ class Panel(ibus.PanelBase): def __set_im_name(self, name): self.__language_bar.set_im_name(name) + def __use_bridge_hotkey(self): + if not ibus.use_bridge_hotkey(): + return False + if self.__config == None: + return True + return self.__config.get_value("general/hotkey", "use_bridge_hotkey", + True) + + def __set_default_layout_engine(self, use_bridge_hotkey): + default_layout = self.__default_layout + default_model = self.__default_model + if default_layout == 'default': + default_layout = self.__xkblayout.get_default_layout()[0] + default_model = self.__xkblayout.get_default_layout()[1] + if default_model == 'default': + default_model = self.__xkblayout.get_default_layout()[1] + layouts = default_layout.split(',') + models = None + if default_model != None and default_model != '': + models = default_model.split(',') + if self.__disabled_engines == None or self.__disabled_engines == []: + self.__disabled_engines = [] + for i, layout in enumerate(layouts): + registry = ibus.XKBConfigRegistry() + langs = registry.get_layout_lang()[layout] + lang = 'en' + if langs != None: + lang = str(langs[0]) + model = None + if i == 0: + layout = default_layout + model = default_model + elif i < len(models): + model = models[i] + if model == '': + model = None + model_desc = _("Default Layout") + if model != None: + model_desc = model_desc + " (" + model + ")" + name = ibus.DEFAULT_BRIDGE_ENGINE_NAME + "#" + str(i) + engine = registry.engine_desc_new(lang, + layout, + _("Default Layout"), + model, + model_desc, + name) + self.__disabled_engines.append(engine) + self.__disabled_engines_id = self.__xkblayout.get_group() + if not use_bridge_hotkey: + return + if self.__disabled_engines != None and self.__disabled_engines != []: + if self.__focus_ic == None: + return + engine = self.__focus_ic.get_engine() + if engine == None: + if self.__disabled_engines_id < 0: + self.__disabled_engines_id = 0 + self.__focus_ic.focus_in() + self.__focus_ic.set_engine(self.__disabled_engines[self.__disabled_engines_id]) + elif engine != None and \ + not self.__focus_ic.is_enabled(): + self.__focus_ic.focus_in() + self.__focus_ic.enable() + def focus_in(self, ic): self.reset() self.__focus_ic = ibus.InputContext(self.__bus, ic) enabled = self.__focus_ic.is_enabled() - self.__language_bar.set_enabled(enabled) + use_bridge_hotkey = self.__use_bridge_hotkey() + self.__set_default_layout_engine(use_bridge_hotkey) + if use_bridge_hotkey: + if self.__show != 1: + self.__language_bar.set_enabled(enabled) + elif enabled: + engine = self.__focus_ic.get_engine() + if engine != None and \ + not engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + self.__language_bar.set_enabled(enabled) + else: + self.__language_bar.set_enabled(False) + else: + self.__language_bar.set_enabled(False) + else: + self.__language_bar.set_enabled(enabled) if not enabled: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) @@ -250,7 +345,7 @@ class Panel(ibus.PanelBase): self.__set_im_icon(engine.icon) self.__set_im_name(engine.longname) if self.__bus.get_use_sys_layout(): - self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine)) + self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine, False)) else: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) @@ -273,7 +368,21 @@ class Panel(ibus.PanelBase): return enabled = self.__focus_ic.is_enabled() - self.__language_bar.set_enabled(enabled) + + if self.__use_bridge_hotkey(): + if self.__show != 1: + self.__language_bar.set_enabled(enabled) + elif enabled: + engine = self.__focus_ic.get_engine() + if engine != None and \ + not engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + self.__language_bar.set_enabled(enabled) + else: + self.__language_bar.set_enabled(False) + else: + self.__language_bar.set_enabled(False) + else: + self.__language_bar.set_enabled(enabled) if enabled == False: self.reset() @@ -287,7 +396,7 @@ class Panel(ibus.PanelBase): self.__set_im_icon(engine.icon) self.__set_im_name(engine.longname) if self.__bus.get_use_sys_layout(): - self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine)) + self.__xkblayout.set_layout(self.__engine_get_layout_wrapper(engine, True)) else: self.__set_im_icon(ICON_KEYBOARD) self.__set_im_name(None) @@ -315,6 +424,7 @@ class Panel(ibus.PanelBase): def __config_load_show(self): show = self.__config.get_value("panel", "show", 0) + self.__show = show self.__language_bar.set_show(show) def __config_load_position(self): @@ -443,6 +553,21 @@ class Panel(ibus.PanelBase): # menu.set_take_focus(False) # return menu + def __add_engine_in_menu(self, menu, engine, is_bold, size): + language = engine.language + lang = ibus.get_language_name(language) + item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname)) + if is_bold: + for widget in item.get_children(): + if isinstance(widget, gtk.Label): + widget.set_markup("%s" % widget.get_text()) + if engine.icon: + item.set_image(_icon.IconWidget(engine.icon, size[0])) + else: + item.set_image(_icon.IconWidget(ICON_ENGINE, size[0])) + item.connect("activate", self.__im_menu_item_activate_cb, engine) + menu.add(item) + def __create_im_menu(self): engines = self.__bus.list_active_engines() current_engine = \ @@ -453,25 +578,39 @@ class Panel(ibus.PanelBase): size = gtk.icon_size_lookup(gtk.ICON_SIZE_MENU) menu = gtk.Menu() for i, engine in enumerate(engines): - lang = ibus.get_language_name(engine.language) - item = gtk.ImageMenuItem("%s - %s" % (lang, engine.longname)) - if current_engine and current_engine.name == engine.name: - for widget in item.get_children(): - if isinstance(widget, gtk.Label): - widget.set_markup("%s" % widget.get_text()) - if engine.icon: - item.set_image(_icon.IconWidget(engine.icon, size[0])) - else: - item.set_image(_icon.IconWidget(ICON_ENGINE, size[0])) - item.connect("activate", self.__im_menu_item_activate_cb, engine) - menu.add(item) + if engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + if not self.__use_bridge_hotkey(): + continue + if self.__disabled_engines == None: + continue + engine.disabled_engines_id = -1 + for j, kb_engine in enumerate(self.__disabled_engines): + if engine.name == kb_engine.name: + engine.disabled_engines_id = j + break + if engine.disabled_engines_id == -1: + continue + kb_engine = self.__disabled_engines[engine.disabled_engines_id] + kb_engine.is_bridge = True + kb_engine.disabled_engines_id = engine.disabled_engines_id + is_bold = True if (current_engine != None and \ + current_engine.name == kb_engine.name) else False + self.__add_engine_in_menu(menu, kb_engine, + is_bold, + size) + continue + engine.is_bridge = False + is_bold = True if (current_engine != None and \ + current_engine.name == engine.name) else False + self.__add_engine_in_menu(menu, engine, is_bold, size) item = gtk.ImageMenuItem(_("Turn off input method")) item.set_image(_icon.IconWidget("gtk-close", size[0])) item.connect("activate", self.__im_menu_item_activate_cb, None) if self.__focus_ic == None or not self.__focus_ic.is_enabled(): item.set_sensitive(False) - menu.add(item) + if not self.__use_bridge_hotkey(): + menu.add(item) menu.show_all() menu.set_take_focus(False) @@ -523,8 +662,25 @@ class Panel(ibus.PanelBase): if not self.__focus_ic: return if engine: - self.__focus_ic.set_engine(engine) + if self.__use_bridge_hotkey() and engine.is_bridge: + engines = self.__bus.list_active_engines() + current_engine = \ + (self.__focus_ic != None and self.__focus_ic.get_engine()) or \ + (engines and engines[0]) or \ + None + if current_engine and \ + current_engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + self.__disabled_engines_prev_id = self.__disabled_engines_id + self.__disabled_engines_swapped = 0 + else: + self.__disabled_engines_prev_id = -1 + self.__disabled_engines_id = engine.disabled_engines_id + self.__focus_ic.set_engine(self.__disabled_engines[self.__disabled_engines_id]) + else: + self.__disabled_engines_prev_id = -1 + self.__focus_ic.set_engine(engine) else: + self.__disabled_engines_prev_id = -1 self.__focus_ic.disable() def __sys_menu_item_activate_cb(self, item, command): @@ -573,12 +729,85 @@ class Panel(ibus.PanelBase): self.__setup_pid = pid glib.child_watch_add(self.__setup_pid, self.__child_watch_cb) - def __engine_get_layout_wrapper(self, engine): + def __get_model_from_layout(self, layout): + left_bracket = layout.find('(') + right_bracket = layout.find(')') + if left_bracket >= 0 and right_bracket > left_bracket: + return (layout[:left_bracket] + layout[right_bracket + 1:], \ + layout[left_bracket + 1:right_bracket]) + return (layout, "default") + + def __get_option_from_layout(self, layout): + left_bracket = layout.find('[') + right_bracket = layout.find(']') + if left_bracket >= 0 and right_bracket > left_bracket: + return (layout[:left_bracket] + layout[right_bracket + 1:], \ + layout[left_bracket + 1:right_bracket]) + return (layout, "default") + + def __merge_models_and_options(self, cur_layout, engine_layout): + orig_layout = cur_layout + engine_model = "default" + engine_option = "default" + (engine_layout, engine_model) = \ + self.__get_model_from_layout(engine_layout) + (engine_layout, engine_option) = \ + self.__get_option_from_layout(engine_layout) + if (engine_model == None or engine_model == "default") and \ + (engine_option == None or engine_option == "default"): + return cur_layout + cur_model = "default" + cur_option = "default" + (cur_layout, cur_model) = \ + self.__get_model_from_layout(cur_layout) + (cur_layout, cur_option) = \ + self.__get_option_from_layout(cur_layout) + # Currently implemented options only. + # Merging layouts and models are a little complicated. + # e.g. ja,ru + ja(kana) == ja,ru,ja(,,kana) + if engine_option != None and engine_option != "default": + if cur_option == None or cur_option == "default": + cur_option = engine_option + elif cur_option != None and cur_option != "default": + cur_option = "%s,%s" % (cur_option, engine_option) + if cur_model != None and cur_model != "default": + cur_layout = "%s(%s)" % (cur_layout, cur_model) + if cur_option != None and cur_option != "default": + cur_layout = "%s[%s]" % (cur_layout, cur_option) + return cur_layout + return orig_layout + + def __engine_get_layout_wrapper(self, engine, changed_state): # This code is for the back compatibility. # Should we remove the codes after all IM engines are changed # to "default" layout? - if engine.name != None and engine.name.startswith("xkb:layout:"): + if engine.name != None and engine.name.startswith("xkb:layout:") and \ + not self.__use_bridge_hotkey(): + return engine.layout + elif engine.name != None and \ + engine.name.startswith("xkb:layout:") and \ + self.__use_bridge_hotkey() and \ + not engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): return engine.layout + elif self.__use_bridge_hotkey() and \ + self.__disabled_engines_id >= 0 and \ + self.__disabled_engines != None and \ + self.__disabled_engines_id < len(self.__disabled_engines): + if changed_state and self.__disabled_engines_prev_id != -1: + # state_changed is always called twice because we change + # the engine. So the first two calls are ignored here. + if self.__disabled_engines_swapped < 2: + self.__disabled_engines_swapped = \ + self.__disabled_engines_swapped + 1 + else: + x = self.__disabled_engines_prev_id + self.__disabled_engines_prev_id = self.__disabled_engines_id + self.__disabled_engines_id = x + self.__disabled_engines_swapped = 1 + retval = self.__disabled_engines[self.__disabled_engines_id].layout + if engine.layout != None and engine.layout.startswith("default"): + return self.__merge_models_and_options(retval, engine.layout) + return retval elif engine.layout != None and engine.layout.startswith("default"): return engine.layout else: diff --git a/xkb/Makefile.am b/xkb/Makefile.am index ad9cdd9..c4d5afb 100644 --- a/xkb/Makefile.am +++ b/xkb/Makefile.am @@ -28,6 +28,8 @@ INCLUDES = \ -I$(top_srcdir)/src \ -DIBUS_LOCALEDIR=\"$(datadir)/locale\" \ -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DUSE_BRIDGE_HOTKEY=$(USE_BRIDGE_HOTKEY) \ + -DDEFAULT_BRIDGE_ENGINE_NAME=\"$(DEFAULT_BRIDGE_ENGINE_NAME)\" \ $(NULL) noinst_PROGRAMS = $(TESTS) diff --git a/xkb/ibus-engine-xkb-main.c b/xkb/ibus-engine-xkb-main.c index 0fb0f0c..4787bf2 100644 --- a/xkb/ibus-engine-xkb-main.c +++ b/xkb/ibus-engine-xkb-main.c @@ -290,6 +290,9 @@ print_component () const gchar *desc; gchar *output; GString *str; +#if USE_BRIDGE_HOTKEY + int i; +#endif #ifdef XKBLAYOUTCONFIG_FILE layout_config = ibus_xkb_layout_config_new (XKBLAYOUTCONFIG_FILE); @@ -302,6 +305,19 @@ print_component () layout_desc = (GHashTable *) ibus_xkb_config_registry_get_layout_desc (config_registry); variant_desc = (GHashTable *) ibus_xkb_config_registry_get_variant_desc (config_registry); component = ibus_xkb_component_new (); +#if USE_BRIDGE_HOTKEY + for (i = 0; i < 4; i++) { + gchar *name = g_strdup_printf ("%s#%d", DEFAULT_BRIDGE_ENGINE_NAME, i); + engine = ibus_xkb_engine_desc_new ("eng", + "us", + _("Default Layout"), + NULL, + NULL, + name); + g_free (name); + ibus_component_add_engine (component, engine); + } +#endif for (keys = g_hash_table_get_keys (layout_list); keys; keys = keys->next) { if (keys->data == NULL) { continue; diff --git a/xkb/xkbxml.c b/xkb/xkbxml.c index d59a929..86bcf8f 100644 --- a/xkb/xkbxml.c +++ b/xkb/xkbxml.c @@ -25,6 +25,7 @@ #endif #include +#include #include "xkbxml.h" #include "ibus.h" @@ -275,6 +276,7 @@ ibus_xkb_engine_desc_new (const gchar *l gchar *desc = NULL; gchar *engine_layout = NULL; const gchar *name_prefix = "xkb:layout:"; + const gchar *icon = "ibus-engine"; g_return_val_if_fail (lang != NULL && layout != NULL, NULL); @@ -304,6 +306,12 @@ ibus_xkb_engine_desc_new (const gchar *l desc = g_strdup_printf ("XKB %s keyboard layout", layout); engine_layout = g_strdup (layout); } +#if USE_BRIDGE_HOTKEY + if (g_ascii_strncasecmp (name, DEFAULT_BRIDGE_ENGINE_NAME, + strlen (DEFAULT_BRIDGE_ENGINE_NAME)) == 0) { + icon = "input-keyboard-symbolic"; + } +#endif engine = ibus_engine_desc_new (name, longname, @@ -311,7 +319,7 @@ ibus_xkb_engine_desc_new (const gchar *l lang, "LGPL2.1", "Takao Fujiwara ", - "ibus-engine", + icon, engine_layout); g_free (name); -- 1.7.5.4