diff options
author | sidharthms@chromium.org <sidharthms@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-28 08:23:59 +0000 |
---|---|---|
committer | sidharthms@chromium.org <sidharthms@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-28 08:23:59 +0000 |
commit | f528059f1c18b4f5b29509d6270bed5812d8b467 (patch) | |
tree | f73e32df4235f022055f2376a16718ccaa00532f /chrome | |
parent | afd7186b06526ff947e6df14ce25a51c7297ff47 (diff) | |
download | chromium_src-f528059f1c18b4f5b29509d6270bed5812d8b467.zip chromium_src-f528059f1c18b4f5b29509d6270bed5812d8b467.tar.gz chromium_src-f528059f1c18b4f5b29509d6270bed5812d8b467.tar.bz2 |
Update Status Icon menu when menu model changes (Linux Aura)
Status icon context menus currently do not update themselves when the menu model
changes or when the check state of a menu item changes. This patch fixes that
behavior.
BUG=263926,262395
Review URL: https://chromiumcodereview.appspot.com/20728003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219964 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
29 files changed, 585 insertions, 151 deletions
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc index 579f031..7dc5c5e 100644 --- a/chrome/browser/background/background_mode_manager.cc +++ b/chrome/browser/background/background_mode_manager.cc @@ -63,24 +63,7 @@ BackgroundModeManager::BackgroundModeData::~BackgroundModeData() { } /////////////////////////////////////////////////////////////////////////////// -// BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides -bool BackgroundModeManager::BackgroundModeData::IsCommandIdChecked( - int command_id) const { - NOTREACHED() << "There are no checked items in the profile submenu."; - return false; -} - -bool BackgroundModeManager::BackgroundModeData::IsCommandIdEnabled( - int command_id) const { - return command_id != IDC_MinimumLabelValue; -} - -bool BackgroundModeManager::BackgroundModeData::GetAcceleratorForCommandId( - int command_id, ui::Accelerator* accelerator) { - // No accelerators for status icon context menus. - return false; -} - +// BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides void BackgroundModeManager::BackgroundModeData::ExecuteCommand( int item, int event_flags) { @@ -110,14 +93,15 @@ int BackgroundModeManager::BackgroundModeData::GetBackgroundAppCount() const { } void BackgroundModeManager::BackgroundModeData::BuildProfileMenu( - ui::SimpleMenuModel* menu, - ui::SimpleMenuModel* containing_menu) { + StatusIconMenuModel* menu, + StatusIconMenuModel* containing_menu) { int position = 0; // When there are no background applications, we want to display // just a label stating that none are running. if (applications_->size() < 1) { menu->AddItemWithStringId(IDC_MinimumLabelValue, IDS_BACKGROUND_APP_NOT_INSTALLED); + menu->SetCommandIdEnabled(IDC_MinimumLabelValue, false); } else { for (extensions::ExtensionList::const_iterator cursor = applications_->begin(); @@ -459,29 +443,7 @@ void BackgroundModeManager::OnProfileNameChanged( } /////////////////////////////////////////////////////////////////////////////// -// BackgroundModeManager::BackgroundModeData, ui::SimpleMenuModel overrides -bool BackgroundModeManager::IsCommandIdChecked( - int command_id) const { - DCHECK(command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); - return true; -} - -bool BackgroundModeManager::IsCommandIdEnabled( - int command_id) const { - if (command_id == IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND) { - PrefService* service = g_browser_process->local_state(); - DCHECK(service); - return service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled); - } - return command_id != IDC_MinimumLabelValue; -} - -bool BackgroundModeManager::GetAcceleratorForCommandId( - int command_id, ui::Accelerator* accelerator) { - // No accelerators for status icon context menus. - return false; -} - +// BackgroundModeManager::BackgroundModeData, StatusIconMenuModel overrides void BackgroundModeManager::ExecuteCommand(int command_id, int event_flags) { // When a browser window is necessary, we use the first profile. The windows // opened for these commands are not profile-specific, so any profile would @@ -699,7 +661,7 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { // TODO(rlp): Add current profile color or indicator. // Create a context menu item for Chrome. - ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(this); + scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this)); // Add About item menu->AddItem(IDC_ABOUT, l10n_util::GetStringUTF16(IDS_ABOUT)); menu->AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); @@ -724,8 +686,8 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { // We should only display the profile in the status icon if it has at // least one background app. if (bmd->GetBackgroundAppCount() > 0) { - ui::SimpleMenuModel* submenu = new ui::SimpleMenuModel(bmd); - bmd->BuildProfileMenu(submenu, menu); + StatusIconMenuModel* submenu = new StatusIconMenuModel(bmd); + bmd->BuildProfileMenu(submenu, menu.get()); profiles_with_apps++; } } @@ -738,17 +700,27 @@ void BackgroundModeManager::UpdateStatusTrayIconContextMenu() { // have any profiles in the cache. DCHECK(profile_cache_->GetNumberOfProfiles() == size_t(1) || keep_alive_for_test_); - background_mode_data_.begin()->second->BuildProfileMenu(menu, NULL); + background_mode_data_.begin()->second->BuildProfileMenu(menu.get(), NULL); } menu->AddSeparator(ui::NORMAL_SEPARATOR); menu->AddCheckItemWithStringId( IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND, IDS_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND); + menu->SetCommandIdChecked(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND, + true); + + PrefService* service = g_browser_process->local_state(); + DCHECK(service); + bool enabled = + service->IsUserModifiablePreference(prefs::kBackgroundModeEnabled); + menu->SetCommandIdEnabled(IDC_STATUS_TRAY_KEEP_CHROME_RUNNING_IN_BACKGROUND, + enabled); + menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); - context_menu_ = menu; - status_icon_->SetContextMenu(menu); + context_menu_ = menu.get(); + status_icon_->SetContextMenu(menu.Pass()); } void BackgroundModeManager::RemoveStatusTrayIcon() { diff --git a/chrome/browser/background/background_mode_manager.h b/chrome/browser/background/background_mode_manager.h index e1c8051..118d386 100644 --- a/chrome/browser/background/background_mode_manager.h +++ b/chrome/browser/background/background_mode_manager.h @@ -12,10 +12,10 @@ #include "chrome/browser/background/background_application_list_model.h" #include "chrome/browser/profiles/profile_info_cache_observer.h" #include "chrome/browser/status_icons/status_icon.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "components/browser_context_keyed_service/browser_context_keyed_service.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" -#include "ui/base/models/simple_menu_model.h" class Browser; class CommandLine; @@ -47,7 +47,7 @@ class BackgroundModeManager : public content::NotificationObserver, public BackgroundApplicationListModel::Observer, public ProfileInfoCacheObserver, - public ui::SimpleMenuModel::Delegate { + public StatusIconMenuModel::Delegate { public: BackgroundModeManager(CommandLine* command_line, ProfileInfoCache* profile_cache); @@ -88,7 +88,7 @@ class BackgroundModeManager FRIEND_TEST_ALL_PREFIXES(BackgroundAppBrowserTest, ReloadBackgroundApp); - class BackgroundModeData : public ui::SimpleMenuModel::Delegate { + class BackgroundModeData : public StatusIconMenuModel::Delegate { public: explicit BackgroundModeData( int command_id, @@ -98,12 +98,7 @@ class BackgroundModeManager // The cached list of BackgroundApplications. scoped_ptr<BackgroundApplicationListModel> applications_; - // Overrides from SimpleMenuModel::Delegate implementation. - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - virtual bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) - OVERRIDE; + // Overrides from StatusIconMenuModel::Delegate implementation. virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; // Returns a browser window, or creates one if none are open. Used by @@ -118,8 +113,8 @@ class BackgroundModeManager // be a submenu in the case of multi-profiles or the main menu in the case // of the single profile case. If containing_menu is valid, we will add // menu as a submenu to it. - void BuildProfileMenu(ui::SimpleMenuModel* menu, - ui::SimpleMenuModel* containing_menu); + void BuildProfileMenu(StatusIconMenuModel* menu, + StatusIconMenuModel* containing_menu); // Set the name associated with this background mode data for displaying in // the status tray. @@ -175,12 +170,7 @@ class BackgroundModeManager virtual void OnProfileNameChanged(const base::FilePath& profile_path, const string16& old_profile_name) OVERRIDE; - // Overrides from SimpleMenuModel::Delegate implementation. - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - virtual bool GetAcceleratorForCommandId(int command_id, - ui::Accelerator* accelerator) - OVERRIDE; + // Overrides from StatusIconMenuModel::Delegate implementation. virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; // Invoked when an extension is installed so we can ensure that @@ -295,7 +285,7 @@ class BackgroundModeManager // Reference to our status icon's context menu (if any) - owned by the // status_icon_. - ui::SimpleMenuModel* context_menu_; + StatusIconMenuModel* context_menu_; // Set to true when we are running in background mode. Allows us to track our // current background state so we can take the appropriate action when the diff --git a/chrome/browser/media/media_stream_capture_indicator.cc b/chrome/browser/media/media_stream_capture_indicator.cc index e92304a..6a375d3 100644 --- a/chrome/browser/media/media_stream_capture_indicator.cc +++ b/chrome/browser/media/media_stream_capture_indicator.cc @@ -279,23 +279,6 @@ MediaStreamCaptureIndicator::RegisterMediaStream( return usage->RegisterMediaStream(devices); } -bool MediaStreamCaptureIndicator::IsCommandIdChecked( - int command_id) const { - NOTIMPLEMENTED() << "There are no checked items in the MediaStream menu."; - return false; -} - -bool MediaStreamCaptureIndicator::IsCommandIdEnabled( - int command_id) const { - return command_id != IDC_MinimumLabelValue; -} - -bool MediaStreamCaptureIndicator::GetAcceleratorForCommandId( - int command_id, ui::Accelerator* accelerator) { - // No accelerators for status icon context menu. - return false; -} - void MediaStreamCaptureIndicator::ExecuteCommand(int command_id, int event_flags) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -409,7 +392,7 @@ void MediaStreamCaptureIndicator::MaybeDestroyStatusTrayIcon() { void MediaStreamCaptureIndicator::UpdateNotificationUserInterface() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - scoped_ptr<ui::SimpleMenuModel> menu(new ui::SimpleMenuModel(this)); + scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this)); bool audio = false; bool video = false; @@ -435,6 +418,10 @@ void MediaStreamCaptureIndicator::UpdateNotificationUserInterface() { command_targets_.push_back(web_contents); menu->AddItem(command_id, GetTitle(web_contents)); + // If the menu item is not a label, enable it. + menu->SetCommandIdEnabled(command_id, + command_id != IDC_MinimumLabelValue); + // If reaching the maximum number, no more item will be added to the menu. if (command_id == IDC_MEDIA_CONTEXT_MEDIA_STREAM_CAPTURE_LIST_LAST) break; @@ -450,7 +437,7 @@ void MediaStreamCaptureIndicator::UpdateNotificationUserInterface() { // The icon will take the ownership of the passed context menu. MaybeCreateStatusTrayIcon(audio, video); if (status_icon_) { - status_icon_->SetContextMenu(menu.release()); + status_icon_->SetContextMenu(menu.Pass()); } } diff --git a/chrome/browser/media/media_stream_capture_indicator.h b/chrome/browser/media/media_stream_capture_indicator.h index 0d9d369..30eb338 100644 --- a/chrome/browser/media/media_stream_capture_indicator.h +++ b/chrome/browser/media/media_stream_capture_indicator.h @@ -12,8 +12,8 @@ #include "base/callback_forward.h" #include "base/memory/ref_counted.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "content/public/common/media_stream_request.h" -#include "ui/base/models/simple_menu_model.h" namespace content { class WebContents; @@ -30,7 +30,7 @@ class StatusTray; // (MediaCaptureDevicesDispatcher is a singleton). class MediaStreamCaptureIndicator : public base::RefCountedThreadSafe<MediaStreamCaptureIndicator>, - public ui::SimpleMenuModel::Delegate { + public StatusIconMenuModel::Delegate { public: MediaStreamCaptureIndicator(); @@ -40,12 +40,7 @@ class MediaStreamCaptureIndicator content::WebContents* web_contents, const content::MediaStreamDevices& devices); - // Overrides from SimpleMenuModel::Delegate implementation. - virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; - virtual bool GetAcceleratorForCommandId( - int command_id, - ui::Accelerator* accelerator) OVERRIDE; + // Overrides from StatusIconMenuModel::Delegate implementation. virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; // Returns true if the |web_contents| is capturing user media (e.g., webcam or diff --git a/chrome/browser/notifications/message_center_notification_manager.cc b/chrome/browser/notifications/message_center_notification_manager.cc index 21e902c..86b741f 100644 --- a/chrome/browser/notifications/message_center_notification_manager.cc +++ b/chrome/browser/notifications/message_center_notification_manager.cc @@ -24,6 +24,7 @@ #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" #include "content/public/common/url_constants.h" +#include "ui/gfx/image/image_skia.h" #include "ui/message_center/message_center_style.h" #include "ui/message_center/message_center_tray.h" #include "ui/message_center/notifier_settings.h" diff --git a/chrome/browser/status_icons/status_icon.cc b/chrome/browser/status_icons/status_icon.cc index 52a36ab..458fb35 100644 --- a/chrome/browser/status_icons/status_icon.cc +++ b/chrome/browser/status_icons/status_icon.cc @@ -5,7 +5,6 @@ #include "chrome/browser/status_icons/status_icon.h" #include "chrome/browser/status_icons/status_icon_observer.h" -#include "ui/base/models/menu_model.h" StatusIcon::StatusIcon() { } @@ -35,10 +34,10 @@ void StatusIcon::DispatchBalloonClickEvent() { } #endif -void StatusIcon::SetContextMenu(ui::MenuModel* menu) { +void StatusIcon::SetContextMenu(scoped_ptr<StatusIconMenuModel> menu) { // The UI may been showing a menu for the current model, don't destroy it // until we've notified the UI of the change. - scoped_ptr<ui::MenuModel> old_menu = context_menu_contents_.Pass(); - context_menu_contents_.reset(menu); - UpdatePlatformContextMenu(menu); + scoped_ptr<StatusIconMenuModel> old_menu = context_menu_contents_.Pass(); + context_menu_contents_ = menu.Pass(); + UpdatePlatformContextMenu(context_menu_contents_.get()); } diff --git a/chrome/browser/status_icons/status_icon.h b/chrome/browser/status_icons/status_icon.h index ccc8c2e..5e50961 100644 --- a/chrome/browser/status_icons/status_icon.h +++ b/chrome/browser/status_icons/status_icon.h @@ -9,15 +9,12 @@ #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/strings/string16.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" namespace gfx { class ImageSkia; } -namespace ui { -class MenuModel; -} - class StatusIconObserver; class StatusIcon { @@ -45,7 +42,7 @@ class StatusIcon { // Set the context menu for this icon. The icon takes ownership of the passed // context menu. Passing NULL results in no menu at all. - void SetContextMenu(ui::MenuModel* menu); + void SetContextMenu(scoped_ptr<StatusIconMenuModel> menu); // Adds/Removes an observer for clicks on the status icon. If an observer is // registered, then left clicks on the status icon will result in the observer @@ -67,13 +64,13 @@ class StatusIcon { // Invoked after a call to SetContextMenu() to let the platform-specific // subclass update the native context menu based on the new model. If NULL is // passed, subclass should destroy the native context menu. - virtual void UpdatePlatformContextMenu(ui::MenuModel* model) = 0; + virtual void UpdatePlatformContextMenu(StatusIconMenuModel* model) = 0; private: ObserverList<StatusIconObserver> observers_; // Context menu, if any. - scoped_ptr<ui::MenuModel> context_menu_contents_; + scoped_ptr<StatusIconMenuModel> context_menu_contents_; DISALLOW_COPY_AND_ASSIGN(StatusIcon); }; diff --git a/chrome/browser/status_icons/status_icon_menu_model.cc b/chrome/browser/status_icons/status_icon_menu_model.cc new file mode 100644 index 0000000..bfb7a4b --- /dev/null +++ b/chrome/browser/status_icons/status_icon_menu_model.cc @@ -0,0 +1,181 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/status_icons/status_icon_menu_model.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/image/image.h" + +struct StatusIconMenuModel::ItemState { + ItemState() + : checked(false), + enabled(true), + visible(true), + is_dynamic(false) {} + bool checked; + bool enabled; + bool visible; + bool is_dynamic; + ui::Accelerator accelerator; + base::string16 label; + base::string16 sublabel; + gfx::Image icon; +}; + +//////////////////////////////////////////////////////////////////////////////// +// StatusIconMenuModel::Delegate, public: + +void StatusIconMenuModel::Delegate::CommandIdHighlighted(int command_id) { +} + +//////////////////////////////////////////////////////////////////////////////// +// StatusIconMenuModel, public: + +StatusIconMenuModel::StatusIconMenuModel(Delegate* delegate) + : ui::SimpleMenuModel(this), delegate_(delegate) { +} + +StatusIconMenuModel::~StatusIconMenuModel() { +} + +void StatusIconMenuModel::SetCommandIdChecked(int command_id, bool checked) { + item_states_[command_id].checked = checked; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::SetCommandIdEnabled(int command_id, bool enabled) { + item_states_[command_id].enabled = enabled; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::SetCommandIdVisible(int command_id, bool visible) { + item_states_[command_id].visible = visible; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::SetAcceleratorForCommandId( + int command_id, const ui::Accelerator* accelerator) { + item_states_[command_id].accelerator = *accelerator; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::ChangeLabelForCommandId(int command_id, + const base::string16& label) { + item_states_[command_id].is_dynamic = true; + item_states_[command_id].label = label; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::ChangeSublabelForCommandId( + int command_id, const base::string16& sublabel) { + item_states_[command_id].is_dynamic = true; + item_states_[command_id].sublabel = sublabel; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::ChangeIconForCommandId( + int command_id, const gfx::Image& icon) { + item_states_[command_id].is_dynamic = true; + item_states_[command_id].icon = icon; + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void StatusIconMenuModel::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +bool StatusIconMenuModel::IsCommandIdChecked(int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.checked; + return false; +} + +bool StatusIconMenuModel::IsCommandIdEnabled(int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.enabled; + return true; +} + +bool StatusIconMenuModel::IsCommandIdVisible(int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.visible; + return true; +} + +bool StatusIconMenuModel::GetAcceleratorForCommandId( + int command_id, ui::Accelerator* accelerator) { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end() && + iter->second.accelerator.key_code() != ui::VKEY_UNKNOWN) { + *accelerator = iter->second.accelerator; + return true; + } + return false; +} + +bool StatusIconMenuModel::IsItemForCommandIdDynamic(int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.is_dynamic; + return false; +} + +base::string16 StatusIconMenuModel::GetLabelForCommandId(int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.label; + return base::string16(); +} + +base::string16 StatusIconMenuModel::GetSublabelForCommandId( + int command_id) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end()) + return iter->second.sublabel; + return base::string16(); +} + +bool StatusIconMenuModel::GetIconForCommandId(int command_id, + gfx::Image* image_skia) const { + ItemStateMap::const_iterator iter = item_states_.find(command_id); + if (iter != item_states_.end() && !iter->second.icon.IsEmpty()) { + *image_skia = iter->second.icon; + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// StatusIconMenuModel, protected: + +void StatusIconMenuModel::MenuItemsChanged() { + NotifyMenuStateChanged(); +} + +void StatusIconMenuModel::NotifyMenuStateChanged() { + FOR_EACH_OBSERVER(Observer, observer_list_, OnMenuStateChanged()); +} + +//////////////////////////////////////////////////////////////////////////////// +// StatusIconMenuModel, private: + +void StatusIconMenuModel::CommandIdHighlighted(int command_id) { + if (delegate_) + delegate_->CommandIdHighlighted(command_id); +} + +void StatusIconMenuModel::ExecuteCommand(int command_id, int event_flags) { + if (delegate_) + delegate_->ExecuteCommand(command_id, event_flags); +} diff --git a/chrome/browser/status_icons/status_icon_menu_model.h b/chrome/browser/status_icons/status_icon_menu_model.h new file mode 100644 index 0000000..fa43874 --- /dev/null +++ b/chrome/browser/status_icons/status_icon_menu_model.h @@ -0,0 +1,119 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_ +#define CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_ + +#include <map> + +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "ui/base/models/simple_menu_model.h" + +namespace gfx { +class Image; +} + +class StatusIconMenuModelTest; + +// StatusIconMenuModel contains the state of the SimpleMenuModel as well as that +// of its delegate. This is done so that we can easily identify when the menu +// model state has changed and can tell the status icon to update the menu. This +// is necessary some platforms which do not notify us before showing the menu +// (like Ubuntu Unity). +class StatusIconMenuModel + : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate, + public base::SupportsWeakPtr<StatusIconMenuModel> { + public: + class Delegate { + public: + // Notifies the delegate that the item with the specified command id was + // visually highlighted within the menu. + virtual void CommandIdHighlighted(int command_id); + + // Performs the action associates with the specified command id. + // The passed |event_flags| are the flags from the event which issued this + // command and they can be examined to find modifier keys. + virtual void ExecuteCommand(int command_id, int event_flags) = 0; + + protected: + virtual ~Delegate() {} + }; + + class Observer { + public: + // Invoked when the menu model has changed. + virtual void OnMenuStateChanged() {} + + protected: + virtual ~Observer() {} + }; + + // The Delegate can be NULL. + explicit StatusIconMenuModel(Delegate* delegate); + virtual ~StatusIconMenuModel(); + + // Methods for seting the state of specific command ids. + void SetCommandIdChecked(int command_id, bool checked); + void SetCommandIdEnabled(int command_id, bool enabled); + void SetCommandIdVisible(int command_id, bool visible); + + // Sets the accelerator for the specified command id. + void SetAcceleratorForCommandId( + int command_id, const ui::Accelerator* accelerator); + + // Calling any of these "change" methods will mark the menu item as "dynamic" + // (see menu_model.h:IsItemDynamicAt) which many platforms take as a cue to + // refresh the label, sublabel and icon of the menu item each time the menu is + // shown. + void ChangeLabelForCommandId(int command_id, const base::string16& label); + void ChangeSublabelForCommandId( + int command_id, const base::string16& sublabel); + void ChangeIconForCommandId(int command_id, const gfx::Image& icon); + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + // Overridden from ui::SimpleMenuModel::Delegate: + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual bool IsCommandIdVisible(int command_id) const OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, ui::Accelerator* accelerator) OVERRIDE; + virtual bool IsItemForCommandIdDynamic(int command_id) const OVERRIDE; + virtual base::string16 GetLabelForCommandId(int command_id) const OVERRIDE; + virtual base::string16 GetSublabelForCommandId(int command_id) const OVERRIDE; + virtual bool GetIconForCommandId(int command_id, gfx::Image* icon) const + OVERRIDE; + + protected: + // Overriden from ui::SimpleMenuModel: + virtual void MenuItemsChanged() OVERRIDE; + + void NotifyMenuStateChanged(); + + void set_delegate(Delegate* delegate) { delegate_ = delegate; } + Delegate* delegate() { return delegate_; } + + private: + // Overridden from ui::SimpleMenuModel::Delegate: + virtual void CommandIdHighlighted(int command_id) OVERRIDE; + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + + struct ItemState; + + // Map the properties to the command id (used as key). + typedef std::map<int, ItemState> ItemStateMap; + + ItemStateMap item_states_; + + ObserverList<Observer> observer_list_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(StatusIconMenuModel); +}; + +#endif // CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_MENU_MODEL_H_ diff --git a/chrome/browser/status_icons/status_icon_menu_model_unittest.cc b/chrome/browser/status_icons/status_icon_menu_model_unittest.cc new file mode 100644 index 0000000..a81d0fa --- /dev/null +++ b/chrome/browser/status_icons/status_icon_menu_model_unittest.cc @@ -0,0 +1,118 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/status_icons/status_icon_menu_model.h" + +#include "base/compiler_specific.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/status_icons/status_icon.h" +#include "chrome/browser/status_icons/status_tray.h" +#include "grit/chrome_unscaled_resources.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/accelerators/accelerator.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image.h" + +class StatusIconMenuModelTest : public testing::Test, + public StatusIconMenuModel::Observer { + public: + virtual void SetUp() OVERRIDE { + menu_.reset(new StatusIconMenuModel(NULL)); + menu_->AddObserver(this); + changed_count_ = 0; + } + + virtual void TearDown() OVERRIDE { + menu_->RemoveObserver(this); + } + + virtual int changed_count() { + return changed_count_; + } + + StatusIconMenuModel* menu_model() { + return menu_.get(); + } + + private: + virtual void OnMenuStateChanged() OVERRIDE { + ++changed_count_; + } + + scoped_ptr<StatusIconMenuModel> menu_; + int changed_count_; +}; + +TEST_F(StatusIconMenuModelTest, ToggleBooleanProperties) { + menu_model()->AddItem(0, ASCIIToUTF16("foo")); + + menu_model()->SetCommandIdChecked(0, true); + EXPECT_TRUE(menu_model()->IsCommandIdChecked(0)); + menu_model()->SetCommandIdChecked(0, false); + EXPECT_FALSE(menu_model()->IsCommandIdChecked(0)); + + menu_model()->SetCommandIdEnabled(0, true); + EXPECT_TRUE(menu_model()->IsCommandIdEnabled(0)); + menu_model()->SetCommandIdEnabled(0, false); + EXPECT_FALSE(menu_model()->IsCommandIdEnabled(0)); + + menu_model()->SetCommandIdVisible(0, true); + EXPECT_TRUE(menu_model()->IsCommandIdVisible(0)); + menu_model()->SetCommandIdVisible(0, false); + EXPECT_FALSE(menu_model()->IsCommandIdVisible(0)); + + // Menu state should have changed 7 times in this test. + EXPECT_EQ(7, changed_count()); +} + +TEST_F(StatusIconMenuModelTest, SetProperties) { + menu_model()->AddItem(0, ASCIIToUTF16("foo1")); + menu_model()->AddItem(1, ASCIIToUTF16("foo2")); + + ui::Accelerator test_accel(ui::VKEY_A, ui::EF_NONE); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + gfx::Image test_image1(*rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON)); + gfx::Image test_image2(*rb.GetImageSkiaNamed(IDR_STATUS_TRAY_ICON_PRESSED)); + ui::Accelerator accel_arg; + gfx::Image image_arg; + + EXPECT_FALSE(menu_model()->GetAcceleratorForCommandId(0, &accel_arg)); + EXPECT_FALSE(menu_model()->GetIconForCommandId(0, &image_arg)); + EXPECT_FALSE(menu_model()->IsItemForCommandIdDynamic(0)); + + // Set the accelerator and label for the first menu item. + menu_model()->SetAcceleratorForCommandId(0, &test_accel); + EXPECT_TRUE(menu_model()->GetAcceleratorForCommandId(0, &accel_arg)); + EXPECT_EQ(test_accel, accel_arg); + + // Try setting label and changing it. Also ensure that menu item is marked + // dynamic since the label has changed. + menu_model()->ChangeLabelForCommandId(0, ASCIIToUTF16("label1")); + EXPECT_TRUE(menu_model()->IsItemForCommandIdDynamic(0)); + EXPECT_EQ(ASCIIToUTF16("label1"), menu_model()->GetLabelForCommandId(0)); + menu_model()->ChangeLabelForCommandId(0, ASCIIToUTF16("label2")); + EXPECT_EQ(ASCIIToUTF16("label2"), menu_model()->GetLabelForCommandId(0)); + + // Set the sublabel and icon image for the second menu item. + menu_model()->ChangeSublabelForCommandId(1, ASCIIToUTF16("sublabel")); + EXPECT_EQ(ASCIIToUTF16("sublabel"), menu_model()->GetSublabelForCommandId(1)); + + // Try setting icon image and changing it. + menu_model()->ChangeIconForCommandId(1, test_image1); + EXPECT_TRUE(menu_model()->GetIconForCommandId(1, &image_arg)); + EXPECT_EQ(image_arg.ToImageSkia(), test_image1.ToImageSkia()); + menu_model()->ChangeIconForCommandId(1, test_image2); + EXPECT_TRUE(menu_model()->GetIconForCommandId(1, &image_arg)); + EXPECT_EQ(image_arg.ToImageSkia(), test_image2.ToImageSkia()); + + // Ensure changes to one menu item does not affect the other menu item. + EXPECT_FALSE(menu_model()->GetAcceleratorForCommandId(1, &accel_arg)); + EXPECT_EQ(string16(), menu_model()->GetLabelForCommandId(1)); + EXPECT_EQ(string16(), menu_model()->GetSublabelForCommandId(0)); + EXPECT_FALSE(menu_model()->GetIconForCommandId(0, &image_arg)); + + // Menu state should have changed 8 times in this test. + EXPECT_EQ(8, changed_count()); +} diff --git a/chrome/browser/status_icons/status_icon_unittest.cc b/chrome/browser/status_icons/status_icon_unittest.cc index 8078665..cdc22b7 100644 --- a/chrome/browser/status_icons/status_icon_unittest.cc +++ b/chrome/browser/status_icons/status_icon_unittest.cc @@ -21,7 +21,8 @@ class TestStatusIcon : public StatusIcon { virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE {} virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE {} virtual void SetToolTip(const string16& tool_tip) OVERRIDE {} - virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE {} + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* menu) OVERRIDE {} virtual void DisplayBalloon(const gfx::ImageSkia& icon, const string16& title, const string16& contents) OVERRIDE {} diff --git a/chrome/browser/status_icons/status_tray_unittest.cc b/chrome/browser/status_icons/status_tray_unittest.cc index 238d8b7..674c8fc 100644 --- a/chrome/browser/status_icons/status_tray_unittest.cc +++ b/chrome/browser/status_icons/status_tray_unittest.cc @@ -19,7 +19,8 @@ class MockStatusIcon : public StatusIcon { virtual void DisplayBalloon(const gfx::ImageSkia& icon, const string16& title, const string16& contents) OVERRIDE {} - virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE {} + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* menu) OVERRIDE {} }; class TestStatusTray : public StatusTray { diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h index 8b380c4..6bd6570 100644 --- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h +++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.h @@ -35,7 +35,8 @@ class StatusIconMac : public StatusIcon { protected: // Overridden from StatusIcon. - virtual void UpdatePlatformContextMenu(ui::MenuModel* model) OVERRIDE; + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* model) OVERRIDE; private: FRIEND_TEST_ALL_PREFIXES(StatusIconMacTest, CreateMenu); diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm index d02c792..cc79fc6 100644 --- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm +++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac.mm @@ -101,7 +101,7 @@ bool StatusIconMac::HasStatusIconMenu() { return menu_.get() != nil; } -void StatusIconMac::UpdatePlatformContextMenu(ui::MenuModel* model) { +void StatusIconMac::UpdatePlatformContextMenu(StatusIconMenuModel* model) { if (!model) { menu_.reset(); } else { diff --git a/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm b/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm index 7675b98..c3ae23d 100644 --- a/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm +++ b/chrome/browser/ui/cocoa/status_icons/status_icon_mac_unittest.mm @@ -5,12 +5,12 @@ #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" #include "chrome/browser/ui/cocoa/status_icons/status_icon_mac.h" #include "grit/chrome_unscaled_resources.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" class SkBitmap; @@ -32,7 +32,7 @@ TEST_F(StatusIconMacTest, Create) { TEST_F(StatusIconMacTest, CreateMenu) { // Create a menu and verify by getting the title of the first menu item. const char* menu_title = "Menu Title"; - scoped_ptr<ui::SimpleMenuModel> model(new ui::SimpleMenuModel(NULL)); + scoped_ptr<StatusIconMenuModel> model(new StatusIconMenuModel(NULL)); model->AddItem(0, ASCIIToUTF16(menu_title)); scoped_ptr<StatusIconMac> icon(new StatusIconMac()); @@ -48,7 +48,7 @@ TEST_F(StatusIconMacTest, MenuToolTip) { // first menu item. const char* menu_title = "Menu Title"; const char* tool_tip = "Tool tip"; - scoped_ptr<ui::SimpleMenuModel> model(new ui::SimpleMenuModel(NULL)); + scoped_ptr<StatusIconMenuModel> model(new StatusIconMenuModel(NULL)); model->AddItem(0, ASCIIToUTF16(menu_title)); scoped_ptr<StatusIconMac> icon(new StatusIconMac()); diff --git a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc index 185f93d..8916cdd 100644 --- a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc +++ b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.cc @@ -54,7 +54,7 @@ void StatusIconGtk::OnClick(GtkWidget* widget) { DispatchClickEvent(); } -void StatusIconGtk::UpdatePlatformContextMenu(ui::MenuModel* model) { +void StatusIconGtk::UpdatePlatformContextMenu(StatusIconMenuModel* model) { if (!model) menu_.reset(); else diff --git a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h index b85edde..2e7f010 100644 --- a/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h +++ b/chrome/browser/ui/gtk/status_icons/status_icon_gtk.h @@ -32,7 +32,8 @@ class StatusIconGtk : public StatusIcon { protected: // Overridden from StatusIcon. - virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE; + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* menu) OVERRIDE; private: // Callback invoked when user right-clicks on the status icon. diff --git a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc index 3ca1df2..e548db6 100644 --- a/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc +++ b/chrome/browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc @@ -6,12 +6,12 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "chrome/browser/status_icons/status_icon_observer.h" #include "chrome/browser/ui/gtk/status_icons/status_icon_gtk.h" #include "grit/chrome_unscaled_resources.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia.h" @@ -35,9 +35,9 @@ TEST(StatusTrayGtkTest, CreateIcon) { StatusIcon* icon = tray.CreateStatusIcon( StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip")); icon->SetPressedImage(*image); - ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL); + scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(NULL)); menu->AddItem(0, ASCIIToUTF16("foo")); - icon->SetContextMenu(menu); + icon->SetContextMenu(menu.Pass()); } TEST(StatusTrayGtkTest, ClickOnIcon) { diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc index 1e4a7bb..326149e 100644 --- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc +++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.cc @@ -140,8 +140,6 @@ AppIndicatorIcon::AppIndicatorIcon(std::string id, AppIndicatorIcon::~AppIndicatorIcon() { if (icon_) { app_indicator_set_status(icon_, APP_INDICATOR_STATUS_PASSIVE); - if (menu_model_) - menu_model_->MenuClosed(); if (gtk_menu_) DestroyMenu(); g_object_unref(icon_); @@ -218,6 +216,11 @@ void AppIndicatorIcon::UpdatePlatformContextMenu(ui::MenuModel* model) { SetMenu(); } +void AppIndicatorIcon::RefreshPlatformContextMenu() { + gtk_container_foreach( + GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_); +} + void AppIndicatorIcon::SetImageFromFile(base::FilePath icon_file_path) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); if (!icon_file_path.empty()) { @@ -268,8 +271,7 @@ void AppIndicatorIcon::SetMenu() { G_CALLBACK(OnMenuItemActivatedThunk), &block_activation_, this); - UpdateMenu(); - menu_model_->MenuWillShow(); + RefreshPlatformContextMenu(); } app_indicator_set_menu(icon_, GTK_MENU(gtk_menu_)); } @@ -287,8 +289,6 @@ void AppIndicatorIcon::CreateClickActionReplacement() { } void AppIndicatorIcon::DestroyMenu() { - if (menu_model_) - menu_model_->MenuClosed(); gtk_widget_destroy(gtk_menu_); gtk_menu_ = NULL; menu_model_ = NULL; @@ -335,11 +335,6 @@ void AppIndicatorIcon::DeletePath(base::FilePath icon_file_path) { } } -void AppIndicatorIcon::UpdateMenu() { - gtk_container_foreach( - GTK_CONTAINER(gtk_menu_), SetMenuItemInfo, &block_activation_); -} - void AppIndicatorIcon::OnClick(GtkWidget* menu_item) { if (delegate()) delegate()->OnClick(); @@ -373,7 +368,6 @@ void AppIndicatorIcon::OnMenuItemActivated(GtkWidget* menu_item) { // The menu item can still be activated by hotkeys even if it is disabled. if (menu_model_->IsEnabledAt(id)) ExecuteCommand(model, id); - UpdateMenu(); } } // namespace libgtk2ui diff --git a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h index 5e2e60c..840f579 100644 --- a/chrome/browser/ui/libgtk2ui/app_indicator_icon.h +++ b/chrome/browser/ui/libgtk2ui/app_indicator_icon.h @@ -8,6 +8,7 @@ #include "base/files/file_path.h" #include "base/memory/scoped_ptr.h" #include "chrome/browser/ui/libgtk2ui/gtk2_signal.h" +#include "ui/base/models/menu_model.h" #include "ui/linux_ui/status_icon_linux.h" typedef struct _AppIndicator AppIndicator; @@ -31,14 +32,12 @@ class AppIndicatorIcon : public StatusIconLinux { // Indicates whether libappindicator so could be opened. static bool CouldOpen(); - // Overridden from StatusIcon: + // Overridden from StatusIconLinux: virtual void SetImage(const gfx::ImageSkia& image) OVERRIDE; virtual void SetPressedImage(const gfx::ImageSkia& image) OVERRIDE; virtual void SetToolTip(const string16& tool_tip) OVERRIDE; - - protected: - // Overridden from StatusIcon. virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE; + virtual void RefreshPlatformContextMenu() OVERRIDE; private: void SetImageFromFile(base::FilePath icon_file_path); @@ -56,9 +55,6 @@ class AppIndicatorIcon : public StatusIconLinux { std::string id); static void DeletePath(base::FilePath icon_file_path); - // Updates all the enabled/checked states and the dynamic labels. - void UpdateMenu(); - // Callback for when the status icon click replacement menu item is clicked. CHROMEGTK_CALLBACK_0(AppIndicatorIcon, void, OnClick); diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.cc b/chrome/browser/ui/views/message_center/web_notification_tray.cc index 5fcf10a..ac31aa9 100644 --- a/chrome/browser/ui/views/message_center/web_notification_tray.cc +++ b/chrome/browser/ui/views/message_center/web_notification_tray.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/status_icons/status_icon.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "chrome/browser/status_icons/status_tray.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/user_metrics.h" @@ -39,6 +40,11 @@ const int kNumberOfSystemTraySprites = 10; // Number of pixels the message center is offset from the mouse. const int kMouseOffset = 5; +// Menu commands +const int kToggleQuietMode = 0; +const int kEnableQuietModeHour = 1; +const int kEnableQuietModeDay = 2; + gfx::ImageSkia* GetIcon(int unread_count, bool is_quiet_mode) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); int resource_id = IDR_NOTIFICATION_TRAY_EMPTY; @@ -128,10 +134,12 @@ MessageCenterTrayDelegate* CreateMessageCenterTray() { WebNotificationTray::WebNotificationTray() : message_center_delegate_(NULL), status_icon_(NULL), + status_icon_menu_(NULL), message_center_visible_(false), should_update_tray_content_(true) { message_center_tray_.reset( new MessageCenterTray(this, g_browser_process->message_center())); + last_quiet_mode_state_ = message_center()->IsQuietMode(); } WebNotificationTray::~WebNotificationTray() { @@ -189,6 +197,17 @@ bool WebNotificationTray::ShowNotifierSettings() { } void WebNotificationTray::OnMessageCenterTrayChanged() { + if (status_icon_) { + bool quiet_mode_state = message_center()->IsQuietMode(); + if (last_quiet_mode_state_ != quiet_mode_state) { + last_quiet_mode_state_ = quiet_mode_state; + + // Quiet mode has changed, update the quiet mode menu. + status_icon_menu_->SetCommandIdChecked(kToggleQuietMode, + quiet_mode_state); + } + } + // See the comments in ash/system/web_notification/web_notification_tray.cc // for why PostTask. should_update_tray_content_ = true; @@ -204,6 +223,18 @@ void WebNotificationTray::OnStatusIconClicked() { message_center_tray_->ToggleMessageCenterBubble(); } +void WebNotificationTray::ExecuteCommand(int command_id, int event_flags) { + if (command_id == kToggleQuietMode) { + bool in_quiet_mode = message_center()->IsQuietMode(); + message_center()->SetQuietMode(!in_quiet_mode); + return; + } + base::TimeDelta expires_in = command_id == kEnableQuietModeDay + ? base::TimeDelta::FromDays(1) + : base::TimeDelta::FromHours(1); + message_center()->EnterQuietModeWithExpire(expires_in); +} + void WebNotificationTray::UpdateStatusIcon() { if (!should_update_tray_content_) return; @@ -319,12 +350,24 @@ void WebNotificationTray::DestroyStatusIcon() { StatusTray* status_tray = g_browser_process->status_tray(); if (status_tray) status_tray->RemoveStatusIcon(status_icon_); + status_icon_menu_ = NULL; status_icon_ = NULL; } void WebNotificationTray::AddQuietModeMenu(StatusIcon* status_icon) { DCHECK(status_icon); - status_icon->SetContextMenu(message_center_tray_->CreateQuietModeMenu()); + + scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(this)); + menu->AddCheckItem(kToggleQuietMode, + l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE)); + menu->SetCommandIdChecked(kToggleQuietMode, message_center()->IsQuietMode()); + menu->AddItem(kEnableQuietModeHour, + l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1HOUR)); + menu->AddItem(kEnableQuietModeDay, + l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_QUIET_MODE_1DAY)); + + status_icon_menu_ = menu.get(); + status_icon->SetContextMenu(menu.Pass()); } MessageCenterWidgetDelegate* diff --git a/chrome/browser/ui/views/message_center/web_notification_tray.h b/chrome/browser/ui/views/message_center/web_notification_tray.h index 040f9a6..a37dd9e 100644 --- a/chrome/browser/ui/views/message_center/web_notification_tray.h +++ b/chrome/browser/ui/views/message_center/web_notification_tray.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_VIEWS_MESSAGE_CENTER_WEB_NOTIFICATION_TRAY_H_ #include "base/memory/weak_ptr.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "chrome/browser/status_icons/status_icon_observer.h" #include "chrome/browser/ui/views/message_center/message_center_widget_delegate.h" #include "content/public/browser/notification_observer.h" @@ -39,7 +40,8 @@ class MessageCenterWidgetDelegate; // tray icon on click. class WebNotificationTray : public message_center::MessageCenterTrayDelegate, public StatusIconObserver, - public base::SupportsWeakPtr<WebNotificationTray> { + public base::SupportsWeakPtr<WebNotificationTray>, + public StatusIconMenuModel::Delegate { public: WebNotificationTray(); virtual ~WebNotificationTray(); @@ -64,6 +66,9 @@ class WebNotificationTray : public message_center::MessageCenterTrayDelegate, void DisplayFirstRunBalloon(); #endif + // StatusIconMenuModel::Delegate implementation. + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + // Changes the icon and hovertext based on number of unread notifications. void UpdateStatusIcon(); void SendHideMessageCenter(); @@ -96,11 +101,13 @@ class WebNotificationTray : public message_center::MessageCenterTrayDelegate, scoped_ptr<message_center::MessagePopupCollection> popup_collection_; StatusIcon* status_icon_; + StatusIconMenuModel* status_icon_menu_; bool message_center_visible_; scoped_ptr<MessageCenterTray> message_center_tray_; gfx::Point mouse_click_point_; bool should_update_tray_content_; + bool last_quiet_mode_state_; DISALLOW_COPY_AND_ASSIGN(WebNotificationTray); }; diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc index 931abef..3656bd5 100644 --- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc +++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.cc @@ -1,15 +1,20 @@ + // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h" -StatusIconLinuxWrapper::StatusIconLinuxWrapper(StatusIconLinux* status_icon) { +StatusIconLinuxWrapper::StatusIconLinuxWrapper(StatusIconLinux* status_icon) + : menu_model_(NULL) { status_icon_.reset(status_icon); status_icon_->set_delegate(this); } -StatusIconLinuxWrapper::~StatusIconLinuxWrapper() {} +StatusIconLinuxWrapper::~StatusIconLinuxWrapper() { + if (menu_model_) + menu_model_->RemoveObserver(this); +} void StatusIconLinuxWrapper::SetImage(const gfx::ImageSkia& image) { status_icon_->SetImage(image); @@ -37,6 +42,10 @@ bool StatusIconLinuxWrapper::HasClickAction() { return HasObservers(); } +void StatusIconLinuxWrapper::OnMenuStateChanged() { + status_icon_->RefreshPlatformContextMenu(); +} + StatusIconLinuxWrapper* StatusIconLinuxWrapper::CreateWrappedStatusIcon( const gfx::ImageSkia& image, const string16& tool_tip) { @@ -50,6 +59,15 @@ StatusIconLinuxWrapper* StatusIconLinuxWrapper::CreateWrappedStatusIcon( return NULL; } -void StatusIconLinuxWrapper::UpdatePlatformContextMenu(ui::MenuModel* model) { +void StatusIconLinuxWrapper::UpdatePlatformContextMenu( + StatusIconMenuModel* model) { + // If a menu already exists, remove ourself from its oberver list. + if (menu_model_) + menu_model_->RemoveObserver(this); + status_icon_->UpdatePlatformContextMenu(model); + menu_model_ = model; + + if (model) + model->AddObserver(this); } diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h index 153ce37..30ccf1f 100644 --- a/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h +++ b/chrome/browser/ui/views/status_icons/status_icon_linux_wrapper.h @@ -13,7 +13,8 @@ // Wrapper class for StatusIconLinux that implements the standard StatusIcon // interface. Also handles callbacks from StatusIconLinux. class StatusIconLinuxWrapper : public StatusIcon, - public StatusIconLinux::Delegate { + public StatusIconLinux::Delegate, + public StatusIconMenuModel::Observer { public: virtual ~StatusIconLinuxWrapper(); @@ -29,6 +30,9 @@ class StatusIconLinuxWrapper : public StatusIcon, virtual void OnClick() OVERRIDE; virtual bool HasClickAction() OVERRIDE; + // StatusIconMenuModel::Observer overrides: + virtual void OnMenuStateChanged() OVERRIDE; + static StatusIconLinuxWrapper* CreateWrappedStatusIcon( const gfx::ImageSkia& image, const string16& tool_tip); @@ -38,7 +42,8 @@ class StatusIconLinuxWrapper : public StatusIcon, // Invoked after a call to SetContextMenu() to let the platform-specific // subclass update the native context menu based on the new model. If NULL is // passed, subclass should destroy the native context menu. - virtual void UpdatePlatformContextMenu(ui::MenuModel* model) OVERRIDE; + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* model) OVERRIDE; private: // A status icon wrapper should only be created by calling @@ -47,8 +52,11 @@ class StatusIconLinuxWrapper : public StatusIcon, // Notification balloon. DesktopNotificationBalloon notification_; + scoped_ptr<StatusIconLinux> status_icon_; + StatusIconMenuModel* menu_model_; + DISALLOW_COPY_AND_ASSIGN(StatusIconLinuxWrapper); }; diff --git a/chrome/browser/ui/views/status_icons/status_icon_win.cc b/chrome/browser/ui/views/status_icons/status_icon_win.cc index a5af417..f49642e 100644 --- a/chrome/browser/ui/views/status_icons/status_icon_win.cc +++ b/chrome/browser/ui/views/status_icons/status_icon_win.cc @@ -147,7 +147,7 @@ void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, //////////////////////////////////////////////////////////////////////////////// // StatusIconWin, private: -void StatusIconWin::UpdatePlatformContextMenu(ui::MenuModel* menu) { +void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) // so that it doesn't attempt to continue using |menu_model_|. menu_runner_.reset(); @@ -210,7 +210,7 @@ void StatusIconMetro::DisplayBalloon(const gfx::ImageSkia& icon, } } -void StatusIconMetro::UpdatePlatformContextMenu(ui::MenuModel* menu) { +void StatusIconMetro::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { DVLOG(1) << __FUNCTION__ << " This functionality is not supported in Windows 8 metro"; } diff --git a/chrome/browser/ui/views/status_icons/status_icon_win.h b/chrome/browser/ui/views/status_icons/status_icon_win.h index 8f4cf1b..8576f5d 100644 --- a/chrome/browser/ui/views/status_icons/status_icon_win.h +++ b/chrome/browser/ui/views/status_icons/status_icon_win.h @@ -52,7 +52,8 @@ class StatusIconWin : public StatusIcon { protected: // Overridden from StatusIcon: - virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE; + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* menu) OVERRIDE; private: void InitIconData(NOTIFYICONDATA* icon_data); @@ -96,7 +97,8 @@ class StatusIconMetro : public StatusIcon { const string16& title, const string16& contents) OVERRIDE; protected: - virtual void UpdatePlatformContextMenu(ui::MenuModel* menu) OVERRIDE; + virtual void UpdatePlatformContextMenu( + StatusIconMenuModel* menu) OVERRIDE; private: string16 tool_tip_; diff --git a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc index 15ce2f8..cbff6d1 100644 --- a/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc +++ b/chrome/browser/ui/views/status_icons/status_tray_win_unittest.cc @@ -8,11 +8,11 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/status_icons/status_icon_menu_model.h" #include "chrome/browser/status_icons/status_icon_observer.h" #include "chrome/browser/ui/views/status_icons/status_icon_win.h" #include "grit/chrome_unscaled_resources.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia.h" @@ -49,9 +49,9 @@ TEST(StatusTrayWinTest, CreateIconAndMenu) { StatusIcon* icon = tray.CreateStatusIcon( StatusTray::OTHER_ICON, *image, ASCIIToUTF16("tool tip")); icon->SetPressedImage(*image); - ui::SimpleMenuModel* menu = new ui::SimpleMenuModel(NULL); + scoped_ptr<StatusIconMenuModel> menu(new StatusIconMenuModel(NULL)); menu->AddItem(0, L"foo"); - icon->SetContextMenu(menu); + icon->SetContextMenu(menu.Pass()); } #if !defined(USE_AURA) // http://crbug.com/156370 diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 926a329..b7f99b90 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2012,6 +2012,8 @@ 'browser/status_icons/desktop_notification_balloon.h', 'browser/status_icons/status_icon.cc', 'browser/status_icons/status_icon.h', + 'browser/status_icons/status_icon_menu_model.cc', + 'browser/status_icons/status_icon_menu_model.h', 'browser/status_icons/status_icon_observer.h', 'browser/status_icons/status_tray.cc', 'browser/status_icons/status_tray.h', diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index f49c7a7..80b080d 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1208,6 +1208,7 @@ 'browser/spellchecker/spellcheck_service_unittest.cc', 'browser/spellchecker/spelling_service_client_unittest.cc', 'browser/spellchecker/word_trimmer_unittest.cc', + 'browser/status_icons/status_icon_menu_model_unittest.cc', 'browser/status_icons/status_icon_unittest.cc', 'browser/status_icons/status_tray_unittest.cc', 'browser/storage_monitor/image_capture_device_manager_unittest.mm', |