- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / autofill / autofill_popup_view_views.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/autofill/autofill_popup_view_views.h"
6
7 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
8 #include "grit/ui_resources.h"
9 #include "third_party/WebKit/public/web/WebAutofillClient.h"
10 #include "ui/base/resource/resource_bundle.h"
11 #include "ui/events/keycodes/keyboard_codes.h"
12 #include "ui/gfx/canvas.h"
13 #include "ui/gfx/image/image.h"
14 #include "ui/gfx/native_widget_types.h"
15 #include "ui/gfx/point.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/gfx/screen.h"
18 #include "ui/views/border.h"
19 #include "ui/views/event_utils.h"
20 #include "ui/views/widget/widget.h"
21
22 #if defined(USE_AURA)
23 #include "ui/views/corewm/window_animations.h"
24 #endif
25
26 using WebKit::WebAutofillClient;
27
28 namespace {
29
30 const SkColor kBorderColor = SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE);
31 const SkColor kHoveredBackgroundColor = SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD);
32 const SkColor kItemTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
33 const SkColor kPopupBackground = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
34 const SkColor kValueTextColor = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00);
35 const SkColor kWarningTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
36
37 }  // namespace
38
39 namespace autofill {
40
41 AutofillPopupViewViews::AutofillPopupViewViews(
42     AutofillPopupController* controller, views::Widget* observing_widget)
43     : controller_(controller),
44       observing_widget_(observing_widget) {}
45
46 AutofillPopupViewViews::~AutofillPopupViewViews() {
47   if (controller_) {
48     controller_->ViewDestroyed();
49
50     HideInternal();
51   }
52 }
53
54 void AutofillPopupViewViews::Hide() {
55   // The controller is no longer valid after it hides us.
56   controller_ = NULL;
57
58   HideInternal();
59
60   if (GetWidget()) {
61     // Don't call CloseNow() because some of the functions higher up the stack
62     // assume the the widget is still valid after this point.
63     // http://crbug.com/229224
64     // NOTE: This deletes |this|.
65     GetWidget()->Close();
66   } else {
67     delete this;
68   }
69 }
70
71 void AutofillPopupViewViews::OnPaint(gfx::Canvas* canvas) {
72   if (!controller_)
73     return;
74
75   canvas->DrawColor(kPopupBackground);
76   OnPaintBorder(canvas);
77
78   for (size_t i = 0; i < controller_->names().size(); ++i) {
79     gfx::Rect line_rect = controller_->GetRowBounds(i);
80
81     if (controller_->identifiers()[i] ==
82             WebAutofillClient::MenuItemIDSeparator) {
83       canvas->DrawRect(line_rect, kItemTextColor);
84     } else {
85       DrawAutofillEntry(canvas, i, line_rect);
86     }
87   }
88 }
89
90 void AutofillPopupViewViews::OnMouseCaptureLost() {
91   if (controller_)
92     controller_->MouseExitedPopup();
93 }
94
95 bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent& event) {
96   if (!controller_)
97     return false;
98
99   if (HitTestPoint(event.location())) {
100     controller_->MouseHovered(event.x(), event.y());
101
102     // We must return true in order to get future OnMouseDragged and
103     // OnMouseReleased events.
104     return true;
105   }
106
107   // If we move off of the popup, we lose the selection.
108   controller_->MouseExitedPopup();
109   return false;
110 }
111
112 void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) {
113   if (controller_)
114     controller_->MouseExitedPopup();
115 }
116
117 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) {
118   if (!controller_)
119     return;
120
121   if (HitTestPoint(event.location()))
122     controller_->MouseHovered(event.x(), event.y());
123   else
124     controller_->MouseExitedPopup();
125 }
126
127 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) {
128   if (HitTestPoint(event.location()))
129     return true;
130
131   if (controller_->hide_on_outside_click()) {
132     GetWidget()->ReleaseCapture();
133
134     gfx::Point screen_loc = event.location();
135     views::View::ConvertPointToScreen(this, &screen_loc);
136
137     ui::MouseEvent mouse_event = event;
138     mouse_event.set_location(screen_loc);
139
140     if (controller_->ShouldRepostEvent(mouse_event)) {
141       gfx::NativeView native_view = GetWidget()->GetNativeView();
142       gfx::Screen* screen = gfx::Screen::GetScreenFor(native_view);
143       gfx::NativeWindow window = screen->GetWindowAtScreenPoint(screen_loc);
144       views::RepostLocatedEvent(window, mouse_event);
145     }
146
147     controller_->Hide();
148     // |this| is now deleted.
149   }
150
151   return false;
152 }
153
154 void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent& event) {
155   if (!controller_)
156     return;
157
158   // Because this view can can be shown in response to a mouse press, it can
159   // receive an OnMouseReleased event just after showing. This breaks the mouse
160   // capture, so restart capturing here.
161   if (controller_->hide_on_outside_click() && GetWidget())
162     GetWidget()->SetCapture(this);
163
164   // We only care about the left click.
165   if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location()))
166     controller_->MouseClicked(event.x(), event.y());
167 }
168
169 void AutofillPopupViewViews::OnWidgetBoundsChanged(
170     views::Widget* widget,
171     const gfx::Rect& new_bounds) {
172   DCHECK_EQ(widget, observing_widget_);
173   controller_->Hide();
174 }
175
176 void AutofillPopupViewViews::Show() {
177   if (!GetWidget()) {
178     observing_widget_->AddObserver(this);
179
180     // The widget is destroyed by the corresponding NativeWidget, so we use
181     // a weak pointer to hold the reference and don't have to worry about
182     // deletion.
183     views::Widget* widget = new views::Widget;
184     views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
185     params.delegate = this;
186     params.parent = controller_->container_view();
187     widget->Init(params);
188     widget->SetContentsView(this);
189 #if defined(USE_AURA)
190     // No animation for popup appearance (too distracting).
191     views::corewm::SetWindowVisibilityAnimationTransition(
192         widget->GetNativeView(), views::corewm::ANIMATE_HIDE);
193 #endif
194   }
195
196   set_border(views::Border::CreateSolidBorder(kBorderThickness, kBorderColor));
197
198   UpdateBoundsAndRedrawPopup();
199   GetWidget()->Show();
200
201   if (controller_->hide_on_outside_click())
202     GetWidget()->SetCapture(this);
203 }
204
205 void AutofillPopupViewViews::InvalidateRow(size_t row) {
206   SchedulePaintInRect(controller_->GetRowBounds(row));
207 }
208
209 void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() {
210   GetWidget()->SetBounds(controller_->popup_bounds());
211   SchedulePaint();
212 }
213
214 void AutofillPopupViewViews::HideInternal() {
215   observing_widget_->RemoveObserver(this);
216 }
217
218 void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas,
219                                                int index,
220                                                const gfx::Rect& entry_rect) {
221   if (controller_->selected_line() == index)
222     canvas->FillRect(entry_rect, kHoveredBackgroundColor);
223
224   bool is_rtl = controller_->IsRTL();
225   int value_text_width = controller_->GetNameFontForRow(index).GetStringWidth(
226       controller_->names()[index]);
227   int value_content_x = is_rtl ?
228       entry_rect.width() - value_text_width - kEndPadding : kEndPadding;
229
230   canvas->DrawStringInt(
231       controller_->names()[index],
232       controller_->GetNameFontForRow(index),
233       controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor,
234       value_content_x,
235       entry_rect.y(),
236       canvas->GetStringWidth(controller_->names()[index],
237                              controller_->GetNameFontForRow(index)),
238       entry_rect.height(),
239       gfx::Canvas::TEXT_ALIGN_CENTER);
240
241   // Use this to figure out where all the other Autofill items should be placed.
242   int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding;
243
244   // Draw the Autofill icon, if one exists
245   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
246   int row_height = controller_->GetRowBounds(index).height();
247   if (!controller_->icons()[index].empty()) {
248     int icon = controller_->GetIconResourceID(controller_->icons()[index]);
249     DCHECK_NE(-1, icon);
250     const gfx::ImageSkia* image = rb.GetImageSkiaNamed(icon);
251     int icon_y = entry_rect.y() + (row_height - image->height()) / 2;
252
253     x_align_left += is_rtl ? 0 : -image->width();
254
255     canvas->DrawImageInt(*image, x_align_left, icon_y);
256
257     x_align_left += is_rtl ? image->width() + kIconPadding : -kIconPadding;
258   }
259
260   // Draw the name text.
261   if (!is_rtl) {
262     x_align_left -= canvas->GetStringWidth(controller_->subtexts()[index],
263                                            controller_->subtext_font());
264   }
265
266   canvas->DrawStringInt(
267       controller_->subtexts()[index],
268       controller_->subtext_font(),
269       kItemTextColor,
270       x_align_left,
271       entry_rect.y(),
272       canvas->GetStringWidth(controller_->subtexts()[index],
273                              controller_->subtext_font()),
274       entry_rect.height(),
275       gfx::Canvas::TEXT_ALIGN_CENTER);
276 }
277
278 AutofillPopupView* AutofillPopupView::Create(
279     AutofillPopupController* controller) {
280   views::Widget* observing_widget =
281       views::Widget::GetTopLevelWidgetForNativeView(
282           controller->container_view());
283
284   // If the top level widget can't be found, cancel the popup since we can't
285   // fully set it up.
286   if (!observing_widget)
287     return NULL;
288
289   return new AutofillPopupViewViews(controller, observing_widget);
290 }
291
292 }  // namespace autofill