Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: chrome/browser/chromeos/notifications/notification_panel.cc

Issue 9664072: Removing WmIpc and related files from ChromeOS (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Copyright Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 // Draws the view for the balloons.
6
7 #include "chrome/browser/chromeos/notifications/notification_panel.h"
8
9 #include <algorithm>
10
11 #include "base/bind.h"
12 #include "chrome/browser/chromeos/notifications/balloon_collection_impl.h"
13 #include "chrome/browser/chromeos/notifications/balloon_view.h"
14 #include "chrome/common/chrome_notification_types.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_source.h"
17 #include "grit/generated_resources.h"
18 #include "third_party/cros_system_api/window_manager/chromeos_wm_ipc_enums.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/views/background.h"
23 #include "ui/views/controls/native/native_view_host.h"
24 #include "ui/views/controls/scroll_view.h"
25 #include "ui/views/widget/native_widget_gtk.h"
26
27 #define SET_STATE(state) SetState(state, __PRETTY_FUNCTION__)
28
29 using views::Widget;
30
31 namespace {
32 // Minimum and maximum size of balloon content.
33 const int kBalloonMinWidth = 300;
34 const int kBalloonMaxWidth = 300;
35 const int kBalloonMinHeight = 24;
36 const int kBalloonMaxHeight = 120;
37
38 // Maximum height of the notification panel.
39 // TODO(oshima): Get this from system's metrics.
40 const int kMaxPanelHeight = 400;
41
42 // The duration for a new notification to become stale.
43 const int kStaleTimeoutInSeconds = 10;
44
45 // Panel Background color
46 const SkColor kPanelBackground = 0xFFEEEEEE;
47
48 using chromeos::BalloonViewImpl;
49 using chromeos::NotificationPanel;
50
51 #if !defined(NDEBUG)
52 // A utility function to convert State enum to string.
53 const char* ToStr(const NotificationPanel::State state) {
54 switch (state) {
55 case NotificationPanel::FULL:
56 return "full";
57 case NotificationPanel::KEEP_SIZE:
58 return "keep_size";
59 case NotificationPanel::STICKY_AND_NEW:
60 return "sticky_new";
61 case NotificationPanel::MINIMIZED:
62 return "minimized";
63 case NotificationPanel::CLOSED:
64 return "closed";
65 default:
66 return "unknown";
67 }
68 }
69 #endif
70
71 chromeos::BalloonViewImpl* GetBalloonViewOf(const Balloon* balloon) {
72 return static_cast<chromeos::BalloonViewImpl*>(balloon->view());
73 }
74
75 // A Widget that covers entire ScrollView's viewport. Without this, all
76 // renderer's native gtk widgets are moved one by one via
77 // View::VisibleBoundsInRootChanged() notification, which makes scrolling not
78 // smooth.
79 class ViewportWidget : public views::Widget {
80 public:
81 explicit ViewportWidget(chromeos::NotificationPanel* panel)
82 : panel_(panel),
83 pointer_inside_panel_(false) {
84 }
85
86 void UpdateControl() {
87 if (pointer_inside_panel_)
88 panel_->OnMouseMotion(last_point_);
89 }
90
91 // views::Widget overrides:
92 virtual bool OnMouseEvent(const views::MouseEvent& event) OVERRIDE {
93 bool result = Widget::OnMouseEvent(event);
94 switch (event.type()) {
95 case ui::ET_MOUSE_MOVED:
96 OnMouseMoved(event);
97 break;
98 case ui::ET_MOUSE_EXITED:
99 OnMouseExited(event);
100 break;
101 default:
102 break;
103 }
104 return result;
105 }
106
107 private:
108 void OnMouseMoved(const views::MouseEvent& event) {
109 // Convert the point to the panel's coordinate system.
110 Widget* top_level_widget = GetTopLevelWidget();
111 // TODO: remove cast when this extends Widget.
112 DCHECK(top_level_widget != static_cast<Widget*>(this));
113 gfx::Rect panel_rect(event.location(), gfx::Size());
114 Widget::ConvertRect(this, top_level_widget, &panel_rect);
115 last_point_ = panel_rect.origin();
116 pointer_inside_panel_ = true;
117 panel_->OnMouseMotion(last_point_);
118 }
119
120 void OnMouseExited(const views::MouseEvent& event) {
121 // Filter gtk's fake mouse leave events that are issued when
122 // window activation changes.
123 if (event.location().x() == 0 && event.location().y() == 0)
124 return;
125
126 // Leave notify can happen if the mouse moves into the child gdk window.
127 // Make sure the mouse is outside of the panel.
128 gfx::Rect bounds = GetWindowScreenBounds();
129 if (!bounds.Contains(event.location().x() + bounds.x(),
130 event.location().y() + bounds.y())) {
131 panel_->OnMouseLeave();
132 pointer_inside_panel_ = false;
133 }
134 }
135
136 private:
137 chromeos::NotificationPanel* panel_;
138 gfx::Point last_point_;
139 bool pointer_inside_panel_;
140
141 DISALLOW_COPY_AND_ASSIGN(ViewportWidget);
142 };
143
144 class BalloonSubContainer : public views::View {
145 public:
146 explicit BalloonSubContainer(int margin)
147 : margin_(margin) {
148 }
149
150 virtual ~BalloonSubContainer() {}
151
152 // views::View overrides.
153 virtual gfx::Size GetPreferredSize() {
154 return preferred_size_;
155 }
156
157 virtual void Layout() {
158 // Layout bottom up
159 int height = 0;
160 for (int i = child_count() - 1; i >= 0; --i) {
161 views::View* child = child_at(i);
162 child->SetBounds(0, height, child->width(), child->height());
163 height += child->height() + margin_;
164 }
165 SchedulePaint();
166 }
167
168 // Updates the bound so that it can show all balloons.
169 void UpdateBounds() {
170 int height = 0;
171 int max_width = 0;
172 for (int i = child_count() - 1; i >= 0; --i) {
173 views::View* child = child_at(i);
174 height += child->height() + margin_;
175 max_width = std::max(max_width, child->width());
176 }
177 if (height > 0)
178 height -= margin_;
179 preferred_size_.set_width(max_width);
180 preferred_size_.set_height(height);
181 SizeToPreferredSize();
182 }
183
184 // Returns the bounds that covers new notifications.
185 gfx::Rect GetNewBounds() {
186 gfx::Rect rect;
187 for (int i = child_count() - 1; i >= 0; --i) {
188 BalloonViewImpl* view = static_cast<BalloonViewImpl*>(child_at(i));
189 if (!view->stale()) {
190 if (rect.IsEmpty()) {
191 rect = view->bounds();
192 } else {
193 rect = rect.Union(view->bounds());
194 }
195 }
196 }
197 return gfx::Rect(x(), y(), rect.width(), rect.height());
198 }
199
200 // Returns # of new notifications.
201 int GetNewCount() {
202 int count = 0;
203 for (int i = child_count() - 1; i >= 0; --i) {
204 BalloonViewImpl* view = static_cast<BalloonViewImpl*>(child_at(i));
205 if (!view->stale())
206 count++;
207 }
208 return count;
209 }
210
211 // Make all notifications stale.
212 void MakeAllStale() {
213 for (int i = child_count() - 1; i >= 0; --i) {
214 BalloonViewImpl* view = static_cast<BalloonViewImpl*>(child_at(i));
215 view->set_stale();
216 }
217 }
218
219 void DismissAll() {
220 for (int i = child_count() - 1; i >= 0; --i) {
221 BalloonViewImpl* view = static_cast<BalloonViewImpl*>(child_at(i));
222 view->Close(true);
223 }
224 }
225
226 BalloonViewImpl* FindBalloonView(const Notification& notification) {
227 for (int i = child_count() - 1; i >= 0; --i) {
228 BalloonViewImpl* view = static_cast<BalloonViewImpl*>(child_at(i));
229 if (view->IsFor(notification)) {
230 return view;
231 }
232 }
233 return NULL;
234 }
235
236 BalloonViewImpl* FindBalloonView(const gfx::Point point) {
237 gfx::Point copy(point);
238 ConvertPointFromWidget(this, &copy);
239 for (int i = child_count() - 1; i >= 0; --i) {
240 views::View* view = child_at(i);
241 if (view->bounds().Contains(copy))
242 return static_cast<BalloonViewImpl*>(view);
243 }
244 return NULL;
245 }
246
247 // Returns true if the |view| is in the container.
248 // |view| can be a deleted pointer - we do not dereference it.
249 bool HasChildView(View* view) const {
250 for (int i = 0; i < child_count(); ++i) {
251 if (child_at(i) == view)
252 return true;
253 }
254 return false;
255 }
256
257 private:
258 gfx::Size preferred_size_;
259 int margin_;
260
261 DISALLOW_COPY_AND_ASSIGN(BalloonSubContainer);
262 };
263
264 } // namespace
265
266 namespace chromeos {
267
268 class BalloonContainer : public views::View {
269 public:
270 explicit BalloonContainer(int margin)
271 : margin_(margin),
272 sticky_container_(new BalloonSubContainer(margin)),
273 non_sticky_container_(new BalloonSubContainer(margin)) {
274 AddChildView(sticky_container_);
275 AddChildView(non_sticky_container_);
276 }
277 virtual ~BalloonContainer() {}
278
279 // views::View overrides.
280 virtual void Layout() {
281 int margin =
282 (sticky_container_->child_count() != 0 &&
283 non_sticky_container_->child_count() != 0) ?
284 margin_ : 0;
285 sticky_container_->SetBounds(
286 0, 0, width(), sticky_container_->height());
287 non_sticky_container_->SetBounds(
288 0, sticky_container_->bounds().bottom() + margin,
289 width(), non_sticky_container_->height());
290 }
291
292 virtual gfx::Size GetPreferredSize() {
293 return preferred_size_;
294 }
295
296 // Returns the size that covers sticky and new notifications.
297 gfx::Size GetStickyNewSize() {
298 gfx::Rect sticky = sticky_container_->bounds();
299 gfx::Rect new_non_sticky = non_sticky_container_->GetNewBounds();
300 if (sticky.IsEmpty())
301 return new_non_sticky.size();
302 if (new_non_sticky.IsEmpty())
303 return sticky.size();
304 return sticky.Union(new_non_sticky).size();
305 }
306
307 // Adds a ballon to the panel.
308 void Add(Balloon* balloon) {
309 BalloonViewImpl* view = GetBalloonViewOf(balloon);
310 GetContainerFor(balloon)->AddChildView(view);
311 }
312
313 // Updates the position of the |balloon|.
314 bool Update(Balloon* balloon) {
315 BalloonViewImpl* view = GetBalloonViewOf(balloon);
316 View* container = NULL;
317 if (view->parent() == sticky_container_) {
318 container = sticky_container_;
319 } else if (view->parent() == non_sticky_container_) {
320 container = non_sticky_container_;
321 }
322 if (container) {
323 container->RemoveChildView(view);
324 container->AddChildView(view);
325 return true;
326 } else {
327 return false;
328 }
329 }
330
331 // Removes a ballon from the panel.
332 BalloonViewImpl* Remove(Balloon* balloon) {
333 BalloonViewImpl* view = GetBalloonViewOf(balloon);
334 GetContainerFor(balloon)->RemoveChildView(view);
335 return view;
336 }
337
338 // Returns the number of notifications added to the panel.
339 int GetNotificationCount() {
340 return sticky_container_->child_count() +
341 non_sticky_container_->child_count();
342 }
343
344 // Returns the # of new notifications.
345 int GetNewNotificationCount() {
346 return sticky_container_->GetNewCount() +
347 non_sticky_container_->GetNewCount();
348 }
349
350 // Returns the # of sticky and new notifications.
351 int GetStickyNewNotificationCount() {
352 return sticky_container_->child_count() +
353 non_sticky_container_->GetNewCount();
354 }
355
356 // Returns the # of sticky notifications.
357 int GetStickyNotificationCount() {
358 return sticky_container_->child_count();
359 }
360
361 // Returns true if the |view| is contained in the panel.
362 // |view| can be a deleted pointer - we do not dereference it.
363 bool HasBalloonView(View* view) {
364 return sticky_container_->HasChildView(view) ||
365 non_sticky_container_->HasChildView(view);
366 }
367
368 // Updates the bounds so that all notifications are visible.
369 void UpdateBounds() {
370 sticky_container_->UpdateBounds();
371 non_sticky_container_->UpdateBounds();
372 preferred_size_ = sticky_container_->GetPreferredSize();
373
374 gfx::Size non_sticky_size = non_sticky_container_->GetPreferredSize();
375 int margin =
376 (!preferred_size_.IsEmpty() && !non_sticky_size.IsEmpty()) ?
377 margin_ : 0;
378 preferred_size_.Enlarge(0, non_sticky_size.height() + margin);
379 preferred_size_.set_width(std::max(
380 preferred_size_.width(), non_sticky_size.width()));
381 SizeToPreferredSize();
382 }
383
384 void MakeAllStale() {
385 sticky_container_->MakeAllStale();
386 non_sticky_container_->MakeAllStale();
387 }
388
389 void DismissAllNonSticky() {
390 non_sticky_container_->DismissAll();
391 }
392
393 BalloonViewImpl* FindBalloonView(const Notification& notification) {
394 BalloonViewImpl* view = sticky_container_->FindBalloonView(notification);
395 return view ? view : non_sticky_container_->FindBalloonView(notification);
396 }
397
398 BalloonViewImpl* FindBalloonView(const gfx::Point& point) {
399 BalloonViewImpl* view = sticky_container_->FindBalloonView(point);
400 return view ? view : non_sticky_container_->FindBalloonView(point);
401 }
402
403 private:
404 BalloonSubContainer* GetContainerFor(Balloon* balloon) const {
405 BalloonViewImpl* view = GetBalloonViewOf(balloon);
406 return view->sticky() ?
407 sticky_container_ : non_sticky_container_;
408 }
409
410 int margin_;
411 // Sticky/non-sticky ballon containers. They're child views and
412 // deleted when this container is deleted.
413 BalloonSubContainer* sticky_container_;
414 BalloonSubContainer* non_sticky_container_;
415 gfx::Size preferred_size_;
416
417 DISALLOW_COPY_AND_ASSIGN(BalloonContainer);
418 };
419
420 NotificationPanel::NotificationPanel()
421 : balloon_container_(NULL),
422 panel_widget_(NULL),
423 container_host_(NULL),
424 state_(CLOSED),
425 min_bounds_(0, 0, kBalloonMinWidth, kBalloonMinHeight),
426 stale_timeout_(1000 * kStaleTimeoutInSeconds),
427 active_(NULL),
428 scroll_to_(NULL) {
429 Init();
430 }
431
432 NotificationPanel::~NotificationPanel() {
433 Hide();
434 }
435
436 ////////////////////////////////////////////////////////////////////////////////
437 // NotificationPanel public.
438
439 void NotificationPanel::Show() {
440 if (!panel_widget_) {
441 panel_widget_ = new views::Widget;
442 // TODO(oshima): Using window because Popup widget behaves weird
443 // when resizing. This needs to be investigated.
444 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW_FRAMELESS);
445 // Enable double buffering because the panel has both pure views
446 // control and native controls (scroll bar).
447 params.double_buffer = true;
448
449 gfx::Rect bounds = GetPreferredBounds();
450 bounds = bounds.Union(min_bounds_);
451 params.bounds = bounds;
452 panel_widget_->Init(params);
453 // Set minimum bounds so that it can grow freely.
454 gtk_widget_set_size_request(GTK_WIDGET(panel_widget_->GetNativeView()),
455 min_bounds_.width(), min_bounds_.height());
456
457 views::NativeViewHost* native = new views::NativeViewHost();
458 scroll_view_->SetContents(native);
459
460 panel_widget_->SetContentsView(scroll_view_.get());
461
462 #if defined(USE_AURA)
463 native->AttachToView(balloon_container_.get());
464 #else
465 // Add the view port after scroll_view is attached to the panel widget.
466 ViewportWidget* viewport_widget = new ViewportWidget(this);
467 container_host_ = viewport_widget;
468 container_host_->Init(
469 views::Widget::InitParams(views::Widget::InitParams::TYPE_CONTROL));
470 container_host_->SetContentsView(balloon_container_.get());
471 // The window_contents_ is onwed by the Widget. Increase ref count
472 // so that window_contents does not get deleted when detached.
473 g_object_ref(viewport_widget->GetNativeView());
474 native->Attach(viewport_widget->GetNativeView());
475 #endif
476 UnregisterNotification();
477 panel_controller_.reset(
478 new PanelController(this, GTK_WINDOW(panel_widget_->GetNativeView())));
479 panel_controller_->Init(false /* don't focus when opened */,
480 gfx::Rect(0, 0, kBalloonMinWidth, 1), 0,
481 WM_IPC_PANEL_USER_RESIZE_VERTICALLY);
482 registrar_.Add(this, chrome::NOTIFICATION_PANEL_STATE_CHANGED,
483 content::Source<PanelController>(panel_controller_.get()));
484 }
485 panel_widget_->Show();
486 }
487
488 void NotificationPanel::Hide() {
489 balloon_container_->DismissAllNonSticky();
490 if (panel_widget_) {
491 if (container_host_)
492 container_host_->GetRootView()->RemoveChildView(balloon_container_.get());
493
494 views::NativeViewHost* native =
495 static_cast<views::NativeViewHost*>(scroll_view_->GetContents());
496 native->Detach();
497 scroll_view_->SetContents(NULL);
498 if (container_host_) {
499 container_host_->Hide();
500 container_host_->CloseNow();
501 container_host_ = NULL;
502 }
503
504 UnregisterNotification();
505 panel_controller_->Close();
506 MessageLoop::current()->DeleteSoon(FROM_HERE, panel_controller_.release());
507 // We need to remove & detach the scroll view from hierarchy to
508 // avoid GTK deleting child.
509 // TODO(oshima): handle this details in NativeWidgetGtk.
510 panel_widget_->GetRootView()->RemoveChildView(scroll_view_.get());
511 panel_widget_->Close();
512 panel_widget_ = NULL;
513 }
514 }
515
516 ////////////////////////////////////////////////////////////////////////////////
517 // BalloonCollectionImpl::NotificationUI overrides.
518
519 void NotificationPanel::Add(Balloon* balloon) {
520 balloon_container_->Add(balloon);
521 if (state_ == CLOSED || state_ == MINIMIZED)
522 SET_STATE(STICKY_AND_NEW);
523 Show();
524 // Don't resize the panel yet. The panel will be resized when WebKit tells
525 // the size in ResizeNotification.
526 UpdatePanel(false);
527 UpdateControl();
528 StartStaleTimer(balloon);
529 scroll_to_ = balloon;
530 }
531
532 bool NotificationPanel::Update(Balloon* balloon) {
533 return balloon_container_->Update(balloon);
534 }
535
536 void NotificationPanel::Remove(Balloon* balloon) {
537 BalloonViewImpl* view = balloon_container_->Remove(balloon);
538 if (view == active_)
539 active_ = NULL;
540 if (scroll_to_ == balloon)
541 scroll_to_ = NULL;
542
543 // TODO(oshima): May be we shouldn't close
544 // if the mouse pointer is still on the panel.
545 if (balloon_container_->GetNotificationCount() == 0)
546 SET_STATE(CLOSED);
547 // no change to the state
548 if (state_ == KEEP_SIZE) {
549 // Just update the content.
550 UpdateContainerBounds();
551 } else {
552 if (state_ != CLOSED &&
553 balloon_container_->GetStickyNewNotificationCount() == 0)
554 SET_STATE(MINIMIZED);
555 UpdatePanel(true);
556 }
557 UpdateControl();
558 }
559
560 void NotificationPanel::Show(Balloon* balloon) {
561 if (state_ == CLOSED || state_ == MINIMIZED)
562 SET_STATE(STICKY_AND_NEW);
563 Show();
564 UpdatePanel(true);
565 StartStaleTimer(balloon);
566 ScrollBalloonToVisible(balloon);
567 }
568
569 void NotificationPanel::ResizeNotification(
570 Balloon* balloon, const gfx::Size& size) {
571 // restrict to the min & max sizes
572 gfx::Size real_size(
573 std::max(kBalloonMinWidth,
574 std::min(kBalloonMaxWidth, size.width())),
575 std::max(kBalloonMinHeight,
576 std::min(kBalloonMaxHeight, size.height())));
577
578 // Don't allow balloons to shrink. This avoids flickering
579 // which sometimes rapidly reports alternating sizes. Special
580 // case for setting the minimum value.
581 gfx::Size old_size = balloon->content_size();
582 if (real_size.width() > old_size.width() ||
583 real_size.height() > old_size.height() ||
584 real_size == min_bounds_.size()) {
585 balloon->set_content_size(real_size);
586 GetBalloonViewOf(balloon)->Layout();
587 UpdatePanel(true);
588 if (scroll_to_ == balloon) {
589 ScrollBalloonToVisible(scroll_to_);
590 scroll_to_ = NULL;
591 }
592 }
593 }
594
595 void NotificationPanel::SetActiveView(BalloonViewImpl* view) {
596 // Don't change the active view if it's same notification,
597 // or the notification is being closed.
598 if (active_ == view || (view && view->closed()))
599 return;
600 if (active_)
601 active_->Deactivated();
602 active_ = view;
603 if (active_)
604 active_->Activated();
605 }
606
607 ////////////////////////////////////////////////////////////////////////////////
608 // PanelController overrides.
609
610 string16 NotificationPanel::GetPanelTitle() {
611 return string16(l10n_util::GetStringUTF16(IDS_NOTIFICATION_PANEL_TITLE));
612 }
613
614 SkBitmap NotificationPanel::GetPanelIcon() {
615 return SkBitmap();
616 }
617
618 bool NotificationPanel::CanClosePanel() {
619 return true;
620 }
621
622 void NotificationPanel::ClosePanel() {
623 SET_STATE(CLOSED);
624 UpdatePanel(false);
625 }
626
627 void NotificationPanel::ActivatePanel() {
628 if (active_)
629 active_->Activated();
630 }
631
632 ////////////////////////////////////////////////////////////////////////////////
633 // content::NotificationObserver overrides.
634
635 void NotificationPanel::Observe(int type,
636 const content::NotificationSource& source,
637 const content::NotificationDetails& details) {
638 DCHECK(type == chrome::NOTIFICATION_PANEL_STATE_CHANGED);
639 PanelController::State* state =
640 reinterpret_cast<PanelController::State*>(details.map_key());
641 switch (*state) {
642 case PanelController::EXPANDED:
643 // Geting expanded in STICKY_AND_NEW or in KEEP_SIZE state means
644 // that a new notification is added, so just leave the
645 // state. Otherwise, expand to full.
646 if (state_ != STICKY_AND_NEW && state_ != KEEP_SIZE)
647 SET_STATE(FULL);
648 // When the panel is to be expanded, we either show all, or
649 // show only sticky/new, depending on the state.
650 UpdatePanel(false);
651 break;
652 case PanelController::MINIMIZED:
653 SET_STATE(MINIMIZED);
654 // Make all notifications stale when a user minimize the panel.
655 balloon_container_->MakeAllStale();
656 break;
657 case PanelController::INITIAL:
658 NOTREACHED() << "Transition to Initial state should not happen";
659 }
660 }
661
662 ////////////////////////////////////////////////////////////////////////////////
663 // PanelController public.
664
665 void NotificationPanel::OnMouseLeave() {
666 SetActiveView(NULL);
667 if (balloon_container_->GetNotificationCount() == 0)
668 SET_STATE(CLOSED);
669 UpdatePanel(true);
670 }
671
672 void NotificationPanel::OnMouseMotion(const gfx::Point& point) {
673 SetActiveView(balloon_container_->FindBalloonView(point));
674 // We need to set the focus to scroll view to get mouse wheel
675 // working. Setting focus when mouse moves on the panel
676 // because focus may be taken by other view.
677 scroll_view_->RequestFocus();
678 // This method used to set KEEP_SIZE state. However,
679 // some html notifications may want to change their size,
680 // and setting KEEP_SIZE caused them to behave differently
681 // depending on whether user moved mouse over notification
682 // or not.
683 }
684
685 NotificationPanelTester* NotificationPanel::GetTester() {
686 if (!tester_.get())
687 tester_.reset(new NotificationPanelTester(this));
688 return tester_.get();
689 }
690
691 ////////////////////////////////////////////////////////////////////////////////
692 // NotificationPanel private.
693
694 void NotificationPanel::Init() {
695 DCHECK(!panel_widget_);
696 balloon_container_.reset(new BalloonContainer(1));
697 balloon_container_->set_parent_owned(false);
698 balloon_container_->set_background(
699 views::Background::CreateSolidBackground(kPanelBackground));
700
701 scroll_view_.reset(new views::ScrollView());
702 scroll_view_->set_focusable(true);
703 scroll_view_->set_parent_owned(false);
704 scroll_view_->set_background(
705 views::Background::CreateSolidBackground(SK_ColorWHITE));
706 }
707
708 void NotificationPanel::UnregisterNotification() {
709 if (panel_controller_.get()) {
710 registrar_.Remove(
711 this, chrome::NOTIFICATION_PANEL_STATE_CHANGED,
712 content::Source<PanelController>(panel_controller_.get()));
713 }
714 }
715
716 void NotificationPanel::ScrollBalloonToVisible(Balloon* balloon) {
717 BalloonViewImpl* view = GetBalloonViewOf(balloon);
718 if (!view->closed()) {
719 // We can't use View::ScrollRectToVisible because the viewport is not
720 // ancestor of the BalloonViewImpl.
721 // Use Widget's coordinate which is same as viewport's coordinates.
722 gfx::Point p(0, 0);
723 views::View::ConvertPointToWidget(view, &p);
724 gfx::Rect visible_rect(p.x(), p.y(), view->width(), view->height());
725 scroll_view_->ScrollContentsRegionToBeVisible(visible_rect);
726 }
727 }
728
729 void NotificationPanel::UpdatePanel(bool update_container_size) {
730 if (update_container_size)
731 UpdateContainerBounds();
732 switch (state_) {
733 case KEEP_SIZE: {
734 gfx::Rect min_bounds = GetPreferredBounds();
735 gfx::Rect panel_bounds = panel_widget_->GetWindowScreenBounds();
736 if (min_bounds.height() < panel_bounds.height())
737 panel_widget_->SetBounds(min_bounds);
738 else if (min_bounds.height() > panel_bounds.height()) {
739 // need scroll bar
740 int width = balloon_container_->width() +
741 scroll_view_->GetScrollBarWidth();
742 panel_bounds.set_width(width);
743 panel_widget_->SetBounds(panel_bounds);
744 }
745
746 // no change.
747 break;
748 }
749 case CLOSED:
750 Hide();
751 break;
752 case MINIMIZED:
753 balloon_container_->MakeAllStale();
754 if (panel_controller_.get())
755 panel_controller_->SetState(PanelController::MINIMIZED);
756 break;
757 case FULL:
758 if (panel_widget_) {
759 panel_widget_->SetBounds(GetPreferredBounds());
760 panel_controller_->SetState(PanelController::EXPANDED);
761 }
762 break;
763 case STICKY_AND_NEW:
764 if (panel_widget_) {
765 panel_widget_->SetBounds(GetStickyNewBounds());
766 panel_controller_->SetState(PanelController::EXPANDED);
767 }
768 break;
769 }
770 }
771
772 void NotificationPanel::UpdateContainerBounds() {
773 balloon_container_->UpdateBounds();
774 views::NativeViewHost* native =
775 static_cast<views::NativeViewHost*>(scroll_view_->GetContents());
776 // Update from WebKit may arrive after the panel is closed/hidden
777 // and viewport widget is detached.
778 if (native) {
779 native->SetBoundsRect(balloon_container_->bounds());
780 scroll_view_->Layout();
781 }
782 }
783
784 void NotificationPanel::UpdateControl() {
785 if (container_host_)
786 static_cast<ViewportWidget*>(container_host_)->UpdateControl();
787 }
788
789 gfx::Rect NotificationPanel::GetPreferredBounds() {
790 gfx::Size pref_size = balloon_container_->GetPreferredSize();
791 int new_height = std::min(pref_size.height(), kMaxPanelHeight);
792 int new_width = pref_size.width();
793 // Adjust the width to avoid showing a horizontal scroll bar.
794 if (new_height != pref_size.height()) {
795 new_width += scroll_view_->GetScrollBarWidth();
796 }
797 return gfx::Rect(0, 0, new_width, new_height).Union(min_bounds_);
798 }
799
800 gfx::Rect NotificationPanel::GetStickyNewBounds() {
801 gfx::Size pref_size = balloon_container_->GetPreferredSize();
802 gfx::Size sticky_size = balloon_container_->GetStickyNewSize();
803 int new_height = std::min(sticky_size.height(), kMaxPanelHeight);
804 int new_width = pref_size.width();
805 // Adjust the width to avoid showing a horizontal scroll bar.
806 if (new_height != pref_size.height())
807 new_width += scroll_view_->GetScrollBarWidth();
808 return gfx::Rect(0, 0, new_width, new_height).Union(min_bounds_);
809 }
810
811 void NotificationPanel::StartStaleTimer(Balloon* balloon) {
812 BalloonViewImpl* view = GetBalloonViewOf(balloon);
813 MessageLoop::current()->PostDelayedTask(
814 FROM_HERE,
815 base::Bind(&NotificationPanel::OnStale, AsWeakPtr(), view),
816 stale_timeout_);
817 }
818
819 void NotificationPanel::OnStale(BalloonViewImpl* view) {
820 // Note: |view| may point to deleted memory.
821 if (balloon_container_->HasBalloonView(view) && !view->stale()) {
822 view->set_stale();
823 // don't update panel on stale
824 if (state_ == KEEP_SIZE)
825 return;
826 if (balloon_container_->GetStickyNewNotificationCount() > 0) {
827 SET_STATE(STICKY_AND_NEW);
828 } else {
829 SET_STATE(MINIMIZED);
830 }
831 UpdatePanel(false);
832 }
833 }
834
835 void NotificationPanel::SetState(State new_state, const char* name) {
836 #if !defined(NDEBUG)
837 DVLOG(1) << "state transition " << ToStr(state_) << " >> " << ToStr(new_state)
838 << " in " << name;
839 #endif
840 state_ = new_state;
841 }
842
843 void NotificationPanel::MarkStale(const Notification& notification) {
844 BalloonViewImpl* view = balloon_container_->FindBalloonView(notification);
845 DCHECK(view);
846 OnStale(view);
847 }
848
849 ////////////////////////////////////////////////////////////////////////////////
850 // NotificationPanelTester public.
851
852 int NotificationPanelTester::GetNotificationCount() const {
853 return panel_->balloon_container_->GetNotificationCount();
854 }
855
856 int NotificationPanelTester::GetStickyNotificationCount() const {
857 return panel_->balloon_container_->GetStickyNotificationCount();
858 }
859
860 int NotificationPanelTester::GetNewNotificationCount() const {
861 return panel_->balloon_container_->GetNewNotificationCount();
862 }
863
864 void NotificationPanelTester::SetStaleTimeout(int timeout) {
865 panel_->stale_timeout_ = timeout;
866 }
867
868 void NotificationPanelTester::MarkStale(const Notification& notification) {
869 panel_->MarkStale(notification);
870 }
871
872 PanelController* NotificationPanelTester::GetPanelController() const {
873 return panel_->panel_controller_.get();
874 }
875
876 BalloonViewImpl* NotificationPanelTester::GetBalloonView(
877 BalloonCollectionImpl* collection,
878 const Notification& notification) {
879 Balloon* balloon = collection->FindBalloon(notification);
880 DCHECK(balloon);
881 return GetBalloonViewOf(balloon);
882 }
883
884 bool NotificationPanelTester::IsVisible(const BalloonViewImpl* view) const {
885 gfx::Rect rect = panel_->scroll_view_->GetVisibleRect();
886 gfx::Point origin(0, 0);
887 views::View::ConvertPointToView(view, panel_->balloon_container_.get(),
888 &origin);
889 return rect.Contains(gfx::Rect(origin, view->size()));
890 }
891
892
893 bool NotificationPanelTester::IsActive(const BalloonViewImpl* view) const {
894 return panel_->active_ == view;
895 }
896
897 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698