1f344523e51b07d6a1383d078bedd7238bfc8da3
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / location_bar / content_setting_image_view.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/location_bar/content_setting_image_view.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
9 #include "chrome/browser/ui/content_settings/content_setting_bubble_model.h"
10 #include "chrome/browser/ui/content_settings/content_setting_image_model.h"
11 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
12 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
13 #include "grit/theme_resources.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/gfx/color_utils.h"
17 #include "ui/views/controls/image_view.h"
18 #include "ui/views/controls/label.h"
19 #include "ui/views/widget/widget.h"
20
21
22 namespace {
23 const int kBackgroundImages[] = IMAGE_GRID(IDR_OMNIBOX_CONTENT_SETTING_BUBBLE);
24 const int kStayOpenTimeMS = 3200;  // Time spent with animation fully open.
25 }
26
27
28 // static
29 const int ContentSettingImageView::kOpenTimeMS = 150;
30 const int ContentSettingImageView::kAnimationDurationMS =
31     (kOpenTimeMS * 2) + kStayOpenTimeMS;
32
33 ContentSettingImageView::ContentSettingImageView(
34     ContentSettingsType content_type,
35     LocationBarView* parent,
36     const gfx::FontList& font_list,
37     SkColor text_color,
38     SkColor parent_background_color)
39     : parent_(parent),
40       content_setting_image_model_(
41           ContentSettingImageModel::CreateContentSettingImageModel(
42               content_type)),
43       background_painter_(
44           views::Painter::CreateImageGridPainter(kBackgroundImages)),
45       icon_(new views::ImageView),
46       text_label_(new views::Label(base::string16(), font_list)),
47       slide_animator_(this),
48       pause_animation_(false),
49       pause_animation_state_(0.0),
50       bubble_widget_(NULL) {
51   icon_->SetHorizontalAlignment(views::ImageView::LEADING);
52   AddChildView(icon_);
53
54   text_label_->SetVisible(false);
55   text_label_->SetEnabledColor(text_color);
56   // Calculate the actual background color for the label.  The background images
57   // are painted atop |parent_background_color|.  We grab the color of the
58   // middle pixel of the middle image of the background, which we treat as the
59   // representative color of the entire background (reasonable, given the
60   // current appearance of these images).  Then we alpha-blend it over the
61   // parent background color to determine the actual color the label text will
62   // sit atop.
63   const SkBitmap& bitmap(
64       ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
65           kBackgroundImages[4])->GetRepresentation(1.0f).sk_bitmap());
66   SkAutoLockPixels pixel_lock(bitmap);
67   SkColor background_image_color =
68       bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2);
69   // Tricky bit: We alpha blend an opaque version of |background_image_color|
70   // against |parent_background_color| using the original image grid color's
71   // alpha. This is because AlphaBlend(a, b, 255) always returns |a| unchanged
72   // even if |a| is a color with non-255 alpha.
73   text_label_->SetBackgroundColor(
74       color_utils::AlphaBlend(SkColorSetA(background_image_color, 255),
75                               parent_background_color,
76                               SkColorGetA(background_image_color)));
77   text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
78   text_label_->SetElideBehavior(views::Label::NO_ELIDE);
79   AddChildView(text_label_);
80
81   slide_animator_.SetSlideDuration(kAnimationDurationMS);
82   slide_animator_.SetTweenType(gfx::Tween::LINEAR);
83 }
84
85 ContentSettingImageView::~ContentSettingImageView() {
86   if (bubble_widget_)
87     bubble_widget_->RemoveObserver(this);
88 }
89
90 void ContentSettingImageView::Update(content::WebContents* web_contents) {
91   // Note: We explicitly want to call this even if |web_contents| is NULL, so we
92   // get hidden properly while the user is editing the omnibox.
93   content_setting_image_model_->UpdateFromWebContents(web_contents);
94
95   if (!content_setting_image_model_->is_visible()) {
96     SetVisible(false);
97     return;
98   }
99
100   icon_->SetImage(ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
101       content_setting_image_model_->get_icon()));
102   icon_->SetTooltipText(
103       base::UTF8ToUTF16(content_setting_image_model_->get_tooltip()));
104   SetVisible(true);
105
106   // If the content blockage should be indicated to the user, start the
107   // animation and record that we indicated the blockage.
108   TabSpecificContentSettings* content_settings = web_contents ?
109       TabSpecificContentSettings::FromWebContents(web_contents) : NULL;
110   if (!content_settings || content_settings->IsBlockageIndicated(
111       content_setting_image_model_->get_content_settings_type()))
112     return;
113
114   // We just ignore this blockage if we're already showing some other string to
115   // the user.  If this becomes a problem, we could design some sort of queueing
116   // mechanism to show one after the other, but it doesn't seem important now.
117   int string_id = content_setting_image_model_->explanatory_string_id();
118   if (string_id && !background_showing()) {
119     text_label_->SetText(l10n_util::GetStringUTF16(string_id));
120     text_label_->SetVisible(true);
121     slide_animator_.Show();
122   }
123
124   content_settings->SetBlockageHasBeenIndicated(
125       content_setting_image_model_->get_content_settings_type());
126 }
127
128 // static
129 int ContentSettingImageView::GetBubbleOuterPadding(bool by_icon) {
130   return LocationBarView::kItemPadding - LocationBarView::kBubblePadding +
131       (by_icon ? 0 : LocationBarView::kIconInternalPadding);
132 }
133
134 void ContentSettingImageView::AnimationEnded(const gfx::Animation* animation) {
135   slide_animator_.Reset();
136   if (!pause_animation_) {
137     text_label_->SetVisible(false);
138     parent_->Layout();
139     parent_->SchedulePaint();
140   }
141 }
142
143 void ContentSettingImageView::AnimationProgressed(
144     const gfx::Animation* animation) {
145   if (!pause_animation_) {
146     parent_->Layout();
147     parent_->SchedulePaint();
148   }
149 }
150
151 void ContentSettingImageView::AnimationCanceled(
152     const gfx::Animation* animation) {
153   AnimationEnded(animation);
154 }
155
156 gfx::Size ContentSettingImageView::GetPreferredSize() {
157   // Height will be ignored by the LocationBarView.
158   gfx::Size size(icon_->GetPreferredSize());
159   if (background_showing()) {
160     double state = slide_animator_.GetCurrentValue();
161     // The fraction of the animation we'll spend animating the string into view,
162     // which is also the fraction we'll spend animating it closed; total
163     // animation (slide out, show, then slide in) is 1.0.
164     const double kOpenFraction =
165         static_cast<double>(kOpenTimeMS) / kAnimationDurationMS;
166     double size_fraction = 1.0;
167     if (state < kOpenFraction)
168       size_fraction = state / kOpenFraction;
169     if (state > (1.0 - kOpenFraction))
170       size_fraction = (1.0 - state) / kOpenFraction;
171     size.Enlarge(
172         size_fraction * (text_label_->GetPreferredSize().width() +
173             GetTotalSpacingWhileAnimating()), 0);
174     size.SetToMax(background_painter_->GetMinimumSize());
175   }
176   return size;
177 }
178
179 void ContentSettingImageView::Layout() {
180   const int icon_width = icon_->GetPreferredSize().width();
181   icon_->SetBounds(
182       std::min((width() - icon_width) / 2, GetBubbleOuterPadding(true)), 0,
183       icon_width, height());
184   text_label_->SetBounds(
185       icon_->bounds().right() + LocationBarView::kItemPadding, 0,
186       std::max(width() - GetTotalSpacingWhileAnimating() - icon_width, 0),
187       height());
188 }
189
190 bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent& event) {
191   // We want to show the bubble on mouse release; that is the standard behavior
192   // for buttons.
193   return true;
194 }
195
196 void ContentSettingImageView::OnMouseReleased(const ui::MouseEvent& event) {
197   if (HitTestPoint(event.location()))
198     OnClick();
199 }
200
201 void ContentSettingImageView::OnGestureEvent(ui::GestureEvent* event) {
202   if (event->type() == ui::ET_GESTURE_TAP)
203     OnClick();
204   if ((event->type() == ui::ET_GESTURE_TAP) ||
205       (event->type() == ui::ET_GESTURE_TAP_DOWN))
206     event->SetHandled();
207 }
208
209 void ContentSettingImageView::OnPaintBackground(gfx::Canvas* canvas) {
210   if (background_showing())
211     background_painter_->Paint(canvas, size());
212 }
213
214 void ContentSettingImageView::OnWidgetDestroying(views::Widget* widget) {
215   DCHECK_EQ(bubble_widget_, widget);
216   bubble_widget_->RemoveObserver(this);
217   bubble_widget_ = NULL;
218
219   if (pause_animation_) {
220     slide_animator_.Reset(pause_animation_state_);
221     pause_animation_ = false;
222     slide_animator_.Show();
223   }
224 }
225
226 int ContentSettingImageView::GetTotalSpacingWhileAnimating() const {
227   return GetBubbleOuterPadding(true) + LocationBarView::kItemPadding +
228       GetBubbleOuterPadding(false);
229 }
230
231 void ContentSettingImageView::OnClick() {
232   if (slide_animator_.is_animating()) {
233     if (!pause_animation_) {
234       pause_animation_ = true;
235       pause_animation_state_ = slide_animator_.GetCurrentValue();
236     }
237     slide_animator_.Reset();
238   }
239
240   content::WebContents* web_contents = parent_->GetWebContents();
241   if (web_contents && !bubble_widget_) {
242     bubble_widget_ =
243         parent_->delegate()->CreateViewsBubble(new ContentSettingBubbleContents(
244             ContentSettingBubbleModel::CreateContentSettingBubbleModel(
245                 parent_->delegate()->GetContentSettingBubbleModelDelegate(),
246                 web_contents, parent_->profile(),
247                 content_setting_image_model_->get_content_settings_type()),
248             this, views::BubbleBorder::TOP_RIGHT));
249     bubble_widget_->AddObserver(this);
250     bubble_widget_->Show();
251   }
252 }