1 /* vim:set et sts=4 sw=4:
5 * Copyright(c) 2011 Peng Huang <shawn.p.huang@gmail.com>
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.
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.
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
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;
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");
46 m_config = bus.get_config();
47 assert(m_config != null);
49 m_config.value_changed.connect(handle_config_value_changed);
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");
59 m_candidate_panel = new CandidatePanel();
61 m_candidate_panel.hide();
62 m_candidate_panel.show();
64 Variant variant = m_config.get_value("general", "preload_engines");
65 update_engines(variant);
67 m_switcher = new Switcher();
69 KeybindingManager.get_instance().bind("<Control>space", (e) => {
70 handle_engine_switch(e, false);
73 KeybindingManager.get_instance().bind("<Control><Shift>space", (e) => {
74 handle_engine_switch(e, true);
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);
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);
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];
97 m_engines[0] = engine;
99 if (!m_bus.set_global_engine(engine.get_name())) {
100 warning("Switch engine to %s failed.", engine.get_name());
104 string cmdline = "setxkbmap %s".printf(engine.get_layout());
106 if (!GLib.Process.spawn_command_line_sync(cmdline)) {
107 warning("Switch xkb layout to %s failed.",
108 engine.get_layout());
110 } catch (GLib.SpawnError e) {
111 warning("execute setxkblayout failed");
115 private void handle_config_value_changed(IBus.Config config,
119 if (section == "general" && name == "preload_engines") {
120 update_engines(variant);
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;
132 int i = revert ? m_engines.length - 1 : 1;
133 i = m_switcher.run(event, m_engines, i);
135 debug("switch cancelled");
137 assert(i < m_engines.length);
143 private void update_engines(Variant variant) {
144 string[] engine_names = null;
147 engine_names = variant.dup_strv();
148 if (engine_names == null || engine_names.length == 0)
149 engine_names = {"xkb:layout:us"};
151 var engines = m_bus.get_engines_by_names(engine_names);
153 if (m_engines.length == 0) {
155 switch_engine(0, true);
157 var current_engine = m_engines[0];
160 for (i = 0; i < m_engines.length; i++) {
161 if (current_engine.get_name() == engines[i].get_name()) {
166 switch_engine(0, true);
170 private void status_icon_popup_menu(Gtk.StatusIcon status_icon,
172 uint activate_time) {
173 Gtk.Menu menu = m_property_manager.get_menu();
178 menu.set_take_focus(false);
182 m_status_icon.position_menu,
184 Gtk.get_current_event_time());
187 private void status_icon_activate(Gtk.StatusIcon status_icon) {
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);
200 // Make a copy of engine to workaround a bug in vala.
201 // https://bugzilla.gnome.org/show_bug.cgi?id=628336
203 item.activate.connect((item) => {
204 for (int i = 0; i < m_engines.length; i++) {
205 if (e == m_engines[i]) {
211 m_ime_menu.add(item);
213 m_ime_menu.show_all();
214 m_ime_menu.set_take_focus(false);
216 m_ime_menu.popup(null,
218 m_status_icon.position_menu,
220 Gtk.get_current_event_time());
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);
229 public override void focus_in(string input_context_path) {
231 GLib.Cancellable cancellable = null;
233 new IBus.InputContext(input_context_path,
234 m_bus.get_connection(),
236 } catch (GLib.Error e) {
241 public override void focus_out(string input_context_path) {
242 m_input_context = null;
245 public override void register_properties(IBus.PropList props) {
246 m_property_manager.set_properties(props);
249 public override void update_property(IBus.Property prop) {
250 m_property_manager.update_property(prop);
253 public override void update_preedit_text(IBus.Text text,
257 m_candidate_panel.set_preedit_text(text, cursor_pos);
259 m_candidate_panel.set_preedit_text(null, 0);
262 public override void hide_preedit_text() {
263 m_candidate_panel.set_preedit_text(null, 0);
266 public override void update_auxiliary_text(IBus.Text text,
268 m_candidate_panel.set_auxiliary_text(visible ? text : null);
271 public override void hide_auxiliary_text() {
272 m_candidate_panel.set_auxiliary_text(null);
275 public override void update_lookup_table(IBus.LookupTable table,
277 m_candidate_panel.set_lookup_table(visible ? table : null);
280 public override void hide_lookup_table() {
281 m_candidate_panel.set_lookup_table(null);