Home | History | Annotate | Download | only in autofill
      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/keycodes/keyboard_codes.h"
     11 #include "ui/base/resource/resource_bundle.h"
     12 #include "ui/gfx/canvas.h"
     13 #include "ui/gfx/image/image.h"
     14 #include "ui/gfx/point.h"
     15 #include "ui/gfx/rect.h"
     16 #include "ui/views/border.h"
     17 #include "ui/views/widget/widget.h"
     18 
     19 using WebKit::WebAutofillClient;
     20 
     21 namespace {
     22 
     23 const SkColor kBorderColor = SkColorSetARGB(0xFF, 0xC7, 0xCA, 0xCE);
     24 const SkColor kHoveredBackgroundColor = SkColorSetARGB(0xFF, 0xCD, 0xCD, 0xCD);
     25 const SkColor kItemTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
     26 const SkColor kPopupBackground = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
     27 const SkColor kValueTextColor = SkColorSetARGB(0xFF, 0x00, 0x00, 0x00);
     28 const SkColor kWarningTextColor = SkColorSetARGB(0xFF, 0x7F, 0x7F, 0x7F);
     29 
     30 }  // namespace
     31 
     32 namespace autofill {
     33 
     34 AutofillPopupViewViews::AutofillPopupViewViews(
     35     AutofillPopupController* controller, views::Widget* observing_widget)
     36     : controller_(controller),
     37       observing_widget_(observing_widget) {}
     38 
     39 AutofillPopupViewViews::~AutofillPopupViewViews() {
     40   if (controller_) {
     41     controller_->ViewDestroyed();
     42 
     43     HideInternal();
     44   }
     45 }
     46 
     47 void AutofillPopupViewViews::Hide() {
     48   // The controller is no longer valid after it hides us.
     49   controller_ = NULL;
     50 
     51   HideInternal();
     52 
     53   if (GetWidget()) {
     54     // Don't call CloseNow() because some of the functions higher up the stack
     55     // assume the the widget is still valid after this point.
     56     // http://crbug.com/229224
     57     // NOTE: This deletes |this|.
     58     GetWidget()->Close();
     59   } else {
     60     delete this;
     61   }
     62 }
     63 
     64 void AutofillPopupViewViews::OnPaint(gfx::Canvas* canvas) {
     65   if (!controller_)
     66     return;
     67 
     68   canvas->DrawColor(kPopupBackground);
     69   OnPaintBorder(canvas);
     70 
     71   for (size_t i = 0; i < controller_->names().size(); ++i) {
     72     gfx::Rect line_rect = controller_->GetRowBounds(i);
     73 
     74     if (controller_->identifiers()[i] ==
     75             WebAutofillClient::MenuItemIDSeparator) {
     76       canvas->DrawRect(line_rect, kItemTextColor);
     77     } else {
     78       DrawAutofillEntry(canvas, i, line_rect);
     79     }
     80   }
     81 }
     82 
     83 void AutofillPopupViewViews::OnMouseCaptureLost() {
     84   if (controller_)
     85     controller_->MouseExitedPopup();
     86 }
     87 
     88 bool AutofillPopupViewViews::OnMouseDragged(const ui::MouseEvent& event) {
     89   if (!controller_)
     90     return false;
     91 
     92   if (HitTestPoint(event.location())) {
     93     controller_->MouseHovered(event.x(), event.y());
     94 
     95     // We must return true in order to get future OnMouseDragged and
     96     // OnMouseReleased events.
     97     return true;
     98   }
     99 
    100   // If we move off of the popup, we lose the selection.
    101   controller_->MouseExitedPopup();
    102   return false;
    103 }
    104 
    105 void AutofillPopupViewViews::OnMouseExited(const ui::MouseEvent& event) {
    106   if (controller_)
    107     controller_->MouseExitedPopup();
    108 }
    109 
    110 void AutofillPopupViewViews::OnMouseMoved(const ui::MouseEvent& event) {
    111   if (controller_)
    112     controller_->MouseHovered(event.x(), event.y());
    113 }
    114 
    115 bool AutofillPopupViewViews::OnMousePressed(const ui::MouseEvent& event) {
    116   // We must return true in order to get the OnMouseReleased event later.
    117   return true;
    118 }
    119 
    120 void AutofillPopupViewViews::OnMouseReleased(const ui::MouseEvent& event) {
    121   if (!controller_)
    122     return;
    123 
    124   // We only care about the left click.
    125   if (event.IsOnlyLeftMouseButton() &&
    126       HitTestPoint(event.location()))
    127     controller_->MouseClicked(event.x(), event.y());
    128 }
    129 
    130 void AutofillPopupViewViews::OnWidgetBoundsChanged(
    131     views::Widget* widget,
    132     const gfx::Rect& new_bounds) {
    133   controller_->Hide();
    134 }
    135 
    136 void AutofillPopupViewViews::Show() {
    137   if (!GetWidget()) {
    138     observing_widget_->AddObserver(this);
    139 
    140     // The widget is destroyed by the corresponding NativeWidget, so we use
    141     // a weak pointer to hold the reference and don't have to worry about
    142     // deletion.
    143     views::Widget* widget = new views::Widget;
    144     views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
    145     params.delegate = this;
    146     params.parent = controller_->container_view();
    147     widget->Init(params);
    148     widget->SetContentsView(this);
    149   }
    150 
    151   set_border(views::Border::CreateSolidBorder(kBorderThickness, kBorderColor));
    152 
    153   UpdateBoundsAndRedrawPopup();
    154   GetWidget()->Show();
    155 }
    156 
    157 void AutofillPopupViewViews::InvalidateRow(size_t row) {
    158   SchedulePaintInRect(controller_->GetRowBounds(row));
    159 }
    160 
    161 void AutofillPopupViewViews::UpdateBoundsAndRedrawPopup() {
    162   GetWidget()->SetBounds(controller_->popup_bounds());
    163   SchedulePaint();
    164 }
    165 
    166 void AutofillPopupViewViews::HideInternal() {
    167   observing_widget_->RemoveObserver(this);
    168 }
    169 
    170 void AutofillPopupViewViews::DrawAutofillEntry(gfx::Canvas* canvas,
    171                                                int index,
    172                                                const gfx::Rect& entry_rect) {
    173   if (controller_->selected_line() == index)
    174     canvas->FillRect(entry_rect, kHoveredBackgroundColor);
    175 
    176   bool is_rtl = controller_->IsRTL();
    177   int value_text_width = controller_->GetNameFontForRow(index).GetStringWidth(
    178       controller_->names()[index]);
    179   int value_content_x = is_rtl ?
    180       entry_rect.width() - value_text_width - kEndPadding : kEndPadding;
    181 
    182   canvas->DrawStringInt(
    183       controller_->names()[index],
    184       controller_->GetNameFontForRow(index),
    185       controller_->IsWarning(index) ? kWarningTextColor : kValueTextColor,
    186       value_content_x,
    187       entry_rect.y(),
    188       canvas->GetStringWidth(controller_->names()[index],
    189                              controller_->GetNameFontForRow(index)),
    190       entry_rect.height(),
    191       gfx::Canvas::TEXT_ALIGN_CENTER);
    192 
    193   // Use this to figure out where all the other Autofill items should be placed.
    194   int x_align_left = is_rtl ? kEndPadding : entry_rect.width() - kEndPadding;
    195 
    196   // Draw the Autofill icon, if one exists
    197   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    198   int row_height = controller_->GetRowBounds(index).height();
    199   if (!controller_->icons()[index].empty()) {
    200     int icon = controller_->GetIconResourceID(controller_->icons()[index]);
    201     DCHECK_NE(-1, icon);
    202     int icon_y = entry_rect.y() + (row_height - kAutofillIconHeight) / 2;
    203 
    204     x_align_left += is_rtl ? 0 : -kAutofillIconWidth;
    205 
    206     canvas->DrawImageInt(*rb.GetImageSkiaNamed(icon), x_align_left, icon_y);
    207 
    208     x_align_left += is_rtl ? kAutofillIconWidth + kIconPadding : -kIconPadding;
    209   }
    210 
    211   // Draw the name text.
    212   if (!is_rtl) {
    213     x_align_left -= canvas->GetStringWidth(controller_->subtexts()[index],
    214                                            controller_->subtext_font());
    215   }
    216 
    217   canvas->DrawStringInt(
    218       controller_->subtexts()[index],
    219       controller_->subtext_font(),
    220       kItemTextColor,
    221       x_align_left,
    222       entry_rect.y(),
    223       canvas->GetStringWidth(controller_->subtexts()[index],
    224                              controller_->subtext_font()),
    225       entry_rect.height(),
    226       gfx::Canvas::TEXT_ALIGN_CENTER);
    227 }
    228 
    229 AutofillPopupView* AutofillPopupView::Create(
    230     AutofillPopupController* controller) {
    231   views::Widget* observing_widget =
    232       views::Widget::GetTopLevelWidgetForNativeView(
    233           controller->container_view());
    234 
    235   // If the top level widget can't be found, cancel the popup since we can't
    236   // fully set it up.
    237   if (!observing_widget)
    238     return NULL;
    239 
    240   return new AutofillPopupViewViews(controller, observing_widget);
    241 }
    242 
    243 }  // namespace autofill
    244