<default>[Control+space,Zenkaku_Hankaku,Alt+Kanji,Alt+grave,Hangul,Alt+Release+Alt_R]</default>
<locale name="C">
<short>Trigger shortcut keys</short>
- <long>The shortcut keys for turning input method on or off</long>
+ <long>The shortcut keys for turning input method on or off</long>
+ </locale>
+ </schema>
+ <schema>
+ <key>/schemas/desktop/ibus/general/hotkey/triggers</key>
+ <applyto>/desktop/ibus/general/hotkey/triggers</applyto>
+ <owner>ibus</owner>
+ <type>list</type>
+ <list_type>string</list_type>
+ <default>[<Control>space]</default>
+ <locale name="C">
+ <short>Trigger shortcut keys for gtk_accelerator_parse</short>
+ <long>The shortcut keys for turning input method on or off</long>
</locale>
</schema>
<schema>
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))
+ # <CapsLock> is not parsed by gtk_accelerator_parse()
+ # FIXME: Need to check if ibus gtk panel can enable <Release>.
self.__modifier_buttons.append(("Release",
Gtk.CheckButton.new_with_mnemonic("_Release"),
Gdk.ModifierType.RELEASE_MASK))
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)
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()
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('<Primary>', '<Control>')
+ self.__keycode_entry.set_text(shortcut.rsplit('>', 1)[-1])
def __add_button_clicked_cb(self, button):
shortcut = self.__get_shortcut_from_buttons()
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 = ['<Control>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"))
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:
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):
</child>
<child>
<object class="GtkLabel" id="label9">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The shortcut keys for switching to previous input method in the list</property>
</child>
<child>
<object class="GtkHBox" id="hbox4">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
- <object class="GtkEntry" id="entry_next_engine">
+ <object class="GtkEntry" id="entry_switch_engine">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
</packing>
</child>
<child>
- <object class="GtkButton" id="button_next_engine">
+ <object class="GtkButton" id="button_switch_engine">
<property name="label" translatable="yes">...</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
+ <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox6">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkEntry" id="entry_prev_engine">
- <property name="visible">True</property>
+ > <property name="no_show_all">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<object class="GtkButton" id="button_prev_engine">
<property name="label" translatable="yes">...</property>
<property name="use_action_appearance">False</property>
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
</child>
<child>
<object class="GtkLabel" id="label7">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">The shortcut keys for turning input method on or off</property>
<property name="xalign">0</property>
</child>
<child>
<object class="GtkLabel" id="label18">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Enable:</property>
</child>
<child>
<object class="GtkHBox" id="hbox2">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
</child>
<child>
<object class="GtkLabel" id="label19">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Disable:</property>
</child>
<child>
<object class="GtkHBox" id="hbox3">
- <property name="visible">True</property>
+ <property name="no_show_all">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
*/
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;
private int m_switcher_delay_time = 400;
private const string ACCELERATOR_SWITCH_IME_FOREWARD = "<Control>space";
- private uint m_switch_keysym = 0;
- private Gdk.ModifierType m_switch_modifiers = 0;
+ private GLib.List<Keybinding> m_keybindings = new GLib.List<Keybinding>();
public Panel(IBus.Bus bus) {
GLib.assert(bus.is_connected());
m_candidate_panel.page_down.connect((w) => this.page_down());
m_switcher = new Switcher();
- bind_switch_shortcut();
+ // The initial shortcut is "<Control>space"
+ bind_switch_shortcut(null);
if (m_switcher_delay_time >= 0) {
m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
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<Keybinding> 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() {
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);
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();
if (section == "general" && name == "switcher_delay_time") {
set_switcher_delay_time(variant);
+ return;
}
}
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 {