diff options
author | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-06 00:30:05 +0000 |
---|---|---|
committer | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-06 00:30:05 +0000 |
commit | b0b2a3dd01962592fc8c1f981415ab2bdb8edefb (patch) | |
tree | 6cbda13db92837ae9aadd32a9c4aa6c87aaaf38a /chrome/browser | |
parent | e5ce23e294561b52d35b72eb30c8f5ae869e0fe2 (diff) | |
download | chromium_src-b0b2a3dd01962592fc8c1f981415ab2bdb8edefb.zip chromium_src-b0b2a3dd01962592fc8c1f981415ab2bdb8edefb.tar.gz chromium_src-b0b2a3dd01962592fc8c1f981415ab2bdb8edefb.tar.bz2 |
Allow the user to choose which corner of the screen should get notifications.
BUG=none
TEST=create notifications, use the options menu
Review URL: http://codereview.chromium.org/6006007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70564 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
17 files changed, 397 insertions, 42 deletions
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc index 83fe028..222ee28 100644 --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc @@ -770,7 +770,8 @@ void BrowserProcessImpl::CreateIntranetRedirectDetector() { void BrowserProcessImpl::CreateNotificationUIManager() { DCHECK(notification_ui_manager_.get() == NULL); - notification_ui_manager_.reset(NotificationUIManager::Create()); + notification_ui_manager_.reset(NotificationUIManager::Create(local_state())); + created_notification_ui_manager_ = true; } diff --git a/chrome/browser/chromeos/notifications/balloon_collection_impl.h b/chrome/browser/chromeos/notifications/balloon_collection_impl.h index 03496df..320bfd6 100644 --- a/chrome/browser/chromeos/notifications/balloon_collection_impl.h +++ b/chrome/browser/chromeos/notifications/balloon_collection_impl.h @@ -66,6 +66,7 @@ class BalloonCollectionImpl : public BalloonCollection, virtual void RemoveAll(); virtual bool HasSpace() const; virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size); + virtual void SetPositionPreference(PositionPreference position) {} virtual void DisplayChanged() {} virtual void OnBalloonClosed(Balloon* source); virtual const Balloons& GetActiveBalloons() { return base_.balloons(); } diff --git a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc index 377828b..9a77680 100644 --- a/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc +++ b/chrome/browser/chromeos/notifications/desktop_notifications_unittest.cc @@ -7,6 +7,7 @@ #include "base/stringprintf.h" #include "base/utf_string_conversions.h" #include "chrome/common/render_messages_params.h" +#include "chrome/test/testing_pref_service.h" namespace chromeos { @@ -77,7 +78,8 @@ DesktopNotificationsTest::~DesktopNotificationsTest() { void DesktopNotificationsTest::SetUp() { profile_.reset(new TestingProfile()); balloon_collection_ = new MockBalloonCollection(); - ui_manager_.reset(new NotificationUIManager()); + ui_manager_.reset( + new NotificationUIManager(profile_->GetTestingPrefService())); ui_manager_->Initialize(balloon_collection_); balloon_collection_->set_space_change_listener(ui_manager_.get()); service_.reset(new DesktopNotificationService(profile(), ui_manager_.get())); @@ -86,8 +88,8 @@ void DesktopNotificationsTest::SetUp() { void DesktopNotificationsTest::TearDown() { service_.reset(NULL); - profile_.reset(NULL); ui_manager_.reset(NULL); + profile_.reset(NULL); } ViewHostMsg_ShowNotification_Params diff --git a/chrome/browser/notifications/balloon.h b/chrome/browser/notifications/balloon.h index 3f4ba0b..5b86508 100644 --- a/chrome/browser/notifications/balloon.h +++ b/chrome/browser/notifications/balloon.h @@ -68,6 +68,8 @@ class Balloon { const gfx::Size& content_size() const { return content_size_; } void set_content_size(const gfx::Size& size) { content_size_ = size; } + const BalloonCollection* collection() const { return collection_; } + const gfx::Size& min_scrollbar_size() const { return min_scrollbar_size_; } void set_min_scrollbar_size(const gfx::Size& size) { min_scrollbar_size_ = size; diff --git a/chrome/browser/notifications/balloon_collection.cc b/chrome/browser/notifications/balloon_collection.cc index 218126b..60c9f2d 100644 --- a/chrome/browser/notifications/balloon_collection.cc +++ b/chrome/browser/notifications/balloon_collection.cc @@ -28,20 +28,14 @@ const int kRepositionDelay = 300; } // namespace -// static -// Note that on MacOS, since the coordinate system is inverted vertically from -// the others, this actually produces notifications coming from the TOP right, -// which is what is desired. -BalloonCollectionImpl::Layout::Placement - BalloonCollectionImpl::Layout::placement_ = - Layout::VERTICALLY_FROM_BOTTOM_RIGHT; - BalloonCollectionImpl::BalloonCollectionImpl() #if USE_OFFSETS : ALLOW_THIS_IN_INITIALIZER_LIST(reposition_factory_(this)), added_as_message_loop_observer_(false) #endif { + + SetPositionPreference(BalloonCollection::DEFAULT_POSITION); } BalloonCollectionImpl::~BalloonCollectionImpl() { @@ -221,26 +215,27 @@ void BalloonCollectionImpl::Layout::GetMaxLinearSize(int* max_balloon_size, int* total_size) const { DCHECK(max_balloon_size && total_size); - switch (placement_) { - case VERTICALLY_FROM_TOP_RIGHT: - case VERTICALLY_FROM_BOTTOM_RIGHT: - *total_size = work_area_.height(); - *max_balloon_size = max_balloon_height(); - break; - default: - NOTREACHED(); - break; - } + // All placement schemes are vertical, so we only care about height. + *total_size = work_area_.height(); + *max_balloon_size = max_balloon_height(); } gfx::Point BalloonCollectionImpl::Layout::GetLayoutOrigin() const { int x = 0; int y = 0; switch (placement_) { + case VERTICALLY_FROM_TOP_LEFT: + x = work_area_.x() + HorizontalEdgeMargin(); + y = work_area_.y() + VerticalEdgeMargin(); + break; case VERTICALLY_FROM_TOP_RIGHT: x = work_area_.right() - HorizontalEdgeMargin(); y = work_area_.y() + VerticalEdgeMargin(); break; + case VERTICALLY_FROM_BOTTOM_LEFT: + x = work_area_.x() + HorizontalEdgeMargin(); + y = work_area_.bottom() - VerticalEdgeMargin(); + break; case VERTICALLY_FROM_BOTTOM_RIGHT: x = work_area_.right() - HorizontalEdgeMargin(); y = work_area_.bottom() - VerticalEdgeMargin(); @@ -260,12 +255,24 @@ gfx::Point BalloonCollectionImpl::Layout::NextPosition( int x = 0; int y = 0; switch (placement_) { + case VERTICALLY_FROM_TOP_LEFT: + x = position_iterator->x(); + y = position_iterator->y(); + position_iterator->set_y(position_iterator->y() + balloon_size.height() + + InterBalloonMargin()); + break; case VERTICALLY_FROM_TOP_RIGHT: x = position_iterator->x() - balloon_size.width(); y = position_iterator->y(); position_iterator->set_y(position_iterator->y() + balloon_size.height() + InterBalloonMargin()); break; + case VERTICALLY_FROM_BOTTOM_LEFT: + position_iterator->set_y(position_iterator->y() - balloon_size.height() - + InterBalloonMargin()); + x = position_iterator->x(); + y = position_iterator->y(); + break; case VERTICALLY_FROM_BOTTOM_RIGHT: position_iterator->set_y(position_iterator->y() - balloon_size.height() - InterBalloonMargin()); @@ -283,10 +290,18 @@ gfx::Point BalloonCollectionImpl::Layout::OffScreenLocation() const { int x = 0; int y = 0; switch (placement_) { + case VERTICALLY_FROM_TOP_LEFT: + x = work_area_.x() + HorizontalEdgeMargin(); + y = work_area_.y() + kBalloonMaxHeight + VerticalEdgeMargin(); + break; case VERTICALLY_FROM_TOP_RIGHT: x = work_area_.right() - kBalloonMaxWidth - HorizontalEdgeMargin(); y = work_area_.y() + kBalloonMaxHeight + VerticalEdgeMargin(); break; + case VERTICALLY_FROM_BOTTOM_LEFT: + x = work_area_.x() + HorizontalEdgeMargin(); + y = work_area_.bottom() + kBalloonMaxHeight + VerticalEdgeMargin(); + break; case VERTICALLY_FROM_BOTTOM_RIGHT: x = work_area_.right() - kBalloonMaxWidth - HorizontalEdgeMargin(); y = work_area_.bottom() + kBalloonMaxHeight + VerticalEdgeMargin(); diff --git a/chrome/browser/notifications/balloon_collection.h b/chrome/browser/notifications/balloon_collection.h index 13875dc..6909759 100644 --- a/chrome/browser/notifications/balloon_collection.h +++ b/chrome/browser/notifications/balloon_collection.h @@ -34,6 +34,18 @@ class BalloonCollection { virtual void OnBalloonSpaceChanged() = 0; }; + // Do not change existing values without migration path; these + // are stored as integers in user preferences. + enum PositionPreference { + UPPER_RIGHT = 0, + LOWER_RIGHT = 1, + UPPER_LEFT = 2, + LOWER_LEFT = 3, + + // The default position is different on different platforms. + DEFAULT_POSITION = -1 + }; + static BalloonCollection* Create(); BalloonCollection() @@ -63,6 +75,9 @@ class BalloonCollection { // Request the resizing of a balloon. virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size) = 0; + // Set the position preference for the collection. + virtual void SetPositionPreference(PositionPreference position) = 0; + // Update for new screen dimensions. virtual void DisplayChanged() = 0; diff --git a/chrome/browser/notifications/balloon_collection_impl.h b/chrome/browser/notifications/balloon_collection_impl.h index 7fb1ea9a..d123717 100644 --- a/chrome/browser/notifications/balloon_collection_impl.h +++ b/chrome/browser/notifications/balloon_collection_impl.h @@ -47,6 +47,7 @@ class BalloonCollectionImpl : public BalloonCollection virtual void RemoveAll(); virtual bool HasSpace() const; virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size); + virtual void SetPositionPreference(PositionPreference position); virtual void DisplayChanged(); virtual void OnBalloonClosed(Balloon* source); virtual const Balloons& GetActiveBalloons() { return base_.balloons(); } @@ -68,6 +69,15 @@ class BalloonCollectionImpl : public BalloonCollection public: Layout(); + // These enumerations all are based on a screen orientation where + // the origin is the top-left. + enum Placement { + VERTICALLY_FROM_TOP_LEFT, + VERTICALLY_FROM_TOP_RIGHT, + VERTICALLY_FROM_BOTTOM_LEFT, + VERTICALLY_FROM_BOTTOM_RIGHT + }; + // Refresh the work area and balloon placement. void OnDisplaySettingsChanged(); @@ -80,6 +90,8 @@ class BalloonCollectionImpl : public BalloonCollection // Utility function constrains the input rectangle to the min and max sizes. static gfx::Size ConstrainToSizeLimits(const gfx::Size& rect); + void set_placement(Placement placement) { placement_ = placement; } + // Returns both the total space available and the maximum // allowed per balloon. // @@ -109,11 +121,6 @@ class BalloonCollectionImpl : public BalloonCollection gfx::Point OffScreenLocation() const; private: - enum Placement { - VERTICALLY_FROM_TOP_RIGHT, - VERTICALLY_FROM_BOTTOM_RIGHT - }; - // Layout parameters int VerticalEdgeMargin() const; int HorizontalEdgeMargin() const; @@ -125,7 +132,7 @@ class BalloonCollectionImpl : public BalloonCollection static const int kBalloonMinHeight = 24; static const int kBalloonMaxHeight = 160; - static Placement placement_; + Placement placement_; gfx::Rect work_area_; DISALLOW_COPY_AND_ASSIGN(Layout); }; diff --git a/chrome/browser/notifications/balloon_collection_linux.cc b/chrome/browser/notifications/balloon_collection_linux.cc index 08354a0..099193c 100644 --- a/chrome/browser/notifications/balloon_collection_linux.cc +++ b/chrome/browser/notifications/balloon_collection_linux.cc @@ -67,6 +67,27 @@ bool BalloonCollectionImpl::IsCursorInBalloonCollection() const { return bounds.Contains(cursor); } +void BalloonCollectionImpl::SetPositionPreference( + PositionPreference position) { + if (position == DEFAULT_POSITION) + position = LOWER_RIGHT; + + // All positioning schemes are vertical, and linux + // uses the normal screen orientation. + if (position == UPPER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_RIGHT); + else if (position == UPPER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_LEFT); + else if (position == LOWER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_LEFT); + else if (position == LOWER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_RIGHT); + else + NOTREACHED(); + + PositionBalloons(true); +} + // static BalloonCollection* BalloonCollection::Create() { return new BalloonCollectionImpl(); diff --git a/chrome/browser/notifications/balloon_collection_mac.mm b/chrome/browser/notifications/balloon_collection_mac.mm index 7615415..d31bc39 100644 --- a/chrome/browser/notifications/balloon_collection_mac.mm +++ b/chrome/browser/notifications/balloon_collection_mac.mm @@ -43,6 +43,27 @@ void BalloonCollectionImpl::PositionBalloons(bool reposition) { [NSAnimationContext endGrouping]; } +void BalloonCollectionImpl::SetPositionPreference( + PositionPreference position) { + if (position == DEFAULT_POSITION) + position = UPPER_RIGHT; + + // All positioning schemes are vertical, but mac + // uses a vertically reversed screen orientation. + if (position == UPPER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_RIGHT); + else if (position == UPPER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_LEFT); + else if (position == LOWER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_LEFT); + else if (position == LOWER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_RIGHT); + else + NOTREACHED(); + + PositionBalloons(true); +} + // static BalloonCollection* BalloonCollection::Create() { return new BalloonCollectionImpl(); diff --git a/chrome/browser/notifications/balloon_collection_win.cc b/chrome/browser/notifications/balloon_collection_win.cc index 07bcd18..742872b 100644 --- a/chrome/browser/notifications/balloon_collection_win.cc +++ b/chrome/browser/notifications/balloon_collection_win.cc @@ -62,6 +62,27 @@ bool BalloonCollectionImpl::IsCursorInBalloonCollection() const { return bounds.Contains(cursor); } +void BalloonCollectionImpl::SetPositionPreference( + PositionPreference position) { + if (position == DEFAULT_POSITION) + position = LOWER_RIGHT; + + // All positioning schemes are vertical, and windows + // uses the normal screen orientation. + if (position == UPPER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_RIGHT); + else if (position == UPPER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_TOP_LEFT); + else if (position == LOWER_LEFT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_LEFT); + else if (position == LOWER_RIGHT) + layout_.set_placement(Layout::VERTICALLY_FROM_BOTTOM_RIGHT); + else + NOTREACHED(); + + PositionBalloons(true); +} + // static BalloonCollection* BalloonCollection::Create() { return new BalloonCollectionImpl(); diff --git a/chrome/browser/notifications/desktop_notifications_unittest.cc b/chrome/browser/notifications/desktop_notifications_unittest.cc index ebb6ab8..762990f 100644 --- a/chrome/browser/notifications/desktop_notifications_unittest.cc +++ b/chrome/browser/notifications/desktop_notifications_unittest.cc @@ -6,7 +6,9 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" +#include "chrome/common/pref_names.h" #include "chrome/common/render_messages_params.h" +#include "chrome/test/testing_pref_service.h" // static const int MockBalloonCollection::kMockBalloonSpace = 5; @@ -69,7 +71,8 @@ DesktopNotificationsTest::~DesktopNotificationsTest() { void DesktopNotificationsTest::SetUp() { profile_.reset(new TestingProfile()); balloon_collection_ = new MockBalloonCollection(); - ui_manager_.reset(new NotificationUIManager()); + ui_manager_.reset( + new NotificationUIManager(profile_->GetTestingPrefService())); ui_manager_->Initialize(balloon_collection_); balloon_collection_->set_space_change_listener(ui_manager_.get()); service_.reset(new DesktopNotificationService(profile(), ui_manager_.get())); @@ -78,8 +81,8 @@ void DesktopNotificationsTest::SetUp() { void DesktopNotificationsTest::TearDown() { service_.reset(NULL); - profile_.reset(NULL); ui_manager_.reset(NULL); + profile_.reset(NULL); } ViewHostMsg_ShowNotification_Params @@ -320,3 +323,77 @@ TEST_F(DesktopNotificationsTest, TestUserInputEscaping) { EXPECT_EQ(std::string::npos, data_url.spec().find("%3cscript%3e")); EXPECT_EQ(std::string::npos, data_url.spec().find("%3ci%3e")); } + +TEST_F(DesktopNotificationsTest, TestPositionPreference) { + // Set position preference to lower right. + profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, + BalloonCollection::LOWER_RIGHT); + + // Create some notifications. + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); + for (int id = 0; id <= 3; ++id) { + params.notification_id = id; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); + } + + std::deque<Balloon*>& balloons = balloon_collection_->balloons(); + std::deque<Balloon*>::iterator iter; + + // Check that they decrease in y-position (for MAC, with reversed + // coordinates, they should increase). + int last_y = -1; + int last_x = -1; + + for (iter = balloons.begin(); iter != balloons.end(); ++iter) { + int current_x = (*iter)->GetPosition().x(); + int current_y = (*iter)->GetPosition().y(); + if (last_x > 0) + EXPECT_EQ(last_x, current_x); + + if (last_y > 0) { +#if defined(OS_MACOSX) + EXPECT_GT(current_y, last_y); +#else + EXPECT_LT(current_y, last_y); +#endif + } + + last_x = current_x; + last_y = current_y; + } + + // Now change the position to upper right. This should cause an immediate + // repositioning, and we check for the reverse ordering. + profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, + BalloonCollection::UPPER_RIGHT); + last_x = -1; + last_y = -1; + + for (iter = balloons.begin(); iter != balloons.end(); ++iter) { + int current_x = (*iter)->GetPosition().x(); + int current_y = (*iter)->GetPosition().y(); + + if (last_x > 0) + EXPECT_EQ(last_x, current_x); + + if (last_y > 0) { +#if defined(OS_MACOSX) + EXPECT_LT(current_y, last_y); +#else + EXPECT_GT(current_y, last_y); +#endif + } + + last_x = current_x; + last_y = current_y; + } + + // Now change the position to upper left. Confirm that the X value for the + // balloons gets smaller. + profile_->GetPrefs()->SetInteger(prefs::kDesktopNotificationPosition, + BalloonCollection::UPPER_LEFT); + + int current_x = (*balloons.begin())->GetPosition().x(); + EXPECT_LT(current_x, last_x); +} diff --git a/chrome/browser/notifications/notification_options_menu_model.cc b/chrome/browser/notifications/notification_options_menu_model.cc index 45620c9..fb05bda 100644 --- a/chrome/browser/notifications/notification_options_menu_model.cc +++ b/chrome/browser/notifications/notification_options_menu_model.cc @@ -7,10 +7,14 @@ #include "app/l10n_util.h" #include "base/compiler_specific.h" #include "base/logging.h" +#include "base/utf_string_conversions.h" #include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/notifications/notification.h" +#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/notifications/notifications_prefs_cache.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/chrome_switches.h" @@ -27,6 +31,89 @@ const int kTogglePermissionCommand = 0; const int kToggleExtensionCommand = 1; const int kOpenContentSettingsCommand = 2; +const int kCornerSelectionSubMenu = 3; + +const int kCornerGroupId = 10; +const int kCornerUpperLeft = 11; +const int kCornerUpperRight = 12; +const int kCornerLowerLeft = 13; +const int kCornerLowerRight = 14; +const int kCornerDefault = 20; + +CornerSelectionMenuModel::CornerSelectionMenuModel(Balloon* balloon) + : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)), + balloon_(balloon) { + AddRadioItem(kCornerDefault, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_POSITION_DEFAULT), + kCornerGroupId); + AddSeparator(); + AddRadioItem(kCornerUpperLeft, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_POSITION_UPPER_LEFT), + kCornerGroupId); + AddRadioItem(kCornerUpperRight, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_POSITION_UPPER_RIGHT), + kCornerGroupId); + AddRadioItem(kCornerLowerLeft, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_POSITION_LOWER_LEFT), + kCornerGroupId); + AddRadioItem(kCornerLowerRight, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_POSITION_LOWER_RIGHT), + kCornerGroupId); +} + +CornerSelectionMenuModel::~CornerSelectionMenuModel() { +} + +bool CornerSelectionMenuModel::IsCommandIdChecked(int command_id) const { + NotificationUIManager* ui = g_browser_process->notification_ui_manager(); + BalloonCollection::PositionPreference current = ui->GetPositionPreference(); + + LOG(INFO) << "Current position preference: " << current; + + if (command_id == kCornerUpperLeft) + return (current == BalloonCollection::UPPER_LEFT); + else if (command_id == kCornerUpperRight) + return (current == BalloonCollection::UPPER_RIGHT); + else if (command_id == kCornerLowerLeft) + return (current == BalloonCollection::LOWER_LEFT); + else if (command_id == kCornerLowerRight) + return (current == BalloonCollection::LOWER_RIGHT); + else if (command_id == kCornerDefault) + return (current == BalloonCollection::DEFAULT_POSITION); + + NOTREACHED(); + return false; +} + +bool CornerSelectionMenuModel::IsCommandIdEnabled(int command_id) const { + // All the menu options are always enabled. + return true; +} + +bool CornerSelectionMenuModel::GetAcceleratorForCommandId( + int command_id, menus::Accelerator* accelerator) { + // Currently no accelerators. + return false; +} + +void CornerSelectionMenuModel::ExecuteCommand(int command_id) { + NotificationUIManager* ui = g_browser_process->notification_ui_manager(); + + LOG(INFO) << "Executing command: " << command_id; + + if (command_id == kCornerUpperLeft) + ui->SetPositionPreference(BalloonCollection::UPPER_LEFT); + else if (command_id == kCornerUpperRight) + ui->SetPositionPreference(BalloonCollection::UPPER_RIGHT); + else if (command_id == kCornerLowerLeft) + ui->SetPositionPreference(BalloonCollection::LOWER_LEFT); + else if (command_id == kCornerLowerRight) + ui->SetPositionPreference(BalloonCollection::LOWER_RIGHT); + else if (command_id == kCornerDefault) + ui->SetPositionPreference(BalloonCollection::DEFAULT_POSITION); + else + NOTREACHED(); +} NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)), @@ -49,6 +136,11 @@ NotificationOptionsMenuModel::NotificationOptionsMenuModel(Balloon* balloon) const string16 settings_label = l10n_util::GetStringUTF16( IDS_NOTIFICATIONS_SETTINGS_BUTTON); AddItem(kOpenContentSettingsCommand, settings_label); + + corner_menu_model_.reset(new CornerSelectionMenuModel(balloon)); + AddSubMenu(kCornerSelectionSubMenu, + l10n_util::GetStringUTF16(IDS_NOTIFICATION_CHOOSE_POSITION), + corner_menu_model_.get()); } NotificationOptionsMenuModel::~NotificationOptionsMenuModel() { diff --git a/chrome/browser/notifications/notification_options_menu_model.h b/chrome/browser/notifications/notification_options_menu_model.h index f7f4c77..022b7e0 100644 --- a/chrome/browser/notifications/notification_options_menu_model.h +++ b/chrome/browser/notifications/notification_options_menu_model.h @@ -9,6 +9,28 @@ #include "app/menus/simple_menu_model.h" #include "chrome/browser/notifications/balloon.h" +// Model for the corner-selection submenu. +class CornerSelectionMenuModel : public menus::SimpleMenuModel, + public menus::SimpleMenuModel::Delegate { + public: + explicit CornerSelectionMenuModel(Balloon* balloon); + virtual ~CornerSelectionMenuModel(); + + // Overridden from menus::SimpleMenuModel::Delegate: + virtual bool IsCommandIdChecked(int command_id) const; + virtual bool IsCommandIdEnabled(int command_id) const; + virtual bool GetAcceleratorForCommandId(int command_id, + menus::Accelerator* accelerator); + virtual void ExecuteCommand(int command_id); + + private: + // Not owned. + Balloon* balloon_; + + DISALLOW_COPY_AND_ASSIGN(CornerSelectionMenuModel); +}; + +// Model for the notification options menu itself. class NotificationOptionsMenuModel : public menus::SimpleMenuModel, public menus::SimpleMenuModel::Delegate { public: @@ -29,6 +51,8 @@ class NotificationOptionsMenuModel : public menus::SimpleMenuModel, private: Balloon* balloon_; // Not owned. + scoped_ptr<CornerSelectionMenuModel> corner_menu_model_; + DISALLOW_COPY_AND_ASSIGN(NotificationOptionsMenuModel); }; diff --git a/chrome/browser/notifications/notification_ui_manager.cc b/chrome/browser/notifications/notification_ui_manager.cc index 6e2ff02..950bd6c 100644 --- a/chrome/browser/notifications/notification_ui_manager.cc +++ b/chrome/browser/notifications/notification_ui_manager.cc @@ -7,11 +7,14 @@ #include "base/logging.h" #include "base/scoped_ptr.h" #include "base/stl_util-inl.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/notifications/notification.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" // A class which represents a notification waiting to be shown. class QueuedNotification { @@ -38,10 +41,11 @@ class QueuedNotification { DISALLOW_COPY_AND_ASSIGN(QueuedNotification); }; -NotificationUIManager::NotificationUIManager() +NotificationUIManager::NotificationUIManager(PrefService* local_state) : balloon_collection_(NULL) { registrar_.Add(this, NotificationType::APP_TERMINATING, NotificationService::AllSources()); + position_pref_.Init(prefs::kDesktopNotificationPosition, local_state, this); } NotificationUIManager::~NotificationUIManager() { @@ -49,14 +53,30 @@ NotificationUIManager::~NotificationUIManager() { } // static -NotificationUIManager* NotificationUIManager::Create() { +NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) { BalloonCollection* balloons = BalloonCollection::Create(); - NotificationUIManager* instance = new NotificationUIManager(); + NotificationUIManager* instance = new NotificationUIManager(local_state); instance->Initialize(balloons); balloons->set_space_change_listener(instance); return instance; } +// static +void NotificationUIManager::RegisterPrefs(PrefService* prefs) { + prefs->RegisterIntegerPref(prefs::kDesktopNotificationPosition, + BalloonCollection::DEFAULT_POSITION); +} + +void NotificationUIManager::Initialize( + BalloonCollection* balloon_collection) { + DCHECK(!balloon_collection_.get()); + DCHECK(balloon_collection); + balloon_collection_.reset(balloon_collection); + balloon_collection_->SetPositionPreference( + static_cast<BalloonCollection::PositionPreference>( + position_pref_.GetValue())); +} + void NotificationUIManager::Add(const Notification& notification, Profile* profile) { if (TryReplacement(notification)) { @@ -157,11 +177,33 @@ bool NotificationUIManager::TryReplacement(const Notification& notification) { return false; } +BalloonCollection::PositionPreference +NotificationUIManager::GetPositionPreference() { + LOG(INFO) << "Current position preference: " << position_pref_.GetValue(); + + return static_cast<BalloonCollection::PositionPreference>( + position_pref_.GetValue()); +} + +void NotificationUIManager::SetPositionPreference( + BalloonCollection::PositionPreference preference) { + LOG(INFO) << "Setting position preference: " << preference; + position_pref_.SetValue(static_cast<int>(preference)); + balloon_collection_->SetPositionPreference(preference); +} + void NotificationUIManager::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - if (type == NotificationType::APP_TERMINATING) + if (type == NotificationType::APP_TERMINATING) { CancelAll(); - else + } else if (type == NotificationType::PREF_CHANGED) { + std::string* name = Details<std::string>(details).ptr(); + if (*name == prefs::kDesktopNotificationPosition) + balloon_collection_->SetPositionPreference( + static_cast<BalloonCollection::PositionPreference>( + position_pref_.GetValue())); + } else { NOTREACHED(); + } } diff --git a/chrome/browser/notifications/notification_ui_manager.h b/chrome/browser/notifications/notification_ui_manager.h index de429db..3a0125c 100644 --- a/chrome/browser/notifications/notification_ui_manager.h +++ b/chrome/browser/notifications/notification_ui_manager.h @@ -13,10 +13,12 @@ #include "base/scoped_ptr.h" #include "chrome/browser/notifications/balloon.h" #include "chrome/browser/notifications/balloon_collection.h" +#include "chrome/browser/prefs/pref_member.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" class Notification; +class PrefService; class Profile; class QueuedNotification; class SiteInstance; @@ -27,21 +29,20 @@ class NotificationUIManager : public BalloonCollection::BalloonSpaceChangeListener, public NotificationObserver { public: - NotificationUIManager(); + explicit NotificationUIManager(PrefService* local_state); virtual ~NotificationUIManager(); // Creates an initialized UI manager with a new balloon collection // and the listener relationship setup. // Except for unit tests, this is the way to construct the object. - static NotificationUIManager* Create(); + static NotificationUIManager* Create(PrefService* local_state); + + // Registers preferences. + static void RegisterPrefs(PrefService* prefs); // Initializes the UI manager with a balloon collection; this object // takes ownership of the balloon collection. - void Initialize(BalloonCollection* balloon_collection) { - DCHECK(!balloon_collection_.get()); - DCHECK(balloon_collection); - balloon_collection_.reset(balloon_collection); - } + void Initialize(BalloonCollection* balloon_collection); // Adds a notification to be displayed. Virtual for unit test override. virtual void Add(const Notification& notification, @@ -65,6 +66,13 @@ class NotificationUIManager return balloon_collection_.get(); } + // Gets the preference indicating where notifications should be placed. + BalloonCollection::PositionPreference GetPositionPreference(); + + // Sets the preference that indicates where notifications should + // be placed on the screen. + void SetPositionPreference(BalloonCollection::PositionPreference preference); + // NotificationObserver interface (the event signaling kind of notifications) virtual void Observe(NotificationType type, const NotificationSource& source, @@ -95,6 +103,9 @@ class NotificationUIManager // Registrar for the other kind of notifications (event signaling). NotificationRegistrar registrar_; + // Prefs listener for the position preference. + IntegerPrefMember position_pref_; + DISALLOW_COPY_AND_ASSIGN(NotificationUIManager); }; diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 1d21c1f..1aa3c9e 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -34,6 +34,7 @@ #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/pref_proxy_config_service.h" #include "chrome/browser/net/net_pref_observer.h" +#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/page_info_model.h" #include "chrome/browser/password_manager/password_manager.h" @@ -100,6 +101,7 @@ void RegisterLocalState(PrefService* local_state) { geolocation::RegisterPrefs(local_state); AutoFillManager::RegisterBrowserPrefs(local_state); BackgroundPageTracker::RegisterPrefs(local_state); + NotificationUIManager::RegisterPrefs(local_state); #if defined(OS_CHROMEOS) chromeos::UserManager::RegisterPrefs(local_state); chromeos::UserCrosSettingsProvider::RegisterPrefs(local_state); diff --git a/chrome/browser/ui/cocoa/notifications/balloon_controller_unittest.mm b/chrome/browser/ui/cocoa/notifications/balloon_controller_unittest.mm index a63a327..3b168ef 100644 --- a/chrome/browser/ui/cocoa/notifications/balloon_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/notifications/balloon_controller_unittest.mm @@ -34,8 +34,9 @@ class MockBalloonCollection : public BalloonCollection { virtual bool RemoveBySourceOrigin(const GURL& origin) { return false; } virtual void RemoveAll() {} virtual bool HasSpace() const { return true; } - virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size) {}; + virtual void ResizeBalloon(Balloon* balloon, const gfx::Size& size) {} virtual void DisplayChanged() {} + virtual void SetPositionPreference(PositionPreference preference) {} virtual void OnBalloonClosed(Balloon* source) {}; virtual const Balloons& GetActiveBalloons() { NOTREACHED(); |