Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / autofill / autofill_popup_view_gtk.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/gtk/autofill/autofill_popup_view_gtk.h"
6
7 #include <gdk/gdkkeysyms.h>
8 #include <pango/pango.h>
9
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/ui/autofill/autofill_popup_controller.h"
13 #include "chrome/browser/ui/gtk/gtk_util.h"
14 #include "grit/ui_resources.h"
15 #include "third_party/WebKit/public/web/WebAutofillClient.h"
16 #include "ui/base/gtk/gtk_hig_constants.h"
17 #include "ui/base/gtk/gtk_windowing.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/geometry/point.h"
20 #include "ui/gfx/geometry/rect.h"
21 #include "ui/gfx/gtk_compat.h"
22 #include "ui/gfx/image/image.h"
23 #include "ui/gfx/native_widget_types.h"
24 #include "ui/gfx/pango_util.h"
25 #include "ui/gfx/text_utils.h"
26
27 using blink::WebAutofillClient;
28
29 namespace {
30
31 const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce);
32 const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0xcd, 0xcd, 0xcd);
33 const GdkColor kNameColor = GDK_COLOR_RGB(0x00, 0x00, 0x00);
34 const GdkColor kWarningColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
35 const GdkColor kSubtextColor = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
36
37 }  // namespace
38
39 namespace autofill {
40
41 AutofillPopupViewGtk::AutofillPopupViewGtk(
42     AutofillPopupController* controller)
43     : controller_(controller),
44       window_(gtk_window_new(GTK_WINDOW_POPUP)) {
45   gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
46   gtk_widget_set_app_paintable(window_, TRUE);
47   gtk_widget_set_double_buffered(window_, TRUE);
48
49   // Setup the window to ensure it receives the expose event.
50   gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK |
51                                  GDK_BUTTON_RELEASE_MASK |
52                                  GDK_EXPOSURE_MASK |
53                                  GDK_POINTER_MOTION_MASK);
54
55   GtkWidget* toplevel_window = gtk_widget_get_toplevel(
56       controller->container_view());
57   signals_.Connect(toplevel_window, "configure-event",
58                    G_CALLBACK(HandleConfigureThunk), this);
59   g_signal_connect(window_, "expose-event",
60                    G_CALLBACK(HandleExposeThunk), this);
61   g_signal_connect(window_, "leave-notify-event",
62                    G_CALLBACK(HandleLeaveThunk), this);
63   g_signal_connect(window_, "motion-notify-event",
64                    G_CALLBACK(HandleMotionThunk), this);
65   g_signal_connect(window_, "button-release-event",
66                    G_CALLBACK(HandleButtonReleaseThunk), this);
67
68   // Cache the layout so we don't have to create it for every expose.
69   layout_ = gtk_widget_create_pango_layout(window_, NULL);
70 }
71
72 AutofillPopupViewGtk::~AutofillPopupViewGtk() {
73   g_object_unref(layout_);
74   gtk_widget_destroy(window_);
75 }
76
77 void AutofillPopupViewGtk::Hide() {
78   delete this;
79 }
80
81 void AutofillPopupViewGtk::Show() {
82   UpdateBoundsAndRedrawPopup();
83
84   gtk_widget_show(window_);
85
86   GtkWidget* parent_window =
87       gtk_widget_get_toplevel(controller_->container_view());
88   ui::StackPopupWindow(window_, parent_window);
89 }
90
91 void AutofillPopupViewGtk::InvalidateRow(size_t row) {
92   GdkRectangle row_rect = controller_->GetRowBounds(row).ToGdkRectangle();
93   GdkWindow* gdk_window = gtk_widget_get_window(window_);
94   gdk_window_invalidate_rect(gdk_window, &row_rect, FALSE);
95 }
96
97 void AutofillPopupViewGtk::UpdateBoundsAndRedrawPopup() {
98   gtk_widget_set_size_request(window_,
99                               controller_->popup_bounds().width(),
100                               controller_->popup_bounds().height());
101   gtk_window_move(GTK_WINDOW(window_),
102                   controller_->popup_bounds().x(),
103                   controller_->popup_bounds().y());
104
105   GdkWindow* gdk_window = gtk_widget_get_window(window_);
106   GdkRectangle popup_rect = controller_->popup_bounds().ToGdkRectangle();
107   if (gdk_window != NULL)
108     gdk_window_invalidate_rect(gdk_window, &popup_rect, FALSE);
109 }
110
111 gboolean AutofillPopupViewGtk::HandleConfigure(GtkWidget* widget,
112                                                GdkEventConfigure* event) {
113   controller_->Hide();
114   return FALSE;
115 }
116
117 gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget,
118                                                    GdkEventButton* event) {
119   // We only care about the left click.
120   if (event->button != 1)
121     return FALSE;
122
123   controller_->AcceptSelectionAtPoint(gfx::Point(event->x, event->y));
124   return TRUE;
125 }
126
127 gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget,
128                                             GdkEventExpose* event) {
129   cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(gtk_widget_get_window(widget)));
130   gdk_cairo_rectangle(cr, &event->area);
131   cairo_clip(cr);
132
133   // Draw the 1px border around the entire window.
134   gdk_cairo_set_source_color(cr, &kBorderColor);
135   gdk_cairo_rectangle(cr, &widget->allocation);
136   cairo_stroke(cr);
137   SetUpLayout();
138
139   gfx::Rect damage_rect(event->area);
140
141   for (size_t i = 0; i < controller_->names().size(); ++i) {
142     gfx::Rect line_rect = controller_->GetRowBounds(i);
143     // Only repaint and layout damaged lines.
144     if (!line_rect.Intersects(damage_rect))
145       continue;
146
147     if (controller_->identifiers()[i] == WebAutofillClient::MenuItemIDSeparator)
148       DrawSeparator(cr, line_rect);
149     else
150       DrawAutofillEntry(cr, i, line_rect);
151   }
152
153   cairo_destroy(cr);
154
155   return TRUE;
156 }
157
158 gboolean AutofillPopupViewGtk::HandleLeave(GtkWidget* widget,
159                                            GdkEventCrossing* event) {
160   controller_->SelectionCleared();
161
162   return FALSE;
163 }
164
165 gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget,
166                                             GdkEventMotion* event) {
167   controller_->AcceptSelectionAtPoint(gfx::Point(event->x, event->y));
168
169   return TRUE;
170 }
171
172 void AutofillPopupViewGtk::SetUpLayout() {
173   pango_layout_set_width(layout_, window_->allocation.width * PANGO_SCALE);
174   pango_layout_set_height(layout_, window_->allocation.height * PANGO_SCALE);
175 }
176
177 void AutofillPopupViewGtk::SetLayoutText(const base::string16& text,
178                                          const gfx::FontList& font_list,
179                                          const GdkColor text_color) {
180   PangoAttrList* attrs = pango_attr_list_new();
181
182   PangoAttribute* fg_attr = pango_attr_foreground_new(text_color.red,
183                                                       text_color.green,
184                                                       text_color.blue);
185   pango_attr_list_insert(attrs, fg_attr);  // Ownership taken.
186
187   pango_layout_set_attributes(layout_, attrs);  // Ref taken.
188   pango_attr_list_unref(attrs);
189
190   gfx::ScopedPangoFontDescription font_description(
191       pango_font_description_from_string(
192           font_list.GetFontDescriptionString().c_str()));
193   pango_layout_set_font_description(layout_, font_description.get());
194
195   gtk_util::SetLayoutText(layout_, text);
196
197   // The popup is already the correct size for the text, so set the width to -1
198   // to prevent additional wrapping or ellipsization.
199   pango_layout_set_width(layout_, -1);
200 }
201
202 void AutofillPopupViewGtk::DrawSeparator(cairo_t* cairo_context,
203                                          const gfx::Rect& separator_rect) {
204   cairo_save(cairo_context);
205   cairo_move_to(cairo_context, 0, separator_rect.y());
206   cairo_line_to(cairo_context,
207                 separator_rect.width(),
208                 separator_rect.y() + separator_rect.height());
209   cairo_stroke(cairo_context);
210   cairo_restore(cairo_context);
211 }
212
213 void AutofillPopupViewGtk::DrawAutofillEntry(cairo_t* cairo_context,
214                                              size_t index,
215                                              const gfx::Rect& entry_rect) {
216   if (controller_->selected_line() == static_cast<int>(index)) {
217     gdk_cairo_set_source_color(cairo_context, &kHoveredBackgroundColor);
218     cairo_rectangle(cairo_context, entry_rect.x(), entry_rect.y(),
219                     entry_rect.width(), entry_rect.height());
220     cairo_fill(cairo_context);
221   }
222
223   // Draw the value.
224   SetLayoutText(controller_->names()[index],
225                 controller_->GetNameFontListForRow(index),
226                 controller_->IsWarning(index) ? kWarningColor : kNameColor);
227   int value_text_width =
228       gfx::GetStringWidth(controller_->names()[index],
229                           controller_->GetNameFontListForRow(index));
230
231   // Center the text within the line.
232   int row_height = entry_rect.height();
233   int value_content_y = std::max(
234       entry_rect.y(),
235       entry_rect.y() +
236           (row_height -
237            controller_->GetNameFontListForRow(index).GetHeight()) / 2);
238
239   bool is_rtl = controller_->IsRTL();
240   int value_content_x = is_rtl ?
241       entry_rect.width() - value_text_width - kEndPadding : kEndPadding;
242
243   cairo_save(cairo_context);
244   cairo_move_to(cairo_context, value_content_x, value_content_y);
245   pango_cairo_show_layout(cairo_context, layout_);
246   cairo_restore(cairo_context);
247
248   // Use this to figure out where all the other Autofill items should be placed.
249   int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding;
250
251   // Draw the Autofill icon, if one exists
252   if (!controller_->icons()[index].empty()) {
253     int icon = controller_->GetIconResourceID(controller_->icons()[index]);
254     DCHECK_NE(-1, icon);
255     const gfx::Image& image =
256         ui::ResourceBundle::GetSharedInstance().GetImageNamed(icon);
257     int icon_y = entry_rect.y() + (row_height - image.Height()) / 2;
258
259     x_align_left += is_rtl ? 0 : -image.Width();
260
261     cairo_save(cairo_context);
262     gtk_util::DrawFullImage(cairo_context,
263                             window_,
264                             image,
265                             x_align_left,
266                             icon_y);
267     cairo_restore(cairo_context);
268
269     x_align_left += is_rtl ? image.Width() + kIconPadding : -kIconPadding;
270   }
271
272   // Draw the subtext.
273   SetLayoutText(controller_->subtexts()[index],
274                 controller_->subtext_font_list(),
275                 kSubtextColor);
276   if (!is_rtl) {
277     x_align_left -= gfx::GetStringWidth(controller_->subtexts()[index],
278                                         controller_->subtext_font_list());
279   }
280
281   // Center the text within the line.
282   int subtext_content_y = std::max(
283       entry_rect.y(),
284       entry_rect.y() +
285           (row_height - controller_->subtext_font_list().GetHeight()) / 2);
286
287   cairo_save(cairo_context);
288   cairo_move_to(cairo_context, x_align_left, subtext_content_y);
289   pango_cairo_show_layout(cairo_context, layout_);
290   cairo_restore(cairo_context);
291 }
292
293 AutofillPopupView* AutofillPopupView::Create(
294     AutofillPopupController* controller) {
295   return new AutofillPopupViewGtk(controller);
296 }
297
298 }  // namespace autofill