1namespace Caribou { 2 /** 3 * Object representing a whole keyboard. 4 * 5 * This is used for implementing custom keyboard service. 6 * 7 * A keyboard object consists of {@link GroupModel} objects. 8 */ 9 public class KeyboardModel : Object, IKeyboardObject { 10 public string active_group { get; private set; default = ""; } 11 public string keyboard_type { get; construct; } 12 public string keyboard_file { get; construct; } 13 14 private DisplayAdapter xadapter; 15 private Gee.HashMap<string, GroupModel> groups; 16 private KeyModel last_activated_key; 17 private Gee.HashSet<KeyModel> active_mod_keys; 18 19 public signal void group_added (string name); 20 public signal void group_removed (string name); 21 22 construct { 23 xadapter = DisplayAdapter.get_default (); 24 groups = new Gee.HashMap<string, GroupModel> (); 25 active_mod_keys = new Gee.HashSet<KeyModel> (); 26 27 if (keyboard_file != null) { 28 GroupModel grp = 29 XmlDeserializer.load_group_from_file (keyboard_file); 30 if (grp != null) { 31 grp.key_clicked.connect (on_key_clicked); 32 grp.key_pressed.connect (on_key_pressed); 33 grp.key_released.connect (on_key_released); 34 } 35 36 // Use dummy group/variant names. 37 groups.set ("us", grp); 38 group_added ("us"); 39 active_group = GroupModel.create_group_name ("us", ""); 40 } else { 41 assert (keyboard_type != null); 42 43 xadapter.group_changed.connect (on_group_changed); 44 xadapter.config_changed.connect (on_config_changed); 45 46 on_config_changed (); 47 } 48 } 49 50 private void on_config_changed () { 51 string[] grps, variants; 52 xadapter.get_groups (out grps, out variants); 53 54 var group_names = new Gee.HashSet<string> (); 55 for (var i = 0; i < grps.length; i++) { 56 var group_name = GroupModel.create_group_name (grps[i], 57 variants[i]); 58 group_names.add (group_name); 59 if (!groups.has_key (group_name)) { 60 var grp = populate_group (grps[i], variants[i]); 61 if (grp != null) { 62 groups.set (group_name, grp); 63 group_added (group_name); 64 } 65 } 66 } 67 68 var iter = groups.map_iterator (); 69 while (iter.next ()) { 70 var group_name = iter.get_key (); 71 if (!group_names.contains (group_name)) { 72 iter.unset (); 73 group_removed (group_name); 74 } 75 } 76 77 string group, variant; 78 var grpid = xadapter.get_current_group (out group, out variant); 79 on_group_changed (grpid, group, variant); 80 } 81 82 private GroupModel? populate_group (string group, string variant) { 83 GroupModel grp = XmlDeserializer.load_group (keyboard_type, 84 group, variant); 85 if (grp != null) { 86 grp.key_clicked.connect (on_key_clicked); 87 grp.key_pressed.connect (on_key_pressed); 88 grp.key_released.connect (on_key_released); 89 } 90 return grp; 91 } 92 93 private void on_key_clicked (KeyModel key) { 94 if (key.name == "Caribou_Repeat") 95 last_activated_key.activate (); 96 else 97 last_activated_key = key; 98 99 key_clicked (key); 100 } 101 102 private void on_key_pressed (KeyModel key) { 103 if (key.is_modifier && key.modifier_state == ModifierState.LATCHED) { 104 active_mod_keys.add(key); 105 } 106 } 107 108 private void on_key_released (KeyModel key) { 109 if (!key.is_modifier) { 110 KeyModel[] modifiers = (KeyModel[]) active_mod_keys.to_array (); 111 foreach (KeyModel modifier in modifiers) { 112 if (modifier.modifier_state == ModifierState.LATCHED) { 113 modifier.modifier_state = ModifierState.NONE; 114 modifier.release (); 115 } 116 } 117 } 118 } 119 120 public string[] get_groups () { 121 return (string[]) groups.keys.to_array (); 122 } 123 124 public GroupModel get_group (string group_name) { 125 return groups.get (group_name); 126 } 127 128 private void on_group_changed (uint grpid, string group, string variant) { 129 string group_name = GroupModel.create_group_name (group, variant); 130 if (groups.get (group_name) != null) { 131 active_group = group_name; 132 } else { 133 active_group = get_groups ()[0]; 134 } 135 } 136 137 public IKeyboardObject[] get_children () { 138 return (IKeyboardObject[]) groups.values.to_array (); 139 } 140 } 141} 142