- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / location_bar / zoom_bubble_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/zoom_bubble_view.h"
6
7 #include "base/i18n/rtl.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/chrome_page_zoom.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_finder.h"
12 #include "chrome/browser/ui/browser_window.h"
13 #include "chrome/browser/ui/views/frame/browser_view.h"
14 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
15 #include "chrome/browser/ui/views/location_bar/zoom_view.h"
16 #include "chrome/browser/ui/zoom/zoom_controller.h"
17 #include "content/public/browser/notification_source.h"
18 #include "content/public/browser/web_contents_view.h"
19 #include "grit/generated_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/views/controls/button/label_button.h"
23 #include "ui/views/controls/separator.h"
24 #include "ui/views/layout/box_layout.h"
25 #include "ui/views/layout/layout_constants.h"
26 #include "ui/views/widget/widget.h"
27
28 namespace {
29
30 // The number of milliseconds the bubble should stay on the screen if it will
31 // close automatically.
32 const int kBubbleCloseDelay = 1500;
33
34 // The bubble's padding from the screen edge, used in fullscreen.
35 const int kFullscreenPaddingEnd = 20;
36
37 }  // namespace
38
39 // static
40 ZoomBubbleView* ZoomBubbleView::zoom_bubble_ = NULL;
41
42 // static
43 void ZoomBubbleView::ShowBubble(content::WebContents* web_contents,
44                                 bool auto_close) {
45   Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
46   DCHECK(browser && browser->window() && browser->fullscreen_controller());
47
48   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
49   bool is_fullscreen = browser_view->IsFullscreen();
50   bool anchor_to_view = !is_fullscreen ||
51       browser_view->immersive_mode_controller()->IsRevealed();
52   views::View* anchor_view = anchor_to_view ?
53       browser_view->GetLocationBarView()->zoom_view() : NULL;
54
55   // If the bubble is already showing in this window and its |auto_close_| value
56   // is equal to |auto_close|, the bubble can be reused and only the label text
57   // needs to be updated.
58   if (zoom_bubble_ &&
59       zoom_bubble_->GetAnchorView() == anchor_view &&
60       zoom_bubble_->auto_close_ == auto_close) {
61     zoom_bubble_->Refresh();
62   } else {
63     // If the bubble is already showing but its |auto_close_| value is not equal
64     // to |auto_close|, the bubble's focus properties must change, so the
65     // current bubble must be closed and a new one created.
66     CloseBubble();
67
68     zoom_bubble_ = new ZoomBubbleView(anchor_view,
69                                       web_contents,
70                                       auto_close,
71                                       browser_view->immersive_mode_controller(),
72                                       browser->fullscreen_controller());
73
74     // If we do not have an anchor view, parent the bubble to the content area.
75     if (!anchor_to_view) {
76       zoom_bubble_->set_parent_window(
77           web_contents->GetView()->GetTopLevelNativeWindow());
78     }
79
80     views::BubbleDelegateView::CreateBubble(zoom_bubble_);
81
82     // Adjust for fullscreen after creation as it relies on the content size.
83     if (is_fullscreen)
84       zoom_bubble_->AdjustForFullscreen(browser_view->GetBoundsInScreen());
85
86     if (zoom_bubble_->use_focusless())
87       zoom_bubble_->GetWidget()->ShowInactive();
88     else
89       zoom_bubble_->GetWidget()->Show();
90   }
91 }
92
93 // static
94 void ZoomBubbleView::CloseBubble() {
95   if (zoom_bubble_)
96     zoom_bubble_->Close();
97 }
98
99 // static
100 bool ZoomBubbleView::IsShowing() {
101   // The bubble may be in the process of closing.
102   return zoom_bubble_ != NULL && zoom_bubble_->GetWidget()->IsVisible();
103 }
104
105 // static
106 const ZoomBubbleView* ZoomBubbleView::GetZoomBubbleForTest() {
107   return zoom_bubble_;
108 }
109
110 ZoomBubbleView::ZoomBubbleView(
111     views::View* anchor_view,
112     content::WebContents* web_contents,
113     bool auto_close,
114     ImmersiveModeController* immersive_mode_controller,
115     FullscreenController* fullscreen_controller)
116     : BubbleDelegateView(anchor_view, anchor_view ?
117           views::BubbleBorder::TOP_RIGHT : views::BubbleBorder::NONE),
118       label_(NULL),
119       web_contents_(web_contents),
120       auto_close_(auto_close),
121       immersive_mode_controller_(immersive_mode_controller) {
122   // Compensate for built-in vertical padding in the anchor view's image.
123   set_anchor_view_insets(gfx::Insets(5, 0, 5, 0));
124   set_use_focusless(auto_close);
125   set_notify_enter_exit_on_child(true);
126
127   // Add observers to close the bubble if the fullscreen state or immersive
128   // fullscreen revealed state changes.
129   registrar_.Add(this,
130                  chrome::NOTIFICATION_FULLSCREEN_CHANGED,
131                  content::Source<FullscreenController>(fullscreen_controller));
132   immersive_mode_controller_->AddObserver(this);
133 }
134
135 ZoomBubbleView::~ZoomBubbleView() {
136   if (immersive_mode_controller_)
137     immersive_mode_controller_->RemoveObserver(this);
138 }
139
140 void ZoomBubbleView::AdjustForFullscreen(const gfx::Rect& screen_bounds) {
141   if (GetAnchorView())
142     return;
143
144   // TODO(dbeam): should RTL logic be done in views::BubbleDelegateView?
145   const size_t bubble_half_width = width() / 2;
146   const int x_pos = base::i18n::IsRTL() ?
147       screen_bounds.x() + bubble_half_width + kFullscreenPaddingEnd :
148       screen_bounds.right() - bubble_half_width - kFullscreenPaddingEnd;
149   set_anchor_rect(gfx::Rect(x_pos, screen_bounds.y(), 0, 0));
150
151   // Used to update |views::BubbleDelegate::anchor_rect_| in a semi-hacky way.
152   // TODO(dbeam): update only the bounds of this view or its border or frame.
153   SizeToContents();
154 }
155
156 void ZoomBubbleView::Refresh() {
157   ZoomController* zoom_controller =
158       ZoomController::FromWebContents(web_contents_);
159   int zoom_percent = zoom_controller->zoom_percent();
160   label_->SetText(
161       l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent));
162   StartTimerIfNecessary();
163 }
164
165 void ZoomBubbleView::Close() {
166   GetWidget()->Close();
167 }
168
169 void ZoomBubbleView::StartTimerIfNecessary() {
170   if (auto_close_) {
171     if (timer_.IsRunning()) {
172       timer_.Reset();
173     } else {
174       timer_.Start(
175           FROM_HERE,
176           base::TimeDelta::FromMilliseconds(kBubbleCloseDelay),
177           this,
178           &ZoomBubbleView::Close);
179     }
180   }
181 }
182
183 void ZoomBubbleView::StopTimer() {
184   timer_.Stop();
185 }
186
187 void ZoomBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
188   StopTimer();
189 }
190
191 void ZoomBubbleView::OnMouseExited(const ui::MouseEvent& event) {
192   StartTimerIfNecessary();
193 }
194
195 void ZoomBubbleView::OnGestureEvent(ui::GestureEvent* event) {
196   if (!zoom_bubble_ || !zoom_bubble_->auto_close_ ||
197       event->type() != ui::ET_GESTURE_TAP) {
198     return;
199   }
200
201   // If an auto-closing bubble was tapped, show a non-auto-closing bubble in
202   // its place.
203   ShowBubble(zoom_bubble_->web_contents_, false);
204   event->SetHandled();
205 }
206
207 void ZoomBubbleView::ButtonPressed(views::Button* sender,
208                                    const ui::Event& event) {
209   chrome_page_zoom::Zoom(web_contents_, content::PAGE_ZOOM_RESET);
210 }
211
212 void ZoomBubbleView::Init() {
213   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
214       0, 0, views::kRelatedControlVerticalSpacing));
215
216   ZoomController* zoom_controller =
217       ZoomController::FromWebContents(web_contents_);
218   int zoom_percent = zoom_controller->zoom_percent();
219   label_ = new views::Label(
220       l10n_util::GetStringFUTF16Int(IDS_TOOLTIP_ZOOM, zoom_percent));
221   label_->SetFont(
222       ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont));
223   AddChildView(label_);
224
225   views::LabelButton* set_default_button = new views::LabelButton(
226       this, l10n_util::GetStringUTF16(IDS_ZOOM_SET_DEFAULT));
227   set_default_button->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
228   set_default_button->SetHorizontalAlignment(gfx::ALIGN_CENTER);
229   AddChildView(set_default_button);
230
231   StartTimerIfNecessary();
232 }
233
234 void ZoomBubbleView::Observe(int type,
235                              const content::NotificationSource& source,
236                              const content::NotificationDetails& details) {
237   DCHECK_EQ(type, chrome::NOTIFICATION_FULLSCREEN_CHANGED);
238   CloseBubble();
239 }
240
241 void ZoomBubbleView::OnImmersiveRevealStarted() {
242   CloseBubble();
243 }
244
245 void ZoomBubbleView::OnImmersiveModeControllerDestroyed() {
246   immersive_mode_controller_ = NULL;
247 }
248
249 void ZoomBubbleView::WindowClosing() {
250   // |zoom_bubble_| can be a new bubble by this point (as Close(); doesn't
251   // call this right away). Only set to NULL when it's this bubble.
252   if (zoom_bubble_ == this)
253     zoom_bubble_ = NULL;
254 }