OLD | NEW |
1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos_language.h" | 5 #include "chromeos_language.h" |
6 | 6 |
7 #include <base/logging.h> | |
8 #include <ibus.h> | 7 #include <ibus.h> |
| 8 #include <dbus/dbus-glib-lowlevel.h> // for dbus_g_connection_get_connection. |
9 | 9 |
10 #include <algorithm> // for std::sort. | 10 #include <algorithm> // for std::sort. |
| 11 #include <sstream> |
| 12 #include <stack> |
| 13 #include <utility> |
11 | 14 |
12 #include "chromeos/dbus/dbus.h" | 15 #include "chromeos/dbus/dbus.h" |
13 #include "chromeos/glib/object.h" | 16 #include "chromeos/glib/object.h" |
14 | 17 |
15 namespace { | 18 namespace { |
16 | 19 |
17 const char kCandidateWindowService[] = "org.freedesktop.IBus.Panel"; | 20 const char kCandidateWindowService[] = "org.freedesktop.IBus.Panel"; |
18 const char kCandidateWindowObjectPath[] = "/org/chromium/Chrome/LanguageBar"; | 21 const char kCandidateWindowObjectPath[] = "/org/chromium/Chrome/LanguageBar"; |
19 const char kCandidateWindowInterface[] = "org.freedesktop.IBus.Panel"; | 22 const char kCandidateWindowInterface[] = "org.freedesktop.IBus.Panel"; |
20 | 23 |
| 24 // The list of IME property keys that we don't handle. |
| 25 const char* kImePropertyKeysBlacklist[] = { |
| 26 "setup", // menu for showing setup dialog used in anthy and hangul. |
| 27 "chewing_settings_prop", // menu for showing setup dialog used in chewing. |
| 28 "status", // used in m17n. |
| 29 }; |
| 30 |
21 // Copies IME names in |engines| to |out|. | 31 // Copies IME names in |engines| to |out|. |
22 void AddIMELanguages(const GList* engines, chromeos::InputLanguageList* out) { | 32 void AddIMELanguages(const GList* engines, chromeos::InputLanguageList* out) { |
23 DCHECK(out); | 33 DCHECK(out); |
24 for (; engines; engines = g_list_next(engines)) { | 34 for (; engines; engines = g_list_next(engines)) { |
25 IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(engines->data); | 35 IBusEngineDesc* engine_desc = IBUS_ENGINE_DESC(engines->data); |
26 out->push_back(chromeos::InputLanguage( | 36 out->push_back(chromeos::InputLanguage( |
27 chromeos::LANGUAGE_CATEGORY_IME, | 37 chromeos::LANGUAGE_CATEGORY_IME, |
28 engine_desc->name, engine_desc->longname, engine_desc->icon)); | 38 engine_desc->name, engine_desc->longname, engine_desc->icon)); |
29 g_object_unref(engine_desc); | 39 g_object_unref(engine_desc); |
30 } | 40 } |
31 } | 41 } |
32 | 42 |
33 // Copies XKB layout names in (TBD) to |out|. | 43 // Copies XKB layout names in (TBD) to |out|. |
34 void AddXKBLayouts(chromeos::InputLanguageList* out) { | 44 void AddXKBLayouts(chromeos::InputLanguageList* out) { |
35 DCHECK(out); | 45 DCHECK(out); |
36 // TODO(yusukes): implement this. | 46 // TODO(yusukes): implement this. |
37 out->push_back(chromeos::InputLanguage( | 47 out->push_back(chromeos::InputLanguage( |
38 chromeos::LANGUAGE_CATEGORY_XKB, | 48 chromeos::LANGUAGE_CATEGORY_XKB, |
39 kFallbackXKBId, | 49 kFallbackXKBId, |
40 kFallbackXKBDisplayName, | 50 kFallbackXKBDisplayName, |
41 "" /* no icon */)); // mock | 51 "" /* no icon */)); // mock |
42 } | 52 } |
43 | 53 |
| 54 // Returns IBusInputContext for |input_context_path|. NULL on errors. |
| 55 IBusInputContext* GetInputContext( |
| 56 const std::string& input_context_path, IBusBus* ibus) { |
| 57 IBusInputContext* context = ibus_input_context_get_input_context( |
| 58 input_context_path.c_str(), ibus_bus_get_connection(ibus)); |
| 59 if (!context) { |
| 60 LOG(ERROR) << "IBusInputContext is null: " << input_context_path; |
| 61 } |
| 62 return context; |
| 63 } |
| 64 |
| 65 // Returns true if |key| is blacklisted. |
| 66 bool KeyIsBlacklisted(const char* key) { |
| 67 for (size_t i = 0; i < arraysize(kImePropertyKeysBlacklist); ++i) { |
| 68 if (!std::strcmp(key, kImePropertyKeysBlacklist[i])) { |
| 69 return true; |
| 70 } |
| 71 } |
| 72 return false; |
| 73 } |
| 74 |
| 75 // Returns true if |prop| has children. |
| 76 bool PropertyHasChildren(IBusProperty* prop) { |
| 77 return prop && prop->sub_props && ibus_prop_list_get(prop->sub_props, 0); |
| 78 } |
| 79 |
| 80 // This function is called by and FlattenProperty() and converts IBus |
| 81 // representation of a property, |ibus_prop|, to our own and push_back the |
| 82 // result to |out_prop_list|. This function returns true on success, and |
| 83 // returns false if sanity checks for |ibus_prop| fail. |
| 84 bool ConvertProperty(IBusProperty* ibus_prop, |
| 85 int selection_item_id, |
| 86 chromeos::ImePropertyList* out_prop_list) { |
| 87 DCHECK(ibus_prop); |
| 88 DCHECK(ibus_prop->key); |
| 89 DCHECK(out_prop_list); |
| 90 |
| 91 // Sanity checks. |
| 92 const bool has_sub_props = PropertyHasChildren(ibus_prop); |
| 93 if (has_sub_props && (ibus_prop->type != PROP_TYPE_MENU)) { |
| 94 LOG(ERROR) << "The property has sub properties, " |
| 95 << "but the type of the property is not PROP_TYPE_MENU"; |
| 96 return false; |
| 97 } |
| 98 if ((!has_sub_props) && (ibus_prop->type == PROP_TYPE_MENU)) { |
| 99 LOG(ERROR) << "The property does not have sub properties, " |
| 100 << "but the type of the property is PROP_TYPE_MENU"; |
| 101 return false; |
| 102 } |
| 103 if (ibus_prop->type == PROP_TYPE_SEPARATOR || |
| 104 ibus_prop->type == PROP_TYPE_MENU) { |
| 105 // This is not an error, but we don't push an item for these types. |
| 106 return true; |
| 107 } |
| 108 |
| 109 const bool is_selection_item = (ibus_prop->type == PROP_TYPE_RADIO); |
| 110 selection_item_id |
| 111 = is_selection_item ? selection_item_id : kInvalidSelectionItemId; |
| 112 |
| 113 bool is_selection_item_checked = false; |
| 114 if (ibus_prop->state == PROP_STATE_INCONSISTENT) { |
| 115 LOG(WARNING) << "The property is in PROP_STATE_INCONSISTENT, " |
| 116 << "which is not supported."; |
| 117 } else if ((!is_selection_item) && (ibus_prop->state == PROP_STATE_CHECKED)) { |
| 118 LOG(WARNING) << "PROP_STATE_CHECKED is meaningful only if the type is " |
| 119 << "PROP_TYPE_RADIO."; |
| 120 } else { |
| 121 is_selection_item_checked = (ibus_prop->state == PROP_STATE_CHECKED); |
| 122 } |
| 123 |
| 124 // TODO(yusukes): Probably it's better to generate our own label from the key? |
| 125 std::string label = (ibus_prop->tooltip ? ibus_prop->tooltip->text : ""); |
| 126 if (label.empty()) { |
| 127 // Usually tooltips are more descriptive than labels. |
| 128 label = (ibus_prop->label ? ibus_prop->label->text : ""); |
| 129 } |
| 130 if (label.empty()) { |
| 131 // ibus-pinyin has a property whose label and tooltip are empty. Fall back |
| 132 // to the key. |
| 133 label = ibus_prop->key; |
| 134 } |
| 135 |
| 136 out_prop_list->push_back(chromeos::ImeProperty(ibus_prop->key, |
| 137 ibus_prop->icon, |
| 138 label, |
| 139 is_selection_item, |
| 140 is_selection_item_checked, |
| 141 selection_item_id)); |
| 142 return true; |
| 143 } |
| 144 |
| 145 // Converts |ibus_prop| to |out_prop_list|. Please note that |ibus_prop| |
| 146 // may or may not have children. See the comment for FlattenPropertyList |
| 147 // for details. Returns true if no error is found. |
| 148 bool FlattenProperty( |
| 149 IBusProperty* ibus_prop, chromeos::ImePropertyList* out_prop_list) { |
| 150 // TODO(yusukes): Find a good way to write unit tests for this function. |
| 151 DCHECK(ibus_prop); |
| 152 DCHECK(out_prop_list); |
| 153 |
| 154 int selection_item_id = -1; |
| 155 std::stack<std::pair<IBusProperty*, int> > prop_stack; |
| 156 prop_stack.push(std::make_pair(ibus_prop, selection_item_id)); |
| 157 |
| 158 while (!prop_stack.empty()) { |
| 159 IBusProperty* prop = prop_stack.top().first; |
| 160 const int current_selection_item_id = prop_stack.top().second; |
| 161 prop_stack.pop(); |
| 162 |
| 163 // Filter out unnecessary properties. |
| 164 if (KeyIsBlacklisted(prop->key)) { |
| 165 continue; |
| 166 } |
| 167 |
| 168 // Convert |prop| to ImeProperty and push it to |out_prop_list|. |
| 169 if (!ConvertProperty(prop, current_selection_item_id, out_prop_list)) { |
| 170 return false; |
| 171 } |
| 172 |
| 173 // Process childrens iteratively (if any). Push all sub properties to the |
| 174 // stack. |
| 175 if (PropertyHasChildren(prop)) { |
| 176 ++selection_item_id; |
| 177 for (int i = 0;; ++i) { |
| 178 IBusProperty* sub_prop = ibus_prop_list_get(prop->sub_props, i); |
| 179 if (!sub_prop) { |
| 180 break; |
| 181 } |
| 182 prop_stack.push(std::make_pair(sub_prop, selection_item_id)); |
| 183 } |
| 184 ++selection_item_id; |
| 185 } |
| 186 } |
| 187 std::reverse(out_prop_list->begin(), out_prop_list->end()); |
| 188 |
| 189 return true; |
| 190 } |
| 191 |
| 192 // Converts IBus representation of a property list, |ibus_prop_list| to our |
| 193 // own. This function also flatten the original list (actually it's a tree). |
| 194 // Returns true if no error is found. The conversion to our own type is |
| 195 // necessary since our language switcher in Chrome tree don't (or can't) know |
| 196 // IBus types. Here is an example: |
| 197 // |
| 198 // ====================================================================== |
| 199 // Input: |
| 200 // |
| 201 // --- Item-1 |
| 202 // |- Item-2 |
| 203 // |- SubMenuRoot --- Item-3-1 |
| 204 // | |- Item-3-2 |
| 205 // | |- Item-3-3 |
| 206 // |- Item-4 |
| 207 // |
| 208 // (Note: Item-3-X is a selection item since they're on a sub menu.) |
| 209 // |
| 210 // Output: |
| 211 // |
| 212 // Item-1, Item-2, Item-3-1, Item-3-2, Item-3-3, Item-4 |
| 213 // (Note: SubMenuRoot does not appear in the output.) |
| 214 // ====================================================================== |
| 215 bool FlattenPropertyList( |
| 216 IBusPropList* ibus_prop_list, chromeos::ImePropertyList* out_prop_list) { |
| 217 // TODO(yusukes): Find a good way to write unit tests for this function. |
| 218 DCHECK(ibus_prop_list); |
| 219 DCHECK(out_prop_list); |
| 220 |
| 221 IBusProperty* fake_root_prop = ibus_property_new("Dummy.Key", |
| 222 PROP_TYPE_MENU, |
| 223 NULL, /* label */ |
| 224 "", /* icon */ |
| 225 NULL, /* tooltip */ |
| 226 FALSE, /* sensitive */ |
| 227 FALSE, /* visible */ |
| 228 PROP_STATE_UNCHECKED, |
| 229 ibus_prop_list); |
| 230 |
| 231 const bool result |
| 232 = fake_root_prop && FlattenProperty(fake_root_prop, out_prop_list); |
| 233 g_object_unref(fake_root_prop); |
| 234 |
| 235 return result; |
| 236 } |
| 237 |
| 238 // Debug print functions. |
| 239 const char* PropTypeToString(int prop_type) { |
| 240 switch (static_cast<IBusPropType>(prop_type)) { |
| 241 case PROP_TYPE_NORMAL: |
| 242 return "NORMAL"; |
| 243 case PROP_TYPE_TOGGLE: |
| 244 return "TOGGLE"; |
| 245 case PROP_TYPE_RADIO: |
| 246 return "RADIO"; |
| 247 case PROP_TYPE_MENU: |
| 248 return "MENU"; |
| 249 case PROP_TYPE_SEPARATOR: |
| 250 return "SEPARATOR"; |
| 251 } |
| 252 return "UNKNOWN"; |
| 253 } |
| 254 |
| 255 const char* PropStateToString(int prop_state) { |
| 256 switch (static_cast<IBusPropState>(prop_state)) { |
| 257 case PROP_STATE_UNCHECKED: |
| 258 return "UNCHECKED"; |
| 259 case PROP_STATE_CHECKED: |
| 260 return "CHECKED"; |
| 261 case PROP_STATE_INCONSISTENT: |
| 262 return "INCONSISTENT"; |
| 263 } |
| 264 return "UNKNOWN"; |
| 265 } |
| 266 |
| 267 std::string Spacer(int n) { |
| 268 return std::string(n, ' '); |
| 269 } |
| 270 |
| 271 std::string PrintPropList(IBusPropList *prop_list, int tree_level); |
| 272 std::string PrintProp(IBusProperty *prop, int tree_level) { |
| 273 if (!prop) { |
| 274 return ""; |
| 275 } |
| 276 |
| 277 std::stringstream stream; |
| 278 stream << Spacer(tree_level) << "=========================" << std::endl; |
| 279 stream << Spacer(tree_level) << "key: " << (prop->key ? prop->key : "<none>") |
| 280 << std::endl; |
| 281 stream << Spacer(tree_level) << "icon: " |
| 282 << (prop->icon ? prop->icon : "<none>") << std::endl; |
| 283 stream << Spacer(tree_level) << "label: " |
| 284 << (prop->label ? prop->label->text : "<none>") << std::endl; |
| 285 stream << Spacer(tree_level) << "tooptip: " |
| 286 << (prop->tooltip ? prop->tooltip->text : "<none>") << std::endl; |
| 287 stream << Spacer(tree_level) << "sensitive: " |
| 288 << (prop->sensitive ? "YES" : "NO") << std::endl; |
| 289 stream << Spacer(tree_level) << "visible: " << (prop->visible ? "YES" : "NO") |
| 290 << std::endl; |
| 291 stream << Spacer(tree_level) << "type: " << PropTypeToString(prop->type) |
| 292 << std::endl; |
| 293 stream << Spacer(tree_level) << "state: " << PropStateToString(prop->state) |
| 294 << std::endl; |
| 295 stream << Spacer(tree_level) << "sub_props: " |
| 296 << (PropertyHasChildren(prop) ? "" : "<none>") << std::endl; |
| 297 stream << PrintPropList(prop->sub_props, tree_level + 1); |
| 298 stream << Spacer(tree_level) << "=========================" << std::endl; |
| 299 |
| 300 return stream.str(); |
| 301 } |
| 302 |
| 303 std::string PrintPropList(IBusPropList *prop_list, int tree_level) { |
| 304 if (!prop_list) { |
| 305 return ""; |
| 306 } |
| 307 |
| 308 std::stringstream stream; |
| 309 for (int i = 0;; ++i) { |
| 310 IBusProperty* prop = ibus_prop_list_get(prop_list, i); |
| 311 if (!prop) { |
| 312 break; |
| 313 } |
| 314 stream << PrintProp(prop, tree_level); |
| 315 } |
| 316 return stream.str(); |
| 317 } |
| 318 |
44 } // namespace | 319 } // namespace |
45 | 320 |
46 namespace chromeos { | 321 namespace chromeos { |
47 | 322 |
48 // A class that holds IBus and DBus connections. | 323 // A class that holds IBus and DBus connections. |
49 class LanguageStatusConnection { | 324 class LanguageStatusConnection { |
50 public: | 325 public: |
51 LanguageStatusConnection(LanguageStatusMonitorFunction monitor_function, | 326 LanguageStatusConnection(LanguageStatusMonitorFunctions monitor_functions, |
52 void* language_library) | 327 void* language_library) |
53 : monitor_function_(monitor_function), | 328 : monitor_functions_(monitor_functions), |
54 language_library_(language_library), | 329 language_library_(language_library), |
55 ibus_(NULL), | 330 ibus_(NULL), |
56 ibus_config_(NULL), | 331 ibus_config_(NULL), |
57 dbus_focus_in_(NULL), | |
58 dbus_focus_out_(NULL), | |
59 dbus_state_changed_(NULL), | |
60 input_context_path_("") { | 332 input_context_path_("") { |
61 DCHECK(monitor_function_); | 333 DCHECK(monitor_functions_.current_language); |
| 334 DCHECK(monitor_functions_.register_ime_properties); |
| 335 DCHECK(monitor_functions_.update_ime_property); |
62 DCHECK(language_library_); | 336 DCHECK(language_library_); |
63 } | 337 } |
64 | 338 |
65 ~LanguageStatusConnection() { | 339 ~LanguageStatusConnection() { |
66 // Close IBus and DBus connections. | 340 // Close IBus and DBus connections. |
67 if (ibus_config_) { | 341 if (ibus_config_) { |
68 g_object_unref(ibus_config_); | 342 g_object_unref(ibus_config_); |
69 } | 343 } |
70 if (ibus_) { | 344 if (ibus_) { |
71 g_object_unref(ibus_); | 345 g_object_unref(ibus_); |
72 } | 346 } |
73 if (dbus_focus_in_) { | |
74 dbus::Disconnect(dbus_focus_in_); | |
75 } | |
76 if (dbus_focus_out_) { | |
77 dbus::Disconnect(dbus_focus_out_); | |
78 } | |
79 if (dbus_state_changed_) { | |
80 dbus::Disconnect(dbus_state_changed_); | |
81 } | |
82 } | 347 } |
83 | 348 |
84 // Initializes IBus and DBus connections. | 349 // Initializes IBus and DBus connections. |
85 bool Init() { | 350 bool Init() { |
86 // Establish IBus connection between ibus-daemon to retrieve the list of | 351 // Establish IBus connection between ibus-daemon to retrieve the list of |
87 // available IME engines, change the current IME engine, and so on. | 352 // available IME engines, change the current IME engine, and so on. |
88 ibus_init(); | 353 ibus_init(); |
89 ibus_ = ibus_bus_new(); | 354 ibus_ = ibus_bus_new(); |
90 | 355 |
91 // Check the IBus connection status. | 356 // Check the IBus connection status. |
(...skipping 13 matching lines...) Expand all Loading... |
105 return false; | 370 return false; |
106 } | 371 } |
107 | 372 |
108 // Create the IBus config. | 373 // Create the IBus config. |
109 ibus_config_ = ibus_config_new(ibus_connection); | 374 ibus_config_ = ibus_config_new(ibus_connection); |
110 if (!ibus_config_) { | 375 if (!ibus_config_) { |
111 LOG(ERROR) << "ibus_bus_config_new() failed"; | 376 LOG(ERROR) << "ibus_bus_config_new() failed"; |
112 return false; | 377 return false; |
113 } | 378 } |
114 | 379 |
| 380 // Establish a DBus connection between the candidate_window process for |
| 381 // Chromium OS to handle signals (e.g. "FocusIn") from the process. |
| 382 const char* address = ibus_get_address(); |
| 383 dbus_connection_.reset( |
| 384 new dbus::BusConnection(dbus::GetPrivateBusConnection(address))); |
| 385 LOG(INFO) << "Established private D-Bus connection to: '" << address << "'"; |
115 | 386 |
116 // Establish a DBus connection between the candidate_window process for | 387 // Connect to the candidate_window. Note that dbus_connection_add_filter() |
117 // Chromium OS to handle "FocusIn", "FocusOut", and "StateChanged" signals | 388 // does not work without calling dbus::Proxy(). |
118 // from the process. | 389 // TODO(yusukes): Investigate how we can eliminate the call. |
119 const char* address = ibus_get_address(); | |
120 dbus::BusConnection dbus(dbus::GetPrivateBusConnection(address)); | |
121 LOG(INFO) << "Established private D-Bus connection to: '" << address << "'"; | |
122 | 390 |
123 const bool kConnectToNameOwner = true; | 391 const bool kConnectToNameOwner = true; |
124 // TODO(yusukes): dbus::Proxy instantiation might fail (and abort due to | 392 // TODO(yusukes): dbus::Proxy instantiation might fail (and abort due to |
125 // DCHECK failure) when candidate_window process does not exist yet. | 393 // DCHECK failure) when candidate_window process does not exist yet. |
126 // Would be better to add "bool dbus::Proxy::Init()" or something like that | 394 // Would be better to add "bool dbus::Proxy::Init()" or something like that |
127 // to handle such case? | 395 // to handle such case? |
128 dbus::Proxy candidate_window(dbus, | 396 dbus_proxy_.reset(new dbus::Proxy(*dbus_connection_, |
129 kCandidateWindowService, | 397 kCandidateWindowService, |
130 kCandidateWindowObjectPath, | 398 kCandidateWindowObjectPath, |
131 kCandidateWindowInterface, | 399 kCandidateWindowInterface, |
132 kConnectToNameOwner); | 400 kConnectToNameOwner)); |
133 | 401 |
134 if (!candidate_window) { | 402 // Register DBus signal handler. |
135 LOG(ERROR) << "Can't construct proxy for the candidate window. " | 403 dbus_connection_add_filter( |
136 << "candidate window is not running?"; | 404 dbus_g_connection_get_connection(dbus_connection_->g_connection()), |
137 return false; | 405 &LanguageStatusConnection::DispatchSignalFromCandidateWindow, |
138 } | 406 this, NULL); |
139 | |
140 dbus_focus_in_ = dbus::Monitor(candidate_window, | |
141 "FocusIn", | |
142 &LanguageStatusConnection::FocusIn, | |
143 this); | |
144 dbus_focus_out_ = dbus::Monitor(candidate_window, | |
145 "FocusOut", | |
146 &LanguageStatusConnection::FocusOut, | |
147 this); | |
148 dbus_state_changed_ = dbus::Monitor(candidate_window, | |
149 "StateChanged", | |
150 &LanguageStatusConnection::StateChanged, | |
151 this); | |
152 | 407 |
153 // TODO(yusukes): Investigate what happens if IBus/DBus connections are | 408 // TODO(yusukes): Investigate what happens if IBus/DBus connections are |
154 // suddenly closed. | 409 // suddenly closed. |
155 // TODO(yusukes): Investigate what happens if candidate_window process is | 410 // TODO(yusukes): Investigate what happens if candidate_window process is |
156 // restarted. I'm not sure but we should use dbus_g_proxy_new_for_name(), | 411 // restarted. I'm not sure but we should use dbus_g_proxy_new_for_name(), |
157 // not dbus_g_proxy_new_for_name_owner()? | 412 // not dbus_g_proxy_new_for_name_owner()? |
158 | 413 |
159 return true; | 414 return true; |
160 } | 415 } |
161 | 416 |
(...skipping 22 matching lines...) Expand all Loading... |
184 } | 439 } |
185 InputLanguageList* language_list = new InputLanguageList; | 440 InputLanguageList* language_list = new InputLanguageList; |
186 AddIMELanguages(engines, language_list); | 441 AddIMELanguages(engines, language_list); |
187 AddXKBLayouts(language_list); | 442 AddXKBLayouts(language_list); |
188 std::sort(language_list->begin(), language_list->end()); | 443 std::sort(language_list->begin(), language_list->end()); |
189 | 444 |
190 g_list_free(engines); | 445 g_list_free(engines); |
191 return language_list; | 446 return language_list; |
192 } | 447 } |
193 | 448 |
194 // Called by cros API ChromeOSChangeLanguage(). | 449 // Called by cros API ChromeOS(Activate|Deactive)ImeProperty(). |
195 void SwitchXKB(const char* name) { | 450 void ActivateOrDeactiveImeProperty(const char* key, bool active) { |
196 // TODO(yusukes): implement XKB switching. | |
197 | |
198 if (input_context_path_.empty()) { | 451 if (input_context_path_.empty()) { |
199 LOG(ERROR) << "Input context is unknown"; | 452 LOG(ERROR) << "Input context is unknown"; |
200 return; | 453 return; |
201 } | 454 } |
202 | 455 |
203 IBusInputContext* context | 456 IBusInputContext* context = GetInputContext(input_context_path_, ibus_); |
204 = ibus_input_context_get_input_context(input_context_path_.c_str(), | 457 if (!context) { |
205 ibus_bus_get_connection(ibus_)); | 458 return; |
206 ibus_input_context_disable(context); | 459 } |
| 460 ibus_input_context_property_activate( |
| 461 context, key, (active ? PROP_STATE_CHECKED : PROP_STATE_UNCHECKED)); |
207 g_object_unref(context); | 462 g_object_unref(context); |
| 463 |
208 UpdateUI(); | 464 UpdateUI(); |
209 } | 465 } |
210 | 466 |
211 // Called by cros API ChromeOSChangeLanguage() as well. | 467 // Called by cros API ChromeOSChangeLanguage(). |
212 void SwitchIME(const char* name) { | 468 void ChangeLanguage(LanguageCategory category, const char* name) { |
213 if (input_context_path_.empty()) { | 469 // Clear all IME properties unconditionally. |
214 LOG(ERROR) << "Input context is unknown"; | 470 // - When switching to XKB, it's necessary since XKB layout does not have |
215 return; | 471 // IME properties. |
| 472 // - When switching to IME and a text area is focused, it's okay to clear |
| 473 // IME properties here since RegisterProperties signal for the new IME |
| 474 // will be sent anyway. |
| 475 // - When switching to IME and no text area is focused, RegisterProperties |
| 476 // signal for the new IME will NOT be sent until a text area is focused. |
| 477 // Therefore, we have to clear the old IME properties here to keep the |
| 478 // IME switcher status consistent. |
| 479 RegisterProperties(NULL); |
| 480 |
| 481 switch (category) { |
| 482 case LANGUAGE_CATEGORY_XKB: |
| 483 SwitchToXKB(name); |
| 484 break; |
| 485 case LANGUAGE_CATEGORY_IME: |
| 486 SwitchToIME(name); |
| 487 break; |
216 } | 488 } |
217 | |
218 IBusInputContext* context | |
219 = ibus_input_context_get_input_context(input_context_path_.c_str(), | |
220 ibus_bus_get_connection(ibus_)); | |
221 ibus_input_context_set_engine(context, name); | |
222 g_object_unref(context); | |
223 UpdateUI(); | |
224 } | 489 } |
225 | 490 |
226 // UpdateMode is used for specifying whether we are activating or | 491 // UpdateMode is used for specifying whether we are activating or |
227 // deactivating an input language. We use this mode to consolidate logic | 492 // deactivating an input language. We use this mode to consolidate logic |
228 // for activation and deactivation. | 493 // for activation and deactivation. |
229 enum UpdateMode { | 494 enum UpdateMode { |
230 kActivate, // Activate an input language. | 495 kActivate, // Activate an input language. |
231 kDeactivate, // Deactivate an input language. | 496 kDeactivate, // Deactivate an input language. |
232 }; | 497 }; |
233 | 498 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 "general", | 545 "general", |
281 "preload_engines", | 546 "preload_engines", |
282 &value); | 547 &value); |
283 g_value_unset(&value); | 548 g_value_unset(&value); |
284 g_list_free(engines); | 549 g_list_free(engines); |
285 | 550 |
286 return success; | 551 return success; |
287 } | 552 } |
288 | 553 |
289 InputLanguage* GetCurrentLanguage() { | 554 InputLanguage* GetCurrentLanguage() { |
290 IBusInputContext* context | 555 IBusInputContext* context = GetInputContext(input_context_path_, ibus_); |
291 = ibus_input_context_get_input_context( | 556 if (!context) { |
292 input_context_path_.c_str(), ibus_bus_get_connection(ibus_)); | 557 return NULL; |
| 558 } |
293 | 559 |
294 const bool ime_is_enabled = ibus_input_context_is_enabled(context); | 560 const bool ime_is_enabled = ibus_input_context_is_enabled(context); |
295 g_object_unref(context); | 561 g_object_unref(context); |
296 | 562 |
297 InputLanguage* current_language = NULL; | 563 InputLanguage* current_language = NULL; |
298 if (ime_is_enabled) { | 564 if (ime_is_enabled) { |
299 DLOG(INFO) << "IME is active"; | 565 DLOG(INFO) << "IME is active"; |
300 // Set IME name on current_language. | 566 // Set IME name on current_language. |
301 const IBusEngineDesc* engine_desc | 567 const IBusEngineDesc* engine_desc |
302 = ibus_input_context_get_engine(context); | 568 = ibus_input_context_get_engine(context); |
(...skipping 11 matching lines...) Expand all Loading... |
314 current_language = new InputLanguage(LANGUAGE_CATEGORY_XKB, | 580 current_language = new InputLanguage(LANGUAGE_CATEGORY_XKB, |
315 kFallbackXKBId, | 581 kFallbackXKBId, |
316 kFallbackXKBDisplayName, | 582 kFallbackXKBDisplayName, |
317 "" /* no icon */); // mock | 583 "" /* no icon */); // mock |
318 // TODO(yusukes): implemente this. | 584 // TODO(yusukes): implemente this. |
319 } | 585 } |
320 return current_language; | 586 return current_language; |
321 } | 587 } |
322 | 588 |
323 private: | 589 private: |
324 // Handles "FocusIn" signal from candidate_window. | 590 // Changes the current language to |name|, which is XKB layout. |
325 static void FocusIn(void* object, const char* input_context_path) { | 591 void SwitchToXKB(const char* name) { |
| 592 // TODO(yusukes): implement XKB switching. |
| 593 if (input_context_path_.empty()) { |
| 594 LOG(ERROR) << "Input context is unknown"; |
| 595 return; |
| 596 } |
| 597 |
| 598 IBusInputContext* context = GetInputContext(input_context_path_, ibus_); |
| 599 if (!context) { |
| 600 return; |
| 601 } |
| 602 ibus_input_context_disable(context); |
| 603 g_object_unref(context); |
| 604 UpdateUI(); |
| 605 } |
| 606 |
| 607 // Changes the current language to |name|, which is IME. |
| 608 void SwitchToIME(const char* name) { |
| 609 if (input_context_path_.empty()) { |
| 610 LOG(ERROR) << "Input context is unknown"; |
| 611 return; |
| 612 } |
| 613 |
| 614 IBusInputContext* context = GetInputContext(input_context_path_, ibus_); |
| 615 if (!context) { |
| 616 return; |
| 617 } |
| 618 ibus_input_context_set_engine(context, name); |
| 619 g_object_unref(context); |
| 620 UpdateUI(); |
| 621 } |
| 622 |
| 623 // Handles "FocusIn" signal from the candidate_window process. |
| 624 void FocusIn(const char* input_context_path) { |
326 DCHECK(input_context_path) << "NULL context passed"; | 625 DCHECK(input_context_path) << "NULL context passed"; |
327 DLOG(INFO) << "FocusIn: " << input_context_path; | 626 DLOG(INFO) << "FocusIn: " << input_context_path; |
328 DCHECK(object); | |
329 | 627 |
330 // Remember the current ic path. | 628 // Remember the current ic path. |
331 LanguageStatusConnection* self | 629 input_context_path_ = input_context_path; |
332 = static_cast<LanguageStatusConnection*>(object); | 630 UpdateUI(); // This is necessary since IME status is held per ic. |
333 self->input_context_path_ = input_context_path; | |
334 self->UpdateUI(); // This is necessary since IME status is held per ic. | |
335 } | 631 } |
336 | 632 |
337 // Handles "FocusOut" signal from candidate_window. | 633 // Handles "FocusOut" signal from the candidate_window process. |
338 static void FocusOut(void* object, const char* input_context_path) { | 634 void FocusOut(const char* input_context_path) { |
339 DCHECK(input_context_path) << "NULL context passed"; | 635 DCHECK(input_context_path) << "NULL context passed"; |
340 DLOG(INFO) << "FocusOut: " << input_context_path; | 636 DLOG(INFO) << "FocusOut: " << input_context_path; |
341 DCHECK(object); | |
342 } | 637 } |
343 | 638 |
344 // Handles "StateChanged" signal from candidate_window process. | 639 // Handles "StateChanged" signal from the candidate_window process. |
345 static void StateChanged(void* object, const char* dummy) { | 640 void StateChanged() { |
346 // TODO(yusukes): Modify common/chromeos/dbus/dbus.h so that we can handle | 641 // TODO(yusukes): Modify common/chromeos/dbus/dbus.h so that we can handle |
347 // signals without argument. Then remove the |dummy|. | 642 // signals without argument. Then remove the |dummy|. |
348 DLOG(INFO) << "StateChanged"; | 643 DLOG(INFO) << "StateChanged"; |
349 DCHECK(object); | 644 UpdateUI(); |
350 LanguageStatusConnection* self | 645 } |
351 = static_cast<LanguageStatusConnection*>(object); | 646 |
352 self->UpdateUI(); | 647 // Handles "RegisterProperties" signal from the candidate_window process. |
| 648 void RegisterProperties(IBusPropList* ibus_prop_list) { |
| 649 DLOG(INFO) << "RegisterProperties" << (ibus_prop_list ? "" : " (clear)"); |
| 650 |
| 651 ImePropertyList prop_list; // our representation. |
| 652 if (ibus_prop_list) { |
| 653 // You can call |
| 654 // LOG(INFO) << "\n" << PrintPropList(ibus_prop_list, 0); |
| 655 // here to dump |ibus_prop_list|. |
| 656 if (!FlattenPropertyList(ibus_prop_list, &prop_list)) { |
| 657 LOG(WARNING) << "Malformed properties are detected"; |
| 658 } |
| 659 } |
| 660 // Notify the change. |
| 661 monitor_functions_.register_ime_properties(language_library_, prop_list); |
| 662 } |
| 663 |
| 664 // Handles "UpdateProperty" signal from the candidate_window process. |
| 665 void UpdateProperty(IBusProperty* ibus_prop) { |
| 666 DLOG(INFO) << "UpdateProperty"; |
| 667 DCHECK(ibus_prop); |
| 668 |
| 669 // You can call |
| 670 // LOG(INFO) << "\n" << PrintProp(ibus_prop, 0); |
| 671 // here to dump |ibus_prop|. |
| 672 |
| 673 ImePropertyList prop_list; // our representation. |
| 674 if (!FlattenProperty(ibus_prop, &prop_list)) { |
| 675 LOG(WARNING) << "Malformed properties are detected"; |
| 676 } |
| 677 // Notify the change. |
| 678 if (!prop_list.empty()) { |
| 679 monitor_functions_.update_ime_property(language_library_, prop_list); |
| 680 } |
353 } | 681 } |
354 | 682 |
355 // Retrieve IME/XKB status and notify them to the UI. | 683 // Retrieve IME/XKB status and notify them to the UI. |
356 void UpdateUI() { | 684 void UpdateUI() { |
357 if (input_context_path_.empty()) { | 685 if (input_context_path_.empty()) { |
358 LOG(ERROR) << "Input context is unknown"; | 686 LOG(ERROR) << "Input context is unknown"; |
359 return; | 687 return; |
360 } | 688 } |
361 | 689 |
362 IBusInputContext* context | 690 IBusInputContext* context = GetInputContext(input_context_path_, ibus_); |
363 = ibus_input_context_get_input_context( | 691 if (!context) { |
364 input_context_path_.c_str(), ibus_bus_get_connection(ibus_)); | 692 return; |
| 693 } |
365 | 694 |
366 InputLanguage current_language; | 695 InputLanguage current_language; |
367 const bool ime_is_enabled = ibus_input_context_is_enabled(context); | 696 const bool ime_is_enabled = ibus_input_context_is_enabled(context); |
368 if (ime_is_enabled) { | 697 if (ime_is_enabled) { |
369 DLOG(INFO) << "IME is active"; | 698 DLOG(INFO) << "IME is active"; |
370 // Set IME name on current_language. | 699 // Set IME name on current_language. |
371 const IBusEngineDesc* engine_desc | 700 const IBusEngineDesc* engine_desc |
372 = ibus_input_context_get_engine(context); | 701 = ibus_input_context_get_engine(context); |
373 DCHECK(engine_desc); | 702 DCHECK(engine_desc); |
374 if (!engine_desc) { | 703 if (!engine_desc) { |
375 return; | 704 return; |
376 } | 705 } |
377 current_language = InputLanguage(LANGUAGE_CATEGORY_IME, | 706 current_language = InputLanguage(LANGUAGE_CATEGORY_IME, |
378 engine_desc->name, | 707 engine_desc->name, |
379 engine_desc->longname, | 708 engine_desc->longname, |
380 engine_desc->icon); | 709 engine_desc->icon); |
381 } else { | 710 } else { |
382 DLOG(INFO) << "IME is not active"; | 711 DLOG(INFO) << "IME is not active"; |
383 // Set XKB layout name on current_languages. | 712 // Set XKB layout name on current_languages. |
384 current_language = InputLanguage(LANGUAGE_CATEGORY_XKB, | 713 current_language = InputLanguage(LANGUAGE_CATEGORY_XKB, |
385 kFallbackXKBId, | 714 kFallbackXKBId, |
386 kFallbackXKBDisplayName, | 715 kFallbackXKBDisplayName, |
387 "" /* no icon */); // mock | 716 "" /* no icon */); // mock |
388 // TODO(yusukes): implemente this. | 717 // TODO(yusukes): implemente this. |
389 } | 718 } |
390 DLOG(INFO) << "Updating the UI. ID:" << current_language.id | 719 DLOG(INFO) << "Updating the UI. ID:" << current_language.id |
391 << ", display_name:" << current_language.display_name; | 720 << ", display_name:" << current_language.display_name; |
392 | 721 |
393 // Notify the change to update UI. | 722 // Notify the change to update UI. |
394 monitor_function_(language_library_, current_language); | 723 monitor_functions_.current_language(language_library_, current_language); |
395 g_object_unref(context); | 724 g_object_unref(context); |
396 } | 725 } |
397 | 726 |
398 // A function pointer which points LanguageLibrary::LanguageChangedHandler | 727 // Dispatches signals from candidate_window. In this function, we use the |
399 // function. |monitor_funcion_| is called when Chrome UI needs to be updated. | 728 // IBus's DBus binding (rather than the dbus-glib and its C++ wrapper). |
400 LanguageStatusMonitorFunction monitor_function_; | 729 // This is because arguments of "RegisterProperties" and "UpdateProperty" |
| 730 // are fairly complex IBus types, and thus it's probably not a good idea |
| 731 // to write a deserializer for these types from scratch using dbus-glib. |
| 732 static DBusHandlerResult DispatchSignalFromCandidateWindow( |
| 733 DBusConnection* connection, DBusMessage* message, void* object) { |
| 734 DCHECK(message); |
| 735 DCHECK(object); |
| 736 |
| 737 LanguageStatusConnection* self |
| 738 = static_cast<LanguageStatusConnection*>(object); |
| 739 IBusError* error = NULL; |
| 740 |
| 741 if (ibus_message_is_signal(message, |
| 742 kCandidateWindowInterface, |
| 743 "FocusIn")) { |
| 744 gchar* input_context_path = NULL; |
| 745 const gboolean retval = ibus_message_get_args(message, &error, |
| 746 G_TYPE_STRING, |
| 747 &input_context_path, |
| 748 G_TYPE_INVALID); |
| 749 if (!retval) { |
| 750 LOG(ERROR) << "FocusIn"; |
| 751 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 752 } |
| 753 self->FocusIn(input_context_path); |
| 754 return DBUS_HANDLER_RESULT_HANDLED; |
| 755 } |
| 756 |
| 757 if (ibus_message_is_signal(message, |
| 758 kCandidateWindowInterface, |
| 759 "FocusOut")) { |
| 760 gchar* input_context_path = NULL; |
| 761 const gboolean retval = ibus_message_get_args(message, &error, |
| 762 G_TYPE_STRING, |
| 763 &input_context_path, |
| 764 G_TYPE_INVALID); |
| 765 if (!retval) { |
| 766 LOG(ERROR) << "FocusOut"; |
| 767 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 768 } |
| 769 self->FocusOut(input_context_path); |
| 770 return DBUS_HANDLER_RESULT_HANDLED; |
| 771 } |
| 772 |
| 773 if (ibus_message_is_signal(message, |
| 774 kCandidateWindowInterface, |
| 775 "StateChanged")) { |
| 776 const gboolean retval = ibus_message_get_args(message, &error, |
| 777 /* no arguments */ |
| 778 G_TYPE_INVALID); |
| 779 if (!retval) { |
| 780 LOG(ERROR) << "StateChanged"; |
| 781 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 782 } |
| 783 self->StateChanged(); |
| 784 return DBUS_HANDLER_RESULT_HANDLED; |
| 785 } |
| 786 |
| 787 if (ibus_message_is_signal(message, |
| 788 kCandidateWindowInterface, |
| 789 "RegisterProperties")) { |
| 790 IBusPropList* prop_list = NULL; |
| 791 |
| 792 // The ibus_message_get_args() function automagically deserializes the |
| 793 // complex IBus structure. |
| 794 const gboolean retval = ibus_message_get_args(message, &error, |
| 795 IBUS_TYPE_PROP_LIST, |
| 796 &prop_list, |
| 797 G_TYPE_INVALID); |
| 798 |
| 799 if (!retval) { |
| 800 LOG(ERROR) << "RegisterProperties"; |
| 801 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 802 } |
| 803 self->RegisterProperties(prop_list); |
| 804 g_object_unref(prop_list); |
| 805 return DBUS_HANDLER_RESULT_HANDLED; |
| 806 } |
| 807 |
| 808 if (ibus_message_is_signal(message, |
| 809 kCandidateWindowInterface, |
| 810 "UpdateProperty")) { |
| 811 IBusProperty* prop = NULL; |
| 812 const gboolean retval = ibus_message_get_args(message, &error, |
| 813 IBUS_TYPE_PROPERTY, |
| 814 &prop, |
| 815 G_TYPE_INVALID); |
| 816 if (!retval) { |
| 817 LOG(ERROR) << "UpdateProperty"; |
| 818 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 819 } |
| 820 self->UpdateProperty(prop); |
| 821 g_object_unref(prop); |
| 822 return DBUS_HANDLER_RESULT_HANDLED; |
| 823 } |
| 824 |
| 825 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| 826 } |
| 827 |
| 828 // A function pointers which point LanguageLibrary::XXXHandler functions. |
| 829 // |monitor_funcions_| is used when libcros receives signals from the |
| 830 // candidate_window. |
| 831 LanguageStatusMonitorFunctions monitor_functions_; |
| 832 |
401 // Points to a chromeos::LanguageLibrary object. |language_library_| is used | 833 // Points to a chromeos::LanguageLibrary object. |language_library_| is used |
402 // as the first argument of the |monitor_function_| function. | 834 // as the first argument of the |monitor_functions_| functions. |
403 void* language_library_; | 835 void* language_library_; |
404 | 836 |
405 // Connections to IBus and DBus. | 837 // Connections to IBus and DBus. |
406 typedef dbus::MonitorConnection<void (const char*)>* DBusConnectionType; | |
407 IBusBus* ibus_; | 838 IBusBus* ibus_; |
408 IBusConfig* ibus_config_; | 839 IBusConfig* ibus_config_; |
409 DBusConnectionType dbus_focus_in_; | 840 scoped_ptr<dbus::BusConnection> dbus_connection_; |
410 DBusConnectionType dbus_focus_out_; | 841 scoped_ptr<dbus::Proxy> dbus_proxy_; |
411 DBusConnectionType dbus_state_changed_; | |
412 | 842 |
413 // Current input context path. | 843 // Current input context path. |
414 std::string input_context_path_; | 844 std::string input_context_path_; |
415 }; | 845 }; |
416 | 846 |
417 // | 847 // |
418 // cros APIs | 848 // cros APIs |
419 // | 849 // |
420 | 850 |
421 // The function will be bound to chromeos::MonitorLanguageStatus with dlsym() | 851 // The function will be bound to chromeos::MonitorLanguageStatus with dlsym() |
422 // in load.cc so it needs to be in the C linkage, so the symbol name does not | 852 // in load.cc so it needs to be in the C linkage, so the symbol name does not |
423 // get mangled. | 853 // get mangled. |
424 extern "C" | 854 extern "C" |
425 LanguageStatusConnection* ChromeOSMonitorLanguageStatus( | 855 LanguageStatusConnection* ChromeOSMonitorLanguageStatus( |
426 LanguageStatusMonitorFunction monitor_function, void* language_library) { | 856 LanguageStatusMonitorFunctions monitor_functions, void* language_library) { |
427 LOG(INFO) << "MonitorLanguageStatus"; | 857 LOG(INFO) << "MonitorLanguageStatus"; |
428 LanguageStatusConnection* connection | 858 LanguageStatusConnection* connection |
429 = new LanguageStatusConnection(monitor_function, language_library); | 859 = new LanguageStatusConnection(monitor_functions, language_library); |
430 if (!connection->Init()) { | 860 if (!connection->Init()) { |
431 LOG(WARNING) << "Failed to Init() LanguageStatusConnection. " | 861 LOG(WARNING) << "Failed to Init() LanguageStatusConnection. " |
432 << "Returning NULL"; | 862 << "Returning NULL"; |
433 delete connection; | 863 delete connection; |
434 connection = NULL; | 864 connection = NULL; |
435 } | 865 } |
436 return connection; | 866 return connection; |
437 } | 867 } |
438 | 868 |
439 extern "C" | 869 extern "C" |
(...skipping 21 matching lines...) Expand all Loading... |
461 if (!connection) { | 891 if (!connection) { |
462 LOG(WARNING) << "LanguageStatusConnection is NULL"; | 892 LOG(WARNING) << "LanguageStatusConnection is NULL"; |
463 return NULL; | 893 return NULL; |
464 } | 894 } |
465 // Pass ownership to a caller. Note: GetLanguages() might return NULL. | 895 // Pass ownership to a caller. Note: GetLanguages() might return NULL. |
466 return connection->GetLanguages( | 896 return connection->GetLanguages( |
467 LanguageStatusConnection::kSupportedLanguages); | 897 LanguageStatusConnection::kSupportedLanguages); |
468 } | 898 } |
469 | 899 |
470 extern "C" | 900 extern "C" |
| 901 void ChromeOSActivateImeProperty( |
| 902 LanguageStatusConnection* connection, const char* key) { |
| 903 DLOG(INFO) << "ActivateImeProperty"; |
| 904 DCHECK(key); |
| 905 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for |
| 906 // Chrome OS gets ready. |
| 907 if (!connection) { |
| 908 LOG(WARNING) << "LanguageStatusConnection is NULL"; |
| 909 return; |
| 910 } |
| 911 connection->ActivateOrDeactiveImeProperty(key, true); |
| 912 } |
| 913 |
| 914 extern "C" |
| 915 void ChromeOSDeactivateImeProperty( |
| 916 LanguageStatusConnection* connection, const char* key) { |
| 917 DLOG(INFO) << "DeactivateImeProperty"; |
| 918 DCHECK(key); |
| 919 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for |
| 920 // Chrome OS gets ready. |
| 921 if (!connection) { |
| 922 LOG(WARNING) << "LanguageStatusConnection is NULL"; |
| 923 return; |
| 924 } |
| 925 connection->ActivateOrDeactiveImeProperty(key, false); |
| 926 } |
| 927 |
| 928 extern "C" |
471 void ChromeOSChangeLanguage(LanguageStatusConnection* connection, | 929 void ChromeOSChangeLanguage(LanguageStatusConnection* connection, |
472 LanguageCategory category, | 930 LanguageCategory category, |
473 const char* name) { | 931 const char* name) { |
| 932 DCHECK(name); |
| 933 DLOG(INFO) << "ChangeLanguage: " << name; |
474 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for | 934 // TODO(yusukes): Add DCHECK(connection); here when candidate_window for |
475 // Chrome OS gets ready. | 935 // Chrome OS gets ready. |
476 if (!connection) { | 936 if (!connection) { |
477 LOG(WARNING) << "LanguageStatusConnection is NULL"; | 937 LOG(WARNING) << "LanguageStatusConnection is NULL"; |
478 return; | 938 return; |
479 } | 939 } |
480 DCHECK(name); | 940 connection->ChangeLanguage(category, name); |
481 DLOG(INFO) << "ChangeLanguage: " << name << " [category " << category << "]"; | |
482 switch (category) { | |
483 case LANGUAGE_CATEGORY_XKB: | |
484 connection->SwitchXKB(name); | |
485 break; | |
486 case LANGUAGE_CATEGORY_IME: | |
487 connection->SwitchIME(name); | |
488 break; | |
489 } | |
490 } | 941 } |
491 | 942 |
492 // Helper function for ChromeOSActivateLanguage() and | 943 // Helper function for ChromeOSActivateLanguage() and |
493 // ChromeOSDeactivateLanguage(). | 944 // ChromeOSDeactivateLanguage(). |
494 static bool ActivateOrDeactivateLanguage( | 945 static bool ActivateOrDeactivateLanguage( |
495 LanguageStatusConnection::UpdateMode mode, | 946 LanguageStatusConnection::UpdateMode mode, |
496 LanguageStatusConnection* connection, | 947 LanguageStatusConnection* connection, |
497 LanguageCategory category, | 948 LanguageCategory category, |
498 const char* name) { | 949 const char* name) { |
499 if (!connection) { | 950 if (!connection) { |
(...skipping 27 matching lines...) Expand all Loading... |
527 bool ChromeOSDeactivateLanguage(LanguageStatusConnection* connection, | 978 bool ChromeOSDeactivateLanguage(LanguageStatusConnection* connection, |
528 LanguageCategory category, | 979 LanguageCategory category, |
529 const char* name) { | 980 const char* name) { |
530 DLOG(INFO) << "DeactivateLanguage: " << name << " [category " | 981 DLOG(INFO) << "DeactivateLanguage: " << name << " [category " |
531 << category << "]"; | 982 << category << "]"; |
532 return ActivateOrDeactivateLanguage( | 983 return ActivateOrDeactivateLanguage( |
533 LanguageStatusConnection::kDeactivate, connection, category, name); | 984 LanguageStatusConnection::kDeactivate, connection, category, name); |
534 } | 985 } |
535 | 986 |
536 } // namespace chromeos | 987 } // namespace chromeos |
OLD | NEW |