Launch the second ibus engine too to reduce the launching time.
[platform/upstream/ibus.git] / ui / gtk3 / panel.vala
index 40079ec..0d7a5b2 100644 (file)
  */
 
 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;
@@ -33,10 +47,11 @@ class Panel : IBus.PanelService {
     private GLib.Pid m_setup_pid = 0;
     private Gtk.AboutDialog m_about_dialog;
     private Gtk.CssProvider m_css_provider;
+    private int m_switcher_delay_time = 400;
+    private bool m_use_system_keyboard_layout = false;
     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());
@@ -59,7 +74,12 @@ 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 "<Control>space"
+        bind_switch_shortcut(null);
+
+        if (m_switcher_delay_time >= 0) {
+            m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
+        }
 
         m_property_manager = new PropertyManager();
         m_property_manager.property_activate.connect((k, s) => {
@@ -73,62 +93,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;
+        }
 
-        keybinding_manager.bind(m_switch_keysym,
-                m_switch_modifiers | Gdk.ModifierType.SHIFT_MASK,
+        switch_modifiers |= reverse_modifier;
+
+        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() {
@@ -188,6 +247,41 @@ class Panel : IBus.PanelService {
                                                  Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
     }
 
+    private void set_switcher_delay_time(Variant? variant) {
+        Variant var_switcher_delay_time = variant;
+
+        if (var_switcher_delay_time == null) {
+            var_switcher_delay_time = m_config.get_value("general",
+                                                         "switcher-delay-time");
+        }
+
+        if (var_switcher_delay_time == null) {
+            return;
+        }
+
+        m_switcher_delay_time = var_switcher_delay_time.get_int32();
+
+        if (m_switcher_delay_time >= 0) {
+            m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
+        }
+    }
+
+    private void set_use_system_keyboard_layout(Variant? variant) {
+        Variant var_use_system_kbd_layout = variant;
+
+        if (var_use_system_kbd_layout == null) {
+            var_use_system_kbd_layout = m_config.get_value(
+                    "general",
+                    "use_system_keyboard_layout");
+        }
+
+        if (var_use_system_kbd_layout == null) {
+            return;
+        }
+
+        m_use_system_keyboard_layout = var_use_system_kbd_layout.get_boolean();
+    }
+
     public void set_config(IBus.Config config) {
         if (m_config != null) {
             m_config.value_changed.disconnect(config_value_changed_cb);
@@ -200,10 +294,19 @@ class Panel : IBus.PanelService {
             m_config.value_changed.connect(config_value_changed_cb);
             m_config.watch("general", "preload_engines");
             m_config.watch("general", "engines_order");
+            m_config.watch("general", "switcher_delay_time");
+            m_config.watch("general", "use_system_keyboard_layout");
+            m_config.watch("general/hotkey", "triggers");
             m_config.watch("panel", "custom_font");
             m_config.watch("panel", "use_custom_font");
+            // Update m_use_system_keyboard_layout before update_engines()
+            // is called.
+            set_use_system_keyboard_layout(null);
             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);
         }
@@ -211,6 +314,51 @@ class Panel : IBus.PanelService {
         set_custom_font();
     }
 
+    private void exec_setxkbmap(IBus.EngineDesc engine) {
+        string layout = engine.get_layout();
+        string variant = engine.get_layout_variant();
+        string option = engine.get_layout_option();
+        string standard_error = null;
+        int exit_status = 0;
+        string[] args = { "setxkbmap" };
+
+        if (layout != null && layout != "" && layout != "default") {
+            args += "-layout";
+            args += layout;
+        }
+        if (variant != null && variant != "" && variant != "default") {
+            args += "-variant";
+            args += variant;
+        }
+        if (option != null && option != "" && option != "default") {
+            /*TODO: Need to get the session XKB options */
+            args += "-option";
+            args += "-option";
+            args += option;
+        }
+
+        if (args.length == 1) {
+            return;
+        }
+
+        try {
+            if (!GLib.Process.spawn_sync(null, args, null,
+                                         GLib.SpawnFlags.SEARCH_PATH,
+                                         null, null,
+                                         out standard_error,
+                                         out exit_status)) {
+                warning("Switch xkb layout to %s failed.",
+                        engine.get_layout());
+            }
+        } catch (GLib.SpawnError e) {
+            warning("Execute setxkbmap failed: %s", e.message);
+        }
+
+        if (exit_status != 0) {
+            warning("Execute setxkbmap failed: %s", standard_error ?? "(null)");
+        }
+    }
+
     private void switch_engine(int i, bool force = false) {
         GLib.assert(i >= 0 && i < m_engines.length);
 
@@ -225,14 +373,8 @@ class Panel : IBus.PanelService {
             return;
         }
         // set xkb layout
-        string cmdline = "setxkbmap %s".printf(engine.get_layout());
-        try {
-            if (!GLib.Process.spawn_command_line_sync(cmdline)) {
-                warning("Switch xkb layout to %s failed.",
-                    engine.get_layout());
-            }
-        } catch (GLib.SpawnError e) {
-            warning("Execute setxkbmap failed: %s", e.message);
+        if (!m_use_system_keyboard_layout) {
+            exec_setxkbmap(engine);
         }
     }
 
@@ -245,11 +387,27 @@ 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();
             return;
         }
+
+        if (section == "general" && name == "switcher_delay_time") {
+            set_switcher_delay_time(variant);
+            return;
+        }
+
+        if (section == "general" && name == "use_system_keyboard_layout") {
+            set_use_system_keyboard_layout(variant);
+            return;
+        }
     }
 
     private void handle_engine_switch(Gdk.Event event, bool revert) {
@@ -257,15 +415,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 (pressed) {
+
+        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 {
@@ -278,6 +443,17 @@ class Panel : IBus.PanelService {
         }
     }
 
+    private void run_preload_engines(IBus.EngineDesc[] engines, int index) {
+        string[] names = {};
+
+        if (engines.length <= index) {
+            return;
+        }
+
+        names += engines[index].get_name();
+        m_bus.preload_engines_async(names, -1, null);
+    }
+
     private void update_engines(GLib.Variant? var_engines,
                                 GLib.Variant? var_order) {
         string[] engine_names = null;
@@ -308,6 +484,7 @@ class Panel : IBus.PanelService {
         if (m_engines.length == 0) {
             m_engines = engines;
             switch_engine(0, true);
+            run_preload_engines(engines, 1);
         } else {
             var current_engine = m_engines[0];
             m_engines = engines;
@@ -315,10 +492,16 @@ class Panel : IBus.PanelService {
             for (i = 0; i < m_engines.length; i++) {
                 if (current_engine.get_name() == engines[i].get_name()) {
                     switch_engine(i);
+                    if (i != 0) {
+                        run_preload_engines(engines, 0);
+                    } else {
+                        run_preload_engines(engines, 1);
+                    }
                     return;
                 }
             }
             switch_engine(0, true);
+            run_preload_engines(engines, 1);
         }
 
     }
@@ -425,9 +608,6 @@ class Panel : IBus.PanelService {
 
         m_ime_menu.append(new Gtk.SeparatorMenuItem());
 
-        int width, height;
-        Gtk.icon_size_lookup(Gtk.IconSize.MENU, out width, out height);
-
         // Append IMEs
         foreach (var engine in m_engines) {
             var language = engine.get_language();
@@ -435,7 +615,7 @@ class Panel : IBus.PanelService {
             var item = new Gtk.ImageMenuItem.with_label(
                 "%s - %s".printf (IBus.get_language_name(language), longname));
             if (engine.get_icon() != "") {
-                var icon = new IconWidget(engine.get_icon(), width);
+                var icon = new IconWidget(engine.get_icon(), Gtk.IconSize.MENU);
                  item.set_image(icon);
             }
             // Make a copy of engine to workaround a bug in vala.
@@ -523,8 +703,14 @@ class Panel : IBus.PanelService {
 
         if (icon_name[0] == '/')
             m_status_icon.set_from_file(icon_name);
-        else
-            m_status_icon.set_from_icon_name(icon_name);
+        else {
+            var theme = Gtk.IconTheme.get_default();
+            if (theme.lookup_icon(icon_name, 48, 0) != null) {
+                m_status_icon.set_from_icon_name(icon_name);
+            } else {
+                m_status_icon.set_from_icon_name("ibus-engine");
+            }
+        }
 
         if (engine == null)
             return;