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/bind.h" 11#include "base/location.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/prefs/pref_service.h" 14#include "base/strings/string_util.h" 15#include "base/strings/stringprintf.h" 16#include "chrome/browser/browser_process.h" 17#include "chrome/browser/chromeos/input_method/candidate_window_controller.h" 18#include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h" 19#include "chrome/browser/chromeos/input_method/input_method_engine.h" 20#include "chrome/browser/chromeos/language_preferences.h" 21#include "chromeos/ime/component_extension_ime_manager.h" 22#include "chromeos/ime/extension_ime_util.h" 23#include "chromeos/ime/input_method_delegate.h" 24#include "chromeos/ime/xkeyboard.h" 25#include "third_party/icu/source/common/unicode/uloc.h" 26#include "ui/base/accelerators/accelerator.h" 27 28namespace chromeos { 29namespace input_method { 30 31namespace { 32 33const char nacl_mozc_jp_id[] = 34 "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp"; 35 36bool Contains(const std::vector<std::string>& container, 37 const std::string& value) { 38 return std::find(container.begin(), container.end(), value) != 39 container.end(); 40} 41 42} // namespace 43 44bool InputMethodManagerImpl::IsLoginKeyboard( 45 const std::string& layout) const { 46 const InputMethodDescriptor* ime = 47 util_.GetInputMethodDescriptorFromId(layout); 48 return ime ? ime->is_login_keyboard() : false; 49} 50 51InputMethodManagerImpl::InputMethodManagerImpl( 52 scoped_ptr<InputMethodDelegate> delegate) 53 : delegate_(delegate.Pass()), 54 state_(STATE_LOGIN_SCREEN), 55 util_(delegate_.get(), GetSupportedInputMethods()), 56 component_extension_ime_manager_(new ComponentExtensionIMEManager()), 57 weak_ptr_factory_(this) { 58} 59 60InputMethodManagerImpl::~InputMethodManagerImpl() { 61 if (candidate_window_controller_.get()) 62 candidate_window_controller_->RemoveObserver(this); 63} 64 65void InputMethodManagerImpl::AddObserver( 66 InputMethodManager::Observer* observer) { 67 observers_.AddObserver(observer); 68} 69 70void InputMethodManagerImpl::AddCandidateWindowObserver( 71 InputMethodManager::CandidateWindowObserver* observer) { 72 candidate_window_observers_.AddObserver(observer); 73} 74 75void InputMethodManagerImpl::RemoveObserver( 76 InputMethodManager::Observer* observer) { 77 observers_.RemoveObserver(observer); 78} 79 80void InputMethodManagerImpl::RemoveCandidateWindowObserver( 81 InputMethodManager::CandidateWindowObserver* observer) { 82 candidate_window_observers_.RemoveObserver(observer); 83} 84 85void InputMethodManagerImpl::SetState(State new_state) { 86 const State old_state = state_; 87 state_ = new_state; 88 switch (state_) { 89 case STATE_LOGIN_SCREEN: 90 break; 91 case STATE_BROWSER_SCREEN: 92 if (old_state == STATE_LOCK_SCREEN) 93 OnScreenUnlocked(); 94 break; 95 case STATE_LOCK_SCREEN: 96 OnScreenLocked(); 97 break; 98 case STATE_TERMINATING: { 99 if (candidate_window_controller_.get()) 100 candidate_window_controller_.reset(); 101 break; 102 } 103 } 104} 105 106scoped_ptr<InputMethodDescriptors> 107InputMethodManagerImpl::GetSupportedInputMethods() const { 108 return whitelist_.GetSupportedInputMethods(); 109} 110 111scoped_ptr<InputMethodDescriptors> 112InputMethodManagerImpl::GetActiveInputMethods() const { 113 scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors); 114 // Build the active input method descriptors from the active input 115 // methods cache |active_input_method_ids_|. 116 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { 117 const std::string& input_method_id = active_input_method_ids_[i]; 118 const InputMethodDescriptor* descriptor = 119 util_.GetInputMethodDescriptorFromId(input_method_id); 120 if (descriptor) { 121 result->push_back(*descriptor); 122 } else { 123 std::map<std::string, InputMethodDescriptor>::const_iterator ix = 124 extra_input_methods_.find(input_method_id); 125 if (ix != extra_input_methods_.end()) 126 result->push_back(ix->second); 127 else 128 DVLOG(1) << "Descriptor is not found for: " << input_method_id; 129 } 130 } 131 if (result->empty()) { 132 // Initially |active_input_method_ids_| is empty. browser_tests might take 133 // this path. 134 result->push_back( 135 InputMethodUtil::GetFallbackInputMethodDescriptor()); 136 } 137 return result.Pass(); 138} 139 140const std::vector<std::string>& 141InputMethodManagerImpl::GetActiveInputMethodIds() const { 142 return active_input_method_ids_; 143} 144 145size_t InputMethodManagerImpl::GetNumActiveInputMethods() const { 146 return active_input_method_ids_.size(); 147} 148 149void InputMethodManagerImpl::EnableLayouts(const std::string& language_code, 150 const std::string& initial_layout) { 151 if (state_ == STATE_TERMINATING) 152 return; 153 154 std::vector<std::string> candidates; 155 // Add input methods associated with the language. 156 util_.GetInputMethodIdsFromLanguageCode(language_code, 157 kKeyboardLayoutsOnly, 158 &candidates); 159 // Add the hardware keyboard as well. We should always add this so users 160 // can use the hardware keyboard on the login screen and the screen locker. 161 candidates.push_back(util_.GetHardwareInputMethodId()); 162 163 std::vector<std::string> layouts; 164 // First, add the initial input method ID, if it's requested, to 165 // layouts, so it appears first on the list of active input 166 // methods at the input language status menu. 167 if (util_.IsValidInputMethodId(initial_layout) && 168 IsLoginKeyboard(initial_layout)) { 169 layouts.push_back(initial_layout); 170 } else if (!initial_layout.empty()) { 171 DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: " 172 << initial_layout; 173 } 174 175 // Add candidates to layouts, while skipping duplicates. 176 for (size_t i = 0; i < candidates.size(); ++i) { 177 const std::string& candidate = candidates[i]; 178 // Not efficient, but should be fine, as the two vectors are very 179 // short (2-5 items). 180 if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate)) 181 layouts.push_back(candidate); 182 } 183 184 active_input_method_ids_.swap(layouts); 185 186 // Initialize candidate window controller and widgets such as 187 // candidate window, infolist and mode indicator. Note, mode 188 // indicator is used by only keyboard layout input methods. 189 if (active_input_method_ids_.size() > 1) 190 MaybeInitializeCandidateWindowController(); 191 192 ChangeInputMethod(initial_layout); // you can pass empty |initial_layout|. 193} 194 195// Adds new input method to given list. 196bool InputMethodManagerImpl::EnableInputMethodImpl( 197 const std::string& input_method_id, 198 std::vector<std::string>* new_active_input_method_ids) const { 199 DCHECK(new_active_input_method_ids); 200 if (!util_.IsValidInputMethodId(input_method_id)) { 201 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id; 202 return false; 203 } 204 205 if (!Contains(*new_active_input_method_ids, input_method_id)) 206 new_active_input_method_ids->push_back(input_method_id); 207 208 return true; 209} 210 211// Starts or stops the system input method framework as needed. 212void InputMethodManagerImpl::ReconfigureIMFramework() { 213 if (component_extension_ime_manager_->IsInitialized()) 214 LoadNecessaryComponentExtensions(); 215 216 const bool need_engine = 217 !ContainsOnlyKeyboardLayout(active_input_method_ids_); 218 219 // Initialize candidate window controller and widgets such as 220 // candidate window, infolist and mode indicator. Note, mode 221 // indicator is used by only keyboard layout input methods. 222 if (need_engine || active_input_method_ids_.size() > 1) 223 MaybeInitializeCandidateWindowController(); 224} 225 226bool InputMethodManagerImpl::EnableInputMethod( 227 const std::string& input_method_id) { 228 if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_)) 229 return false; 230 231 ReconfigureIMFramework(); 232 return true; 233} 234 235bool InputMethodManagerImpl::EnableInputMethods( 236 const std::vector<std::string>& new_active_input_method_ids) { 237 if (state_ == STATE_TERMINATING) 238 return false; 239 240 // Filter unknown or obsolete IDs. 241 std::vector<std::string> new_active_input_method_ids_filtered; 242 243 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) 244 EnableInputMethodImpl(new_active_input_method_ids[i], 245 &new_active_input_method_ids_filtered); 246 247 if (new_active_input_method_ids_filtered.empty()) { 248 DVLOG(1) << "EnableInputMethods: No valid input method ID"; 249 return false; 250 } 251 252 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to 253 // keep relative order of the extension input method IDs. 254 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { 255 const std::string& input_method_id = active_input_method_ids_[i]; 256 if (extension_ime_util::IsExtensionIME(input_method_id)) 257 new_active_input_method_ids_filtered.push_back(input_method_id); 258 } 259 active_input_method_ids_.swap(new_active_input_method_ids_filtered); 260 261 ReconfigureIMFramework(); 262 263 // If |current_input_method| is no longer in |active_input_method_ids_|, 264 // ChangeInputMethod() picks the first one in |active_input_method_ids_|. 265 ChangeInputMethod(current_input_method_.id()); 266 return true; 267} 268 269void InputMethodManagerImpl::ChangeInputMethod( 270 const std::string& input_method_id) { 271 ChangeInputMethodInternal(input_method_id, false); 272} 273 274bool InputMethodManagerImpl::ChangeInputMethodInternal( 275 const std::string& input_method_id, 276 bool show_message) { 277 if (state_ == STATE_TERMINATING) 278 return false; 279 280 std::string input_method_id_to_switch = input_method_id; 281 282 // Sanity check. 283 if (!InputMethodIsActivated(input_method_id)) { 284 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); 285 DCHECK(!input_methods->empty()); 286 input_method_id_to_switch = input_methods->at(0).id(); 287 if (!input_method_id.empty()) { 288 DVLOG(1) << "Can't change the current input method to " 289 << input_method_id << " since the engine is not enabled. " 290 << "Switch to " << input_method_id_to_switch << " instead."; 291 } 292 } 293 294 if (!component_extension_ime_manager_->IsInitialized() && 295 !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) { 296 // We can't change input method before the initialization of 297 // component extension ime manager. ChangeInputMethod will be 298 // called with |pending_input_method_| when the initialization is 299 // done. 300 pending_input_method_ = input_method_id_to_switch; 301 return false; 302 } 303 pending_input_method_.clear(); 304 305 // Hide candidate window and info list. 306 if (candidate_window_controller_.get()) 307 candidate_window_controller_->Hide(); 308 309 // Disable the current engine handler. 310 IBusEngineHandlerInterface* engine = 311 IBusBridge::Get()->GetCurrentEngineHandler(); 312 if (engine) 313 engine->Disable(); 314 315 // Configure the next engine handler. 316 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) { 317 IBusBridge::Get()->SetCurrentEngineHandler(NULL); 318 } else { 319 IBusEngineHandlerInterface* next_engine = 320 IBusBridge::Get()->SetCurrentEngineHandlerById( 321 input_method_id_to_switch); 322 323 if (next_engine) 324 next_engine->Enable(); 325 } 326 327 // TODO(komatsu): Check if it is necessary to perform the above routine 328 // when the current input method is equal to |input_method_id_to_swich|. 329 if (current_input_method_.id() != input_method_id_to_switch) { 330 // Clear property list. Property list would be updated by 331 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems. 332 // If the current input method is a keyboard layout, empty 333 // properties are sufficient. 334 const InputMethodPropertyList empty_property_list; 335 SetCurrentInputMethodProperties(empty_property_list); 336 337 const InputMethodDescriptor* descriptor = NULL; 338 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) { 339 DCHECK(extra_input_methods_.find(input_method_id_to_switch) != 340 extra_input_methods_.end()); 341 descriptor = &(extra_input_methods_[input_method_id_to_switch]); 342 } else { 343 descriptor = 344 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch); 345 } 346 DCHECK(descriptor); 347 348 previous_input_method_ = current_input_method_; 349 current_input_method_ = *descriptor; 350 } 351 352 // Change the keyboard layout to a preferred layout for the input method. 353 if (!xkeyboard_->SetCurrentKeyboardLayoutByName( 354 current_input_method_.GetPreferredKeyboardLayout())) { 355 LOG(ERROR) << "Failed to change keyboard layout to " 356 << current_input_method_.GetPreferredKeyboardLayout(); 357 } 358 359 // Update input method indicators (e.g. "US", "DV") in Chrome windows. 360 FOR_EACH_OBSERVER(InputMethodManager::Observer, 361 observers_, 362 InputMethodChanged(this, show_message)); 363 return true; 364} 365 366void InputMethodManagerImpl::OnComponentExtensionInitialized( 367 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) { 368 DCHECK(thread_checker_.CalledOnValidThread()); 369 component_extension_ime_manager_->Initialize(delegate.Pass()); 370 util_.SetComponentExtensions( 371 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor()); 372 373 LoadNecessaryComponentExtensions(); 374 375 if (!pending_input_method_.empty()) 376 ChangeInputMethodInternal(pending_input_method_, false); 377} 378 379void InputMethodManagerImpl::LoadNecessaryComponentExtensions() { 380 if (!component_extension_ime_manager_->IsInitialized()) 381 return; 382 // Load component extensions but also update |active_input_method_ids_| as 383 // some component extension IMEs may have been removed from the Chrome OS 384 // image. If specified component extension IME no longer exists, falling back 385 // to an existing IME. 386 std::vector<std::string> unfiltered_input_method_ids = 387 active_input_method_ids_; 388 active_input_method_ids_.clear(); 389 for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) { 390 if (!extension_ime_util::IsComponentExtensionIME( 391 unfiltered_input_method_ids[i])) { 392 // Legacy IMEs or xkb layouts are alwayes active. 393 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); 394 } else if (component_extension_ime_manager_->IsWhitelisted( 395 unfiltered_input_method_ids[i])) { 396 component_extension_ime_manager_->LoadComponentExtensionIME( 397 unfiltered_input_method_ids[i]); 398 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); 399 } 400 } 401} 402 403void InputMethodManagerImpl::ActivateInputMethodProperty( 404 const std::string& key) { 405 DCHECK(!key.empty()); 406 407 for (size_t i = 0; i < property_list_.size(); ++i) { 408 if (property_list_[i].key == key) { 409 IBusEngineHandlerInterface* engine = 410 IBusBridge::Get()->GetCurrentEngineHandler(); 411 if (engine) 412 engine->PropertyActivate(key); 413 return; 414 } 415 } 416 417 DVLOG(1) << "ActivateInputMethodProperty: unknown key: " << key; 418} 419 420void InputMethodManagerImpl::AddInputMethodExtension( 421 const std::string& id, 422 const std::string& name, 423 const std::vector<std::string>& layouts, 424 const std::vector<std::string>& languages, 425 const GURL& options_url, 426 const GURL& inputview_url, 427 InputMethodEngineInterface* engine) { 428 if (state_ == STATE_TERMINATING) 429 return; 430 431 if (!extension_ime_util::IsExtensionIME(id) && 432 !extension_ime_util::IsComponentExtensionIME(id)) { 433 DVLOG(1) << id << " is not a valid extension input method ID."; 434 return; 435 } 436 437 extra_input_methods_[id] = InputMethodDescriptor( 438 id, name, layouts, languages, false, options_url, inputview_url); 439 if (Contains(enabled_extension_imes_, id) && 440 !extension_ime_util::IsComponentExtensionIME(id)) { 441 if (!Contains(active_input_method_ids_, id)) { 442 active_input_method_ids_.push_back(id); 443 } else { 444 DVLOG(1) << "AddInputMethodExtension: alread added: " 445 << id << ", " << name; 446 // Call Start() anyway, just in case. 447 } 448 449 // Ensure that the input method daemon is running. 450 MaybeInitializeCandidateWindowController(); 451 } 452} 453 454void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) { 455 if (!extension_ime_util::IsExtensionIME(id)) 456 DVLOG(1) << id << " is not a valid extension input method ID."; 457 458 std::vector<std::string>::iterator i = std::find( 459 active_input_method_ids_.begin(), active_input_method_ids_.end(), id); 460 if (i != active_input_method_ids_.end()) 461 active_input_method_ids_.erase(i); 462 extra_input_methods_.erase(id); 463 464 // If |current_input_method| is no longer in |active_input_method_ids_|, 465 // switch to the first one in |active_input_method_ids_|. 466 ChangeInputMethod(current_input_method_.id()); 467 468 if (IBusBridge::Get()->GetCurrentEngineHandler() == 469 IBusBridge::Get()->GetEngineHandler(id)) 470 IBusBridge::Get()->SetCurrentEngineHandler(NULL); 471} 472 473void InputMethodManagerImpl::GetInputMethodExtensions( 474 InputMethodDescriptors* result) { 475 // Build the extension input method descriptors from the extra input 476 // methods cache |extra_input_methods_|. 477 std::map<std::string, InputMethodDescriptor>::iterator iter; 478 for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end(); 479 ++iter) { 480 if (extension_ime_util::IsExtensionIME(iter->first)) 481 result->push_back(iter->second); 482 } 483} 484 485void InputMethodManagerImpl::SetEnabledExtensionImes( 486 std::vector<std::string>* ids) { 487 enabled_extension_imes_.clear(); 488 enabled_extension_imes_.insert(enabled_extension_imes_.end(), 489 ids->begin(), 490 ids->end()); 491 492 bool active_imes_changed = false; 493 494 for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter = 495 extra_input_methods_.begin(); extra_iter != extra_input_methods_.end(); 496 ++extra_iter) { 497 if (extension_ime_util::IsComponentExtensionIME( 498 extra_iter->first)) 499 continue; // Do not filter component extension. 500 std::vector<std::string>::iterator active_iter = std::find( 501 active_input_method_ids_.begin(), active_input_method_ids_.end(), 502 extra_iter->first); 503 504 bool active = active_iter != active_input_method_ids_.end(); 505 bool enabled = Contains(enabled_extension_imes_, extra_iter->first); 506 507 if (active && !enabled) 508 active_input_method_ids_.erase(active_iter); 509 510 if (!active && enabled) 511 active_input_method_ids_.push_back(extra_iter->first); 512 513 if (active == !enabled) 514 active_imes_changed = true; 515 } 516 517 if (active_imes_changed) { 518 MaybeInitializeCandidateWindowController(); 519 520 // If |current_input_method| is no longer in |active_input_method_ids_|, 521 // switch to the first one in |active_input_method_ids_|. 522 ChangeInputMethod(current_input_method_.id()); 523 } 524} 525 526void InputMethodManagerImpl::SetInputMethodDefault() { 527 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty 528 // and US dvorak keyboard layouts. 529 if (g_browser_process && g_browser_process->local_state()) { 530 const std::string locale = g_browser_process->GetApplicationLocale(); 531 // If the preferred keyboard for the login screen has been saved, use it. 532 PrefService* prefs = g_browser_process->local_state(); 533 std::string initial_input_method_id = 534 prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout); 535 if (initial_input_method_id.empty()) { 536 // If kPreferredKeyboardLayout is not specified, use the hardware layout. 537 initial_input_method_id = 538 GetInputMethodUtil()->GetHardwareInputMethodId(); 539 } 540 EnableLayouts(locale, initial_input_method_id); 541 } 542} 543 544bool InputMethodManagerImpl::SwitchToNextInputMethod() { 545 // Sanity checks. 546 if (active_input_method_ids_.empty()) { 547 DVLOG(1) << "active input method is empty"; 548 return false; 549 } 550 551 if (current_input_method_.id().empty()) { 552 DVLOG(1) << "current_input_method_ is unknown"; 553 return false; 554 } 555 556 // Do not consume key event if there is only one input method is enabled. 557 // Ctrl+Space or Alt+Shift may be used by other application. 558 if (active_input_method_ids_.size() == 1) 559 return false; 560 561 // Find the next input method and switch to it. 562 SwitchToNextInputMethodInternal(active_input_method_ids_, 563 current_input_method_.id()); 564 return true; 565} 566 567bool InputMethodManagerImpl::SwitchToPreviousInputMethod( 568 const ui::Accelerator& accelerator) { 569 // Sanity check. 570 if (active_input_method_ids_.empty()) { 571 DVLOG(1) << "active input method is empty"; 572 return false; 573 } 574 575 // Do not consume key event if there is only one input method is enabled. 576 // Ctrl+Space or Alt+Shift may be used by other application. 577 if (active_input_method_ids_.size() == 1) 578 return false; 579 580 if (accelerator.type() == ui::ET_KEY_RELEASED) 581 return true; 582 583 if (previous_input_method_.id().empty() || 584 previous_input_method_.id() == current_input_method_.id()) { 585 return SwitchToNextInputMethod(); 586 } 587 588 std::vector<std::string>::const_iterator iter = 589 std::find(active_input_method_ids_.begin(), 590 active_input_method_ids_.end(), 591 previous_input_method_.id()); 592 if (iter == active_input_method_ids_.end()) { 593 // previous_input_method_ is not supported. 594 return SwitchToNextInputMethod(); 595 } 596 ChangeInputMethodInternal(*iter, true); 597 return true; 598} 599 600bool InputMethodManagerImpl::SwitchInputMethod( 601 const ui::Accelerator& accelerator) { 602 // Sanity check. 603 if (active_input_method_ids_.empty()) { 604 DVLOG(1) << "active input method is empty"; 605 return false; 606 } 607 608 // Get the list of input method ids for the |accelerator|. For example, get 609 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR. 610 std::vector<std::string> input_method_ids_to_switch; 611 switch (accelerator.key_code()) { 612 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard 613 input_method_ids_to_switch.push_back(nacl_mozc_jp_id); 614 break; 615 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard 616 input_method_ids_to_switch.push_back("xkb:jp::jpn"); 617 break; 618 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard 619 case ui::VKEY_DBE_DBCSCHAR: 620 input_method_ids_to_switch.push_back(nacl_mozc_jp_id); 621 input_method_ids_to_switch.push_back("xkb:jp::jpn"); 622 break; 623 default: 624 NOTREACHED(); 625 break; 626 } 627 if (input_method_ids_to_switch.empty()) { 628 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code(); 629 return false; 630 } 631 632 // Obtain the intersection of input_method_ids_to_switch and 633 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is 634 // preserved. 635 std::vector<std::string> ids; 636 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) { 637 const std::string& id = input_method_ids_to_switch[i]; 638 if (Contains(active_input_method_ids_, id)) 639 ids.push_back(id); 640 } 641 if (ids.empty()) { 642 // No input method for the accelerator is active. For example, we should 643 // just ignore VKEY_HANGUL when mozc-hangul is not active. 644 return false; 645 } 646 647 SwitchToNextInputMethodInternal(ids, current_input_method_.id()); 648 return true; // consume the accelerator. 649} 650 651void InputMethodManagerImpl::SwitchToNextInputMethodInternal( 652 const std::vector<std::string>& input_method_ids, 653 const std::string& current_input_method_id) { 654 std::vector<std::string>::const_iterator iter = 655 std::find(input_method_ids.begin(), 656 input_method_ids.end(), 657 current_input_method_id); 658 if (iter != input_method_ids.end()) 659 ++iter; 660 if (iter == input_method_ids.end()) 661 iter = input_method_ids.begin(); 662 ChangeInputMethodInternal(*iter, true); 663} 664 665InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const { 666 if (current_input_method_.id().empty()) 667 return InputMethodUtil::GetFallbackInputMethodDescriptor(); 668 669 return current_input_method_; 670} 671 672InputMethodPropertyList 673InputMethodManagerImpl::GetCurrentInputMethodProperties() const { 674 // This check is necessary since an IME property (e.g. for Pinyin) might be 675 // sent from ibus-daemon AFTER the current input method is switched to XKB. 676 if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id())) 677 return InputMethodPropertyList(); // Empty list. 678 return property_list_; 679} 680 681void InputMethodManagerImpl::SetCurrentInputMethodProperties( 682 const InputMethodPropertyList& property_list) { 683 property_list_ = property_list; 684 PropertyChanged(); 685} 686 687XKeyboard* InputMethodManagerImpl::GetXKeyboard() { 688 return xkeyboard_.get(); 689} 690 691InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() { 692 return &util_; 693} 694 695ComponentExtensionIMEManager* 696 InputMethodManagerImpl::GetComponentExtensionIMEManager() { 697 DCHECK(thread_checker_.CalledOnValidThread()); 698 return component_extension_ime_manager_.get(); 699} 700 701void InputMethodManagerImpl::InitializeComponentExtension() { 702 ComponentExtensionIMEManagerImpl* impl = 703 new ComponentExtensionIMEManagerImpl(); 704 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl); 705 impl->InitializeAsync(base::Bind( 706 &InputMethodManagerImpl::OnComponentExtensionInitialized, 707 weak_ptr_factory_.GetWeakPtr(), 708 base::Passed(&delegate))); 709} 710 711void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) { 712 DCHECK(thread_checker_.CalledOnValidThread()); 713 714 xkeyboard_.reset(XKeyboard::Create()); 715 716 // We can't call impl->Initialize here, because file thread is not available 717 // at this moment. 718 ui_task_runner->PostTask( 719 FROM_HERE, 720 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension, 721 weak_ptr_factory_.GetWeakPtr())); 722} 723 724void InputMethodManagerImpl::SetCandidateWindowControllerForTesting( 725 CandidateWindowController* candidate_window_controller) { 726 candidate_window_controller_.reset(candidate_window_controller); 727 candidate_window_controller_->AddObserver(this); 728} 729 730void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) { 731 xkeyboard_.reset(xkeyboard); 732} 733 734void InputMethodManagerImpl::InitializeComponentExtensionForTesting( 735 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) { 736 OnComponentExtensionInitialized(delegate.Pass()); 737} 738 739void InputMethodManagerImpl::PropertyChanged() { 740 FOR_EACH_OBSERVER(InputMethodManager::Observer, 741 observers_, 742 InputMethodPropertyChanged(this)); 743} 744 745void InputMethodManagerImpl::CandidateClicked(int index) { 746 IBusEngineHandlerInterface* engine = 747 IBusBridge::Get()->GetCurrentEngineHandler(); 748 if (engine) 749 engine->CandidateClicked(index); 750} 751 752void InputMethodManagerImpl::CandidateWindowOpened() { 753 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, 754 candidate_window_observers_, 755 CandidateWindowOpened(this)); 756} 757 758void InputMethodManagerImpl::CandidateWindowClosed() { 759 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, 760 candidate_window_observers_, 761 CandidateWindowClosed(this)); 762} 763 764void InputMethodManagerImpl::OnScreenLocked() { 765 saved_previous_input_method_ = previous_input_method_; 766 saved_current_input_method_ = current_input_method_; 767 saved_active_input_method_ids_ = active_input_method_ids_; 768 769 const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId(); 770 // We'll add the hardware keyboard if it's not included in 771 // |active_input_method_list| so that the user can always use the hardware 772 // keyboard on the screen locker. 773 bool should_add_hardware_keyboard = true; 774 775 active_input_method_ids_.clear(); 776 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) { 777 const std::string& input_method_id = saved_active_input_method_ids_[i]; 778 // Skip if it's not a keyboard layout. Drop input methods including 779 // extension ones. 780 if (!IsLoginKeyboard(input_method_id)) 781 continue; 782 active_input_method_ids_.push_back(input_method_id); 783 if (input_method_id == hardware_keyboard_id) 784 should_add_hardware_keyboard = false; 785 } 786 if (should_add_hardware_keyboard) 787 active_input_method_ids_.push_back(hardware_keyboard_id); 788 789 ChangeInputMethod(current_input_method_.id()); 790} 791 792void InputMethodManagerImpl::OnScreenUnlocked() { 793 previous_input_method_ = saved_previous_input_method_; 794 current_input_method_ = saved_current_input_method_; 795 active_input_method_ids_ = saved_active_input_method_ids_; 796 797 ChangeInputMethod(current_input_method_.id()); 798} 799 800bool InputMethodManagerImpl::InputMethodIsActivated( 801 const std::string& input_method_id) { 802 return Contains(active_input_method_ids_, input_method_id); 803} 804 805bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout( 806 const std::vector<std::string>& value) { 807 for (size_t i = 0; i < value.size(); ++i) { 808 if (!InputMethodUtil::IsKeyboardLayout(value[i])) 809 return false; 810 } 811 return true; 812} 813 814void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() { 815 if (candidate_window_controller_.get()) 816 return; 817 818 candidate_window_controller_.reset( 819 CandidateWindowController::CreateCandidateWindowController()); 820 candidate_window_controller_->AddObserver(this); 821} 822 823} // namespace input_method 824} // namespace chromeos 825