Home | History | Annotate | Download | only in input_method
      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/candidate_window_controller_impl.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "ash/ime/infolist_window.h"
     11 #include "ash/shell.h"
     12 #include "ash/shell_window_ids.h"
     13 #include "ash/wm/window_util.h"
     14 #include "base/logging.h"
     15 #include "chrome/browser/chromeos/input_method/mode_indicator_controller.h"
     16 #include "ui/gfx/screen.h"
     17 #include "ui/views/widget/widget.h"
     18 
     19 namespace chromeos {
     20 namespace input_method {
     21 
     22 namespace {
     23 
     24 }  // namespace
     25 
     26 CandidateWindowControllerImpl::CandidateWindowControllerImpl()
     27     : candidate_window_view_(NULL),
     28       infolist_window_(NULL) {
     29   IMEBridge::Get()->SetCandidateWindowHandler(this);
     30   // Create the mode indicator controller.
     31   mode_indicator_controller_.reset(
     32       new ModeIndicatorController(InputMethodManager::Get()));
     33 }
     34 
     35 CandidateWindowControllerImpl::~CandidateWindowControllerImpl() {
     36   IMEBridge::Get()->SetCandidateWindowHandler(NULL);
     37   if (candidate_window_view_) {
     38     candidate_window_view_->RemoveObserver(this);
     39     candidate_window_view_->GetWidget()->RemoveObserver(this);
     40   }
     41 }
     42 
     43 void CandidateWindowControllerImpl::InitCandidateWindowView() {
     44   if (candidate_window_view_)
     45     return;
     46 
     47   aura::Window* active_window = ash::wm::GetActiveWindow();
     48   candidate_window_view_ =
     49       new ash::ime::CandidateWindowView(ash::Shell::GetContainer(
     50           active_window ? active_window->GetRootWindow()
     51                         : ash::Shell::GetTargetRootWindow(),
     52           ash::kShellWindowId_SettingBubbleContainer));
     53   candidate_window_view_->AddObserver(this);
     54   candidate_window_view_->SetCursorBounds(cursor_bounds_, composition_head_);
     55   views::Widget* widget = candidate_window_view_->InitWidget();
     56   widget->AddObserver(this);
     57   widget->Show();
     58   FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
     59                     CandidateWindowOpened());
     60 }
     61 
     62 void CandidateWindowControllerImpl::Hide() {
     63   if (candidate_window_view_)
     64     candidate_window_view_->GetWidget()->Close();
     65   if (infolist_window_)
     66     infolist_window_->HideImmediately();
     67 }
     68 
     69 void CandidateWindowControllerImpl::SetCursorBounds(
     70     const gfx::Rect& cursor_bounds,
     71     const gfx::Rect& composition_head) {
     72   // A workaround for http://crosbug.com/6460. We should ignore very short Y
     73   // move to prevent the window from shaking up and down.
     74   const int kKeepPositionThreshold = 2;  // px
     75   gfx::Rect last_bounds;
     76   if (candidate_window_view_)
     77     last_bounds = candidate_window_view_->GetAnchorRect();
     78 
     79   const int delta_y = abs(last_bounds.y() - cursor_bounds.y());
     80   if ((last_bounds.x() == cursor_bounds.x()) &&
     81       (delta_y <= kKeepPositionThreshold)) {
     82     DVLOG(1) << "Ignored set_cursor_bounds signal to prevent window shake";
     83     return;
     84   }
     85 
     86   cursor_bounds_ = cursor_bounds;
     87   composition_head_ = composition_head;
     88 
     89   // Remember the cursor bounds.
     90   if (candidate_window_view_)
     91     candidate_window_view_->SetCursorBounds(cursor_bounds, composition_head);
     92 
     93   // Mode indicator controller also needs the cursor bounds.
     94   mode_indicator_controller_->SetCursorBounds(cursor_bounds);
     95 }
     96 
     97 void CandidateWindowControllerImpl::FocusStateChanged(bool is_focused) {
     98   mode_indicator_controller_->FocusStateChanged(is_focused);
     99 }
    100 
    101 void CandidateWindowControllerImpl::UpdateLookupTable(
    102     const ui::CandidateWindow& candidate_window,
    103     bool visible) {
    104   // If it's not visible, hide the lookup table and return.
    105   if (!visible) {
    106     if (candidate_window_view_)
    107       candidate_window_view_->HideLookupTable();
    108     if (infolist_window_)
    109       infolist_window_->HideImmediately();
    110     // TODO(nona): Introduce unittests for crbug.com/170036.
    111     latest_infolist_entries_.clear();
    112     return;
    113   }
    114 
    115   if (!candidate_window_view_)
    116     InitCandidateWindowView();
    117   candidate_window_view_->UpdateCandidates(candidate_window);
    118   candidate_window_view_->ShowLookupTable();
    119 
    120   bool has_highlighted = false;
    121   std::vector<ui::InfolistEntry> infolist_entries;
    122   candidate_window.GetInfolistEntries(&infolist_entries, &has_highlighted);
    123 
    124   // If there is no change, just return.
    125   if (latest_infolist_entries_ == infolist_entries)
    126     return;
    127 
    128   latest_infolist_entries_ = infolist_entries;
    129 
    130   if (infolist_entries.empty()) {
    131     if (infolist_window_)
    132       infolist_window_->HideImmediately();
    133     return;
    134   }
    135 
    136   // Highlight moves out of the infolist entries.
    137   if (!has_highlighted) {
    138     if (infolist_window_)
    139       infolist_window_->HideWithDelay();
    140     return;
    141   }
    142 
    143   if (infolist_window_) {
    144     infolist_window_->Relayout(infolist_entries);
    145   } else {
    146     infolist_window_ = new ash::ime::InfolistWindow(
    147         candidate_window_view_, infolist_entries);
    148     infolist_window_->InitWidget();
    149     infolist_window_->GetWidget()->AddObserver(this);
    150   }
    151   infolist_window_->ShowWithDelay();
    152 }
    153 
    154 void CandidateWindowControllerImpl::UpdatePreeditText(
    155     const base::string16& text, unsigned int cursor, bool visible) {
    156   // If it's not visible, hide the preedit text and return.
    157   if (!visible || text.empty()) {
    158     if (candidate_window_view_)
    159       candidate_window_view_->HidePreeditText();
    160     return;
    161   }
    162   if (!candidate_window_view_)
    163     InitCandidateWindowView();
    164   candidate_window_view_->UpdatePreeditText(text);
    165   candidate_window_view_->ShowPreeditText();
    166 }
    167 
    168 void CandidateWindowControllerImpl::OnCandidateCommitted(int index) {
    169   FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
    170                     CandidateClicked(index));
    171 }
    172 
    173 void CandidateWindowControllerImpl::OnWidgetClosing(views::Widget* widget) {
    174   if (infolist_window_ && widget == infolist_window_->GetWidget()) {
    175     widget->RemoveObserver(this);
    176     infolist_window_ = NULL;
    177   } else if (candidate_window_view_ &&
    178              widget == candidate_window_view_->GetWidget()) {
    179     widget->RemoveObserver(this);
    180     candidate_window_view_->RemoveObserver(this);
    181     candidate_window_view_ = NULL;
    182     FOR_EACH_OBSERVER(CandidateWindowController::Observer, observers_,
    183                       CandidateWindowClosed());
    184   }
    185 }
    186 
    187 void CandidateWindowControllerImpl::AddObserver(
    188     CandidateWindowController::Observer* observer) {
    189   observers_.AddObserver(observer);
    190 }
    191 
    192 void CandidateWindowControllerImpl::RemoveObserver(
    193     CandidateWindowController::Observer* observer) {
    194   observers_.RemoveObserver(observer);
    195 }
    196 
    197 }  // namespace input_method
    198 }  // namespace chromeos
    199