From 9d5387ea430adbce81899a1a2bece6c8520848b1 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Fri, 5 Aug 2011 20:54:37 +0900 Subject: [PATCH] Add a bridge hotkey which use prev-next engines instead of on-off. --- bus/Makefile.am | 20 +- bus/ibusimpl.c | 425 +++++++++++++++++++++++++++++++++------ bus/inputcontext.c | 39 ++++ bus/inputcontext.h | 22 ++ bus/registry.c | 36 ++++ 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 | 16 ++- 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/Makefile.am | 2 + ui/gtk/panel.py | 288 ++++++++++++++++++++++++-- ui/gtk/xkbengine.py | 93 +++++++++ ui/gtk/xkbfactory.py | 117 +++++++++++ xkb/Makefile.am | 2 + xkb/xkbxml.c | 10 +- 28 files changed, 1480 insertions(+), 390 deletions(-) delete mode 100644 data/ibus.schemas.in create mode 100644 data/ibus.schemas.in.in create mode 100644 ui/gtk/xkbengine.py create mode 100644 ui/gtk/xkbfactory.py 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 f13d8e0..44fe954 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,30 @@ _panel_destroy_cb (BusPanelProxy *panel, g_object_unref (panel); } +static IBusEngineDesc * +_find_engine_desc_by_name (BusIBusImpl *ibus, + const gchar *engine_name) +{ + IBusEngineDesc *desc = NULL; + GList *p; + + /* find engine in registered engine list */ + for (p = ibus->register_engine_list; p != NULL; p = p->next) { + desc = (IBusEngineDesc *) p->data; + if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0) + return desc; + } + + /* find engine in preload engine list */ + for (p = ibus->engine_list; p != NULL; p = p->next) { + desc = (IBusEngineDesc *) p->data; + if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0) + return desc; + } + + return NULL; +} + static void _config_set_value_done (GObject *object, GAsyncResult *res, @@ -562,6 +597,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 +669,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 { @@ -1182,28 +1286,110 @@ _ibus_get_address (BusIBusImpl g_variant_new ("(s)", bus_server_get_address ())); } -static IBusEngineDesc * -_find_engine_desc_by_name (BusIBusImpl *ibus, - const gchar *engine_name) -{ - IBusEngineDesc *desc = NULL; - GList *p; +/** + * _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; - /* find engine in registered engine list */ - for (p = ibus->register_engine_list; p != NULL; p = p->next) { - desc = (IBusEngineDesc *) p->data; - if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0) - return desc; + g_assert (ibus != NULL); + g_assert (desc != NULL); + + if (event == 0) { + return FALSE; } - /* find engine in preload engine list */ - for (p = ibus->engine_list; p != NULL; p = p->next) { - desc = (IBusEngineDesc *) p->data; - if (g_strcmp0 (ibus_engine_desc_get_name (desc), engine_name) == 0) - return desc; + 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); } - return NULL; + /* 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); } /** @@ -1216,7 +1402,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; } /** @@ -2270,6 +2520,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"); @@ -2334,6 +2587,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, @@ -2355,6 +2614,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 @@ -2366,8 +2643,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); } @@ -2472,59 +2788,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..07e9148 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 diff --git a/configure.ac b/configure.ac index 4c20ae7..21d35c3 100644 --- a/configure.ac +++ b/configure.ac @@ -456,6 +456,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 @@ -482,6 +510,7 @@ bus/Makefile util/Makefile util/IMdkit/Makefile data/Makefile +data/ibus.schemas.in data/icons/Makefile data/keymaps/Makefile docs/Makefile @@ -534,5 +563,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 = \ $(NULL) 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 7ca4899..42d9297 --- 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 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 16f47df..85d0651 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 @@ -312,6 +314,8 @@ class XKBConfigRegistry(): engine_layout = layout icon = 'ibus-engine' + if name_prefix == 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..20cde3b 100644 --- a/setup/enginetreeview.py +++ b/setup/enginetreeview.py @@ -162,6 +162,14 @@ class EngineTreeView(gtk.TreeView): return row[0] elif property.name == "engines": engines = [ r[0] for r in self.__model if r[0] != None] + for i, e in enumerate(self.__engines): + if ibus.use_bridge_hotkey() and \ + e.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + if i < len(engines): + engines.insert(i, e) + else: + engines.append(e) + break return engines else: raise AttributeError, 'unknown property %s' % property.name @@ -172,8 +180,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/Makefile.am b/ui/gtk/Makefile.am index 1f19748..1702613 100644 --- a/ui/gtk/Makefile.am +++ b/ui/gtk/Makefile.am @@ -33,6 +33,8 @@ ui_gtk_PYTHON = \ propitem.py \ toolitem.py \ engineabout.py \ + xkbengine.py \ + xkbfactory.py \ $(NULL) ui_gtkdir = $(pkgdatadir)/ui/gtk diff --git a/ui/gtk/panel.py b/ui/gtk/panel.py index 8804634..d87c1d2 100644 --- a/ui/gtk/panel.py +++ b/ui/gtk/panel.py @@ -133,6 +133,15 @@ 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.__show = 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,113 @@ 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 + ")" + engine = registry.engine_desc_new(lang, + layout, + _("Default Layout"), + model, + model_desc, + ibus.DEFAULT_BRIDGE_ENGINE_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 != []: + component = ibus.Component("org.freedesktop.IBus.XKB", + _("XKB Component"), + "1.0", + "GPL", + "Takao Fujiwara", + "https://github.com/fujiwarat/ibus/tree/gjs") + for engine in self.__disabled_engines: + component.add_engine(engine.name, + engine.longname, + engine.description, + engine.language, + engine.license, + engine.author, + engine.icon, + engine.layout, + engine.hotkeys) + + import xkbfactory + self.__factory = xkbfactory.EngineFactory(self.__bus) + self.__bus.register_component(component) + 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 +367,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) @@ -260,6 +377,8 @@ class Panel(ibus.PanelBase): def focus_out(self, ic): self.reset() + if self.__focus_ic and self.__focus_ic.is_enabled(): + self.__focus_ic.disable() self.__focus_ic = None self.__language_bar.set_enabled(False) self.__language_bar.focus_out() @@ -273,7 +392,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 +420,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 +448,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 +577,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 +602,34 @@ 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 self.__use_bridge_hotkey() and \ + engine.name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME) and \ + self.__disabled_engines != None: + engine.is_bridge = True + engine.disabled_engines_id = 0 + for j, kb_engine in enumerate(self.__disabled_engines): + if engine.name == kb_engine.name: + engine.disabled_engines_id = j + break + is_bold = True if (current_engine != None and \ + current_engine.name == engine.name and \ + j == self.__disabled_engines_id) else False + self.__add_engine_in_menu(menu, 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 +681,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 +748,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/ui/gtk/xkbengine.py b/ui/gtk/xkbengine.py new file mode 100644 index 0000000..12c07cf --- /dev/null +++ b/ui/gtk/xkbengine.py @@ -0,0 +1,93 @@ +# vim:set et sts=4 sw=4: +# +# ibus - The Input Bus +# +# Copyright(c) 2011 Peng Huang +# Copyright(c) 2011 Takao Fujiwara +# Copyright(c) 2011 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or(at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +import ibus + +class Engine(ibus.EngineBase): + def __init__(self, bus, object_path): + super(Engine, self).__init__(bus, object_path) + + def update_aux_string(self, string, attrs, visible): + self.update_auxiliary_text(ibus.Text(string, attrs), visible) + + def page_up(self): + return True + + def page_down(self): + return True + + def cursor_up(self): + return True + + def cursor_down(self): + return True + + def candidate_clicked(self, index, button, state): + pass + + if ibus.get_version() >= '1.2.0': + def process_key_event(self, keyval, keycode, state): + try: + return self.process_key_event_internal2(keyval, keycode, state) + except: + import traceback + traceback.print_exc() + return False + else: + def process_key_event(self, keyval, state): + try: + return self.process_key_event_internal2(keyval, 0, state) + except: + import traceback + traceback.print_exc() + return False + + def property_activate(self, prop_name, state): + pass + + def focus_in(self): + pass + + def focus_out(self): + pass + + def disable(self): + pass + + def reset(self): + pass + + def do_destroy(self): + super(Engine,self).do_destroy() + +#======================================================================= + @classmethod + def CONFIG_RELOADED(cls, bus): + print 'RELOADED' + + @classmethod + def CONFIG_VALUE_CHANGED(cls, bus, section, name, value): + print 'VALUE_CHAMGED =', section, name, value + + def process_key_event_internal2(self, keyval, keycode, state): + return False diff --git a/ui/gtk/xkbfactory.py b/ui/gtk/xkbfactory.py new file mode 100644 index 0000000..014e20b --- /dev/null +++ b/ui/gtk/xkbfactory.py @@ -0,0 +1,117 @@ +# vim:set et sts=4 sw=4: +# +# ibus - The Input Bus +# +# Copyright(c) 2011 Peng Huang +# Copyright(c) 2011 Takao Fujiwara +# Copyright(c) 2011 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or(at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place, Suite 330, +# Boston, MA 02111-1307 USA + +import ibus +import xkbengine + +from gettext import dgettext +_ = lambda a : dgettext("ibus", a) +N_ = lambda a : a + + +class EngineFactory(ibus.EngineFactoryBase): + FACTORY_PATH = "/com/redhat/IBus/engines/XKB/Factory" + ENGINE_PATH = "/com/redhat/IBus/engines/XKB/Engine" + NAME = "XKBFactory" + LANG = "en" + ICON = "ibus-engine" + AUTHORS = "Takao Fujiwara " + CREDITS = "GPLv2" + + def __init__(self, bus): + self.__bus = bus + super(EngineFactory, self).__init__(bus) + + self.__id = 0 + + def create_engine(self, engine_name): + if engine_name and \ + engine_name.startswith(ibus.DEFAULT_BRIDGE_ENGINE_NAME): + + self.__id += 1 + return xkbengine.Engine(self.__bus, "%s/%d" % (self.ENGINE_PATH, self.__id)) + return super(EngineFactory, self).create_engine(engine_name) + + +def test(): + import gtk + component = ibus.Component("org.freedesktop.IBus.XKB", + _("XKB Component"), + "1.0", + "GPL", + "Takao Fujiwara", + "https://github.com/fujiwara") + engines = [] + engine = ibus.EngineDesc("xkb:layout:default:jp,us", + "Default Layout", + "XKB jp,us keyboard layout", + "jpn", + "LGPL2.1", + "fujiwara", + "ibus-engine", + "jp,us") + engines.append(engine) + engine = ibus.EngineDesc("xkb:layout:default:us", + "Default Layout", + "XKB us keyboard layout", + "eng", + "LGPL2.1", + "fujiwara", + "ibus-engine", + "us") + engines.append(engine) + for engine in engines: + component.add_engine(engine.name, + engine.longname, + engine.description, + engine.language, + engine.license, + engine.author, + engine.icon, + engine.layout, + engine.hotkeys) + + class TestWindow(gtk.Window): + def __init__(self): + super(TestWindow, self).__init__() + self.__bus = ibus.Bus() + self.__bus.connect("disconnected", gtk.main_quit) + path = self.__bus.create_input_context("Test") + self.__ic = ibus.InputContext(self.__bus, path) + self.__ic.set_capabilities(9) + factory = EngineFactory(self.__bus) + self.__bus.register_component(component) + #self.__bus.request_name("org.freedesktop.IBus.XKB", 0) + self.connect("focus-in-event", self.__focus_in) + + def __focus_in(self, widget, event): + print "focused in" + self.__ic.focus_in() + self.__ic.set_engine(engines[0]) + + window = TestWindow() + window.show_all() + gtk.main() + +if __name__ == "__main__": + test() 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/xkbxml.c b/xkb/xkbxml.c index ad10c28..968fb86 100644 --- a/xkb/xkbxml.c +++ b/xkb/xkbxml.c @@ -25,6 +25,7 @@ #endif #include +#include #include "xkbxml.h" #include "ibus.h" @@ -274,6 +275,7 @@ ibus_xkb_engine_desc_new (const gchar *lang, gchar *longname = NULL; gchar *desc = NULL; gchar *engine_layout = NULL; + const gchar *icon = "ibus-engine"; g_return_val_if_fail (lang != NULL && layout != NULL, NULL); @@ -298,6 +300,12 @@ ibus_xkb_engine_desc_new (const gchar *lang, 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, @@ -305,7 +313,7 @@ ibus_xkb_engine_desc_new (const gchar *lang, lang, "LGPL2.1", "Takao Fujiwara ", - "ibus-engine", + icon, engine_layout); g_free (name); -- 1.7.5.4