Handle preload_engines config changes.
[platform/upstream/ibus.git] / ui / gtk3 / panel.vala
1 /* vim:set et sts=4 sw=4:
2  *
3  * ibus - The Input Bus
4  *
5  * Copyright(c) 2011 Peng Huang <shawn.p.huang@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or(at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA  02111-1307  USA
21  */
22
23 using IBus;
24 using GLib;
25 using Gtk;
26
27 class Panel : IBus.PanelService {
28     private IBus.Bus m_bus;
29     private IBus.Config m_config;
30     private Gtk.StatusIcon m_status_icon;
31     private Gtk.Menu m_ime_menu;
32     private IBus.EngineDesc[] m_engines = {};
33     private CandidatePanel m_candidate_panel;
34     private Switcher m_switcher;
35     private PropertyManager m_property_manager;
36     private IBus.InputContext m_input_context;
37
38     public Panel(IBus.Bus bus) {
39         assert(bus.is_connected());
40         // Chain up base class constructor
41         GLib.Object(connection : bus.get_connection(),
42                     object_path : "/org/freedesktop/IBus/Panel");
43
44         m_bus = bus;
45
46         m_config = bus.get_config();
47         assert(m_config != null);
48
49         m_config.value_changed.connect(handle_config_value_changed);
50
51         // init ui
52         m_status_icon = new Gtk.StatusIcon();
53         m_status_icon.set_name("ibus-ui-gtk");
54         m_status_icon.set_title("IBus Panel");
55         m_status_icon.popup_menu.connect(status_icon_popup_menu);
56         m_status_icon.activate.connect(status_icon_activate);
57         m_status_icon.set_from_icon_name("ibus-keyboard");
58
59         m_candidate_panel = new CandidatePanel();
60
61         m_candidate_panel.hide();
62         m_candidate_panel.show();
63
64         Variant variant = m_config.get_value("general", "preload_engines");
65         update_engines(variant);
66
67         m_switcher = new Switcher();
68
69         KeybindingManager.get_instance().bind("<Control>space", (e) => {
70             handle_engine_switch(e, false);
71         });
72
73         KeybindingManager.get_instance().bind("<Control><Shift>space", (e) => {
74             handle_engine_switch(e, true);
75         });
76
77         m_property_manager = new PropertyManager();
78         m_property_manager.property_activate.connect((k, s) => {
79             if (m_input_context != null)
80                 m_input_context.property_activate(k, s);
81         });
82     }
83
84     private void switch_engine(int i, bool force = false) {
85         //  debug("switch_engine i = %d", i);
86         assert(i >= 0 && i < m_engines.length);
87
88         // Do not need siwtch
89         if (i == 0 && !force)
90             return;
91
92         // Move the target engine to the first place.
93         IBus.EngineDesc engine = m_engines[i];
94         for (int j = i; j > 0; j--) {
95             m_engines[j] = m_engines[j - 1];
96         }
97         m_engines[0] = engine;
98
99         if (!m_bus.set_global_engine(engine.get_name())) {
100             warning("Switch engine to %s failed.", engine.get_name());
101             return;
102         }
103         // set xkb layout
104         string cmdline = "setxkbmap %s".printf(engine.get_layout());
105         try {
106             if (!GLib.Process.spawn_command_line_sync(cmdline)) {
107                 warning("Switch xkb layout to %s failed.",
108                     engine.get_layout());
109             }
110         } catch (GLib.SpawnError e) {
111             warning("execute setxkblayout failed");
112         }
113     }
114
115     private void handle_config_value_changed(IBus.Config config,
116                                              string section,
117                                              string name,
118                                              Variant variant) {
119         if (section == "general" && name == "preload_engines") {
120             update_engines(variant);
121         }
122     }
123
124     private void handle_engine_switch(Gdk.Event event, bool revert) {
125         uint primary_modifiers =
126             KeybindingManager.get_primary_modifier(event.key.state);
127         if (!KeybindingManager.primary_modifier_still_pressed(event,
128             primary_modifiers)) {
129             int i = revert ? m_engines.length - 1 : 1;
130             switch_engine(i);
131         } else {
132             int i = revert ? m_engines.length - 1 : 1;
133             i = m_switcher.run(event, m_engines, i);
134             if (i < 0) {
135                 debug("switch cancelled");
136             } else {
137                 assert(i < m_engines.length);
138                 switch_engine(i);
139             }
140         }
141     }
142
143     private void update_engines(Variant variant) {
144         string[] engine_names = null;
145
146         if (variant != null)
147             engine_names = variant.dup_strv();
148         if (engine_names == null || engine_names.length == 0)
149             engine_names = {"xkb:layout:us"};
150
151         var engines = m_bus.get_engines_by_names(engine_names);
152
153         if (m_engines.length == 0) {
154             m_engines = engines;
155             switch_engine(0, true);
156         } else {
157             var current_engine = m_engines[0];
158             m_engines = engines;
159             int i;
160             for (i = 0; i < m_engines.length; i++) {
161                 if (current_engine.get_name() == engines[i].get_name()) {
162                     switch_engine(i);
163                     return;
164                 }
165             }
166             switch_engine(0, true);
167         }
168     }
169
170     private void status_icon_popup_menu(Gtk.StatusIcon status_icon,
171                                         uint button,
172                                         uint activate_time) {
173         Gtk.Menu menu = m_property_manager.get_menu();
174         if (menu == null)
175             return;
176
177         menu.show_all();
178         menu.set_take_focus(false);
179
180         menu.popup(null,
181                    null,
182                    m_status_icon.position_menu,
183                    0,
184                    Gtk.get_current_event_time());
185     }
186
187     private void status_icon_activate(Gtk.StatusIcon status_icon) {
188         int width, height;
189         Gtk.icon_size_lookup(Gtk.IconSize.MENU, out width, out height);
190         if (m_ime_menu == null) {
191             m_ime_menu = new Gtk.Menu();
192             foreach (var engine in m_engines) {
193                 var lang =  engine.get_language();
194                 var name = engine.get_name();
195                 var item = new Gtk.ImageMenuItem.with_label(lang + " - " + name);
196                 if (engine.get_icon() != "") {
197                     var icon = new IconWidget(engine.get_icon(), width);
198                      item.set_image(icon);
199                 }
200                 // Make a copy of engine to workaround a bug in vala.
201                 // https://bugzilla.gnome.org/show_bug.cgi?id=628336
202                 var e = engine;
203                 item.activate.connect((item) => {
204                     for (int i = 0; i < m_engines.length; i++) {
205                         if (e == m_engines[i]) {
206                             switch_engine(i);
207                             break;
208                         }
209                     }
210                 });
211                 m_ime_menu.add(item);
212             }
213             m_ime_menu.show_all();
214             m_ime_menu.set_take_focus(false);
215         }
216         m_ime_menu.popup(null,
217                          null,
218                          m_status_icon.position_menu,
219                          0,
220                          Gtk.get_current_event_time());
221     }
222
223     /* override virtual functions */
224     public override void set_cursor_location(int x, int y,
225                                              int width, int height) {
226         m_candidate_panel.set_cursor_location(x, y, width, height);
227     }
228
229     public override void focus_in(string input_context_path) {
230         try {
231             GLib.Cancellable cancellable = null;
232             m_input_context =
233                 new IBus.InputContext(input_context_path,
234                                       m_bus.get_connection(),
235                                       cancellable);
236         } catch (GLib.Error e) {
237             debug("error");
238         }
239     }
240
241     public override void focus_out(string input_context_path) {
242         m_input_context = null;
243     }
244
245     public override void register_properties(IBus.PropList props) {
246         m_property_manager.set_properties(props);
247     }
248
249     public override void update_property(IBus.Property prop) {
250         m_property_manager.update_property(prop);
251     }
252
253     public override void update_preedit_text(IBus.Text text,
254                                              uint cursor_pos,
255                                              bool visible) {
256         if (visible)
257             m_candidate_panel.set_preedit_text(text, cursor_pos);
258         else
259             m_candidate_panel.set_preedit_text(null, 0);
260     }
261
262     public override void hide_preedit_text() {
263         m_candidate_panel.set_preedit_text(null, 0);
264     }
265
266     public override void update_auxiliary_text(IBus.Text text,
267                                                bool visible) {
268         m_candidate_panel.set_auxiliary_text(visible ? text : null);
269     }
270
271     public override void hide_auxiliary_text() {
272         m_candidate_panel.set_auxiliary_text(null);
273     }
274
275     public override void update_lookup_table(IBus.LookupTable table,
276                                              bool visible) {
277         m_candidate_panel.set_lookup_table(visible ? table : null);
278     }
279
280     public override void hide_lookup_table() {
281         m_candidate_panel.set_lookup_table(null);
282     }
283 }