3
3
* ibus - The Input Bus
5
* Copyright(c) 2011 Peng Huang <shawn.p.huang@gmail.com>
5
* Copyright(c) 2011-2013 Peng Huang <shawn.p.huang@gmail.com>
7
7
* This library is free software; you can redistribute it and/or
8
8
* modify it under the terms of the GNU Lesser General Public
9
9
* License as published by the Free Software Foundation; either
10
* version 2 of the License, or(at your option) any later version.
10
* version 2.1 of the License, or (at your option) any later version.
12
12
* This library is distributed in the hope that it will be useful,
13
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.
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
17
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
18
* License along with this library; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
23
23
class Panel : IBus.PanelService {
38
38
private IBus.Bus m_bus;
39
private IBus.Config m_config;
39
private GLib.Settings m_settings_general = null;
40
private GLib.Settings m_settings_hotkey = null;
41
private GLib.Settings m_settings_panel = null;
40
42
private Gtk.StatusIcon m_status_icon;
41
43
private Gtk.Menu m_ime_menu;
42
44
private Gtk.Menu m_sys_menu;
43
45
private IBus.EngineDesc[] m_engines = {};
46
private GLib.HashTable<string, IBus.EngineDesc> m_engine_contexts =
47
new GLib.HashTable<string, IBus.EngineDesc>(GLib.str_hash,
49
private string m_current_context_path = "";
50
private bool m_use_global_engine = true;
44
51
private CandidatePanel m_candidate_panel;
45
52
private Switcher m_switcher;
53
private bool m_switcher_is_running = false;
46
54
private PropertyManager m_property_manager;
47
55
private GLib.Pid m_setup_pid = 0;
48
56
private Gtk.AboutDialog m_about_dialog;
79
88
m_candidate_panel = new CandidatePanel();
80
89
m_candidate_panel.page_up.connect((w) => this.page_up());
81
90
m_candidate_panel.page_down.connect((w) => this.page_down());
91
m_candidate_panel.candidate_clicked.connect(
92
(w, i, b, s) => this.candidate_clicked(i, b, s));
83
94
m_switcher = new Switcher();
84
95
// The initial shortcut is "<Super>space"
85
bind_switch_shortcut(null);
96
bind_switch_shortcut();
87
98
if (m_switcher_delay_time >= 0) {
88
99
m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
108
119
m_xkblayout = null;
122
private void init_settings() {
123
m_settings_general = new GLib.Settings("org.freedesktop.ibus.general");
125
new GLib.Settings("org.freedesktop.ibus.general.hotkey");
126
m_settings_panel = new GLib.Settings("org.freedesktop.ibus.panel");
128
m_settings_general.changed["preload-engines"].connect((key) => {
129
update_engines(m_settings_general.get_strv(key),
133
m_settings_general.changed["preload-engine-mode"].connect((key) => {
137
m_settings_general.changed["switcher-delay-time"].connect((key) => {
138
set_switcher_delay_time();
141
m_settings_general.changed["use-system-keyboard-layout"].connect(
143
set_use_system_keyboard_layout();
146
m_settings_general.changed["embed-preedit-text"].connect((key) => {
147
set_embed_preedit_text();
150
m_settings_general.changed["use-global-engine"].connect((key) => {
151
set_use_global_engine();
154
m_settings_hotkey.changed["triggers"].connect((key) => {
155
unbind_switch_shortcut();
156
bind_switch_shortcut();
159
m_settings_panel.changed["custom-font"].connect((key) => {
163
m_settings_panel.changed["use-custom-font"].connect((key) => {
167
m_settings_panel.changed["show-icon-on-systray"].connect((key) => {
168
set_show_icon_on_systray();
171
m_settings_panel.changed["lookup-table-orientation"].connect((key) => {
172
set_lookup_table_orientation();
111
176
private void keybinding_manager_bind(KeybindingManager keybinding_manager,
112
177
string? accelerator) {
113
178
uint switch_keysym = 0;
167
232
(e) => handle_engine_switch(e, true));
170
private void bind_switch_shortcut(Variant? variant) {
171
string[] accelerators = {};
172
Variant var_trigger = variant;
174
if (var_trigger == null && m_config != null) {
175
var_trigger = m_config.get_value("general/hotkey",
179
if (var_trigger != null) {
180
accelerators = var_trigger.dup_strv();
182
accelerators += ACCELERATOR_SWITCH_IME_FOREWARD;
235
private void bind_switch_shortcut() {
236
string[] accelerators = m_settings_hotkey.get_strv("triggers");
185
238
var keybinding_manager = KeybindingManager.get_instance();
218
bool use_custom_font = false;
219
GLib.Variant var_use_custom_font = m_config.get_value("panel",
222
if (var_use_custom_font != null) {
223
use_custom_font = var_use_custom_font.get_boolean();
271
bool use_custom_font = m_settings_panel.get_boolean("use-custom-font");
226
273
if (m_css_provider != null) {
227
274
Gtk.StyleContext.remove_provider_for_screen(screen,
236
string font_name = null;
237
GLib.Variant var_custom_font = m_config.get_value("panel",
239
if (var_custom_font != null) {
240
font_name = var_custom_font.dup_string();
283
string font_name = m_settings_panel.get_string("custom-font");
243
285
if (font_name == null) {
244
warning("No config panel:custom_font.");
286
warning("No config panel:custom-font.");
262
304
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
265
private void set_switcher_delay_time(Variant? variant) {
266
Variant var_switcher_delay_time = variant;
268
if (var_switcher_delay_time == null) {
269
var_switcher_delay_time = m_config.get_value("general",
270
"switcher-delay-time");
273
if (var_switcher_delay_time == null) {
277
m_switcher_delay_time = var_switcher_delay_time.get_int32();
279
if (m_switcher_delay_time >= 0) {
307
private void set_switcher_delay_time() {
308
m_switcher_delay_time =
309
m_settings_general.get_int("switcher-delay-time");
311
if (m_switcher != null && m_switcher_delay_time >= 0) {
280
312
m_switcher.set_popup_delay_time((uint) m_switcher_delay_time);
284
private void set_use_system_keyboard_layout(Variant? variant) {
285
Variant var_use_system_kbd_layout = variant;
287
if (var_use_system_kbd_layout == null) {
288
var_use_system_kbd_layout = m_config.get_value(
290
"use_system_keyboard_layout");
293
if (var_use_system_kbd_layout == null) {
297
m_use_system_keyboard_layout = var_use_system_kbd_layout.get_boolean();
300
private void set_embed_preedit_text(Variant? variant) {
301
Variant var_embed_preedit = variant;
303
if (var_embed_preedit == null) {
304
var_embed_preedit = m_config.get_value("general",
305
"embed_preedit_text");
308
if (var_embed_preedit == null) {
312
m_bus.set_ibus_property("EmbedPreeditText",
316
private void set_use_system_keyboard_layout() {
317
m_use_system_keyboard_layout =
318
m_settings_general.get_boolean("use-system-keyboard-layout");
321
private void set_embed_preedit_text() {
323
m_settings_general.get_value("embed-preedit-text");
325
if (variant == null) {
329
m_bus.set_ibus_property("EmbedPreeditText", variant);
332
private void set_use_global_engine() {
333
m_use_global_engine =
334
m_settings_general.get_boolean("use-global-engine");
337
private void set_show_icon_on_systray() {
338
if (m_status_icon == null)
341
m_status_icon.set_visible(
342
m_settings_panel.get_boolean("show-icon-on-systray"));
345
private void set_lookup_table_orientation() {
346
if (m_candidate_panel == null)
349
m_candidate_panel.set_vertical(
350
m_settings_panel.get_int("lookup-table-orientation")
351
== IBus.Orientation.VERTICAL);
316
354
private int compare_versions(string version1, string version2) {
379
417
private void set_version() {
380
Variant var_prev_version = m_config.get_value("general", "version");
381
Variant var_current_version = null;
382
string prev_version = "".dup();
418
string prev_version = m_settings_general.get_string("version");
383
419
string current_version = null;
385
if (var_prev_version != null) {
386
prev_version = var_prev_version.dup_string();
389
421
if (compare_versions(prev_version, "1.5.3") < 0) {
390
422
update_version_1_5_3();
401
var_current_version = new Variant.string(current_version);
402
m_config.set_value("general", "version", var_current_version);
433
m_settings_general.set_string("version", current_version);
405
public void set_config(IBus.Config config) {
406
if (m_config != null) {
407
m_config.value_changed.disconnect(config_value_changed_cb);
408
m_config.watch(null, null);
413
if (m_config != null) {
414
m_config.value_changed.connect(config_value_changed_cb);
415
m_config.watch("general", "preload_engines");
416
m_config.watch("general", "embed_preedit_text");
417
m_config.watch("general", "preload_engines_inited");
418
m_config.watch("general", "preload_engine_mode");
419
m_config.watch("general", "engines_order");
420
m_config.watch("general", "switcher_delay_time");
421
m_config.watch("general", "use_system_keyboard_layout");
422
m_config.watch("general/hotkey", "triggers");
423
m_config.watch("panel", "custom_font");
424
m_config.watch("panel", "use_custom_font");
425
init_engines_order();
426
// Update m_use_system_keyboard_layout before update_engines()
428
set_use_system_keyboard_layout(null);
429
update_engines(m_config.get_value("general", "preload_engines"),
430
m_config.get_value("general", "engines_order"));
431
unbind_switch_shortcut();
432
bind_switch_shortcut(null);
433
set_switcher_delay_time(null);
434
set_embed_preedit_text(null);
439
update_engines(null, null);
436
public void load_settings() {
437
init_engines_order();
438
// Update m_use_system_keyboard_layout before update_engines()
440
set_use_system_keyboard_layout();
441
set_use_global_engine();
442
update_engines(m_settings_general.get_strv("preload-engines"),
443
m_settings_general.get_strv("engines-order"));
444
unbind_switch_shortcut();
445
bind_switch_shortcut();
446
set_switcher_delay_time();
447
set_embed_preedit_text();
449
set_show_icon_on_systray();
450
set_lookup_table_orientation();
443
455
private void gkbdlayout_changed_cb() {
492
GLib.Variant var_engines =
493
m_config.get_value("general", "preload_engines");
494
string[] preload_engines = {};
496
if (var_engines != null) {
497
preload_engines = var_engines.dup_strv();
500
bool preload_engines_inited = false;
501
GLib.Variant var_preload_engines_inited =
502
m_config.get_value("general", "preload_engines_inited");
504
if (var_preload_engines_inited != null) {
505
preload_engines_inited = var_preload_engines_inited.get_boolean();
500
string[] preload_engines =
501
m_settings_general.get_strv("preload-engines");
503
bool preload_engines_inited =
504
m_settings_general.get_boolean("preload-engines-inited");
508
506
// Set preload_engines_inited = true for back compatibility
509
507
if (preload_engines.length != 0 && !preload_engines_inited) {
510
508
preload_engines_inited = true;
511
m_config.set_value("general",
512
"preload_engines_inited",
513
new GLib.Variant.boolean(true));
509
m_settings_general.set_boolean("preload-engines-inited", true);
516
512
update_xkb_engines();
518
514
// Before update preload_engine_mode, update_xkb_engines() is called
519
// because config_value_changed_cb() calls update_im_engines().
520
if (!preload_engines_inited) {
521
GLib.Variant variant = new GLib.Variant.int32(
522
IBus.PreloadEngineMode.LANG_RELATIVE);
523
m_config.set_value("general",
524
"preload_engine_mode",
515
// because "preload-engine-mode" signal calls update_im_engines().
516
if (!preload_engines_inited)
517
m_settings_general.set_int("preload-engine-mode",
518
IBus.PreloadEngineMode.LANG_RELATIVE);
528
520
update_im_engines();
530
if (!preload_engines_inited) {
531
m_config.set_value("general",
532
"preload_engines_inited",
533
new GLib.Variant.boolean(true));
522
if (!preload_engines_inited)
523
m_settings_general.set_boolean("preload-engines-inited", true);
537
526
private bool set_lang_relative_preload_engines() {
538
527
string locale = Intl.setlocale(LocaleCategory.CTYPE, null);
540
if (locale == null) {
544
532
string lang = locale.split(".")[0];
545
533
GLib.List<IBus.EngineDesc> engines = m_bus.list_engines();
563
549
unowned IBus.EngineDesc engine = p.data;
564
if (engine.get_language() == lang &&
565
engine.get_rank() > 0) {
550
if (engine.get_language() == lang && engine.get_rank() > 0)
566
551
im_engines += engine.get_name();
571
if (im_engines.length == 0) {
555
if (im_engines.length == 0)
575
GLib.Variant var_engines =
576
m_config.get_value("general", "preload_engines");
577
string[] orig_preload_engines = {};
558
string[] orig_preload_engines =
559
m_settings_general.get_strv("preload-engines");
578
560
string[] preload_engines = {};
580
if (var_engines != null) {
581
orig_preload_engines = var_engines.dup_strv();
584
562
// clear input method engines
585
563
foreach (string name in orig_preload_engines) {
586
if (name.ascii_ncasecmp("xkb:", 4) != 0) {
564
if (name.ascii_ncasecmp("xkb:", 4) != 0)
589
567
preload_engines += name;
592
570
foreach (string name in im_engines) {
593
if (!(name in preload_engines)) {
571
if (!(name in preload_engines))
594
572
preload_engines += name;
598
575
if ("".joinv(",", orig_preload_engines) !=
599
"".joinv(",", preload_engines)) {
600
m_config.set_value("general",
602
new GLib.Variant.strv(preload_engines));
576
"".joinv(",", preload_engines))
577
m_settings_general.set_strv("preload-engines", preload_engines);
608
582
private void update_im_engines() {
609
int preload_engine_mode = IBus.PreloadEngineMode.USER;
610
GLib.Variant var_preload_engine_mode =
611
m_config.get_value("general", "preload_engine_mode");
613
if (var_preload_engine_mode != null) {
614
preload_engine_mode = var_preload_engine_mode.get_int32();
617
if (preload_engine_mode == IBus.PreloadEngineMode.USER) {
583
int preload_engine_mode =
584
m_settings_general.get_int("preload-engine-mode");
586
if (preload_engine_mode == IBus.PreloadEngineMode.USER)
621
589
set_lang_relative_preload_engines();
660
628
var_xkb_engine_names += "%s:%s:%s".printf("xkb", name, lang);
663
GLib.Variant var_engines =
664
m_config.get_value("general", "preload_engines");
665
string[] engine_names = {};
631
string[] engine_names =
632
m_settings_general.get_strv("preload-engines");
666
633
bool updated_engine_names = false;
668
if (var_engines != null) {
669
engine_names = var_engines.dup_strv();
672
635
foreach (string name in var_xkb_engine_names) {
673
636
if (name in engine_names)
676
639
engine_names += name;
679
if (updated_engine_names) {
680
m_config.set_value("general",
682
new GLib.Variant.strv(engine_names));
642
if (updated_engine_names)
643
m_settings_general.set_strv("preload-engines", engine_names);
685
GLib.Variant var_order =
686
m_config.get_value("general", "engines_order");
687
string[] order_names = {};
645
string[] order_names =
646
m_settings_general.get_strv("engines-order");
688
647
bool updated_order_names = false;
690
if (var_order != null) {
691
order_names = var_order.dup_strv();
694
649
foreach (var name in var_xkb_engine_names) {
695
650
if (name in order_names)
698
653
updated_order_names = true;
701
if (updated_order_names) {
702
m_config.set_value("general",
704
new GLib.Variant.strv(order_names));
656
if (updated_order_names)
657
m_settings_general.set_strv("engines-order", order_names);
708
660
private void set_xkb_group_layout(IBus.EngineDesc engine) {
767
private void engine_contexts_insert(IBus.EngineDesc engine) {
768
if (m_use_global_engine)
771
if (m_engine_contexts.size() >= 200) {
772
warning ("Contexts by windows are too much counted!");
773
m_engine_contexts.remove_all();
776
m_engine_contexts.replace(m_current_context_path, engine);
779
private void set_engine(IBus.EngineDesc engine) {
780
if (!m_bus.set_global_engine(engine.get_name())) {
781
warning("Switch engine to %s failed.", engine.get_name());
786
if (!m_use_system_keyboard_layout)
789
engine_contexts_insert(engine);
815
792
private void switch_engine(int i, bool force = false) {
816
793
GLib.assert(i >= 0 && i < m_engines.length);
822
799
IBus.EngineDesc engine = m_engines[i];
824
if (!m_bus.set_global_engine(engine.get_name())) {
825
warning("Switch engine to %s failed.", engine.get_name());
829
if (!m_use_system_keyboard_layout) {
834
private void config_value_changed_cb(IBus.Config config,
838
if (section == "general" && name == "preload_engine_mode") {
843
if (section == "general" && name == "preload_engines") {
844
update_engines(variant, null);
848
if (section == "general/hotkey" && name == "triggers") {
849
unbind_switch_shortcut();
850
bind_switch_shortcut(variant);
854
if (section == "panel" && (name == "custom_font" ||
855
name == "use_custom_font")) {
860
if (section == "general" && name == "switcher_delay_time") {
861
set_switcher_delay_time(variant);
865
if (section == "general" && name == "use_system_keyboard_layout") {
866
set_use_system_keyboard_layout(variant);
870
if (section == "general" && name == "embed_preedit_text") {
871
set_embed_preedit_text(variant);
876
804
private void handle_engine_switch(Gdk.Event event, bool revert) {
894
822
if (pressed && m_switcher_delay_time >= 0) {
895
823
int i = revert ? m_engines.length - 1 : 1;
825
/* The flag of m_switcher_is_running avoids the following problem:
827
* When an IME is chosen on m_switcher, focus_in() is called
828
* for the root window. If an engine is set in focus_in()
829
* during running m_switcher when m_use_global_engine is false,
830
* state_changed() is also called and m_engines[] is modified
831
* in state_changed() and m_switcher.run() returns the index
832
* for m_engines[] but m_engines[] was modified by state_changed()
833
* and the index is not correct. */
834
m_switcher_is_running = true;
896
835
i = m_switcher.run(keyval, modifiers, event, m_engines, i);
836
m_switcher_is_running = false;
898
839
debug("switch cancelled");
917
858
m_bus.preload_engines_async(names, -1, null);
920
private void update_engines(GLib.Variant? var_engines,
921
GLib.Variant? var_order) {
922
string[] engine_names = null;
861
private void update_engines(string[]? unowned_engine_names,
862
string[]? order_names) {
863
string[]? engine_names = unowned_engine_names;
924
if (var_engines != null)
925
engine_names = var_engines.dup_strv();
926
865
if (engine_names == null || engine_names.length == 0)
927
866
engine_names = {"xkb:us::eng"};
929
string[] order_names =
930
(var_order != null) ? var_order.dup_strv() : null;
932
868
string[] names = {};
934
870
foreach (var name in order_names) {
1115
1051
public override void focus_in(string input_context_path) {
1052
if (m_use_global_engine)
1055
/* Do not change the order of m_engines during running switcher. */
1056
if (m_switcher_is_running)
1059
m_current_context_path = input_context_path;
1061
var engine = m_engine_contexts[m_current_context_path];
1063
if (engine == null) {
1064
/* If engine == null, need to call set_engine(m_engines[0])
1065
* here and update m_engine_contexts[] to avoid the
1066
* following problem:
1068
* If context1 is focused and does not set an engine and
1069
* return here, the current engine1 is used for context1.
1070
* When context2 is focused and switch engine1 to engine2,
1071
* the current engine becomes engine2.
1072
* And when context1 is focused again, context1 still
1073
* does not set an engine and return here,
1074
* engine2 is used for context2 instead of engine1. */
1075
engine = m_engines.length > 0 ? m_engines[0] : null;
1080
bool in_engines = false;
1082
foreach (var e in m_engines) {
1083
if (engine.get_name() == e.get_name()) {
1089
/* The engine is deleted by ibus-setup before focus_in()
1118
1098
public override void focus_out(string input_context_path) {
1099
if (m_use_global_engine)
1102
/* Do not change the order of m_engines during running switcher. */
1103
if (m_switcher_is_running)
1106
m_current_context_path = "";
1109
public override void destroy_context(string input_context_path) {
1110
if (m_use_global_engine)
1113
m_engine_contexts.remove(input_context_path);
1121
1116
public override void register_properties(IBus.PropList props) {