Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(694)

Side by Side Diff: chrome/browser/chromeos/input_method/input_method_manager_impl.cc

Issue 9999018: chrome/browser/chromeos/input_method/ refactoring [part 6 of 6] (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
6
7 #include <algorithm> // std::find
8
9 #include "base/basictypes.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/string_util.h"
12 #include "base/stringprintf.h"
13 #include "chrome/browser/chromeos/input_method/browser_state_monitor.h"
14 #include "chrome/browser/chromeos/input_method/candidate_window.h"
15 #include "chrome/browser/chromeos/input_method/input_method_util.h"
16 #include "chrome/browser/chromeos/input_method/xkeyboard.h"
17 #include "chrome/browser/chromeos/language_preferences.h"
18 #include "ui/base/accelerators/accelerator.h"
19 #include "unicode/uloc.h"
20
21 namespace chromeos {
22 namespace input_method {
23
24 namespace {
25
26 bool Contains(const std::vector<std::string>& container,
27 const std::string& value) {
28 return std::find(container.begin(), container.end(), value) !=
29 container.end();
30 }
31
32 } // namespace
33
34 InputMethodManagerImpl::InputMethodManagerImpl()
35 : ignore_hotkeys_(false),
36 state_(STATE_LOGIN_SCREEN),
37 util_(GetSupportedInputMethods()) {
38 }
39
40 InputMethodManagerImpl::~InputMethodManagerImpl() {
41 if (ibus_controller_.get())
42 ibus_controller_->RemoveObserver(this);
43 if (candidate_window_controller_.get())
44 candidate_window_controller_->RemoveObserver(this);
45 }
46
47 void InputMethodManagerImpl::AddObserver(
48 InputMethodManager::Observer* observer) {
49 observers_.AddObserver(observer);
50 }
51
52 void InputMethodManagerImpl::AddCandidateWindowObserver(
53 InputMethodManager::CandidateWindowObserver* observer) {
54 candidate_window_observers_.AddObserver(observer);
55 }
56
57 void InputMethodManagerImpl::RemoveObserver(
58 InputMethodManager::Observer* observer) {
59 observers_.RemoveObserver(observer);
60 }
61
62 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
63 InputMethodManager::CandidateWindowObserver* observer) {
64 candidate_window_observers_.RemoveObserver(observer);
65 }
66
67 void InputMethodManagerImpl::SetState(State new_state) {
68 const State old_state = state_;
69 state_ = new_state;
70 switch (state_) {
71 case STATE_LOGIN_SCREEN:
72 break;
73 case STATE_BROWSER_SCREEN:
74 if (old_state == STATE_LOCK_SCREEN)
75 OnScreenUnlocked();
76 break;
77 case STATE_LOCK_SCREEN:
78 OnScreenLocked();
79 break;
80 case STATE_TERMINATING:
81 ibus_controller_->Stop();
82 browser_state_monitor_.reset(); // For crbug.com/120183.
83 candidate_window_controller_.reset();
84 break;
85 }
86 }
87
88 InputMethodDescriptors*
89 InputMethodManagerImpl::GetSupportedInputMethods() const {
90 return whitelist_.GetSupportedInputMethods();
91 }
92
93 InputMethodDescriptors* InputMethodManagerImpl::GetActiveInputMethods() const {
94 InputMethodDescriptors* result = new InputMethodDescriptors;
95 // Build the active input method descriptors from the active input
96 // methods cache |active_input_method_ids_|.
97 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
98 const std::string& input_method_id = active_input_method_ids_[i];
99 const InputMethodDescriptor* descriptor =
100 util_.GetInputMethodDescriptorFromId(input_method_id);
101 if (descriptor) {
102 result->push_back(*descriptor);
103 } else {
104 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
105 extra_input_methods_.find(input_method_id);
106 if (ix != extra_input_methods_.end())
107 result->push_back(ix->second);
108 else
109 LOG(ERROR) << "Descriptor is not found for: " << input_method_id;
110 }
111 }
112 if (result->empty()) {
113 // Initially |active_input_method_ids_| is empty. browser_tests might take
114 // this path.
115 result->push_back(
116 InputMethodDescriptor::GetFallbackInputMethodDescriptor());
117 }
118 return result;
119 }
120
121 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
122 return active_input_method_ids_.size();
123 }
124
125 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
126 const std::string& initial_layout) {
127 if (state_ == STATE_TERMINATING)
128 return;
129
130 std::vector<std::string> candidates;
131 // Add input methods associated with the language.
132 util_.GetInputMethodIdsFromLanguageCode(language_code,
133 kKeyboardLayoutsOnly,
134 &candidates);
135 // Add the hardware keyboard as well. We should always add this so users
136 // can use the hardware keyboard on the login screen and the screen locker.
137 candidates.push_back(util_.GetHardwareInputMethodId());
138
139 std::vector<std::string> layouts;
140 // First, add the initial input method ID, if it's requested, to
141 // layouts, so it appears first on the list of active input
142 // methods at the input language status menu.
143 if (util_.IsValidInputMethodId(initial_layout) &&
144 InputMethodUtil::IsKeyboardLayout(initial_layout)) {
145 layouts.push_back(initial_layout);
146 } else if (!initial_layout.empty()) {
147 LOG(ERROR) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
148 << initial_layout;
149 }
150
151 // Add candidates to layouts, while skipping duplicates.
152 for (size_t i = 0; i < candidates.size(); ++i) {
153 const std::string& candidate = candidates[i];
154 // Not efficient, but should be fine, as the two vectors are very
155 // short (2-5 items).
156 if (!Contains(layouts, candidate))
157 layouts.push_back(candidate);
158 }
159
160 active_input_method_ids_.swap(layouts);
161 ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|.
162 }
163
164 bool InputMethodManagerImpl::EnableInputMethods(
165 const std::vector<std::string>& new_active_input_method_ids) {
166 if (state_ == STATE_TERMINATING)
167 return false;
168
169 // Filter unknown or obsolete IDs.
170 std::vector<std::string> new_active_input_method_ids_filtered;
171
172 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) {
173 const std::string& input_method_id = new_active_input_method_ids[i];
174 if (util_.IsValidInputMethodId(input_method_id))
175 new_active_input_method_ids_filtered.push_back(input_method_id);
176 else
177 LOG(ERROR) << "EnableInputMethods: Invalid ID: " << input_method_id;
178 }
179
180 if (new_active_input_method_ids_filtered.empty()) {
181 LOG(ERROR) << "EnableInputMethods: No valid input method ID";
182 return false;
183 }
184
185 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
186 // keep relative order of the extension input method IDs.
187 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
188 const std::string& input_method_id = active_input_method_ids_[i];
189 if (InputMethodUtil::IsExtensionInputMethod(input_method_id))
190 new_active_input_method_ids_filtered.push_back(input_method_id);
191 }
192 active_input_method_ids_.swap(new_active_input_method_ids_filtered);
193
194 if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
195 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
196 // at crosbug.com/27051.
197 // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
198 // is implemented.
199 } else {
200 MaybeInitializeCandidateWindowController();
201 ibus_controller_->Start();
202 }
203
204 // If |current_input_method| is no longer in |active_input_method_ids_|,
205 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
206 ChangeInputMethod(current_input_method_.id());
207 return true;
208 }
209
210 bool InputMethodManagerImpl::SetInputMethodConfig(
211 const std::string& section,
212 const std::string& config_name,
213 const InputMethodConfigValue& value) {
214 DCHECK(section != language_prefs::kGeneralSectionName ||
215 config_name != language_prefs::kPreloadEnginesConfigName);
216
217 if (state_ == STATE_TERMINATING)
218 return false;
219 return ibus_controller_->SetInputMethodConfig(section, config_name, value);
220 }
221
222 void InputMethodManagerImpl::ChangeInputMethod(
223 const std::string& input_method_id) {
224 if (state_ == STATE_TERMINATING)
225 return;
226
227 std::string input_method_id_to_switch = input_method_id;
228
229 // Sanity check.
230 if (!InputMethodIsActivated(input_method_id)) {
231 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
232 DCHECK(!input_methods->empty());
233 input_method_id_to_switch = input_methods->at(0).id();
234 if (!input_method_id.empty()) {
235 VLOG(1) << "Can't change the current input method to "
236 << input_method_id << " since the engine is not enabled. "
237 << "Switch to " << input_method_id_to_switch << " instead.";
238 }
239 }
240
241 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
242 FOR_EACH_OBSERVER(InputMethodManager::Observer,
243 observers_,
244 InputMethodPropertyChanged(this));
245 } else {
246 ibus_controller_->ChangeInputMethod(input_method_id_to_switch);
247 }
248
249 if (current_input_method_.id() != input_method_id_to_switch) {
250 const InputMethodDescriptor* descriptor = NULL;
251 if (!InputMethodUtil::IsExtensionInputMethod(input_method_id_to_switch)) {
252 descriptor =
253 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
254 } else {
255 std::map<std::string, InputMethodDescriptor>::const_iterator i =
256 extra_input_methods_.find(input_method_id_to_switch);
257 DCHECK(i != extra_input_methods_.end());
258 descriptor = &(i->second);
259 }
260 DCHECK(descriptor);
261
262 previous_input_method_ = current_input_method_;
263 current_input_method_ = *descriptor;
264
265 // Change the keyboard layout to a preferred layout for the input method.
266 if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
267 current_input_method_.keyboard_layout())) {
268 LOG(ERROR) << "Failed to change keyboard layout to "
269 << current_input_method_.keyboard_layout();
270 }
271 }
272
273 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
274 FOR_EACH_OBSERVER(InputMethodManager::Observer,
275 observers_,
276 InputMethodChanged(this));
277 }
278
279 void InputMethodManagerImpl::ActivateInputMethodProperty(
280 const std::string& key) {
281 DCHECK(!key.empty());
282 ibus_controller_->ActivateInputMethodProperty(key);
283 }
284
285 void InputMethodManagerImpl::AddInputMethodExtension(
286 const std::string& id,
287 const std::string& name,
288 const std::vector<std::string>& layouts,
289 const std::string& language) {
290 if (state_ == STATE_TERMINATING)
291 return;
292
293 if (!InputMethodUtil::IsExtensionInputMethod(id)) {
294 LOG(ERROR) << id << " is not a valid extension input method ID.";
295 return;
296 }
297
298 const std::string virtual_layouts = JoinString(layouts, ',');
299 extra_input_methods_[id] = InputMethodDescriptor(
300 whitelist_, id, name, virtual_layouts, language);
301
302 if (!Contains(active_input_method_ids_, id)) {
303 active_input_method_ids_.push_back(id);
304 } else {
305 LOG(ERROR) << "AddInputMethodExtension: alread added: "
306 << id << ", " << name;
307 // Call Start() anyway, just in case.
308 }
309
310 // Ensure that the input method daemon is running.
311 MaybeInitializeCandidateWindowController();
312 ibus_controller_->Start();
313 }
314
315 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
316 if (!InputMethodUtil::IsExtensionInputMethod(id))
317 LOG(ERROR) << id << " is not a valid extension input method ID.";
318
319 std::vector<std::string>::iterator i = std::find(
320 active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
321 if (i != active_input_method_ids_.end())
322 active_input_method_ids_.erase(i);
323 extra_input_methods_.erase(id);
324
325 if (ContainOnlyKeyboardLayout(active_input_method_ids_)) {
326 // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
327 // at crosbug.com/27051.
328 // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
329 // is implemented.
330 }
331
332 // If |current_input_method| is no longer in |active_input_method_ids_|,
333 // switch to the first one in |active_input_method_ids_|.
334 ChangeInputMethod(current_input_method_.id());
335 }
336
337 void InputMethodManagerImpl::EnableHotkeys() {
338 ignore_hotkeys_ = false;
339 }
340
341 void InputMethodManagerImpl::DisableHotkeys() {
342 ignore_hotkeys_ = true;
343 }
344
345 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
346 if (ignore_hotkeys_)
347 return false;
348
349 // Sanity checks.
350 if (active_input_method_ids_.empty()) {
351 LOG(ERROR) << "active input method is empty";
352 return false;
353 }
354 if (current_input_method_.id().empty()) {
355 LOG(ERROR) << "current_input_method_ is unknown";
356 return false;
357 }
358
359 // Find the next input method and switch to it.
360 SwitchToNextInputMethodInternal(active_input_method_ids_,
361 current_input_method_.id());
362 return true;
363 }
364
365 bool InputMethodManagerImpl::SwitchToPreviousInputMethod() {
366 if (ignore_hotkeys_)
367 return false;
368
369 // Sanity check.
370 if (active_input_method_ids_.empty()) {
371 LOG(ERROR) << "active input method is empty";
372 return false;
373 }
374
375 if (previous_input_method_.id().empty() ||
376 previous_input_method_.id() == current_input_method_.id()) {
377 return SwitchToNextInputMethod();
378 }
379
380 std::vector<std::string>::const_iterator iter =
381 std::find(active_input_method_ids_.begin(),
382 active_input_method_ids_.end(),
383 previous_input_method_.id());
384 if (iter == active_input_method_ids_.end()) {
385 // previous_input_method_ is not supported.
386 return SwitchToNextInputMethod();
387 }
388 ChangeInputMethod(*iter);
389 return true;
390 }
391
392 bool InputMethodManagerImpl::SwitchInputMethod(
393 const ui::Accelerator& accelerator) {
394 if (ignore_hotkeys_)
395 return false;
396
397 // Sanity check.
398 if (active_input_method_ids_.empty()) {
399 LOG(ERROR) << "active input method is empty";
400 return false;
401 }
402
403 // Get the list of input method ids for the |accelerator|. For example, get
404 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
405 std::vector<std::string> input_method_ids_to_switch;
406 switch (accelerator.key_code()) {
407 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
408 input_method_ids_to_switch.push_back("mozc-jp");
409 break;
410 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
411 input_method_ids_to_switch.push_back("xkb:jp::jpn");
412 break;
413 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
414 case ui::VKEY_DBE_DBCSCHAR:
415 input_method_ids_to_switch.push_back("mozc-jp");
416 input_method_ids_to_switch.push_back("xkb:jp::jpn");
417 break;
418 case ui::VKEY_HANGUL: // Hangul (or right Alt) key on Korean keyboard
419 case ui::VKEY_SPACE: // Shift+Space
420 input_method_ids_to_switch.push_back("mozc-hangul");
421 input_method_ids_to_switch.push_back("xkb:kr:kr104:kor");
422 break;
423 default:
424 NOTREACHED();
425 break;
426 }
427 if (input_method_ids_to_switch.empty()) {
428 LOG(ERROR) << "Unexpected VKEY: " << accelerator.key_code();
429 return false;
430 }
431
432 // Obtain the intersection of input_method_ids_to_switch and
433 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
434 // preserved.
435 std::vector<std::string> ids;
436 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
437 const std::string& id = input_method_ids_to_switch[i];
438 if (Contains(active_input_method_ids_, id))
439 ids.push_back(id);
440 }
441 if (ids.empty()) {
442 // No input method for the accelerator is active. For example, we should
443 // just ignore VKEY_HANGUL when mozc-hangul is not active.
444 return false;
445 }
446
447 SwitchToNextInputMethodInternal(ids, current_input_method_.id());
448 return true; // consume the accelerator.
449 }
450
451 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
452 const std::vector<std::string>& input_method_ids,
453 const std::string& current_input_method_id) {
454 std::vector<std::string>::const_iterator iter =
455 std::find(input_method_ids.begin(),
456 input_method_ids.end(),
457 current_input_method_id);
458 if (iter != input_method_ids.end())
459 ++iter;
460 if (iter == input_method_ids.end())
461 iter = input_method_ids.begin();
462 ChangeInputMethod(*iter);
463 }
464
465 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
466 if (current_input_method_.id().empty())
467 return InputMethodDescriptor::GetFallbackInputMethodDescriptor();
468 return current_input_method_;
469 }
470
471 InputMethodPropertyList
472 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
473 // This check is necessary since an IME property (e.g. for Pinyin) might be
474 // sent from ibus-daemon AFTER the current input method is switched to XKB.
475 if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
476 return InputMethodPropertyList();
477 return ibus_controller_->GetCurrentProperties();
478 }
479
480 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
481 return xkeyboard_.get();
482 }
483
484 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
485 return &util_;
486 }
487
488 void InputMethodManagerImpl::Init() {
489 DCHECK(!ibus_controller_.get());
490
491 browser_state_monitor_.reset(new BrowserStateMonitor(this));
492 ibus_controller_.reset(IBusController::Create());
493 xkeyboard_.reset(XKeyboard::Create(util_));
494 ibus_controller_->AddObserver(this);
495 }
496
497 void InputMethodManagerImpl::SetIBusControllerForTesting(
498 IBusController* ibus_controller) {
499 ibus_controller_.reset(ibus_controller);
500 ibus_controller_->AddObserver(this);
501 }
502
503 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
504 CandidateWindowController* candidate_window_controller) {
505 candidate_window_controller_.reset(candidate_window_controller);
506 candidate_window_controller_->Init();
507 candidate_window_controller_->AddObserver(this);
508 }
509
510 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
511 xkeyboard_.reset(xkeyboard);
512 }
513
514 void InputMethodManagerImpl::PropertyChanged() {
515 FOR_EACH_OBSERVER(InputMethodManager::Observer,
516 observers_,
517 InputMethodPropertyChanged(this));
518 }
519
520 void InputMethodManagerImpl::CandidateWindowOpened() {
521 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
522 candidate_window_observers_,
523 CandidateWindowOpened(this));
524 }
525
526 void InputMethodManagerImpl::CandidateWindowClosed() {
527 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
528 candidate_window_observers_,
529 CandidateWindowClosed(this));
530 }
531
532 void InputMethodManagerImpl::OnScreenLocked() {
533 saved_previous_input_method_ = previous_input_method_;
534 saved_current_input_method_ = current_input_method_;
535 saved_active_input_method_ids_ = active_input_method_ids_;
536
537 const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
538 // We'll add the hardware keyboard if it's not included in
539 // |active_input_method_list| so that the user can always use the hardware
540 // keyboard on the screen locker.
541 bool should_add_hardware_keyboard = true;
542
543 active_input_method_ids_.clear();
544 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
545 const std::string& input_method_id = saved_active_input_method_ids_[i];
546 // Skip if it's not a keyboard layout. Drop input methods including
547 // extension ones.
548 if (!InputMethodUtil::IsKeyboardLayout(input_method_id))
549 continue;
550 active_input_method_ids_.push_back(input_method_id);
551 if (input_method_id == hardware_keyboard_id)
552 should_add_hardware_keyboard = false;
553 }
554 if (should_add_hardware_keyboard)
555 active_input_method_ids_.push_back(hardware_keyboard_id);
556
557 ChangeInputMethod(current_input_method_.id());
558 }
559
560 void InputMethodManagerImpl::OnScreenUnlocked() {
561 previous_input_method_ = saved_previous_input_method_;
562 current_input_method_ = saved_current_input_method_;
563 active_input_method_ids_ = saved_active_input_method_ids_;
564
565 ChangeInputMethod(current_input_method_.id());
566 }
567
568 bool InputMethodManagerImpl::InputMethodIsActivated(
569 const std::string& input_method_id) {
570 return Contains(active_input_method_ids_, input_method_id);
571 }
572
573 bool InputMethodManagerImpl::ContainOnlyKeyboardLayout(
574 const std::vector<std::string>& value) {
575 for (size_t i = 0; i < value.size(); ++i) {
576 if (!InputMethodUtil::IsKeyboardLayout(value[i]))
577 return false;
578 }
579 return true;
580 }
581
582 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
583 #if !defined(USE_VIRTUAL_KEYBOARD)
584 if (candidate_window_controller_.get())
585 return;
586
587 candidate_window_controller_.reset(
588 CandidateWindowController::CreateCandidateWindowController());
589 if (candidate_window_controller_->Init())
590 candidate_window_controller_->AddObserver(this);
591 else
592 LOG(WARNING) << "Failed to initialize the candidate window controller";
593 #endif
594 }
595
596 // static
597 InputMethodManagerImpl* InputMethodManagerImpl::GetInstanceForTesting() {
598 return new InputMethodManagerImpl;
599 }
600
601 } // namespace input_method
602 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698