OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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/notifications/balloons.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/gfx/rect.h" |
| 9 #include "chrome/browser/notifications/balloon_view_win.h" |
| 10 #include "chrome/browser/renderer_host/render_view_host.h" |
| 11 #include "chrome/browser/renderer_host/render_widget_host_view_win.h" |
| 12 #include "chrome/browser/renderer_host/site_instance.h" |
| 13 #include "views/window/window_win.h" |
| 14 |
| 15 namespace { |
| 16 // Portion of the screen allotted for notifications. |
| 17 // When notification balloons extend over this threshold, no new notifications |
| 18 // are shown until some balloons are expired/closed. |
| 19 const double kPercentBalloonFillFactor = 0.7; |
| 20 |
| 21 // Allow at least this number of balloons on the screen. |
| 22 const int kMinAllowedBalloonCount = 2; |
| 23 |
| 24 void GetMainScreenWorkArea(gfx::Rect* bounds) { |
| 25 DCHECK(bounds); |
| 26 RECT work_area = {0}; |
| 27 if (::SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0)) { |
| 28 bounds->SetRect(work_area.left, |
| 29 work_area.top, |
| 30 work_area.right, |
| 31 work_area.bottom); |
| 32 } else { |
| 33 // If failed to call SystemParametersInfo for some reason, we simply get the |
| 34 // full screen size as an alternative. |
| 35 bounds->SetRect(0, |
| 36 0, |
| 37 ::GetSystemMetrics(SM_CXSCREEN) - 1, |
| 38 ::GetSystemMetrics(SM_CYSCREEN) - 1); |
| 39 } |
| 40 } |
| 41 |
| 42 double GetSystemFontScaleFactor() { |
| 43 return 1.0; |
| 44 } |
| 45 } // namespace |
| 46 |
| 47 BalloonCollection::BalloonCollection() { |
| 48 } |
| 49 |
| 50 void BalloonCollection::AddObserver(BalloonCollectionObserver* observer) { |
| 51 DCHECK(observer); |
| 52 observers_.push_back(observer); |
| 53 observer->OnBalloonSpaceChanged(); |
| 54 } |
| 55 |
| 56 bool BalloonCollection::HasSpace() const { |
| 57 if (count() < kMinAllowedBalloonCount) { |
| 58 return true; |
| 59 } |
| 60 |
| 61 int max_balloon_length = 0; |
| 62 int total_length = 0; |
| 63 layout_.GetMaxLengths(&max_balloon_length, &total_length); |
| 64 |
| 65 int current_max_length = max_balloon_length * count(); |
| 66 int max_allowed_length = static_cast<int>(total_length * |
| 67 kPercentBalloonFillFactor); |
| 68 return current_max_length < max_allowed_length - max_balloon_length; |
| 69 } |
| 70 |
| 71 void BalloonCollection::Add(const Notification& notification, |
| 72 Profile* profile, |
| 73 SiteInstance* site_instance) { |
| 74 Balloon* new_balloon = MakeBalloon(notification, profile, site_instance); |
| 75 gfx::Size size(layout_.min_balloon_width(), layout_.min_balloon_height()); |
| 76 new_balloon->SetSize(size); |
| 77 balloons_.push_back(new_balloon); |
| 78 gfx::Point origin = layout_.GetOrigin(); |
| 79 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) { |
| 80 gfx::Point upper_left = layout_.NextPosition((*it)->size(), &origin); |
| 81 (*it)->SetPosition(upper_left, false); |
| 82 } |
| 83 new_balloon->Show(); |
| 84 notification.display(); |
| 85 for (BalloonObservers::iterator it = observers_.begin(); |
| 86 it != observers_.end(); |
| 87 ++it) { |
| 88 (*it)->OnBalloonSpaceChanged(); |
| 89 } |
| 90 } |
| 91 |
| 92 void BalloonCollection::OnBalloonClosed(Balloon* source) { |
| 93 source->notification().close(true); |
| 94 |
| 95 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) { |
| 96 if (*it == source) { |
| 97 balloons_.erase(it); |
| 98 break; |
| 99 } |
| 100 } |
| 101 |
| 102 gfx::Point origin = layout_.GetOrigin(); |
| 103 for (Balloons::iterator it = balloons_.begin(); it != balloons_.end(); ++it) { |
| 104 gfx::Point upper_left = layout_.NextPosition((*it)->size(), &origin); |
| 105 (*it)->SetPosition(upper_left, true); |
| 106 } |
| 107 } |
| 108 |
| 109 void BalloonCollection::ShowAll() { |
| 110 } |
| 111 |
| 112 void BalloonCollection::HideAll() { |
| 113 } |
| 114 |
| 115 Balloon::Balloon(const Notification& notification, |
| 116 Profile* profile, |
| 117 SiteInstance* site_instance, |
| 118 BalloonCloseListener* listener) |
| 119 : profile_(profile), |
| 120 site_instance_(site_instance), |
| 121 notification_(notification), |
| 122 close_listener_(listener) { |
| 123 } |
| 124 |
| 125 Balloon::~Balloon() { |
| 126 } |
| 127 |
| 128 void Balloon::Show() { |
| 129 if (!balloon_view_.get()) { |
| 130 balloon_view_.reset(new BalloonView()); |
| 131 balloon_view_->Show(this); |
| 132 } |
| 133 } |
| 134 |
| 135 |
| 136 void Balloon::Close() { |
| 137 if (close_listener_) { |
| 138 close_listener_->OnBalloonClosed(this); |
| 139 } |
| 140 } |
| 141 |
| 142 void Balloon::SetPosition(const gfx::Point& upper_left, bool reposition) { |
| 143 position_ = upper_left; |
| 144 if (reposition && balloon_view_.get()) { |
| 145 balloon_view_->RepositionToBalloon(); |
| 146 } |
| 147 } |
| 148 |
| 149 void Balloon::SetSize(const gfx::Size& size) { |
| 150 size_ = size; |
| 151 } |
| 152 |
| 153 const gfx::Point& Balloon::position() const { |
| 154 return position_; |
| 155 } |
| 156 |
| 157 const gfx::Size& Balloon::size() const { |
| 158 return size_; |
| 159 } |
| 160 |
| 161 BalloonCollection::Layout::Placement BalloonCollection::Layout::placement_ = |
| 162 BalloonCollection::Layout::VERTICALLY_FROM_BOTTOM_RIGHT; |
| 163 |
| 164 BalloonCollection::Layout::Layout() |
| 165 : font_scale_factor_(1.0) { |
| 166 RefreshSystemMetrics(); |
| 167 } |
| 168 |
| 169 // Scale the size to count in the system font factor |
| 170 int BalloonCollection::Layout::ScaleSize(int size) const { |
| 171 return static_cast<int>(size * font_scale_factor_); |
| 172 } |
| 173 |
| 174 int BalloonCollection::Layout::min_balloon_width() const { |
| 175 return ScaleSize(kBalloonMinWidth); |
| 176 } |
| 177 |
| 178 int BalloonCollection::Layout::max_balloon_width() const { |
| 179 return ScaleSize(kBalloonMaxWidth); |
| 180 } |
| 181 |
| 182 int BalloonCollection::Layout::min_balloon_height() const { |
| 183 return ScaleSize(kBalloonMinHeight); |
| 184 } |
| 185 |
| 186 int BalloonCollection::Layout::max_balloon_height() const { |
| 187 return ScaleSize(kBalloonMaxHeight); |
| 188 } |
| 189 |
| 190 gfx::Point BalloonCollection::Layout::GetOrigin() const { |
| 191 int x = 0; |
| 192 int y = 0; |
| 193 switch (placement_) { |
| 194 case HORIZONTALLY_FROM_BOTTOM_LEFT: |
| 195 x = work_area_.x(); |
| 196 y = work_area_.bottom(); |
| 197 break; |
| 198 case HORIZONTALLY_FROM_BOTTOM_RIGHT: |
| 199 x = work_area_.right(); |
| 200 y = work_area_.bottom(); |
| 201 break; |
| 202 case VERTICALLY_FROM_TOP_RIGHT: |
| 203 x = work_area_.right(); |
| 204 y = work_area_.y(); |
| 205 break; |
| 206 case VERTICALLY_FROM_BOTTOM_RIGHT: |
| 207 x = work_area_.right(); |
| 208 y = work_area_.bottom(); |
| 209 break; |
| 210 default: |
| 211 NOTREACHED(); |
| 212 break; |
| 213 } |
| 214 return gfx::Point(x, y); |
| 215 } |
| 216 |
| 217 gfx::Point BalloonCollection::Layout::NextPosition( |
| 218 const gfx::Size& balloon_size, |
| 219 gfx::Point* origin) const { |
| 220 DCHECK(origin); |
| 221 |
| 222 int x = 0; |
| 223 int y = 0; |
| 224 switch (placement_) { |
| 225 case HORIZONTALLY_FROM_BOTTOM_LEFT: |
| 226 x = origin->x(); |
| 227 y = origin->y() - balloon_size.height(); |
| 228 origin->set_x(origin->x() + balloon_size.width()); |
| 229 break; |
| 230 case HORIZONTALLY_FROM_BOTTOM_RIGHT: |
| 231 origin->set_x(origin->x() - balloon_size.width()); |
| 232 x = origin->x(); |
| 233 y = origin->y() - balloon_size.height(); |
| 234 break; |
| 235 case VERTICALLY_FROM_TOP_RIGHT: |
| 236 x = origin->x() - balloon_size.width(); |
| 237 y = origin->y(); |
| 238 origin->set_y(origin->y() + balloon_size.height()); |
| 239 break; |
| 240 case VERTICALLY_FROM_BOTTOM_RIGHT: |
| 241 origin->set_y(origin->y() - balloon_size.height()); |
| 242 x = origin->x() - balloon_size.width(); |
| 243 y = origin->y(); |
| 244 break; |
| 245 default: |
| 246 NOTREACHED(); |
| 247 break; |
| 248 } |
| 249 return gfx::Point(x, y); |
| 250 } |
| 251 |
| 252 bool BalloonCollection::Layout::RefreshSystemMetrics() { |
| 253 bool changed = false; |
| 254 |
| 255 gfx::Rect new_work_area(work_area_.x(), work_area_.y(), |
| 256 work_area_.width(), work_area_.height()); |
| 257 GetMainScreenWorkArea(&new_work_area); |
| 258 if (!work_area_.Equals(new_work_area)) { |
| 259 work_area_.SetRect(new_work_area.x(), new_work_area.y(), |
| 260 new_work_area.width(), new_work_area.height()); |
| 261 changed = true; |
| 262 } |
| 263 |
| 264 double new_font_scale_factor = font_scale_factor_; |
| 265 new_font_scale_factor = GetSystemFontScaleFactor(); |
| 266 if (font_scale_factor_ != new_font_scale_factor) { |
| 267 font_scale_factor_ = new_font_scale_factor; |
| 268 changed = true; |
| 269 } |
| 270 return changed; |
| 271 } |
| 272 |
| 273 const void BalloonCollection::Layout::GetMaxLengths( |
| 274 int* max_balloon_length, |
| 275 int* total_length) const { |
| 276 DCHECK(max_balloon_length && total_length); |
| 277 |
| 278 switch (placement_) { |
| 279 case HORIZONTALLY_FROM_BOTTOM_LEFT: |
| 280 case HORIZONTALLY_FROM_BOTTOM_RIGHT: |
| 281 *total_length = work_area_.width(); |
| 282 *max_balloon_length = max_balloon_width(); |
| 283 break; |
| 284 case VERTICALLY_FROM_TOP_RIGHT: |
| 285 case VERTICALLY_FROM_BOTTOM_RIGHT: |
| 286 *total_length = work_area_.height(); |
| 287 *max_balloon_length = max_balloon_height(); |
| 288 break; |
| 289 default: |
| 290 NOTREACHED(); |
| 291 break; |
| 292 } |
| 293 } |
| 294 |
OLD | NEW |