172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
44a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
54a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/chromeos/input_method/candidate_window.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/observer_list.h"
143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_util.h"
153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stringprintf.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
1721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "third_party/cros/chromeos_input_method_ui.h"
1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas.h"
1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/font.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/label.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/textfield/textfield.h"
2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "views/events/event.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "views/layout/fill_layout.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "views/layout/grid_layout.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/screen.h"
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/root_view.h"
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_gtk.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/non_client_view.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window_delegate.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Colors used in the candidate window UI.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kFrameColor = SkColorSetRGB(0x96, 0x96, 0x96);
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kShortcutBackgroundColor = SkColorSetARGB(0x10, 0x3, 0x4, 0xf);
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kSelectedRowBackgroundColor = SkColorSetRGB(0xd1, 0xea, 0xff);
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kDefaultBackgroundColor = SkColorSetRGB(0xff, 0xff, 0xff);
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kSelectedRowFrameColor = SkColorSetRGB(0x7f, 0xac, 0xdd);
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kFooterTopColor = SkColorSetRGB(0xff, 0xff, 0xff);
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kFooterBottomColor = SkColorSetRGB(0xee, 0xee, 0xee);
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kShortcutColor = SkColorSetRGB(0x61, 0x61, 0x61);
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst SkColor kDisabledShortcutColor = SkColorSetRGB(0xcc, 0xcc, 0xcc);
453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst SkColor kAnnotationColor = SkColorSetRGB(0x88, 0x88, 0x88);
463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// We'll use a bigger font size, so Chinese characters are more readable
483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// in the candidate window.
49201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#if defined(CROS_FONTS_USING_BCI)
50201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochconst int kFontSizeDelta = 1;
51201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#else
52201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochconst int kFontSizeDelta = 2;
53201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch#endif
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The minimum width of candidate labels in the vertical candidate
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// window. We use this value to prevent the candidate window from being
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// too narrow when all candidates are short.
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMinCandidateLabelWidth = 100;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The maximum width of candidate labels in the vertical candidate
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// window. We use this value to prevent the candidate window from being
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// too wide when one of candidates are long.
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMaxCandidateLabelWidth = 500;
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// VerticalCandidateLabel is used for rendering candidate text in
653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// the vertical candidate window.
663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickclass VerticalCandidateLabel : public views::Label {
673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  virtual ~VerticalCandidateLabel() {}
683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Returns the preferred size, but guarantees that the width has at
703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // least kMinCandidateLabelWidth pixels.
713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  virtual gfx::Size GetPreferredSize() {
723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    gfx::Size size = Label::GetPreferredSize();
733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Hack. +2 is needed to prevent labels from getting elided like
743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // "abc..." in some cases. TODO(satorux): Figure out why it's
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // necessary.
763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    size.set_width(size.width() + 2);
773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (size.width() < kMinCandidateLabelWidth) {
783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      size.set_width(kMinCandidateLabelWidth);
793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (size.width() > kMaxCandidateLabelWidth) {
813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      size.set_width(kMaxCandidateLabelWidth);
823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return size;
843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick};
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Wraps the given view with some padding, and returns it.
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochviews::View* WrapWithPadding(views::View* view, const gfx::Insets& insets) {
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* wrapper = new views::View;
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Use GridLayout to give some insets inside.
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::GridLayout* layout = new views::GridLayout(wrapper);
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  wrapper->SetLayoutManager(layout);  // |wrapper| owns |layout|.
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->SetInsets(insets);
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::ColumnSet* column_set = layout->AddColumnSet(0);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  column_set->AddColumn(
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::GridLayout::FILL, views::GridLayout::FILL,
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      1, views::GridLayout::USE_PREF, 0, 0);
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->StartRow(0, 0);
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add the view contents.
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(view);  // |view| is owned by |wraper|, not |layout|.
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return wrapper;
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Creates shortcut text from the given index and the orientation.
1073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickstd::wstring CreateShortcutText(int index,
1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    chromeos::InputMethodLookupTable::Orientation orientation) {
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Choose the character used for the shortcut label.
1103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const wchar_t kShortcutCharacters[] = L"1234567890ABCDEF";
1113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // The default character should not be used but just in case.
1123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  wchar_t shortcut_character = L'?';
1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // -1 to exclude the null character at the end.
1143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (index < static_cast<int>(arraysize(kShortcutCharacters) - 1)) {
1153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_character = kShortcutCharacters[index];
1163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::wstring shortcut_text;
1193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (orientation == chromeos::InputMethodLookupTable::kVertical) {
1203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_text = base::StringPrintf(L"%lc", shortcut_character);
1213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  } else {
1223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_text = base::StringPrintf(L"%lc.", shortcut_character);
1233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return shortcut_text;
1263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Creates the shortcut label, and returns it (never returns NULL).
1293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// The label text is not set in this function.
1303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickviews::Label* CreateShortcutLabel(
1313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    chromeos::InputMethodLookupTable::Orientation orientation) {
1323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Create the shortcut label. The label will be owned by
1333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |wrapped_shortcut_label|, hence it's deleted when
1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |wrapped_shortcut_label| is deleted.
1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Label* shortcut_label = new views::Label;
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (orientation == chromeos::InputMethodLookupTable::kVertical) {
1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_label->SetFont(
1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        shortcut_label->font().DeriveFont(kFontSizeDelta, gfx::Font::BOLD));
1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  } else {
1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_label->SetFont(
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        shortcut_label->font().DeriveFont(kFontSizeDelta));
1433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // TODO(satorux): Maybe we need to use language specific fonts for
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // candidate_label, like Chinese font for Chinese input method?
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  shortcut_label->SetColor(kShortcutColor);
1473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return shortcut_label;
1493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Wraps the shortcut label, then decorates wrapped shortcut label
1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// and returns it (never returns NULL).
1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// The label text is not set in this function.
1543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickviews::View* CreateWrappedShortcutLabel(views::Label* shortcut_label,
1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    chromeos::InputMethodLookupTable::Orientation orientation) {
1563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Wrap it with padding.
1573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const gfx::Insets kVerticalShortcutLabelInsets(1, 6, 1, 6);
1583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const gfx::Insets kHorizontalShortcutLabelInsets(1, 3, 1, 0);
1593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const gfx::Insets insets =
1603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      (orientation == chromeos::InputMethodLookupTable::kVertical ?
1613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick       kVerticalShortcutLabelInsets :
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick       kHorizontalShortcutLabelInsets);
1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::View* wrapped_shortcut_label =
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      WrapWithPadding(shortcut_label, insets);
1653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Add decoration based on the orientation.
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (orientation == chromeos::InputMethodLookupTable::kVertical) {
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Set the background color.
1693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    wrapped_shortcut_label->set_background(
1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        views::Background::CreateSolidBackground(
1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick            kShortcutBackgroundColor));
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return wrapped_shortcut_label;
1753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Creates the candidate label, and returns it (never returns NULL).
1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// The label text is not set in this function.
1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickviews::Label* CreateCandidateLabel(
1803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    chromeos::InputMethodLookupTable::Orientation orientation) {
1813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Label* candidate_label = NULL;
1823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Create the candidate label. The label will be added to |this| as a
1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // child view, hence it's deleted when |this| is deleted.
1853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (orientation == chromeos::InputMethodLookupTable::kVertical) {
1863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_label = new VerticalCandidateLabel;
1873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  } else {
1883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_label = new views::Label;
1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Change the font size.
1923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  candidate_label->SetFont(
1933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      candidate_label->font().DeriveFont(kFontSizeDelta));
1943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  candidate_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
1953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return candidate_label;
1973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Creates the annotation label, and return it (never returns NULL).
2003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// The label text is not set in this function.
2013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickviews::Label* CreateAnnotationLabel(
2023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    chromeos::InputMethodLookupTable::Orientation orientation) {
2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Create the annotation label.
2043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Label* annotation_label = new views::Label;
2053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Change the font size and color.
2073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  annotation_label->SetFont(
2083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      annotation_label->font().DeriveFont(kFontSizeDelta));
2093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  annotation_label->SetColor(kAnnotationColor);
2103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  annotation_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
2113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return annotation_label;
2133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Computes shortcut column width.
2163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickint ComputeShortcutColumnWidth(
2173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const chromeos::InputMethodLookupTable& lookup_table) {
2183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int shortcut_column_width = 0;
2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Create the shortcut label. The label will be owned by
2203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |wrapped_shortcut_label|, hence it's deleted when
2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |wrapped_shortcut_label| is deleted.
2223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Label* shortcut_label = CreateShortcutLabel(lookup_table.orientation);
2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_ptr<views::View> wrapped_shortcut_label(
2243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      CreateWrappedShortcutLabel(shortcut_label, lookup_table.orientation));
2253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute the max width in shortcut labels.
2273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We'll create temporary shortcut labels, and choose the largest width.
2283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (int i = 0; i < lookup_table.page_size; ++i) {
2293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_label->SetText(
2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        CreateShortcutText(i, lookup_table.orientation));
2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_column_width =
2323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        std::max(shortcut_column_width,
2333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 wrapped_shortcut_label->GetPreferredSize().width());
2343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return shortcut_column_width;
2373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
23921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// Computes the page index. For instance, if the page size is 9, and the
24021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// cursor is pointing to 13th candidate, the page index will be 1 (2nd
24121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// page, as the index is zero-origin). Returns -1 on error.
24221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenint ComputePageIndex(const chromeos::InputMethodLookupTable& lookup_table) {
24321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (lookup_table.page_size > 0)
24421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return lookup_table.cursor_absolute_index / lookup_table.page_size;
24521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return -1;
24621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
24721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Computes candidate column width.
2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickint ComputeCandidateColumnWidth(
2503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const chromeos::InputMethodLookupTable& lookup_table) {
2513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int candidate_column_width = 0;
2523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_ptr<views::Label> candidate_label(
2533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      CreateCandidateLabel(lookup_table.orientation));
2543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute the start index of |lookup_table_|.
25621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int current_page_index = ComputePageIndex(lookup_table);
25721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (current_page_index < 0)
25821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return 0;
2593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const size_t start_from = current_page_index * lookup_table.page_size;
2603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute the max width in candidate labels.
2623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We'll create temporary candidate labels, and choose the largest width.
263dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (size_t i = 0; i + start_from < lookup_table.candidates.size(); ++i) {
2643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const size_t index = start_from + i;
2653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_label->SetText(
2673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        UTF8ToWide(lookup_table.candidates[index]));
2683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_column_width =
2693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        std::max(candidate_column_width,
2703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 candidate_label->GetPreferredSize().width());
2713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return candidate_column_width;
2743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Computes annotation column width.
2773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickint ComputeAnnotationColumnWidth(
2783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const chromeos::InputMethodLookupTable& lookup_table) {
2793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int annotation_column_width = 0;
2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  scoped_ptr<views::Label> annotation_label(
2813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      CreateAnnotationLabel(lookup_table.orientation));
2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute the start index of |lookup_table_|.
28421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int current_page_index = ComputePageIndex(lookup_table);
28521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (current_page_index < 0)
28621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return 0;
2873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const size_t start_from = current_page_index * lookup_table.page_size;
2883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute max width in annotation labels.
2903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We'll create temporary annotation labels, and choose the largest width.
291dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  for (size_t i = 0; i + start_from < lookup_table.annotations.size(); ++i) {
2923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const size_t index = start_from + i;
2933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    annotation_label->SetText(
2953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        UTF8ToWide(lookup_table.annotations[index]));
2963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    annotation_column_width =
2973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        std::max(annotation_column_width,
2983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                 annotation_label->GetPreferredSize().width());
2993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
3013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return annotation_column_width;
3023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
3033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace chromeos {
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass CandidateView;
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CandidateWindowView is the main container of the candidate window UI.
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass CandidateWindowView : public views::View {
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The object can be monitored by the observer.
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  class Observer {
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   public:
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    virtual ~Observer() {}
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The function is called when a candidate is committed.
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // See comments at NotifyCandidateClicke() in chromeos_input_method_ui.h for
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // details about the parameters.
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    virtual void OnCandidateCommitted(int index, int button, int flag) = 0;
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit CandidateWindowView(views::Widget* parent_frame);
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~CandidateWindowView() {}
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Init();
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Adds the given observer. The ownership is not transferred.
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void AddObserver(Observer* observer) {
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    observers_.AddObserver(observer);
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Removes the given observer.
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void RemoveObserver(Observer* observer) {
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    observers_.RemoveObserver(observer);
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Selects the candidate specified by the index in the current page
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // (zero-origin).  Changes the appearance of the selected candidate,
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // updates the information in the candidate window as needed.
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SelectCandidateAt(int index_in_page);
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when a candidate is being dragged. From the
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // given point, locates the candidate under the mouse cursor, and
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selects it.
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void OnCandidateDragged(const gfx::Point& point);
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Commits the candidate currently being selected.
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CommitCandidate();
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Hides the lookup table.
35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void HideLookupTable();
35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Hides the auxiliary text.
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void HideAuxiliaryText();
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Shows the auxiliary text.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void ShowAuxiliaryText();
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Updates the auxiliary text.
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateAuxiliaryText(const std::string& utf8_text);
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Returns true if we should update candidate views in the window.  For
36321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // instance, if we are going to show the same candidates as before, we
36421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // don't have to update candidate views. This happens when the user just
36521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // moves the cursor in the same page in the candidate window.
36621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  bool ShouldUpdateCandidateViews(
36721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      const InputMethodLookupTable& old_table,
36821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      const InputMethodLookupTable& new_table);
36921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Updates candidates of the candidate window from |lookup_table|.
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Candidates are arranged per |orientation|.
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateCandidates(const InputMethodLookupTable& lookup_table);
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
37421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Resizes and moves the parent frame. The two actions should be
37521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // performed consecutively as resizing may require the candidate window
37621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // to move. For instance, we may need to move the candidate window from
37721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // below the cursor to above the cursor, if the candidate window becomes
37821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // too big to be shown near the bottom of the screen.  This function
37921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // needs to be called when the visible contents of the candidate window
38021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // are modified.
38121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  void ResizeAndMoveParentFrame();
38221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
38321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Resizes the parent frame per the current contents size.
38421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  //
38521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // The function is rarely used solely. See comments at
38621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // ResizeAndMoveParentFrame().
38721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  void ResizeParentFrame();
38821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
38921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Moves the candidate window per the current cursor location, and the
39021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // horizontal offset.
39121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  //
39221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // The function is rarely used solely. See comments at
39321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // ResizeAndMoveParentFrame().
39421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  void MoveParentFrame();
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the horizontal offset used for placing the vertical candidate
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // window so that the first candidate is aligned with the the text being
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // converted like:
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //      XXX           <- The user is converting XXX
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //   +-----+
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //   |1 XXX|
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //   |2 YYY|
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //   |3 ZZZ|
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns 0 if no candidate is present.
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int GetHorizontalOffset();
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // A function to be called when one of the |candidate_views_| receives a mouse
41072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // press event.
41172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void OnMousePressed();
41272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // A function to be called when one of the |candidate_views_| receives a mouse
41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // release event.
41472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void OnMouseReleased();
41572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
41621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  void set_cursor_location(const gfx::Rect& cursor_location) {
41721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    cursor_location_ = cursor_location;
41821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
41921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
42021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const gfx::Rect& cursor_location() const { return cursor_location_; }
42121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
42221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen protected:
42321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Override View::VisibilityChanged()
424ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void VisibilityChanged(View* starting_from, bool is_visible) OVERRIDE;
42521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
426dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Override View::OnBoundsChanged()
427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
42821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initializes the candidate views if needed.
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void MaybeInitializeCandidateViews(
4323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      const InputMethodLookupTable& lookup_table);
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Creates the footer area, where we show status information.
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // For instance, we show a cursor position like 2/19.
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* CreateFooterArea();
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Creates the header area, where we show auxiliary text.
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* CreateHeaderArea();
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The lookup table (candidates).
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InputMethodLookupTable lookup_table_;
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The index in the current page of the candidate currently being selected.
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int selected_candidate_index_in_page_;
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The observers of the object.
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ObserverList<Observer> observers_;
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The parent frame.
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Widget* parent_frame_;
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Views created in the class will be part of tree of |this|, so these
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // child views will be deleted when |this| is deleted.
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The candidate area is where candidates are rendered.
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* candidate_area_;
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The footer area is where the auxiliary text is shown, if the
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // orientation is vertical. Usually the auxiliary text is used for
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // showing candidate number information like 2/19.
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* footer_area_;
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use this when we show something in the footer area.
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<views::View> footer_area_contents_;
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use this when we show nothing in the footer area.
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<views::View> footer_area_place_holder_;
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The header area is where the auxiliary text is shown, if the
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // orientation is horizontal. If the auxiliary text is not provided, we
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // show nothing.  For instance, we show pinyin text like "zhong'guo".
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* header_area_;
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use this when we show something in the header area.
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<views::View> header_area_contents_;
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We use this when we show nothing in the header area.
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<views::View> header_area_place_holder_;
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The candidate views are used for rendering candidates.
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<CandidateView*> candidate_views_;
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The header label is shown in the header area.
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Label* header_label_;
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The footer label is shown in the footer area.
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Label* footer_label_;
4803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Current columns width in |candidate_area_|.
4823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int previous_shortcut_column_width_;
4833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int previous_candidate_column_width_;
4843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int previous_annotation_column_width_;
48521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
48621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // The last cursor location.
48721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  gfx::Rect cursor_location_;
48872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
48972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // true if a mouse button is pressed, and is not yet released.
49072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool mouse_is_pressed_;
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CandidateRow renderes a row of a candidate.
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass CandidateView : public views::View {
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CandidateView(CandidateWindowView* parent_candidate_window,
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                int index_in_page,
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                InputMethodLookupTable::Orientation orientation);
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~CandidateView() {}
5003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Initializes the candidate view with the given column widths.
5013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // A width of 0 means that the column is resizable.
5023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void Init(int shortcut_column_width,
5033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick            int candidate_column_width,
5043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick            int annotation_column_width);
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Sets candidate text to the given text.
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetCandidateText(const std::wstring& text);
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Sets shortcut text to the given text.
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetShortcutText(const std::wstring& text);
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Sets annotation text to the given text.
5133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void SetAnnotationText(const std::wstring& text);
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Selects the candidate row. Changes the appearance to make it look
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // like a selected candidate.
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Select();
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Unselects the candidate row. Changes the appearance to make it look
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // like an unselected candidate.
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Unselect();
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Enables or disables the candidate row based on |enabled|. Changes the
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // appearance to make it look like unclickable area.
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void SetRowEnabled(bool enabled);
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the relative position of the candidate label.
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point GetCandidateLabelPosition() const;
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Overridden from View:
532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE;
534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
535ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnMouseCaptureLost() OVERRIDE;
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Zero-origin index in the current page.
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index_in_page_;
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The orientation of the candidate view.
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InputMethodLookupTable::Orientation orientation_;
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The parent candidate window that contains this view.
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CandidateWindowView* parent_candidate_window_;
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Views created in the class will be part of tree of |this|, so these
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // child views will be deleted when |this| is deleted.
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The shortcut label renders shortcut numbers like 1, 2, and 3.
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Label* shortcut_label_;
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The candidate label renders candidates.
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Label* candidate_label_;
5533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // The annotation label renders annotations.
5543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::Label* annotation_label_;
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// The implementation of CandidateWindowController.
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// CandidateWindowController controls the CandidateWindow.
5594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass CandidateWindowController::Impl : public CandidateWindowView::Observer {
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
5614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  Impl();
5624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  virtual ~Impl();
5634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
5644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Initializes the candidate window. Returns true on success.
5653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  bool Init();
566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch private:
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // CandidateWindowView::Observer implementation.
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void OnCandidateCommitted(int index,
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    int button,
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    int flags);
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Creates the candidate window view.
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CreateView();
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when |HideAuxiliaryText| signal is received in
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // libcros. |input_method_library| is a void pointer to this object.
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static void OnHideAuxiliaryText(void* input_method_library);
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when |HideLookupTable| signal is received in
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // libcros. |input_method_library| is a void pointer to this object.
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static void OnHideLookupTable(void* input_method_library);
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when |SetCursorLocation| signal is received
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // in libcros. |input_method_library| is a void pointer to this object.
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static void OnSetCursorLocation(void* input_method_library,
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  int x,
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  int y,
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  int width,
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  int height);
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when |UpdateAuxiliaryText| signal is received
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // in libcros. |input_method_library| is a void pointer to this object.
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static void OnUpdateAuxiliaryText(void* input_method_library,
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    const std::string& utf8_text,
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    bool visible);
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The function is called when |UpdateLookupTable| signal is received
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // in libcros. |input_method_library| is a void pointer to this object.
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static void OnUpdateLookupTable(void* input_method_library,
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  const InputMethodLookupTable& lookup_table);
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // This function is called by libcros when ibus connects or disconnects.
6043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |input_method_library| is a void pointer to this object.
6053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  static void OnConnectionChange(void* input_method_library, bool connected);
6063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The connection is used for communicating with input method UI logic
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // in libcros.
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InputMethodUiStatusConnection* ui_status_connection_;
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The candidate window view.
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CandidateWindowView* candidate_window_;
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This is the outer frame of the candidate window view. The frame will
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // own |candidate_window_|.
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<views::Widget> frame_;
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCandidateView::CandidateView(
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CandidateWindowView* parent_candidate_window,
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int index_in_page,
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    InputMethodLookupTable::Orientation orientation)
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : index_in_page_(index_in_page),
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      orientation_(orientation),
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      parent_candidate_window_(parent_candidate_window),
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_label_(NULL),
6273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      candidate_label_(NULL),
6283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      annotation_label_(NULL) {
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CandidateView::Init(int shortcut_column_width,
6323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                         int candidate_column_width,
6333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                         int annotation_column_width) {
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::GridLayout* layout = new views::GridLayout(this);
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetLayoutManager(layout);  // |this| owns |layout|.
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Create Labels.
6383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  shortcut_label_ = CreateShortcutLabel(orientation_);
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* wrapped_shortcut_label =
6403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      CreateWrappedShortcutLabel(shortcut_label_, orientation_);
6413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  candidate_label_ = CreateCandidateLabel(orientation_);
6423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  annotation_label_ = CreateAnnotationLabel(orientation_);
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Initialize the column set with three columns.
6453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  views::ColumnSet* column_set = layout->AddColumnSet(0);
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // If orientation is vertical, each column width is fixed.
6483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Otherwise the width is resizable.
6493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const views::GridLayout::SizeType column_type =
6503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      orientation_ == InputMethodLookupTable::kVertical ?
6513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          views::GridLayout::FIXED : views::GridLayout::USE_PREF;
652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const int padding_column_width =
6543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      orientation_ == InputMethodLookupTable::kVertical ? 4 : 6;
655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Set shortcut column type and width.
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
6583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        0, column_type, shortcut_column_width, 0);
6593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  column_set->AddPaddingColumn(0, padding_column_width);
6603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
6613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Set candidate column type and width.
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
6633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        0, column_type, candidate_column_width, 0);
6643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  column_set->AddPaddingColumn(0, padding_column_width);
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Set annotation column type and width.
6673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
6683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        0, column_type, annotation_column_width, 0);
6693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  column_set->AddPaddingColumn(0, padding_column_width);
6703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
6713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Add the shortcut label, the candidate label, and annotation label.
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->StartRow(0, 0);
6733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // |wrapped_shortcut_label|, |candidate_label_|, and |annotation_label_|
6743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // will be owned by |this|.
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(wrapped_shortcut_label);
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(candidate_label_);
6773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  layout->AddView(annotation_label_);
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateView::SetCandidateText(const std::wstring& text) {
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_label_->SetText(text);
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateView::SetShortcutText(const std::wstring& text) {
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shortcut_label_->SetText(text);
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid CandidateView::SetAnnotationText(const std::wstring& text) {
6893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  annotation_label_->SetText(text);
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateView::Select() {
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_background(
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateSolidBackground(kSelectedRowBackgroundColor));
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_border(views::Border::CreateSolidBorder(1, kSelectedRowFrameColor));
69621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Need to call SchedulePaint() for background and border color changes.
69721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  SchedulePaint();
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateView::Unselect() {
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_background(NULL);
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_border(NULL);
70321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  SchedulePaint();  // See comments at Select().
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateView::SetRowEnabled(bool enabled) {
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shortcut_label_->SetColor(
708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      enabled ? kShortcutColor : kDisabledShortcutColor);
709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Point CandidateView::GetCandidateLabelPosition() const {
71272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return candidate_label_->GetMirroredPosition();
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool CandidateView::OnMousePressed(const views::MouseEvent& event) {
71672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  parent_candidate_window_->OnMousePressed();
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Select the candidate. We'll commit the candidate when the mouse
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // button is released.
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  parent_candidate_window_->SelectCandidateAt(index_in_page_);
720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Request MouseDraggged and MouseReleased events.
721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool CandidateView::OnMouseDragged(const views::MouseEvent& event) {
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point location_in_candidate_window = event.location();
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View::ConvertPointToView(this, parent_candidate_window_,
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  &location_in_candidate_window);
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Notify the candidate window that a candidate is now being dragged.
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  parent_candidate_window_->OnCandidateDragged(location_in_candidate_window);
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Request MouseReleased event.
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
734ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CandidateView::OnMouseReleased(const views::MouseEvent& event) {
735ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Commit the current candidate.
736ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  parent_candidate_window_->CommitCandidate();
737ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  OnMouseCaptureLost();
738ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
739ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
740ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CandidateView::OnMouseCaptureLost() {
74172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  parent_candidate_window_->OnMouseReleased();
742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCandidateWindowView::CandidateWindowView(
745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    views::Widget* parent_frame)
74621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    : selected_candidate_index_in_page_(0),
747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      parent_frame_(parent_frame),
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      candidate_area_(NULL),
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      footer_area_(NULL),
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_area_(NULL),
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_label_(NULL),
7523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      footer_label_(NULL),
7533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      previous_shortcut_column_width_(0),
7543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      previous_candidate_column_width_(0),
75572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      previous_annotation_column_width_(0),
75672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      mouse_is_pressed_(false) {
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::Init() {
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set the background and the border of the view.
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_background(
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateSolidBackground(kDefaultBackgroundColor));
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  set_border(views::Border::CreateSolidBorder(1, kFrameColor));
764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the header area.
766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_ = CreateHeaderArea();
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the candidate area.
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_area_ = new views::View;
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the footer area.
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_ = CreateFooterArea();
771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set the window layout of the view
773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::GridLayout* layout = new views::GridLayout(this);
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetLayoutManager(layout);  // |this| owns layout|.
775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::ColumnSet* column_set = layout->AddColumnSet(0);
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL,
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        0, views::GridLayout::USE_PREF, 0, 0);
778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add the header area.
780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->StartRow(0, 0);
781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(header_area_);  // |header_area_| is owned by |this|.
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add the candidate area.
784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->StartRow(0, 0);
785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(candidate_area_);  // |candidate_area_| is owned by |this|.
786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add the footer area.
788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->StartRow(0, 0);
789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->AddView(footer_area_);  // |footer_area_| is owned by |this|.
790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid CandidateWindowView::HideLookupTable() {
79372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!mouse_is_pressed_) {
79472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    parent_frame_->Hide();
79572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return;
79672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
79772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
79872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // We should not hide the |frame_| when a mouse is pressed, so we don't run
79972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // into issues below.
80072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
80172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // First, in the following scenario, it seems that the Views popup window does
80272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // not release mouse/keyboard grab even after it gets hidden.
80372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
804ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // 1. create a popup window by views::Widget::CreateWidget() with the
805ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  //    accept_events flag set to true on the CreateParams.
80672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 2. press a mouse button on the window.
80772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 3. before releasing the mouse button, Hide() the window.
80872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 4. release the button.
80972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
81072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // And if we embed IME candidate window into Chrome, the window sometimes
81172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // receives an extra 'hide-lookup-table' event before mouse button is
81272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // released:
81372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
81472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 1. the candidate window is clicked.
81572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 2. The mouse click handler in this file, OnMousePressed() in CandidateView,
81672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //    is called, and the handler consumes the event by returning true.
81772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 3. HOWEVER, if the candidate window is embedded into Chrome, the event is
81872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //    also sent to Chrome! (problem #1)
81972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 4. im-ibus.so in Chrome sends 'focus-out' event to ibus-daemon.
82072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 5. ibus-daemon sends 'hide-lookup-table' event to the candidate window.
82172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 6. the window is hidden, but the window does not release mouse/keyboard
82272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //    grab! (problem #2)
82372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 7. mouse button is released.
82472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // 8. now all mouse/keyboard events are consumed by the hidden popup, and are
82572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //    not sent to Chrome.
82672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
82772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(yusukes): investigate why the click event is sent to both candidate
82872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // window and Chrome. http://crosbug.com/11423
82972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(yusukes): investigate if we could fix Views so it always releases grab
83072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // when a popup window gets hidden. http://crosbug.com/11422
83172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
83272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LOG(WARNING) << "Can't hide the table since a mouse button is not released.";
83372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
83472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
83572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid CandidateWindowView::OnMousePressed() {
83672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  mouse_is_pressed_ = true;
83772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
83872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
83972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid CandidateWindowView::OnMouseReleased() {
84072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  mouse_is_pressed_ = false;
84172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
84272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::HideAuxiliaryText() {
844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* target_area = (
845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kHorizontal ?
846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_area_ : footer_area_);
847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* target_place_holder = (
848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kHorizontal ?
849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_area_place_holder_.get() :
850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      footer_area_place_holder_.get());
851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Put the place holder to the target display area.
852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  target_area->RemoveAllChildViews(false);  // Don't delete child views.
853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  target_area->AddChildView(target_place_holder);
854c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
855c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
856c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::ShowAuxiliaryText() {
857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* target_area = (
858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kHorizontal ?
859c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_area_ : footer_area_);
860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* target_contents = (
861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kHorizontal ?
862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_area_contents_.get() :
863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      footer_area_contents_.get());
86421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
86572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (target_contents->parent() != target_area) {
86621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // If contents not in display area, put it in.
86721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    target_area->RemoveAllChildViews(false);  // Don't delete child views.
86821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    target_area->AddChildView(target_contents);
86921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::UpdateAuxiliaryText(const std::string& utf8_text) {
873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::Label* target_label = (
874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kHorizontal ?
875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      header_label_ : footer_label_);
876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  target_label->SetText(UTF8ToWide(utf8_text));
877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool CandidateWindowView::ShouldUpdateCandidateViews(
88021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const InputMethodLookupTable& old_table,
88121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const InputMethodLookupTable& new_table) {
88221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Check if most table contents are identical.
88321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (old_table.page_size == new_table.page_size &&
88421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      old_table.orientation == new_table.orientation &&
88521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      old_table.candidates == new_table.candidates &&
88621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      old_table.labels == new_table.labels &&
88721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      old_table.annotations == new_table.annotations &&
88821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      // Check if the page indexes are identical.
88921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      ComputePageIndex(old_table) == ComputePageIndex(new_table)) {
89021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // If all of the conditions are met, we don't have to update candidate
89121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // views.
89221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return false;
89321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
89421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return true;
89521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
89621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::UpdateCandidates(
89821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const InputMethodLookupTable& new_lookup_table) {
89921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const bool should_update = ShouldUpdateCandidateViews(lookup_table_,
90021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                                                        new_lookup_table);
90121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Updating the candidate views is expensive. We'll skip this if possible.
90221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (should_update) {
90321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // Initialize candidate views if necessary.
90421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    MaybeInitializeCandidateViews(new_lookup_table);
90521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
90621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // Compute the index of the current page.
90721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const int current_page_index = ComputePageIndex(new_lookup_table);
90821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (current_page_index < 0) {
90921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      LOG(ERROR) << "Invalid lookup_table: " << new_lookup_table.ToString();
91021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      return;
911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
91221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
91321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // Update the candidates in the current page.
91421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const size_t start_from = current_page_index * new_lookup_table.page_size;
91521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
91621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // In some cases, engines send empty shortcut labels. For instance,
91721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // ibus-mozc sends empty labels when they show suggestions. In this
91821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // case, we should not show shortcut labels.
91921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    const bool no_shortcut_mode =
92021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        (start_from < new_lookup_table.labels.size() &&
92121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen         new_lookup_table.labels[start_from] == "");
92221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    for (size_t i = 0; i < candidate_views_.size(); ++i) {
92321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      const size_t index_in_page = i;
92421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      const size_t candidate_index = start_from + index_in_page;
92521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      CandidateView* candidate_view = candidate_views_[index_in_page];
92621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      // Set the shortcut text.
92721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      if (no_shortcut_mode) {
92821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetShortcutText(L"");
92921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      } else {
93021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // At this moment, we don't use labels sent from engines for UX
93121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // reasons. First, we want to show shortcut labels in empty rows
93221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // (ex. show 6, 7, 8, ... in empty rows when the number of
93321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // candidates is 5). Second, we want to add a period after each
93421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // shortcut label when the candidate window is horizontal.
93521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetShortcutText(
93621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen            CreateShortcutText(i, new_lookup_table.orientation));
93721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      }
93821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      // Set the candidate text.
93921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      if (candidate_index < new_lookup_table.candidates.size() &&
94021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen          candidate_index < new_lookup_table.annotations.size()) {
94121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetCandidateText(
94221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen            UTF8ToWide(new_lookup_table.candidates[candidate_index]));
94321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetAnnotationText(
94421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen            UTF8ToWide(new_lookup_table.annotations[candidate_index]));
94521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetRowEnabled(true);
94621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      } else {
94721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        // Disable the empty row.
94821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetCandidateText(L"");
94921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetAnnotationText(L"");
95021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        candidate_view->SetRowEnabled(false);
95121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      }
952c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
95421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Update the current lookup table. We'll use lookup_table_ from here.
95521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Note that SelectCandidateAt() uses lookup_table_.
95621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  lookup_table_ = new_lookup_table;
95721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
95821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Select the current candidate in the page.
95921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int current_candidate_in_page =
96021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      lookup_table_.cursor_absolute_index % lookup_table_.page_size;
96121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  SelectCandidateAt(current_candidate_in_page);
962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::MaybeInitializeCandidateViews(
9653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const InputMethodLookupTable& lookup_table) {
9663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const InputMethodLookupTable::Orientation orientation =
9673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      lookup_table.orientation;
9683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const int page_size = lookup_table.page_size;
9693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Current column width.
9713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int shortcut_column_width = 0;
9723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int candidate_column_width = 0;
9733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  int annotation_column_width = 0;
9743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // If orientation is horizontal, don't need to compute width,
9763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // because each label is left aligned.
9773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (orientation == InputMethodLookupTable::kVertical) {
9783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    shortcut_column_width = ComputeShortcutColumnWidth(lookup_table);
9793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_column_width = ComputeCandidateColumnWidth(lookup_table);
9803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    annotation_column_width = ComputeAnnotationColumnWidth(lookup_table);
9813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
9823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // If the requested number of views matches the number of current views, and
9843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // previous and current column width are same, just reuse these.
98521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  //
98621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Note that the early exit logic is not only useful for improving
98721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // performance, but also necessary for the horizontal candidate window
98821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // to be redrawn properly. If we get rid of the logic, the horizontal
98921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // candidate window won't get redrawn properly for some reason when
99021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // there is no size change. You can test this by removing "return" here
99121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // and type "ni" with Pinyin input method.
9923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (static_cast<int>(candidate_views_.size()) == page_size &&
9933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      lookup_table_.orientation == orientation &&
9943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      previous_shortcut_column_width_ == shortcut_column_width &&
9953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      previous_candidate_column_width_ == candidate_column_width &&
9963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      previous_annotation_column_width_ == annotation_column_width) {
997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Update the previous column widths.
10013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  previous_shortcut_column_width_ = shortcut_column_width;
10023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  previous_candidate_column_width_ = candidate_column_width;
10033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  previous_annotation_column_width_ = annotation_column_width;
10043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1005c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Clear the existing candidate_views if any.
1006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < candidate_views_.size(); ++i) {
1007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    candidate_area_->RemoveChildView(candidate_views_[i]);
1008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1009c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_views_.clear();
1010c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1011c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::GridLayout* layout = new views::GridLayout(candidate_area_);
1012c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |candidate_area_| owns |layout|.
1013c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_area_->SetLayoutManager(layout);
1014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initialize the column set.
1015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::ColumnSet* column_set = layout->AddColumnSet(0);
1016c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (orientation == InputMethodLookupTable::kVertical) {
1017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    column_set->AddColumn(views::GridLayout::FILL,
1018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          views::GridLayout::FILL,
1019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          0, views::GridLayout::USE_PREF, 0, 0);
1020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
10213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    for (int i = 0; i < page_size; ++i) {
1022c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      column_set->AddColumn(views::GridLayout::FILL,
1023c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            views::GridLayout::FILL,
1024c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            0, views::GridLayout::USE_PREF, 0, 0);
1025c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1026c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1027c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1028c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set insets so the border of the selected candidate is drawn inside of
1029c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the border of the main candidate window, but we don't have the inset
1030c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // at the top and the bottom as we have the borders of the header and
1031c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // footer areas.
1032c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const gfx::Insets kCandidateAreaInsets(0, 1, 0, 1);
1033c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  layout->SetInsets(kCandidateAreaInsets.top(),
1034c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    kCandidateAreaInsets.left(),
1035c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    kCandidateAreaInsets.bottom(),
1036c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    kCandidateAreaInsets.right());
1037c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1038c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add views to the candidate area.
1039c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (orientation == InputMethodLookupTable::kHorizontal) {
1040c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    layout->StartRow(0, 0);
1041c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
10423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
10433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (int i = 0; i < page_size; ++i) {
1044c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CandidateView* candidate_row = new CandidateView(this, i, orientation);
10453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    candidate_row->Init(shortcut_column_width,
10463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        candidate_column_width,
10473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        annotation_column_width);
1048c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    candidate_views_.push_back(candidate_row);
1049c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (orientation == InputMethodLookupTable::kVertical) {
1050c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      layout->StartRow(0, 0);
1051c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
10523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // |candidate_row| will be owned by |candidate_area_|.
1053c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    layout->AddView(candidate_row);
1054c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
10553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
10563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Compute views size in |layout|.
10573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // If we don't call this function, GetHorizontalOffset() often
10583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // returns invalid value (returns 0), then candidate window
105921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // moves right from the correct position in MoveParentFrame().
10603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // TODO(nhiroki): Figure out why it returns invalid value.
10613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // It seems that the x-position of the candidate labels is not set.
106272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  layout->Layout(candidate_area_);
1063c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1064c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1065c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochviews::View* CandidateWindowView::CreateHeaderArea() {
1066c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_area_place_holder_| will not be owned by another view.
1067c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This will be deleted by scoped_ptr.
1068c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  //
1069c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This is because we swap the contents of |header_area_| between
1070c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_area_place_holder_| (to show nothing) and
1071c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_area_contents_| (to show something). In other words,
1072c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_area_| only contains one of the two views hence cannot own
1073c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the two views at the same time.
1074c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_place_holder_.reset(new views::View);
1075c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_place_holder_->set_parent_owned(false);  // Won't be owened.
1076c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1077c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_label_| will be owned by |header_area_contents_|.
1078c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_label_ = new views::Label;
1079c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
1080c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1081c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const gfx::Insets kHeaderInsets(2, 2, 2, 4);
1082c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |header_area_contents_| will not be owned by another view.
1083c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See a comment at |header_area_place_holder_| for why.
1084c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_contents_.reset(
1085c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      WrapWithPadding(header_label_, kHeaderInsets));
1086c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_contents_->set_parent_owned(false);  // Won't be owened.
1087c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_contents_->set_border(
1088c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Border::CreateSolidBorder(1, kFrameColor));
1089c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area_contents_->set_background(
1090c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateVerticalGradientBackground(
1091c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          kFooterTopColor,
1092c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          kFooterBottomColor));
1093c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1094c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* header_area = new views::View;
1095c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area->SetLayoutManager(new views::FillLayout);
1096c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initialize the header area with the place holder (i.e. show nothing).
1097c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  header_area->AddChildView(header_area_place_holder_.get());
1098c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return header_area;
1099c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochviews::View* CandidateWindowView::CreateFooterArea() {
1102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |footer_area_place_holder_| will not be owned by another view.
1103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See also the comment about |header_area_place_holder_| in
1104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // CreateHeaderArea().
1105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_place_holder_.reset(new views::View);
1106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_place_holder_->set_parent_owned(false);  // Won't be owened.
1107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_label_ = new views::Label();
1109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_label_->SetHorizontalAlignment(views::Label::ALIGN_RIGHT);
1110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const gfx::Insets kFooterInsets(2, 2, 2, 4);
1112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_contents_.reset(
1113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      WrapWithPadding(footer_label_, kFooterInsets));
1114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_contents_->set_parent_owned(false);  // Won't be owened.
1115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_contents_->set_border(
1116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Border::CreateSolidBorder(1, kFrameColor));
1117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area_contents_->set_background(
1118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      views::Background::CreateVerticalGradientBackground(
1119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          kFooterTopColor,
1120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          kFooterBottomColor));
1121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::View* footer_area = new views::View;
1123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area->SetLayoutManager(new views::FillLayout);
1124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initialize the footer area with the place holder (i.e. show nothing).
1125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  footer_area->AddChildView(footer_area_place_holder_.get());
1126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return footer_area;
1127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::SelectCandidateAt(int index_in_page) {
113021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int current_page_index = ComputePageIndex(lookup_table_);
113121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (current_page_index < 0) {
113221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    LOG(ERROR) << "Invalid lookup_table: " << lookup_table_.ToString();
113321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    return;
113421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
113521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
113621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int cursor_absolute_index =
113721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      lookup_table_.page_size * current_page_index + index_in_page;
1138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Ignore click on out of range views.
1139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (cursor_absolute_index < 0 ||
1140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      cursor_absolute_index >=
1141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int>(lookup_table_.candidates.size())) {
1142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Unselect the currently selected candidate.
114621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  candidate_views_[selected_candidate_index_in_page_]->Unselect();
1147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Remember the currently selected candidate index in the current page.
1148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  selected_candidate_index_in_page_ = index_in_page;
1149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Select the candidate specified by index_in_page.
1151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_views_[index_in_page]->Select();
1152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update the cursor indexes in the model.
1154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  lookup_table_.cursor_absolute_index = cursor_absolute_index;
1155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::OnCandidateDragged(
1158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const gfx::Point& location) {
1159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < candidate_views_.size(); ++i) {
1160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Point converted_location = location;
1161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    views::View::ConvertPointToView(this, candidate_views_[i],
1162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    &converted_location);
1163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (candidate_views_[i]->HitTest(converted_location)) {
1164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SelectCandidateAt(i);
1165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
1166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CandidateWindowView::CommitCandidate() {
1171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // For now, we don't distinguish left and right clicks.
1172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int button = 1;  // Left button.
1173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int key_modifilers = 0;
1174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FOR_EACH_OBSERVER(Observer, observers_,
1175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    OnCandidateCommitted(selected_candidate_index_in_page_,
1176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         button,
1177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         key_modifilers));
1178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid CandidateWindowView::ResizeAndMoveParentFrame() {
118121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  ResizeParentFrame();
118221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  MoveParentFrame();
118321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
118421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
118521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid CandidateWindowView::ResizeParentFrame() {
1186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Resize the parent frame, with the current candidate window size.
1187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Size size = GetPreferredSize();
1188dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect bounds = parent_frame_->GetClientAreaScreenBounds();
118921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // SetBounds() is not cheap. Only call this when the size is changed.
119021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (bounds.size() != size) {
119121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    bounds.set_size(size);
119221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    parent_frame_->SetBounds(bounds);
119321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
119421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
1195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
119621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid CandidateWindowView::MoveParentFrame() {
119721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int x = cursor_location_.x();
119821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int y = cursor_location_.y();
119921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int height = cursor_location_.height();
120021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int horizontal_offset = GetHorizontalOffset();
120121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
1202dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gfx::Rect frame_bounds = parent_frame_->GetClientAreaScreenBounds();
120321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  gfx::Rect screen_bounds = views::Screen::GetMonitorWorkAreaNearestWindow(
120421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      parent_frame_->GetNativeView());
120521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
120621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // The default position.
120721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  frame_bounds.set_x(x + horizontal_offset);
120821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  frame_bounds.set_y(y + height);
120921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
121021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Handle overflow at the left and the top.
121121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  frame_bounds.set_x(std::max(frame_bounds.x(), screen_bounds.x()));
121221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  frame_bounds.set_y(std::max(frame_bounds.y(), screen_bounds.y()));
121321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
121421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Handle overflow at the right.
121521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int right_overflow = frame_bounds.right() - screen_bounds.right();
121621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (right_overflow > 0) {
121721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    frame_bounds.set_x(frame_bounds.x() - right_overflow);
121821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
121921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
122021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Handle overflow at the bottom.
122121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const int bottom_overflow = frame_bounds.bottom() - screen_bounds.bottom();
122221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (bottom_overflow > 0) {
122321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    frame_bounds.set_y(frame_bounds.y() - height - frame_bounds.height());
122421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
122521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
122621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // Move the window per the cursor location.
122721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  parent_frame_->SetBounds(frame_bounds);
1228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint CandidateWindowView::GetHorizontalOffset() {
1231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Compute the horizontal offset if the lookup table is vertical.
1232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!candidate_views_.empty() &&
1233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      lookup_table_.orientation == InputMethodLookupTable::kVertical) {
1234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return - candidate_views_[0]->GetCandidateLabelPosition().x();
1235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return 0;
1237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenvoid CandidateWindowView::VisibilityChanged(View* starting_from,
124021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                                            bool is_visible) {
124121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (is_visible) {
124221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // If the visibility of candidate window is changed,
124321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    // we should move the frame to the right position.
124421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    MoveParentFrame();
124521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
124621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
124721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
1248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid CandidateWindowView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
124921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // If the bounds(size) of candidate window is changed,
125021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // we should move the frame to the right position.
1251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  View::OnBoundsChanged(previous_bounds);
125221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  MoveParentFrame();
125321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
1254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CandidateWindowController::Impl::Init() {
1256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initialize the input method UI status connection.
1257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  InputMethodUiStatusMonitorFunctions functions;
1258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  functions.hide_auxiliary_text =
12594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnHideAuxiliaryText;
1260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  functions.hide_lookup_table =
12614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnHideLookupTable;
1262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  functions.set_cursor_location =
12634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnSetCursorLocation;
1264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  functions.update_auxiliary_text =
12654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnUpdateAuxiliaryText;
1266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  functions.update_lookup_table =
12674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnUpdateLookupTable;
1268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ui_status_connection_ = MonitorInputMethodUiStatus(functions, this);
12693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!ui_status_connection_) {
12703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    LOG(ERROR) << "MonitorInputMethodUiStatus() failed.";
12713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
12723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
12734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  MonitorInputMethodConnection(
12744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      ui_status_connection_,
12754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      &CandidateWindowController::Impl::OnConnectionChange);
1276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the candidate window view.
1278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateView();
12793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
12803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return true;
1281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::CreateView() {
1284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a non-decorated frame.
1285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  frame_.reset(views::Widget::CreateWidget(
1286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      views::Widget::CreateParams(views::Widget::CreateParams::TYPE_POPUP)));
1287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The size is initially zero.
1288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  frame_->Init(NULL, gfx::Rect(0, 0));
1289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the candidate window.
1291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_window_ = new CandidateWindowView(frame_.get());
1292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_window_->Init();
1293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_window_->AddObserver(this);
1294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Put the candidate window view on the frame.  The frame is resized
1296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // later when the candidate window is shown.
1297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  views::RootView* root_view = frame_->GetRootView();
1298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |root_view| owns the |candidate_window_|, thus |frame_| effectively
1299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // owns |candidate_window_|.
1300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  root_view->SetContentsView(candidate_window_);
1301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochCandidateWindowController::Impl::Impl()
1304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : ui_status_connection_(NULL),
1305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      frame_(NULL) {
1306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochCandidateWindowController::Impl::~Impl() {
1309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  candidate_window_->RemoveObserver(this);
1310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  chromeos::DisconnectInputMethodUiStatus(ui_status_connection_);
1311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnHideAuxiliaryText(
1314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    void* input_method_library) {
13154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CandidateWindowController::Impl* controller =
13164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      static_cast<CandidateWindowController::Impl*>(input_method_library);
1317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller->candidate_window_->HideAuxiliaryText();
131921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  controller->candidate_window_->ResizeParentFrame();
1320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnHideLookupTable(
1323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    void* input_method_library) {
13244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CandidateWindowController::Impl* controller =
13254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      static_cast<CandidateWindowController::Impl*>(input_method_library);
1326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
132772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  controller->candidate_window_->HideLookupTable();
1328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnSetCursorLocation(
1331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    void* input_method_library,
1332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int x,
1333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int y,
1334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int width,
1335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int height) {
13364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CandidateWindowController::Impl* controller =
13374a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      static_cast<CandidateWindowController::Impl*>(input_method_library);
1338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // A workaround for http://crosbug.com/6460. We should ignore very short Y
13403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // move to prevent the window from shaking up and down.
13413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const int kKeepPositionThreshold = 2;  // px
134221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  const gfx::Rect& last_location =
134321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      controller->candidate_window_->cursor_location();
13443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const int delta_y = abs(last_location.y() - y);
13453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if ((last_location.x() == x) && (delta_y <= kKeepPositionThreshold)) {
13463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    DLOG(INFO) << "Ignored set_cursor_location signal to prevent window shake";
13473345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
13483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
13493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Remember the cursor location.
135121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  controller->candidate_window_->set_cursor_location(
135221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      gfx::Rect(x, y, width, height));
1353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Move the window per the cursor location.
135421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  controller->candidate_window_->MoveParentFrame();
1355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnUpdateAuxiliaryText(
1358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    void* input_method_library,
1359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& utf8_text,
1360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool visible) {
13614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CandidateWindowController::Impl* controller =
13624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      static_cast<CandidateWindowController::Impl*>(input_method_library);
1363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If it's not visible, hide the auxiliary text and return.
1364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!visible) {
1365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    controller->candidate_window_->HideAuxiliaryText();
1366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller->candidate_window_->UpdateAuxiliaryText(utf8_text);
1369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller->candidate_window_->ShowAuxiliaryText();
137021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  controller->candidate_window_->ResizeParentFrame();
1371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnUpdateLookupTable(
1374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    void* input_method_library,
1375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const InputMethodLookupTable& lookup_table) {
13764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  CandidateWindowController::Impl* controller =
13774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      static_cast<CandidateWindowController::Impl*>(input_method_library);
1378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If it's not visible, hide the window and return.
1380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!lookup_table.visible) {
138172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    controller->candidate_window_->HideLookupTable();
1382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller->candidate_window_->UpdateCandidates(lookup_table);
138621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  controller->candidate_window_->ResizeParentFrame();
1387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller->frame_->Show();
1388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnCandidateCommitted(int index,
139172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                           int button,
139272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                           int flags) {
1393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NotifyCandidateClicked(ui_status_connection_, index, button, flags);
1394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochvoid CandidateWindowController::Impl::OnConnectionChange(
13973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    void* input_method_library,
13983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    bool connected) {
13993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!connected) {
140072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CandidateWindowController::Impl* controller =
140172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        static_cast<CandidateWindowController::Impl*>(input_method_library);
140272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    controller->candidate_window_->HideLookupTable();
14033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
14043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
14053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1406201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben MurdochCandidateWindowController::CandidateWindowController()
1407201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    : impl_(new CandidateWindowController::Impl) {
14084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochCandidateWindowController::~CandidateWindowController() {
14114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  delete impl_;
14124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
1413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool CandidateWindowController::Init() {
14154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return impl_->Init();
1416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
14174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
14184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace chromeos
1419