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