From a1840c00fad1798e7b62f469286415e8458125c4 Mon Sep 17 00:00:00 2001 From: fujiwarat Date: Wed, 5 Dec 2012 11:20:37 +0900 Subject: [PATCH] Add Ctrl+space customization. Review URL: https://codereview.appspot.com/6822102 --- data/ibus.schemas.in | 14 ++++- setup/keyboardshortcut.py | 90 ++++++++++++++++---------------- setup/main.py | 44 ++++++++-------- setup/setup.ui | 25 ++++----- ui/gtk3/panel.vala | 128 +++++++++++++++++++++++++++++++++++----------- 5 files changed, 195 insertions(+), 106 deletions(-) diff --git a/data/ibus.schemas.in b/data/ibus.schemas.in index a8c7d7f..dbb6da8 100644 --- a/data/ibus.schemas.in +++ b/data/ibus.schemas.in @@ -50,7 +50,19 @@ [Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R] Trigger shortcut keys - The shortcut keys for turning input method on or off + The shortcut keys for turning input method on or off + + + + /schemas/desktop/ibus/general/hotkey/triggers + /desktop/ibus/general/hotkey/triggers + ibus + list + string + [<Control>space] + + Trigger shortcut keys for gtk_accelerator_parse + The shortcut keys for turning input method on or off diff --git a/setup/keyboardshortcut.py b/setup/keyboardshortcut.py index 7cc8649..2e8ca54 100644 --- a/setup/keyboardshortcut.py +++ b/setup/keyboardshortcut.py @@ -102,9 +102,8 @@ class KeyboardShortcutSelection(Gtk.VBox): self.__modifier_buttons.append(("Hyper", Gtk.CheckButton.new_with_mnemonic("_Hyper"), Gdk.ModifierType.HYPER_MASK)) - self.__modifier_buttons.append(("Capslock", - Gtk.CheckButton.new_with_mnemonic("Capsloc_k"), - Gdk.ModifierType.LOCK_MASK)) + # is not parsed by gtk_accelerator_parse() + # FIXME: Need to check if ibus gtk panel can enable . self.__modifier_buttons.append(("Release", Gtk.CheckButton.new_with_mnemonic("_Release"), Gdk.ModifierType.RELEASE_MASK)) @@ -118,7 +117,6 @@ class KeyboardShortcutSelection(Gtk.VBox): table.attach(self.__modifier_buttons[4][1], 0, 1, 1, 2) table.attach(self.__modifier_buttons[5][1], 1, 2, 1, 2) table.attach(self.__modifier_buttons[6][1], 2, 3, 1, 2) - table.attach(self.__modifier_buttons[7][1], 3, 4, 1, 2) hbox.pack_start(table, True, True, 4) self.pack_start(hbox, False, True, 4) @@ -184,19 +182,20 @@ class KeyboardShortcutSelection(Gtk.VBox): modifiers.append(name) if keycode.startswith("_"): keycode = keycode[1:] - keys = modifiers + [keycode] - shortcut = "+".join(keys) + shortcut = "".join(map(lambda m: '<' + m + '>', modifiers)) + shortcut += keycode return shortcut def __set_shortcut_to_buttons(self, shortcut): - keys = shortcut.split("+") - mods = keys[:-1] + (keyval, state) = Gtk.accelerator_parse(shortcut) + if keyval == 0 and state == 0: + return for name, button, mask in self.__modifier_buttons: - if name in mods: + if state & mask: button.set_active(True) else: button.set_active(False) - self.__keycode_entry.set_text(keys[-1]) + self.__keycode_entry.set_text(shortcut.rsplit('>', 1)[-1]) def __get_selected_shortcut(self): model = self.__shortcut_view.get_model() @@ -251,49 +250,52 @@ class KeyboardShortcutSelection(Gtk.VBox): message = _("Please press a key (or a key combination).\nThe dialog will be closed when the key is released.") dlg.set_markup(message) dlg.set_title(_("Please press a key (or a key combination)")) - - def __key_press_event(d, k, out): - out.append(k.copy()) - - def __key_release_event(d, k, out): - d.response(Gtk.ResponseType.OK) - - dlg.connect("key-press-event", __key_press_event, out) - dlg.connect("key-release-event", __key_release_event, None) + sw = Gtk.ScrolledWindow() + + def __accel_edited_cb(c, path, keyval, state, keycode): + out.append(keyval) + out.append(state) + out.append(keycode) + dlg.response(Gtk.ResponseType.OK) + + model = Gtk.ListStore(GObject.TYPE_INT, + GObject.TYPE_UINT, + GObject.TYPE_UINT) + accel_view = Gtk.TreeView(model) + sw.add(accel_view) + column = Gtk.TreeViewColumn() + renderer = Gtk.CellRendererAccel(accel_mode=Gtk.CellRendererAccelMode.OTHER, + editable=True) + renderer.connect('accel-edited', __accel_edited_cb) + column.pack_start(renderer, True) + column.add_attribute(renderer, 'accel-mods', 0) + column.add_attribute(renderer, 'accel-key', 1) + column.add_attribute(renderer, 'keycode', 2) + accel_view.append_column(column) + it = model.append(None) + area = dlg.get_message_area() + area.pack_end(sw, True, True, 0) + sw.show_all() id = dlg.run() dlg.destroy() - if id != Gtk.ResponseType.OK or not out: + if id != Gtk.ResponseType.OK or len(out) < 3: return - keyevent = out[len(out) - 1] - state = keyevent.state & (Gdk.ModifierType.CONTROL_MASK | \ - Gdk.ModifierType.SHIFT_MASK | \ - Gdk.ModifierType.MOD1_MASK | \ - Gdk.ModifierType.META_MASK | \ - Gdk.ModifierType.SUPER_MASK | \ - Gdk.ModifierType.HYPER_MASK) - - - if state == 0: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Control_L, Gdk.KEY_Control_R) and state == Gdk.ModifierType.CONTROL_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Shift_L, Gdk.KEY_Shift_R) and state == Gdk.ModifierType.SHIFT_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Alt_L, Gdk.KEY_Alt_R) and state == Gdk.ModifierType.MOD1_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Meta_L, Gdk.KEY_Meta_R) and state == Gdk.ModifierType.META_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Super_L, Gdk.KEY_Super_R) and state == Gdk.ModifierType.SUPER_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK - elif keyevent.keyval in (Gdk.KEY_Hyper_L, Gdk.KEY_Hyper_R) and state == Gdk.ModifierType.HYPER_MASK: - state = state | Gdk.ModifierType.RELEASE_MASK + keyval = out[0] + state = out[1] + keycode = out[2] for name, button, mask in self.__modifier_buttons: if state & mask: button.set_active(True) else: button.set_active(False) - self.__keycode_entry.set_text(Gdk.keyval_name(keyevent.keyval)) + + shortcut = Gtk.accelerator_name_with_keycode(None, + keyval, + keycode, + state) + shortcut = shortcut.replace('', '') + self.__keycode_entry.set_text(shortcut.rsplit('>', 1)[-1]) def __add_button_clicked_cb(self, button): shortcut = self.__get_shortcut_from_buttons() diff --git a/setup/main.py b/setup/main.py index f527da1..a8acc7a 100644 --- a/setup/main.py +++ b/setup/main.py @@ -83,22 +83,23 @@ class Setup(object): self.__init_ui() def __init_hotkey(self): - default_values = { - "trigger" : (N_("trigger"), ["Control+space"]), - "enable_unconditional" : (N_("enable"), []), - "disable_unconditional" : (N_("disable"), []) - } - - values = dict(self.__config.get_values("general/hotkey")) - - for name, (label, shortcuts) in default_values.items(): - shortcuts = values.get(name, shortcuts) - button = self.__builder.get_object("button_%s" % name) - entry = self.__builder.get_object("entry_%s" % name) - entry.set_text("; ".join(shortcuts)) - entry.set_tooltip_text("\n".join(shortcuts)) - button.connect("clicked", self.__shortcut_button_clicked_cb, - label, "general/hotkey", name, entry) + name = 'triggers' + label = 'switch_engine' + variant = self.__config.get_value('general/hotkey', name) + if variant != None: + shortcuts = variant.dup_strv() + else: + shortcuts = ['space'] + + button = self.__builder.get_object("button_%s" % label) + entry = self.__builder.get_object("entry_%s" % label) + entry.set_text("; ".join(shortcuts)) + tooltip = "\n".join(shortcuts) + tooltip += "\n" + \ + _("Use shortcut with shift to switch to the previous input method") + entry.set_tooltip_text(tooltip) + button.connect("clicked", self.__shortcut_button_clicked_cb, + name, "general/hotkey", label, entry) def __init_panel(self): values = dict(self.__config.get_values("panel")) @@ -375,7 +376,8 @@ class Setup(object): def __shortcut_button_clicked_cb(self, button, name, section, _name, entry): buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK) - title = _("Select keyboard shortcut for %s") % _(name) + title = _("Select keyboard shortcut for %s") % \ + _("switching input methods") dialog = keyboardshortcut.KeyboardShortcutSelectionDialog(buttons = buttons, title = title) text = entry.get_text() if text: @@ -388,11 +390,13 @@ class Setup(object): dialog.destroy() if id != Gtk.ResponseType.OK: return - self.__config.set_value(section, _name, GLib.Variant.new_strv(shortcuts)) + self.__config.set_value(section, name, GLib.Variant.new_strv(shortcuts)) text = "; ".join(shortcuts) entry.set_text(text) - entry.set_tooltip_text(text) - + tooltip = "\n".join(shortcuts) + tooltip += "\n" + \ + _("Use shortcut with shift to switch to the previous input method") + entry.set_tooltip_text(tooltip) def __item_started_column_toggled_cb(self, cell, path_str, model): diff --git a/setup/setup.ui b/setup/setup.ui index 8121d62..04bb493 100644 --- a/setup/setup.ui +++ b/setup/setup.ui @@ -121,7 +121,7 @@ - True + True False False The shortcut keys for switching to previous input method in the list @@ -137,7 +137,7 @@ - True + True False 6 @@ -180,7 +180,7 @@ False 6 - + True True False @@ -192,7 +192,7 @@ - + ... False True @@ -213,16 +213,17 @@ 2 3 4 + GTK_FILL - True + True False 6 - True + > True False True False @@ -237,7 +238,7 @@ ... False - True + True False True False @@ -260,7 +261,7 @@ - True + True False The shortcut keys for turning input method on or off 0 @@ -273,7 +274,7 @@ - True + True False 0 Enable: @@ -287,7 +288,7 @@ - True + True False 6 @@ -328,7 +329,7 @@ - True + True False 0 Disable: @@ -342,7 +343,7 @@ - True + True False 6 diff --git a/ui/gtk3/panel.vala b/ui/gtk3/panel.vala index 3df0be7..24e6b2e 100644 --- a/ui/gtk3/panel.vala +++ b/ui/gtk3/panel.vala @@ -21,6 +21,20 @@ */ class Panel : IBus.PanelService { + private class Keybinding { + public Keybinding(uint keysym, + Gdk.ModifierType modifiers, + bool reverse) { + this.keysym = keysym; + this.modifiers = modifiers; + this.reverse = reverse; + } + + public uint keysym { get; set; } + public Gdk.ModifierType modifiers { get; set; } + public bool reverse { get; set; } + } + private IBus.Bus m_bus; private IBus.Config m_config; private Gtk.StatusIcon m_status_icon; @@ -36,8 +50,7 @@ class Panel : IBus.PanelService { private int m_switcher_delay_time = 400; private const string ACCELERATOR_SWITCH_IME_FOREWARD = "space"; - private uint m_switch_keysym = 0; - private Gdk.ModifierType m_switch_modifiers = 0; + private GLib.List m_keybindings = new GLib.List(); public Panel(IBus.Bus bus) { GLib.assert(bus.is_connected()); @@ -60,7 +73,8 @@ class Panel : IBus.PanelService { m_candidate_panel.page_down.connect((w) => this.page_down()); m_switcher = new Switcher(); - bind_switch_shortcut(); + // The initial shortcut is "space" + bind_switch_shortcut(null); if (m_switcher_delay_time >= 0) { m_switcher.set_popup_delay_time((uint) m_switcher_delay_time); @@ -78,62 +92,101 @@ class Panel : IBus.PanelService { unbind_switch_shortcut(); } - private void bind_switch_shortcut() { - var keybinding_manager = KeybindingManager.get_instance(); + private void keybinding_manager_bind(KeybindingManager keybinding_manager, + string? accelerator) { + uint switch_keysym = 0; + Gdk.ModifierType switch_modifiers = 0; + Gdk.ModifierType reverse_modifier = Gdk.ModifierType.SHIFT_MASK; + Keybinding keybinding; - var accelerator = ACCELERATOR_SWITCH_IME_FOREWARD; Gtk.accelerator_parse(accelerator, - out m_switch_keysym, out m_switch_modifiers); + out switch_keysym, out switch_modifiers); - // Map virtual modifiers to (i.e.Mod2, Mod3, ...) + // Map virtual modifiers to (i.e. Mod2, Mod3, ...) const Gdk.ModifierType VIRTUAL_MODIFIERS = ( Gdk.ModifierType.SUPER_MASK | Gdk.ModifierType.HYPER_MASK | Gdk.ModifierType.META_MASK); - if ((m_switch_modifiers & VIRTUAL_MODIFIERS) != 0) { + if ((switch_modifiers & VIRTUAL_MODIFIERS) != 0) { // workaround a bug in gdk vapi vala > 0.18 // https://bugzilla.gnome.org/show_bug.cgi?id=677559 #if VALA_0_18 Gdk.Keymap.get_default().map_virtual_modifiers( - ref m_switch_modifiers); + ref switch_modifiers); #else - if ((m_switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0) - m_switch_modifiers |= Gdk.ModifierType.MOD4_MASK; - if ((m_switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0) - m_switch_modifiers |= Gdk.ModifierType.MOD4_MASK; + if ((switch_modifiers & Gdk.ModifierType.SUPER_MASK) != 0) + switch_modifiers |= Gdk.ModifierType.MOD4_MASK; + if ((switch_modifiers & Gdk.ModifierType.HYPER_MASK) != 0) + switch_modifiers |= Gdk.ModifierType.MOD4_MASK; #endif - m_switch_modifiers &= ~VIRTUAL_MODIFIERS; + switch_modifiers &= ~VIRTUAL_MODIFIERS; } - if (m_switch_keysym == 0 && m_switch_modifiers == 0) { + if (switch_keysym == 0 && switch_modifiers == 0) { warning("Parse accelerator '%s' failed!", accelerator); return; } - keybinding_manager.bind(m_switch_keysym, m_switch_modifiers, + keybinding = new Keybinding(switch_keysym, + switch_modifiers, + false); + m_keybindings.append(keybinding); + + keybinding_manager.bind(switch_keysym, switch_modifiers, (e) => handle_engine_switch(e, false)); // accelerator already has Shift mask - if ((m_switch_modifiers & Gdk.ModifierType.SHIFT_MASK) != 0) + if ((switch_modifiers & reverse_modifier) != 0) { return; + } + + switch_modifiers |= reverse_modifier; - keybinding_manager.bind(m_switch_keysym, - m_switch_modifiers | Gdk.ModifierType.SHIFT_MASK, + keybinding = new Keybinding(switch_keysym, + switch_modifiers, + true); + m_keybindings.append(keybinding); + + keybinding_manager.bind(switch_keysym, switch_modifiers, (e) => handle_engine_switch(e, true)); } + private void bind_switch_shortcut(Variant? variant) { + string[] accelerators = {}; + Variant var_trigger = variant; + + if (var_trigger == null && m_config != null) { + var_trigger = m_config.get_value("general/hotkey", + "triggers"); + } + + if (var_trigger != null) { + accelerators = var_trigger.dup_strv(); + } else { + accelerators += ACCELERATOR_SWITCH_IME_FOREWARD; + } + + var keybinding_manager = KeybindingManager.get_instance(); + + foreach (var accelerator in accelerators) { + keybinding_manager_bind(keybinding_manager, accelerator); + } + } + private void unbind_switch_shortcut() { var keybinding_manager = KeybindingManager.get_instance(); - if (m_switch_keysym == 0 && m_switch_modifiers == 0) - return; + unowned GLib.List keybindings = m_keybindings; - keybinding_manager.unbind(m_switch_keysym, m_switch_modifiers); - keybinding_manager.unbind(m_switch_keysym, - m_switch_modifiers | Gdk.ModifierType.SHIFT_MASK); + while (keybindings != null) { + Keybinding keybinding = keybindings.data; - m_switch_keysym = 0; - m_switch_modifiers = 0; + keybinding_manager.unbind(keybinding.keysym, + keybinding.modifiers); + keybindings = keybindings.next; + } + + m_keybindings = null; } private void set_custom_font() { @@ -225,10 +278,13 @@ class Panel : IBus.PanelService { m_config.watch("general", "preload_engines"); m_config.watch("general", "engines_order"); m_config.watch("general", "switcher_delay_time"); + m_config.watch("general/hotkey", "triggers"); m_config.watch("panel", "custom_font"); m_config.watch("panel", "use_custom_font"); update_engines(m_config.get_value("general", "preload_engines"), m_config.get_value("general", "engines_order")); + unbind_switch_shortcut(); + bind_switch_shortcut(null); set_switcher_delay_time(null); } else { update_engines(null, null); @@ -308,6 +364,12 @@ class Panel : IBus.PanelService { return; } + if (section == "general/hotkey" && name == "triggers") { + unbind_switch_shortcut(); + bind_switch_shortcut(variant); + return; + } + if (section == "panel" && (name == "custom_font" || name == "use_custom_font")) { set_custom_font(); @@ -316,6 +378,7 @@ class Panel : IBus.PanelService { if (section == "general" && name == "switcher_delay_time") { set_switcher_delay_time(variant); + return; } } @@ -324,15 +387,22 @@ class Panel : IBus.PanelService { if (m_engines.length <= 1) return; + uint keyval = event.key.keyval; + uint modifiers = KeybindingManager.MODIFIER_FILTER & event.key.state; + uint primary_modifiers = KeybindingManager.get_primary_modifier(event.key.state); bool pressed = KeybindingManager.primary_modifier_still_pressed( event, primary_modifiers); + + if (revert) { + modifiers &= ~Gdk.ModifierType.SHIFT_MASK; + } + if (pressed && m_switcher_delay_time >= 0) { int i = revert ? m_engines.length - 1 : 1; - i = m_switcher.run(m_switch_keysym, m_switch_modifiers, event, - m_engines, i); + i = m_switcher.run(keyval, modifiers, event, m_engines, i); if (i < 0) { debug("switch cancelled"); } else { -- 2.7.4