| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/message_center/message_popup_bubble.h" | 5 #include "ui/message_center/message_popup_bubble.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/stl_util.h" |
| 8 #include "ui/message_center/message_view.h" | 9 #include "ui/message_center/message_view.h" |
| 9 #include "ui/message_center/notification_view.h" | 10 #include "ui/message_center/notification_view.h" |
| 10 #include "ui/notifications/notification_types.h" | 11 #include "ui/notifications/notification_types.h" |
| 11 #include "ui/views/bubble/tray_bubble_view.h" | 12 #include "ui/views/bubble/tray_bubble_view.h" |
| 12 #include "ui/views/layout/box_layout.h" | 13 #include "ui/views/layout/box_layout.h" |
| 13 #include "ui/views/view.h" | 14 #include "ui/views/view.h" |
| 14 #include "ui/views/widget/widget.h" | 15 #include "ui/views/widget/widget.h" |
| 15 | 16 |
| 16 namespace message_center { | 17 namespace message_center { |
| 17 namespace { | 18 namespace { |
| 18 | 19 |
| 19 const int kAutocloseHighPriorityDelaySeconds = 25; | 20 const int kAutocloseHighPriorityDelaySeconds = 25; |
| 20 const int kAutocloseDefaultDelaySeconds = 8; | 21 const int kAutocloseDefaultDelaySeconds = 8; |
| 21 | 22 |
| 22 std::vector<const NotificationList::Notification*> GetNewNotifications( | |
| 23 const NotificationList::Notifications& old_list, | |
| 24 const NotificationList::Notifications& new_list) { | |
| 25 std::set<std::string> existing_ids; | |
| 26 std::vector<const NotificationList::Notification*> result; | |
| 27 for (NotificationList::Notifications::const_iterator iter = old_list.begin(); | |
| 28 iter != old_list.end(); ++iter) { | |
| 29 existing_ids.insert(iter->id); | |
| 30 } | |
| 31 for (NotificationList::Notifications::const_iterator iter = new_list.begin(); | |
| 32 iter != new_list.end(); ++iter) { | |
| 33 if (existing_ids.find(iter->id) == existing_ids.end()) | |
| 34 result.push_back(&(*iter)); | |
| 35 } | |
| 36 return result; | |
| 37 } | |
| 38 | |
| 39 } // namespace | 23 } // namespace |
| 40 | 24 |
| 41 // Popup notifications contents. | 25 // Popup notifications contents. |
| 42 class PopupBubbleContentsView : public views::View { | 26 class PopupBubbleContentsView : public views::View { |
| 43 public: | 27 public: |
| 44 explicit PopupBubbleContentsView(NotificationList::Delegate* list_delegate) | 28 explicit PopupBubbleContentsView(NotificationList::Delegate* list_delegate); |
| 45 : list_delegate_(list_delegate) { | |
| 46 SetLayoutManager( | |
| 47 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); | |
| 48 | 29 |
| 49 content_ = new views::View; | 30 void Update(const NotificationList::Notifications& popup_notifications); |
| 50 content_->SetLayoutManager( | |
| 51 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); | |
| 52 AddChildView(content_); | |
| 53 | |
| 54 if (get_use_acceleration_when_possible()) { | |
| 55 content_->SetPaintToLayer(true); | |
| 56 content_->SetFillsBoundsOpaquely(false); | |
| 57 content_->layer()->SetMasksToBounds(true); | |
| 58 } | |
| 59 } | |
| 60 | |
| 61 void Update(const NotificationList::Notifications& popup_notifications) { | |
| 62 content_->RemoveAllChildViews(true); | |
| 63 for (NotificationList::Notifications::const_iterator iter = | |
| 64 popup_notifications.begin(); | |
| 65 iter != popup_notifications.end(); ++iter) { | |
| 66 MessageView* view = | |
| 67 NotificationView::ViewForNotification(*iter, list_delegate_); | |
| 68 view->SetUpView(); | |
| 69 content_->AddChildView(view); | |
| 70 } | |
| 71 content_->SizeToPreferredSize(); | |
| 72 content_->InvalidateLayout(); | |
| 73 Layout(); | |
| 74 if (GetWidget()) | |
| 75 GetWidget()->GetRootView()->SchedulePaint(); | |
| 76 } | |
| 77 | 31 |
| 78 size_t NumMessageViews() const { | 32 size_t NumMessageViews() const { |
| 79 return content_->child_count(); | 33 return content_->child_count(); |
| 80 } | 34 } |
| 81 | 35 |
| 82 private: | 36 private: |
| 83 NotificationList::Delegate* list_delegate_; | 37 NotificationList::Delegate* list_delegate_; |
| 84 views::View* content_; | 38 views::View* content_; |
| 85 | 39 |
| 86 DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView); | 40 DISALLOW_COPY_AND_ASSIGN(PopupBubbleContentsView); |
| 87 }; | 41 }; |
| 88 | 42 |
| 43 PopupBubbleContentsView::PopupBubbleContentsView( |
| 44 NotificationList::Delegate* list_delegate) |
| 45 : list_delegate_(list_delegate) { |
| 46 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
| 47 |
| 48 content_ = new views::View; |
| 49 content_->SetLayoutManager( |
| 50 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); |
| 51 AddChildView(content_); |
| 52 |
| 53 if (get_use_acceleration_when_possible()) { |
| 54 content_->SetPaintToLayer(true); |
| 55 content_->SetFillsBoundsOpaquely(false); |
| 56 content_->layer()->SetMasksToBounds(true); |
| 57 } |
| 58 } |
| 59 |
| 60 void PopupBubbleContentsView::Update( |
| 61 const NotificationList::Notifications& popup_notifications) { |
| 62 content_->RemoveAllChildViews(true); |
| 63 for (NotificationList::Notifications::const_iterator iter = |
| 64 popup_notifications.begin(); |
| 65 iter != popup_notifications.end(); ++iter) { |
| 66 MessageView* view = |
| 67 NotificationView::ViewForNotification(*iter, list_delegate_); |
| 68 view->SetUpView(); |
| 69 content_->AddChildView(view); |
| 70 } |
| 71 content_->SizeToPreferredSize(); |
| 72 content_->InvalidateLayout(); |
| 73 Layout(); |
| 74 if (GetWidget()) |
| 75 GetWidget()->GetRootView()->SchedulePaint(); |
| 76 } |
| 77 |
| 78 // The timer to call OnAutoClose for |notification|. |
| 79 class MessagePopupBubble::AutocloseTimer { |
| 80 public: |
| 81 AutocloseTimer(const NotificationList::Notification& notification, |
| 82 MessagePopupBubble* bubble); |
| 83 |
| 84 void Start(); |
| 85 |
| 86 void Suspend(); |
| 87 |
| 88 private: |
| 89 const std::string id_; |
| 90 base::TimeDelta delay_; |
| 91 base::Time start_time_; |
| 92 MessagePopupBubble* bubble_; |
| 93 base::OneShotTimer<MessagePopupBubble> timer_; |
| 94 |
| 95 DISALLOW_COPY_AND_ASSIGN(AutocloseTimer); |
| 96 }; |
| 97 |
| 98 MessagePopupBubble::AutocloseTimer::AutocloseTimer( |
| 99 const NotificationList::Notification& notification, |
| 100 MessagePopupBubble* bubble) |
| 101 : id_(notification.id), |
| 102 bubble_(bubble) { |
| 103 int seconds = kAutocloseDefaultDelaySeconds; |
| 104 if (notification.priority > ui::notifications::DEFAULT_PRIORITY) |
| 105 seconds = kAutocloseHighPriorityDelaySeconds; |
| 106 delay_ = base::TimeDelta::FromSeconds(seconds); |
| 107 } |
| 108 |
| 109 void MessagePopupBubble::AutocloseTimer::Start() { |
| 110 start_time_ = base::Time::Now(); |
| 111 timer_.Start(FROM_HERE, |
| 112 delay_, |
| 113 base::Bind(&MessagePopupBubble::OnAutoClose, |
| 114 base::Unretained(bubble_), id_)); |
| 115 } |
| 116 |
| 117 void MessagePopupBubble::AutocloseTimer::Suspend() { |
| 118 base::TimeDelta passed = base::Time::Now() - start_time_; |
| 119 delay_ = std::max(base::TimeDelta(), delay_ - passed); |
| 120 timer_.Reset(); |
| 121 } |
| 122 |
| 89 // MessagePopupBubble | 123 // MessagePopupBubble |
| 90 MessagePopupBubble::MessagePopupBubble(NotificationList::Delegate* delegate) | 124 MessagePopupBubble::MessagePopupBubble(NotificationList::Delegate* delegate) |
| 91 : MessageBubbleBase(delegate), | 125 : MessageBubbleBase(delegate), |
| 92 contents_view_(NULL), | 126 contents_view_(NULL) { |
| 93 should_run_default_timer_(false), | |
| 94 should_run_high_timer_(false) { | |
| 95 } | 127 } |
| 96 | 128 |
| 97 MessagePopupBubble::~MessagePopupBubble() { | 129 MessagePopupBubble::~MessagePopupBubble() { |
| 130 STLDeleteContainerPairSecondPointers(autoclose_timers_.begin(), |
| 131 autoclose_timers_.end()); |
| 98 } | 132 } |
| 99 | 133 |
| 100 views::TrayBubbleView::InitParams MessagePopupBubble::GetInitParams( | 134 views::TrayBubbleView::InitParams MessagePopupBubble::GetInitParams( |
| 101 views::TrayBubbleView::AnchorAlignment anchor_alignment) { | 135 views::TrayBubbleView::AnchorAlignment anchor_alignment) { |
| 102 views::TrayBubbleView::InitParams init_params = | 136 views::TrayBubbleView::InitParams init_params = |
| 103 GetDefaultInitParams(anchor_alignment); | 137 GetDefaultInitParams(anchor_alignment); |
| 104 init_params.arrow_color = kBackgroundColor; | 138 init_params.arrow_color = kBackgroundColor; |
| 105 init_params.close_on_deactivate = false; | 139 init_params.close_on_deactivate = false; |
| 106 return init_params; | 140 return init_params; |
| 107 } | 141 } |
| 108 | 142 |
| 109 void MessagePopupBubble::InitializeContents( | 143 void MessagePopupBubble::InitializeContents( |
| 110 views::TrayBubbleView* new_bubble_view) { | 144 views::TrayBubbleView* new_bubble_view) { |
| 111 set_bubble_view(new_bubble_view); | 145 set_bubble_view(new_bubble_view); |
| 112 contents_view_ = new PopupBubbleContentsView(list_delegate()); | 146 contents_view_ = new PopupBubbleContentsView(list_delegate()); |
| 113 bubble_view()->AddChildView(contents_view_); | 147 bubble_view()->AddChildView(contents_view_); |
| 114 UpdateBubbleView(); | 148 UpdateBubbleView(); |
| 115 } | 149 } |
| 116 | 150 |
| 117 void MessagePopupBubble::OnBubbleViewDestroyed() { | 151 void MessagePopupBubble::OnBubbleViewDestroyed() { |
| 118 contents_view_ = NULL; | 152 contents_view_ = NULL; |
| 119 } | 153 } |
| 120 | 154 |
| 121 void MessagePopupBubble::UpdateBubbleView() { | 155 void MessagePopupBubble::UpdateBubbleView() { |
| 122 NotificationList::Notifications new_notifications; | 156 NotificationList::Notifications popups; |
| 123 list_delegate()->GetNotificationList()->GetPopupNotifications( | 157 list_delegate()->GetNotificationList()->GetPopupNotifications(&popups); |
| 124 &new_notifications); | 158 |
| 125 if (new_notifications.size() == 0) { | 159 if (popups.size() == 0) { |
| 126 if (bubble_view()) | 160 if (bubble_view()) |
| 127 bubble_view()->delegate()->HideBubble(bubble_view()); // deletes |this| | 161 bubble_view()->delegate()->HideBubble(bubble_view()); // deletes |this| |
| 128 return; | 162 return; |
| 129 } | 163 } |
| 130 // Only reset the timer when the number of visible notifications changes. | 164 |
| 131 std::vector<const NotificationList::Notification*> added = | 165 contents_view_->Update(popups); |
| 132 GetNewNotifications(popup_notifications_, new_notifications); | |
| 133 bool run_default = false; | |
| 134 bool run_high = false; | |
| 135 for (std::vector<const NotificationList::Notification*>::const_iterator iter = | |
| 136 added.begin(); iter != added.end(); ++iter) { | |
| 137 if ((*iter)->priority == ui::notifications::DEFAULT_PRIORITY) | |
| 138 run_default = true; | |
| 139 else if ((*iter)->priority >= ui::notifications::HIGH_PRIORITY) | |
| 140 run_high = true; | |
| 141 } | |
| 142 // Currently MAX priority is same as HIGH priority. | |
| 143 if (run_high) | |
| 144 StartAutoCloseTimer(ui::notifications::MAX_PRIORITY); | |
| 145 if (run_default) | |
| 146 StartAutoCloseTimer(ui::notifications::DEFAULT_PRIORITY); | |
| 147 should_run_high_timer_ = run_high; | |
| 148 should_run_default_timer_ = run_default; | |
| 149 contents_view_->Update(new_notifications); | |
| 150 popup_notifications_.swap(new_notifications); | |
| 151 bubble_view()->Show(); | 166 bubble_view()->Show(); |
| 152 bubble_view()->UpdateBubble(); | 167 bubble_view()->UpdateBubble(); |
| 168 |
| 169 std::set<std::string> old_popup_ids; |
| 170 old_popup_ids.swap(popup_ids_); |
| 171 |
| 172 // Start autoclose timers. |
| 173 for (NotificationList::Notifications::const_iterator iter = popups.begin(); |
| 174 iter != popups.end(); ++iter) { |
| 175 std::map<std::string, AutocloseTimer*>::const_iterator timer_iter = |
| 176 autoclose_timers_.find(iter->id); |
| 177 if (timer_iter == autoclose_timers_.end()) { |
| 178 AutocloseTimer *timer = new AutocloseTimer(*iter, this); |
| 179 autoclose_timers_[iter->id] = timer; |
| 180 timer->Start(); |
| 181 } |
| 182 popup_ids_.insert(iter->id); |
| 183 old_popup_ids.erase(iter->id); |
| 184 } |
| 185 |
| 186 // Stops timers whose notifications are gone. |
| 187 for (std::set<std::string>::const_iterator iter = old_popup_ids.begin(); |
| 188 iter != old_popup_ids.end(); ++iter) { |
| 189 DeleteTimer(*iter); |
| 190 } |
| 153 } | 191 } |
| 154 | 192 |
| 155 void MessagePopupBubble::OnMouseEnteredView() { | 193 void MessagePopupBubble::OnMouseEnteredView() { |
| 156 StopAutoCloseTimer(); | 194 for (std::map<std::string, AutocloseTimer*>::iterator iter = |
| 195 autoclose_timers_.begin(); iter != autoclose_timers_.end(); ++iter) { |
| 196 iter->second->Suspend(); |
| 197 } |
| 157 } | 198 } |
| 158 | 199 |
| 159 void MessagePopupBubble::OnMouseExitedView() { | 200 void MessagePopupBubble::OnMouseExitedView() { |
| 160 if (should_run_high_timer_) | 201 for (std::map<std::string, AutocloseTimer*>::iterator iter = |
| 161 StartAutoCloseTimer(ui::notifications::HIGH_PRIORITY); | 202 autoclose_timers_.begin(); iter != autoclose_timers_.end(); ++iter) { |
| 162 if (should_run_default_timer_) | 203 iter->second->Start(); |
| 163 StartAutoCloseTimer(ui::notifications::DEFAULT_PRIORITY); | 204 } |
| 164 } | 205 } |
| 165 | 206 |
| 166 void MessagePopupBubble::StartAutoCloseTimer(int priority) { | 207 void MessagePopupBubble::OnAutoClose(const std::string& id) { |
| 167 base::TimeDelta seconds; | 208 DeleteTimer(id); |
| 168 base::OneShotTimer<MessagePopupBubble>* timer = NULL; | 209 list_delegate()->GetNotificationList()->MarkSinglePopupAsShown(id, false); |
| 169 if (priority == ui::notifications::MAX_PRIORITY) { | |
| 170 seconds = base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds); | |
| 171 timer = &autoclose_high_; | |
| 172 } else { | |
| 173 seconds = base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds); | |
| 174 timer = &autoclose_default_; | |
| 175 } | |
| 176 | |
| 177 timer->Start(FROM_HERE, | |
| 178 seconds, | |
| 179 base::Bind(&MessagePopupBubble::OnAutoClose, | |
| 180 base::Unretained(this), priority)); | |
| 181 } | |
| 182 | |
| 183 void MessagePopupBubble::StopAutoCloseTimer() { | |
| 184 autoclose_high_.Stop(); | |
| 185 autoclose_default_.Stop(); | |
| 186 } | |
| 187 | |
| 188 void MessagePopupBubble::OnAutoClose(int priority) { | |
| 189 list_delegate()->GetNotificationList()->MarkPopupsAsShown(priority); | |
| 190 if (priority == ui::notifications::MAX_PRIORITY) | |
| 191 list_delegate()->GetNotificationList()->MarkPopupsAsShown( | |
| 192 ui::notifications::HIGH_PRIORITY); | |
| 193 | |
| 194 if (priority >= ui::notifications::MAX_PRIORITY) | |
| 195 should_run_high_timer_ = false; | |
| 196 else | |
| 197 should_run_default_timer_ = false; | |
| 198 UpdateBubbleView(); | 210 UpdateBubbleView(); |
| 199 } | 211 } |
| 200 | 212 |
| 213 void MessagePopupBubble::DeleteTimer(const std::string& id) { |
| 214 std::map<std::string, AutocloseTimer*>::iterator iter = |
| 215 autoclose_timers_.find(id); |
| 216 if (iter != autoclose_timers_.end()) { |
| 217 scoped_ptr<AutocloseTimer> release_timer(iter->second); |
| 218 autoclose_timers_.erase(iter); |
| 219 } |
| 220 } |
| 221 |
| 201 size_t MessagePopupBubble::NumMessageViewsForTest() const { | 222 size_t MessagePopupBubble::NumMessageViewsForTest() const { |
| 202 return contents_view_->NumMessageViews(); | 223 return contents_view_->NumMessageViews(); |
| 203 } | 224 } |
| 204 | 225 |
| 205 } // namespace message_center | 226 } // namespace message_center |
| OLD | NEW |