OLD | NEW |
| (Empty) |
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/chromeos/notifications/balloon_view.h" | |
6 | |
7 #include <algorithm> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/message_loop.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "chrome/browser/chromeos/notifications/balloon_view_host.h" | |
14 #include "chrome/browser/chromeos/notifications/notification_panel.h" | |
15 #include "chrome/browser/notifications/balloon.h" | |
16 #include "chrome/browser/notifications/desktop_notification_service.h" | |
17 #include "chrome/browser/notifications/desktop_notification_service_factory.h" | |
18 #include "chrome/browser/notifications/notification.h" | |
19 #include "chrome/browser/profiles/profile.h" | |
20 #include "chrome/browser/ui/views/notifications/balloon_view_host.h" | |
21 #include "chrome/common/chrome_notification_types.h" | |
22 #include "content/public/browser/notification_details.h" | |
23 #include "content/public/browser/notification_source.h" | |
24 #include "content/public/browser/render_view_host.h" | |
25 #include "content/public/browser/render_widget_host_view.h" | |
26 #include "content/public/browser/web_contents.h" | |
27 #include "grit/generated_resources.h" | |
28 #include "grit/theme_resources.h" | |
29 #include "grit/theme_resources_standard.h" | |
30 #include "ui/base/l10n/l10n_util.h" | |
31 #include "ui/base/models/simple_menu_model.h" | |
32 #include "ui/base/resource/resource_bundle.h" | |
33 #include "ui/views/background.h" | |
34 #include "ui/views/controls/button/button.h" | |
35 #include "ui/views/controls/button/image_button.h" | |
36 #include "ui/views/controls/button/menu_button.h" | |
37 #include "ui/views/controls/button/menu_button_listener.h" | |
38 #include "ui/views/controls/label.h" | |
39 #include "ui/views/controls/menu/menu_item_view.h" | |
40 #include "ui/views/controls/menu/menu_model_adapter.h" | |
41 #include "ui/views/controls/menu/menu_runner.h" | |
42 #include "ui/views/widget/widget.h" | |
43 | |
44 using content::RenderWidgetHostView; | |
45 | |
46 namespace { | |
47 // Menu commands | |
48 const int kNoopCommand = 0; | |
49 const int kRevokePermissionCommand = 1; | |
50 | |
51 // Vertical margin between close button and menu button. | |
52 const int kControlButtonsMargin = 6; | |
53 | |
54 // Top, Right margin for notification control view. | |
55 const int kControlViewTopMargin = 4; | |
56 const int kControlViewRightMargin = 6; | |
57 } // namespace | |
58 | |
59 namespace chromeos { | |
60 | |
61 // NotificationControlView has close and menu buttons and | |
62 // overlays on top of renderer view. | |
63 class NotificationControlView : public views::View, | |
64 public views::MenuButtonListener, | |
65 public ui::SimpleMenuModel::Delegate, | |
66 public views::ButtonListener { | |
67 public: | |
68 explicit NotificationControlView(BalloonViewImpl* view) | |
69 : balloon_view_(view), | |
70 close_button_(NULL), | |
71 options_menu_contents_(NULL), | |
72 options_menu_button_(NULL) { | |
73 // TODO(oshima): make background transparent. | |
74 set_background(views::Background::CreateSolidBackground(SK_ColorWHITE)); | |
75 | |
76 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
77 | |
78 SkBitmap* close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE); | |
79 SkBitmap* close_button_m = rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK); | |
80 SkBitmap* close_button_h = rb.GetBitmapNamed(IDR_TAB_CLOSE_H); | |
81 SkBitmap* close_button_p = rb.GetBitmapNamed(IDR_TAB_CLOSE_P); | |
82 | |
83 close_button_ = new views::ImageButton(this); | |
84 close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n); | |
85 close_button_->SetImage(views::CustomButton::BS_HOT, close_button_h); | |
86 close_button_->SetImage(views::CustomButton::BS_PUSHED, close_button_p); | |
87 close_button_->SetBackground( | |
88 SK_ColorBLACK, close_button_n, close_button_m); | |
89 | |
90 AddChildView(close_button_); | |
91 | |
92 options_menu_button_ = new views::MenuButton( | |
93 NULL, string16(), this, false); | |
94 options_menu_button_->SetFont(rb.GetFont(ResourceBundle::SmallFont)); | |
95 options_menu_button_->SetIcon(*rb.GetBitmapNamed(IDR_NOTIFICATION_MENU)); | |
96 options_menu_button_->set_border(NULL); | |
97 | |
98 options_menu_button_->set_icon_placement(views::TextButton::ICON_ON_RIGHT); | |
99 AddChildView(options_menu_button_); | |
100 | |
101 // The control view will never be resized, so just layout once. | |
102 gfx::Size options_size = options_menu_button_->GetPreferredSize(); | |
103 gfx::Size button_size = close_button_->GetPreferredSize(); | |
104 | |
105 int height = std::max(options_size.height(), button_size.height()); | |
106 options_menu_button_->SetBounds( | |
107 0, (height - options_size.height()) / 2, | |
108 options_size.width(), options_size.height()); | |
109 | |
110 close_button_->SetBounds( | |
111 options_size.width() + kControlButtonsMargin, | |
112 (height - button_size.height()) / 2, | |
113 button_size.width(), button_size.height()); | |
114 | |
115 SizeToPreferredSize(); | |
116 } | |
117 | |
118 virtual gfx::Size GetPreferredSize() { | |
119 gfx::Rect total_bounds = | |
120 close_button_->bounds().Union(options_menu_button_->bounds()); | |
121 return total_bounds.size(); | |
122 } | |
123 | |
124 // Overridden from views::MenuButtonListener: | |
125 virtual void OnMenuButtonClicked(views::View* source, | |
126 const gfx::Point& point) OVERRIDE { | |
127 CreateOptionsMenu(); | |
128 | |
129 views::MenuModelAdapter menu_model_adapter(options_menu_contents_.get()); | |
130 menu_runner_.reset(new views::MenuRunner(menu_model_adapter.CreateMenu())); | |
131 | |
132 gfx::Point screen_location; | |
133 views::View::ConvertPointToScreen(options_menu_button_, &screen_location); | |
134 if (menu_runner_->RunMenuAt( | |
135 source->GetWidget()->GetTopLevelWidget(), options_menu_button_, | |
136 gfx::Rect(screen_location, options_menu_button_->size()), | |
137 views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) == | |
138 views::MenuRunner::MENU_DELETED) | |
139 return; | |
140 } | |
141 | |
142 // views::ButtonListener implements. | |
143 virtual void ButtonPressed(views::Button* sender, const views::Event&) { | |
144 balloon_view_->Close(true); | |
145 } | |
146 | |
147 // ui::SimpleMenuModel::Delegate impglements. | |
148 virtual bool IsCommandIdChecked(int /* command_id */) const { | |
149 // Nothing in the menu is checked. | |
150 return false; | |
151 } | |
152 | |
153 virtual bool IsCommandIdEnabled(int /* command_id */) const { | |
154 // All the menu options are always enabled. | |
155 return true; | |
156 } | |
157 | |
158 virtual bool GetAcceleratorForCommandId( | |
159 int /* command_id */, ui::Accelerator* /* accelerator */) { | |
160 // Currently no accelerators. | |
161 return false; | |
162 } | |
163 | |
164 virtual void ExecuteCommand(int command_id) { | |
165 switch (command_id) { | |
166 case kRevokePermissionCommand: | |
167 balloon_view_->DenyPermission(); | |
168 default: | |
169 NOTIMPLEMENTED(); | |
170 } | |
171 } | |
172 | |
173 private: | |
174 void CreateOptionsMenu() { | |
175 if (options_menu_contents_.get()) | |
176 return; | |
177 const string16 source_label_text = l10n_util::GetStringFUTF16( | |
178 IDS_NOTIFICATION_BALLOON_SOURCE_LABEL, | |
179 balloon_view_->balloon_->notification().display_source()); | |
180 const string16 label_text = l10n_util::GetStringFUTF16( | |
181 IDS_NOTIFICATION_BALLOON_REVOKE_MESSAGE, | |
182 balloon_view_->balloon_->notification().display_source()); | |
183 | |
184 options_menu_contents_.reset(new ui::SimpleMenuModel(this)); | |
185 // TODO(oshima): Showing the source info in the menu for now. | |
186 // Figure out where to show the source info. | |
187 options_menu_contents_->AddItem(kNoopCommand, source_label_text); | |
188 options_menu_contents_->AddItem(kRevokePermissionCommand, label_text); | |
189 } | |
190 | |
191 BalloonViewImpl* balloon_view_; | |
192 | |
193 views::ImageButton* close_button_; | |
194 | |
195 // The options menu. | |
196 scoped_ptr<ui::SimpleMenuModel> options_menu_contents_; | |
197 scoped_ptr<views::MenuRunner> menu_runner_; | |
198 views::MenuButton* options_menu_button_; | |
199 | |
200 DISALLOW_COPY_AND_ASSIGN(NotificationControlView); | |
201 }; | |
202 | |
203 BalloonViewImpl::BalloonViewImpl(bool sticky, bool controls, bool web_ui) | |
204 : balloon_(NULL), | |
205 html_contents_(NULL), | |
206 stale_(false), | |
207 sticky_(sticky), | |
208 controls_(controls), | |
209 closed_(false), | |
210 web_ui_(web_ui) { | |
211 // This object is not to be deleted by the views hierarchy, | |
212 // as it is owned by the balloon. | |
213 set_parent_owned(false); | |
214 } | |
215 | |
216 BalloonViewImpl::~BalloonViewImpl() { | |
217 if (control_view_host_.get()) { | |
218 control_view_host_->CloseNow(); | |
219 } | |
220 if (html_contents_.get()) { | |
221 html_contents_->Shutdown(); | |
222 } | |
223 } | |
224 | |
225 //////////////////////////////////////////////////////////////////////////////// | |
226 // BallonViewImpl, BalloonView implementation. | |
227 | |
228 void BalloonViewImpl::Show(Balloon* balloon) { | |
229 balloon_ = balloon; | |
230 html_contents_.reset(new BalloonViewHost(balloon)); | |
231 if (web_ui_) | |
232 html_contents_->EnableWebUI(); | |
233 AddChildView(html_contents_->view()); | |
234 notification_registrar_.Add(this, | |
235 chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED, | |
236 content::Source<Balloon>(balloon)); | |
237 } | |
238 | |
239 void BalloonViewImpl::Update() { | |
240 stale_ = false; | |
241 if (!html_contents_->web_contents()) | |
242 return; | |
243 html_contents_->web_contents()->GetController().LoadURL( | |
244 balloon_->notification().content_url(), content::Referrer(), | |
245 content::PAGE_TRANSITION_LINK, std::string()); | |
246 } | |
247 | |
248 void BalloonViewImpl::Close(bool by_user) { | |
249 closed_ = true; | |
250 MessageLoop::current()->PostTask( | |
251 FROM_HERE, | |
252 base::Bind(&BalloonViewImpl::DelayedClose, AsWeakPtr(), by_user)); | |
253 } | |
254 | |
255 gfx::Size BalloonViewImpl::GetSize() const { | |
256 // Not used. The layout is managed by the Panel. | |
257 return gfx::Size(0, 0); | |
258 } | |
259 | |
260 BalloonHost* BalloonViewImpl::GetHost() const { | |
261 return html_contents_.get(); | |
262 } | |
263 | |
264 void BalloonViewImpl::RepositionToBalloon() { | |
265 // Not used. The layout is managed by the Panel. | |
266 } | |
267 | |
268 //////////////////////////////////////////////////////////////////////////////// | |
269 // views::View interface overrides. | |
270 | |
271 void BalloonViewImpl::Layout() { | |
272 gfx::Size size = balloon_->content_size(); | |
273 | |
274 SetBounds(x(), y(), size.width(), size.height()); | |
275 | |
276 html_contents_->view()->SetBounds(0, 0, size.width(), size.height()); | |
277 if (html_contents_->web_contents()) { | |
278 RenderWidgetHostView* view = | |
279 html_contents_->web_contents()->GetRenderViewHost()->GetView(); | |
280 if (view) | |
281 view->SetSize(size); | |
282 } | |
283 } | |
284 | |
285 void BalloonViewImpl::ViewHierarchyChanged( | |
286 bool is_add, View* parent, View* child) { | |
287 if (is_add && GetWidget() && !control_view_host_.get() && controls_) { | |
288 control_view_host_.reset(new views::Widget); | |
289 views::Widget::InitParams params( | |
290 views::Widget::InitParams::TYPE_CONTROL); | |
291 params.double_buffer = true; | |
292 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
293 params.parent = GetParentNativeView(); | |
294 control_view_host_->Init(params); | |
295 NotificationControlView* control = new NotificationControlView(this); | |
296 control_view_host_->SetContentsView(control); | |
297 } | |
298 if (!is_add && this == child && control_view_host_.get() && controls_) | |
299 control_view_host_.release()->CloseNow(); | |
300 } | |
301 | |
302 gfx::Size BalloonViewImpl::GetPreferredSize() { | |
303 return gfx::Size(1000, 1000); | |
304 } | |
305 | |
306 //////////////////////////////////////////////////////////////////////////////// | |
307 // content::NotificationObserver overrides. | |
308 | |
309 void BalloonViewImpl::Observe(int type, | |
310 const content::NotificationSource& source, | |
311 const content::NotificationDetails& details) { | |
312 if (type != chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED) { | |
313 NOTREACHED(); | |
314 return; | |
315 } | |
316 | |
317 // If the renderer process attached to this balloon is disconnected | |
318 // (e.g., because of a crash), we want to close the balloon. | |
319 notification_registrar_.Remove(this, | |
320 chrome::NOTIFICATION_NOTIFY_BALLOON_DISCONNECTED, | |
321 content::Source<Balloon>(balloon_)); | |
322 Close(false); | |
323 } | |
324 | |
325 //////////////////////////////////////////////////////////////////////////////// | |
326 // BalloonViewImpl public. | |
327 | |
328 bool BalloonViewImpl::IsFor(const Notification& notification) const { | |
329 return balloon_->notification().notification_id() == | |
330 notification.notification_id(); | |
331 } | |
332 | |
333 void BalloonViewImpl::Activated() { | |
334 if (!control_view_host_.get()) | |
335 return; | |
336 | |
337 // Get the size of Control View. | |
338 gfx::Size size = | |
339 control_view_host_->GetRootView()->child_at(0)->GetPreferredSize(); | |
340 control_view_host_->Show(); | |
341 control_view_host_->SetBounds( | |
342 gfx::Rect(width() - size.width() - kControlViewRightMargin, | |
343 kControlViewTopMargin, | |
344 size.width(), size.height())); | |
345 } | |
346 | |
347 void BalloonViewImpl::Deactivated() { | |
348 if (control_view_host_.get()) { | |
349 control_view_host_->Hide(); | |
350 } | |
351 } | |
352 | |
353 //////////////////////////////////////////////////////////////////////////////// | |
354 // BalloonViewImpl private. | |
355 | |
356 void BalloonViewImpl::DelayedClose(bool by_user) { | |
357 html_contents_->Shutdown(); | |
358 html_contents_.reset(); | |
359 balloon_->OnClose(by_user); | |
360 } | |
361 | |
362 void BalloonViewImpl::DenyPermission() { | |
363 DesktopNotificationService* service = | |
364 DesktopNotificationServiceFactory::GetForProfile(balloon_->profile()); | |
365 service->DenyPermission(balloon_->notification().origin_url()); | |
366 } | |
367 | |
368 gfx::NativeView BalloonViewImpl::GetParentNativeView() { | |
369 RenderWidgetHostView* view = | |
370 html_contents_->web_contents()->GetRenderViewHost()->GetView(); | |
371 DCHECK(view); | |
372 return view->GetNativeView(); | |
373 } | |
374 | |
375 } // namespace chromeos | |
OLD | NEW |