diff options
author | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-20 04:52:44 +0000 |
---|---|---|
committer | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-08-20 04:52:44 +0000 |
commit | 9979f5d4a25cab18fa10885c5c7b64ea9f8d8eeb (patch) | |
tree | 0204e3328959fb902f7fb613d0c99103a0e858c7 | |
parent | c78b1e1619d7e700fce78f29533eba5ee78b0c70 (diff) | |
download | chromium_src-9979f5d4a25cab18fa10885c5c7b64ea9f8d8eeb.zip chromium_src-9979f5d4a25cab18fa10885c5c7b64ea9f8d8eeb.tar.gz chromium_src-9979f5d4a25cab18fa10885c5c7b64ea9f8d8eeb.tar.bz2 |
Added support for context menus to status icons.
BUG=37375
TEST=updated StatusIcon unit tests
Review URL: http://codereview.chromium.org/3189003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56812 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/background_mode_manager.cc | 54 | ||||
-rw-r--r-- | chrome/browser/background_mode_manager.h | 11 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_icons/status_icon_mac.h | 3 | ||||
-rw-r--r-- | chrome/browser/cocoa/status_icons/status_icon_mac.mm | 5 | ||||
-rw-r--r-- | chrome/browser/gtk/status_icons/status_icon_gtk.cc | 5 | ||||
-rw-r--r-- | chrome/browser/gtk/status_icons/status_icon_gtk.h | 3 | ||||
-rw-r--r-- | chrome/browser/status_icons/status_icon.cc | 18 | ||||
-rw-r--r-- | chrome/browser/status_icons/status_icon.h | 35 | ||||
-rw-r--r-- | chrome/browser/status_icons/status_icon_unittest.cc | 1 | ||||
-rw-r--r-- | chrome/browser/status_icons/status_tray_unittest.cc | 2 | ||||
-rw-r--r-- | chrome/browser/views/status_icons/status_icon_win.cc | 31 | ||||
-rw-r--r-- | chrome/browser/views/status_icons/status_icon_win.h | 16 | ||||
-rw-r--r-- | chrome/browser/views/status_icons/status_tray_win.cc | 13 | ||||
-rw-r--r-- | chrome/browser/views/status_icons/status_tray_win_unittest.cc | 11 |
14 files changed, 189 insertions, 19 deletions
diff --git a/chrome/browser/background_mode_manager.cc b/chrome/browser/background_mode_manager.cc index 814e2a2..f67a34b 100644 --- a/chrome/browser/background_mode_manager.cc +++ b/chrome/browser/background_mode_manager.cc @@ -5,6 +5,7 @@ #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "base/command_line.h" +#include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/background_mode_manager.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/metrics/user_metrics.h" @@ -19,6 +20,7 @@ #include "chrome/common/pref_names.h" #include "grit/browser_resources.h" #include "grit/chromium_strings.h" +#include "grit/generated_resources.h" #include "grit/theme_resources.h" BackgroundModeManager::BackgroundModeManager(Profile* profile) @@ -64,6 +66,9 @@ BackgroundModeManager::BackgroundModeManager(Profile* profile) } BackgroundModeManager::~BackgroundModeManager() { + // If we're going away, remove our status tray icon so we don't get any events + // from it. + RemoveStatusTrayIcon(); } bool BackgroundModeManager::IsBackgroundModeEnabled() { @@ -183,7 +188,30 @@ void BackgroundModeManager::CreateStatusTrayIcon() { IDR_STATUS_TRAY_ICON); status_icon_->SetImage(*bitmap); status_icon_->SetToolTip(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); - status_icon_->AddObserver(this); + + // Create a context menu item for Chrome. + menus::SimpleMenuModel* menu = new menus::SimpleMenuModel(this); + menu->AddItem(IDC_ABOUT, l10n_util::GetStringFUTF16(IDS_ABOUT, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + menu->AddSeparator(); + menu->AddItemWithStringId(IDC_EXIT, IDS_EXIT); + status_icon_->SetContextMenu(menu); +} + +bool BackgroundModeManager::IsCommandIdChecked(int command_id) const { + return false; +} + +bool BackgroundModeManager::IsCommandIdEnabled(int command_id) const { + // For now, we do not support disabled items. + return true; +} + +bool BackgroundModeManager::GetAcceleratorForCommandId( + int command_id, + menus::Accelerator* accelerator) { + // No accelerators for status icon context menus. + return false; } void BackgroundModeManager::RemoveStatusTrayIcon() { @@ -192,9 +220,27 @@ void BackgroundModeManager::RemoveStatusTrayIcon() { status_icon_ = NULL; } -void BackgroundModeManager::OnClicked() { - UserMetrics::RecordAction(UserMetricsAction("Exit"), profile_); - BrowserList::CloseAllBrowsersAndExit(); + +void BackgroundModeManager::ExecuteCommand(int item) { + switch (item) { + case IDC_EXIT: + UserMetrics::RecordAction(UserMetricsAction("Exit"), profile_); + BrowserList::CloseAllBrowsersAndExit(); + break; + case IDC_ABOUT: { + // Need to display a browser window to put up the about dialog. + Browser* browser = BrowserList::GetLastActive(); + if (!browser) { + Browser::OpenEmptyWindow(profile_); + browser = BrowserList::GetLastActive(); + } + browser->OpenAboutChromeDialog(); + break; + } + default: + NOTREACHED(); + break; + } } // static diff --git a/chrome/browser/background_mode_manager.h b/chrome/browser/background_mode_manager.h index fa5296b..4ae4959 100644 --- a/chrome/browser/background_mode_manager.h +++ b/chrome/browser/background_mode_manager.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_BACKGROUND_MODE_MANAGER_H_ #pragma once +#include "app/menus/simple_menu_model.h" #include "base/gtest_prod_util.h" #include "chrome/browser/status_icons/status_icon.h" #include "chrome/common/notification_observer.h" @@ -34,7 +35,7 @@ class StatusTray; // background. class BackgroundModeManager : private NotificationObserver, - private StatusIcon::Observer { + private menus::SimpleMenuModel::Delegate { public: explicit BackgroundModeManager(Profile* profile); virtual ~BackgroundModeManager(); @@ -53,8 +54,12 @@ class BackgroundModeManager const NotificationSource& source, const NotificationDetails& details); - // StatusIcon::Observer implementation. - virtual void OnClicked(); + // SimpleMenuModel::Delegate implementation. + 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); // Called when an extension is loaded to manage count of background apps. void OnBackgroundAppLoaded(); diff --git a/chrome/browser/cocoa/status_icons/status_icon_mac.h b/chrome/browser/cocoa/status_icons/status_icon_mac.h index 3008607..3f4fbe2 100644 --- a/chrome/browser/cocoa/status_icons/status_icon_mac.h +++ b/chrome/browser/cocoa/status_icons/status_icon_mac.h @@ -26,6 +26,9 @@ class StatusIconMac : public StatusIcon { virtual void SetPressedImage(const SkBitmap& image); virtual void SetToolTip(const string16& tool_tip); + protected: + virtual void ResetContextMenu(menus::MenuModel* menu); + private: // Getter for item_ that allows lazy initialization. NSStatusItem* item(); diff --git a/chrome/browser/cocoa/status_icons/status_icon_mac.mm b/chrome/browser/cocoa/status_icons/status_icon_mac.mm index 5e824b8..0d50c76 100644 --- a/chrome/browser/cocoa/status_icons/status_icon_mac.mm +++ b/chrome/browser/cocoa/status_icons/status_icon_mac.mm @@ -75,3 +75,8 @@ void StatusIconMac::SetPressedImage(const SkBitmap& bitmap) { void StatusIconMac::SetToolTip(const string16& tool_tip) { [item() setToolTip:base::SysUTF16ToNSString(tool_tip)]; } + +void StatusIconMac::ResetContextMenu(menus::MenuModel* menu) { + // TODO(atwilson): Add support for context menus for Mac when actually needed + // (not yet used by anything) - http://crbug.com/37375. +} diff --git a/chrome/browser/gtk/status_icons/status_icon_gtk.cc b/chrome/browser/gtk/status_icons/status_icon_gtk.cc index 3dacfbc..d6af0a7 100644 --- a/chrome/browser/gtk/status_icons/status_icon_gtk.cc +++ b/chrome/browser/gtk/status_icons/status_icon_gtk.cc @@ -40,6 +40,11 @@ void StatusIconGtk::SetToolTip(const string16& tool_tip) { gtk_status_icon_set_tooltip(icon_, UTF16ToUTF8(tool_tip).c_str()); } +void StatusIconGtk::ResetContextMenu(menus::MenuModel* menu) { + // TODO(atwilson): Add support for context menus for GTK + // (http://crbug.com.37375). +} + void StatusIconGtk::OnClick(GtkWidget* widget) { DispatchClickEvent(); } diff --git a/chrome/browser/gtk/status_icons/status_icon_gtk.h b/chrome/browser/gtk/status_icons/status_icon_gtk.h index adb139b..4aa6c93 100644 --- a/chrome/browser/gtk/status_icons/status_icon_gtk.h +++ b/chrome/browser/gtk/status_icons/status_icon_gtk.h @@ -26,6 +26,9 @@ class StatusIconGtk : public StatusIcon { // Exposed for testing. CHROMEGTK_CALLBACK_0(StatusIconGtk, void, OnClick); + protected: + virtual void ResetContextMenu(menus::MenuModel* menu); + private: // The currently-displayed icon for the window. GtkStatusIcon* icon_; diff --git a/chrome/browser/status_icons/status_icon.cc b/chrome/browser/status_icons/status_icon.cc index 9fed83d..066419f 100644 --- a/chrome/browser/status_icons/status_icon.cc +++ b/chrome/browser/status_icons/status_icon.cc @@ -4,6 +4,15 @@ #include "chrome/browser/status_icons/status_icon.h" +#include "app/menus/menu_model.h" + +StatusIcon::StatusIcon() +{ +} + +StatusIcon::~StatusIcon() { +} + void StatusIcon::AddObserver(Observer* observer) { observers_.AddObserver(observer); } @@ -12,6 +21,15 @@ void StatusIcon::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } +bool StatusIcon::HasObservers() { + return observers_.size() > 0; +} + void StatusIcon::DispatchClickEvent() { FOR_EACH_OBSERVER(Observer, observers_, OnClicked()); } + +void StatusIcon::SetContextMenu(menus::MenuModel* menu) { + context_menu_contents_.reset(menu); + ResetContextMenu(menu); +} diff --git a/chrome/browser/status_icons/status_icon.h b/chrome/browser/status_icons/status_icon.h index 8245e12..8f868b5 100644 --- a/chrome/browser/status_icons/status_icon.h +++ b/chrome/browser/status_icons/status_icon.h @@ -7,14 +7,19 @@ #pragma once #include "base/observer_list.h" +#include "base/scoped_ptr.h" #include "base/string16.h" class SkBitmap; +namespace menus { +class MenuModel; +} + class StatusIcon { public: - StatusIcon() {} - virtual ~StatusIcon() {} + StatusIcon(); + virtual ~StatusIcon(); // Sets the image associated with this status icon. virtual void SetImage(const SkBitmap& image) = 0; @@ -25,23 +30,45 @@ class StatusIcon { // Sets the hover text for this status icon. virtual void SetToolTip(const string16& tool_tip) = 0; + // 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(menus::MenuModel* menu); + class Observer { public: virtual ~Observer() {} - // Called when the user clicks on the system tray icon. + // Called when the user clicks on the system tray icon. Clicks that result + // in the context menu being displayed will not be passed to this observer + // (i.e. if there's a context menu set on this status icon, and the user + // right clicks on the icon to display the context menu, OnClicked will not + // be called). virtual void OnClicked() = 0; }; - // Adds/Removes an observer for status bar events. + // 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 + // being called, otherwise, both left and right clicks will display the + // context menu (if any). void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + // Returns true if there are registered click observers. + bool HasObservers(); + // Dispatches a click event to the observers. void DispatchClickEvent(); + protected: + // 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 ResetContextMenu(menus::MenuModel* model) = 0; + private: ObserverList<Observer> observers_; + // Context menu, if any. + scoped_ptr<menus::MenuModel> context_menu_contents_; DISALLOW_COPY_AND_ASSIGN(StatusIcon); }; diff --git a/chrome/browser/status_icons/status_icon_unittest.cc b/chrome/browser/status_icons/status_icon_unittest.cc index db8c529..1738ff7 100644 --- a/chrome/browser/status_icons/status_icon_unittest.cc +++ b/chrome/browser/status_icons/status_icon_unittest.cc @@ -18,6 +18,7 @@ class TestStatusIcon : public StatusIcon { virtual void SetImage(const SkBitmap& image) {} virtual void SetPressedImage(const SkBitmap& image) {} virtual void SetToolTip(const string16& tool_tip) {} + virtual void ResetContextMenu(menus::MenuModel* menu) {} }; TEST(StatusIconTest, ObserverAdd) { diff --git a/chrome/browser/status_icons/status_tray_unittest.cc b/chrome/browser/status_icons/status_tray_unittest.cc index 26219bd..1d43cdc 100644 --- a/chrome/browser/status_icons/status_tray_unittest.cc +++ b/chrome/browser/status_icons/status_tray_unittest.cc @@ -15,6 +15,7 @@ class MockStatusIcon : public StatusIcon { virtual void SetImage(const SkBitmap& image) {} virtual void SetPressedImage(const SkBitmap& image) {} virtual void SetToolTip(const string16& tool_tip) {} + virtual void ResetContextMenu(menus::MenuModel* menu) {} virtual void AddObserver(StatusIcon::Observer* observer) {} virtual void RemoveObserver(StatusIcon::Observer* observer) {} }; @@ -22,6 +23,7 @@ class MockStatusIcon : public StatusIcon { class TestStatusTray : public StatusTray { public: MOCK_METHOD0(CreatePlatformStatusIcon, StatusIcon*()); + MOCK_METHOD1(ResetContextMenu, void(menus::MenuModel*)); }; TEST(StatusTrayTest, Create) { diff --git a/chrome/browser/views/status_icons/status_icon_win.cc b/chrome/browser/views/status_icons/status_icon_win.cc index 53cfcad..524829e 100644 --- a/chrome/browser/views/status_icons/status_icon_win.cc +++ b/chrome/browser/views/status_icons/status_icon_win.cc @@ -4,9 +4,11 @@ #include "chrome/browser/views/status_icons/status_icon_win.h" -#include "gfx/icon_util.h" #include "base/sys_string_conversions.h" +#include "gfx/icon_util.h" +#include "gfx/point.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "views/controls/menu/menu_2.h" StatusIconWin::StatusIconWin(UINT id, HWND window, UINT message) : icon_id_(id), @@ -58,3 +60,30 @@ void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { icon_data->hWnd = window_; icon_data->uID = icon_id_; } + +void StatusIconWin::ResetContextMenu(menus::MenuModel* menu) { + // If no items are passed, blow away our context menu. + if (!menu) { + context_menu_.reset(); + return; + } + + // Create context menu with the new contents. + context_menu_.reset(new views::Menu2(menu)); +} + +void StatusIconWin::HandleClickEvent(int x, int y, bool left_mouse_click) { + // Pass to the observer if appropriate. + if (left_mouse_click && HasObservers()) { + DispatchClickEvent(); + return; + } + + // Event not sent to the observer, so display the context menu if one exists. + if (context_menu_.get()) { + // Set our window as the foreground window, so the context menu closes when + // we click away from it. + SetForegroundWindow(window_); + context_menu_->RunContextMenuAt(gfx::Point(x, y)); + } +} diff --git a/chrome/browser/views/status_icons/status_icon_win.h b/chrome/browser/views/status_icons/status_icon_win.h index e3f88f4..f698fc4 100644 --- a/chrome/browser/views/status_icons/status_icon_win.h +++ b/chrome/browser/views/status_icons/status_icon_win.h @@ -10,8 +10,13 @@ #include <shellapi.h> #include "base/scoped_handle_win.h" +#include "base/scoped_ptr.h" #include "chrome/browser/status_icons/status_icon.h" +namespace views { +class Menu2; +} + class StatusIconWin : public StatusIcon { public: // Constructor which provides this icon's unique ID and messaging window. @@ -27,6 +32,14 @@ class StatusIconWin : public StatusIcon { UINT message_id() const { return message_id_; } + // Handles a click event from the user - if |left_button_click| is true and + // there is a registered observer, passes the click event to the observer, + // otherwise displays the context menu if there is one. + void HandleClickEvent(int x, int y, bool left_button_click); + + protected: + virtual void ResetContextMenu(menus::MenuModel* menu); + private: void InitIconData(NOTIFYICONDATA* icon_data); @@ -42,6 +55,9 @@ class StatusIconWin : public StatusIcon { // The currently-displayed icon for the window. ScopedHICON icon_; + // Context menu associated with this icon (if any). + scoped_ptr<views::Menu2> context_menu_; + DISALLOW_COPY_AND_ASSIGN(StatusIconWin); }; diff --git a/chrome/browser/views/status_icons/status_tray_win.cc b/chrome/browser/views/status_icons/status_tray_win.cc index 5a4587f..29ed025 100644 --- a/chrome/browser/views/status_icons/status_tray_win.cc +++ b/chrome/browser/views/status_icons/status_tray_win.cc @@ -41,20 +41,23 @@ LRESULT CALLBACK StatusTrayWin::WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - // TODO(atwilson): Add support for right clicks and context menu messages - // (tracked in http://crbug.com/37375). switch (message) { case kStatusIconMessage: switch (lparam) { case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_CONTEXTMENU: // Walk our icons, find which one was clicked on, and invoke its - // DispatchClickEvent() method. + // HandleClickEvent() method. for (StatusIconList::const_iterator iter = status_icons().begin(); iter != status_icons().end(); ++iter) { StatusIconWin* win_icon = static_cast<StatusIconWin*>(*iter); - if (win_icon->icon_id() == wparam) - win_icon->DispatchClickEvent(); + if (win_icon->icon_id() == wparam) { + POINT p; + GetCursorPos(&p); + win_icon->HandleClickEvent(p.x, p.y, lparam == WM_LBUTTONDOWN); + } } return TRUE; } diff --git a/chrome/browser/views/status_icons/status_tray_win_unittest.cc b/chrome/browser/views/status_icons/status_tray_win_unittest.cc index e5499b9e..c824755 100644 --- a/chrome/browser/views/status_icons/status_tray_win_unittest.cc +++ b/chrome/browser/views/status_icons/status_tray_win_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "app/menus/simple_menu_model.h" #include "app/resource_bundle.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -24,8 +25,9 @@ TEST(StatusTrayWinTest, CreateTray) { StatusTrayWin tray; } -TEST(StatusTrayWinTest, CreateIcon) { - // Create an icon, set the images and tooltip, then shut it down. +TEST(StatusTrayWinTest, CreateIconAndMenu) { + // Create an icon, set the images, tooltip, and context menu, then shut it + // down. StatusTrayWin tray; StatusIcon* icon = tray.CreateStatusIcon(); SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed( @@ -33,6 +35,9 @@ TEST(StatusTrayWinTest, CreateIcon) { icon->SetImage(*bitmap); icon->SetPressedImage(*bitmap); icon->SetToolTip(ASCIIToUTF16("tool tip")); + menus::SimpleMenuModel* menu = new menus::SimpleMenuModel(NULL); + menu->AddItem(0, L"foo"); + icon->SetContextMenu(menu); } TEST(StatusTrayWinTest, ClickOnIcon) { @@ -44,5 +49,7 @@ TEST(StatusTrayWinTest, ClickOnIcon) { EXPECT_CALL(observer, OnClicked()); // Mimic a click. tray.WndProc(NULL, icon->message_id(), icon->icon_id(), WM_LBUTTONDOWN); + // Mimic a right-click - observer should not be called. + tray.WndProc(NULL, icon->message_id(), icon->icon_id(), WM_RBUTTONDOWN); icon->RemoveObserver(&observer); } |