1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/views/status_icons/status_icon_win.h" 6 7 #include "base/strings/string_number_conversions.h" 8 #include "base/win/metro.h" 9 #include "base/win/windows_version.h" 10 #include "third_party/skia/include/core/SkBitmap.h" 11 #include "ui/gfx/icon_util.h" 12 #include "ui/gfx/point.h" 13 #include "ui/views/controls/menu/menu_item_view.h" 14 #include "ui/views/controls/menu/menu_runner.h" 15 #include "win8/util/win8_util.h" 16 17 //////////////////////////////////////////////////////////////////////////////// 18 // StatusIconWin, public: 19 20 StatusIconWin::StatusIconWin(UINT id, HWND window, UINT message) 21 : icon_id_(id), 22 window_(window), 23 message_id_(message), 24 menu_model_(NULL) { 25 NOTIFYICONDATA icon_data; 26 InitIconData(&icon_data); 27 icon_data.uFlags = NIF_MESSAGE; 28 icon_data.uCallbackMessage = message_id_; 29 BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 30 // This can happen if the explorer process isn't running when we try to 31 // create the icon for some reason (for example, at startup). 32 if (!result) 33 LOG(WARNING) << "Unable to create status tray icon."; 34 } 35 36 StatusIconWin::~StatusIconWin() { 37 // Remove our icon. 38 NOTIFYICONDATA icon_data; 39 InitIconData(&icon_data); 40 Shell_NotifyIcon(NIM_DELETE, &icon_data); 41 } 42 43 void StatusIconWin::HandleClickEvent(const gfx::Point& cursor_pos, 44 bool left_mouse_click) { 45 // Pass to the observer if appropriate. 46 if (left_mouse_click && HasObservers()) { 47 DispatchClickEvent(); 48 return; 49 } 50 51 if (!menu_model_) 52 return; 53 54 // Set our window as the foreground window, so the context menu closes when 55 // we click away from it. 56 if (!SetForegroundWindow(window_)) 57 return; 58 59 menu_runner_.reset(new views::MenuRunner(menu_model_)); 60 61 ignore_result(menu_runner_->RunMenuAt(NULL, NULL, 62 gfx::Rect(cursor_pos, gfx::Size()), views::MenuItemView::TOPLEFT, 63 ui::MENU_SOURCE_MOUSE, views::MenuRunner::HAS_MNEMONICS)); 64 } 65 66 void StatusIconWin::HandleBalloonClickEvent() { 67 if (HasObservers()) 68 DispatchBalloonClickEvent(); 69 } 70 71 void StatusIconWin::ResetIcon() { 72 NOTIFYICONDATA icon_data; 73 InitIconData(&icon_data); 74 // Delete any previously existing icon. 75 Shell_NotifyIcon(NIM_DELETE, &icon_data); 76 InitIconData(&icon_data); 77 icon_data.uFlags = NIF_MESSAGE; 78 icon_data.uCallbackMessage = message_id_; 79 icon_data.hIcon = icon_.Get(); 80 // If we have an image, then set the NIF_ICON flag, which tells 81 // Shell_NotifyIcon() to set the image for the status icon it creates. 82 if (icon_data.hIcon) 83 icon_data.uFlags |= NIF_ICON; 84 // Re-add our icon. 85 BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 86 if (!result) 87 LOG(WARNING) << "Unable to re-create status tray icon."; 88 } 89 90 void StatusIconWin::SetImage(const gfx::ImageSkia& image) { 91 // Create the icon. 92 NOTIFYICONDATA icon_data; 93 InitIconData(&icon_data); 94 icon_data.uFlags = NIF_ICON; 95 icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap())); 96 icon_data.hIcon = icon_.Get(); 97 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 98 if (!result) 99 LOG(WARNING) << "Error setting status tray icon image"; 100 } 101 102 void StatusIconWin::SetPressedImage(const gfx::ImageSkia& image) { 103 // Ignore pressed images, since the standard on Windows is to not highlight 104 // pressed status icons. 105 } 106 107 void StatusIconWin::SetToolTip(const string16& tool_tip) { 108 // Create the icon. 109 NOTIFYICONDATA icon_data; 110 InitIconData(&icon_data); 111 icon_data.uFlags = NIF_TIP; 112 wcscpy_s(icon_data.szTip, tool_tip.c_str()); 113 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 114 if (!result) 115 LOG(WARNING) << "Unable to set tooltip for status tray icon"; 116 } 117 118 void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, 119 const string16& title, 120 const string16& contents) { 121 NOTIFYICONDATA icon_data; 122 InitIconData(&icon_data); 123 icon_data.uFlags = NIF_INFO; 124 icon_data.dwInfoFlags = NIIF_INFO; 125 wcscpy_s(icon_data.szInfoTitle, title.c_str()); 126 wcscpy_s(icon_data.szInfo, contents.c_str()); 127 icon_data.uTimeout = 0; 128 129 base::win::Version win_version = base::win::GetVersion(); 130 if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) { 131 balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap())); 132 if (win_version >= base::win::VERSION_VISTA) { 133 icon_data.hBalloonIcon = balloon_icon_.Get(); 134 icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; 135 } else { 136 icon_data.hIcon = balloon_icon_.Get(); 137 icon_data.uFlags |= NIF_ICON; 138 icon_data.dwInfoFlags = NIIF_USER; 139 } 140 } 141 142 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 143 if (!result) 144 LOG(WARNING) << "Unable to create status tray balloon."; 145 } 146 147 //////////////////////////////////////////////////////////////////////////////// 148 // StatusIconWin, private: 149 150 void StatusIconWin::UpdatePlatformContextMenu(ui::MenuModel* menu) { 151 // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 152 // so that it doesn't attempt to continue using |menu_model_|. 153 menu_runner_.reset(); 154 DCHECK(menu); 155 menu_model_ = menu; 156 } 157 158 void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 159 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 160 memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 161 icon_data->cbSize = sizeof(NOTIFYICONDATA); 162 } else { 163 memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 164 icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 165 } 166 167 icon_data->hWnd = window_; 168 icon_data->uID = icon_id_; 169 } 170 171 //////////////////////////////////////////////////////////////////////////////// 172 // StatusIconMetro 173 174 StatusIconMetro::StatusIconMetro(UINT id) 175 : id_(id) { 176 DCHECK(win8::IsSingleWindowMetroMode()); 177 } 178 179 StatusIconMetro::~StatusIconMetro() { 180 } 181 182 void StatusIconMetro::SetImage(const gfx::ImageSkia& image) { 183 DVLOG(1) << __FUNCTION__; 184 } 185 186 void StatusIconMetro::SetPressedImage(const gfx::ImageSkia& image) { 187 DVLOG(1) << __FUNCTION__; 188 } 189 190 void StatusIconMetro::SetToolTip(const string16& tool_tip) { 191 DVLOG(1) << __FUNCTION__; 192 tool_tip_ = tool_tip; 193 } 194 195 void StatusIconMetro::DisplayBalloon(const gfx::ImageSkia& icon, 196 const string16& title, 197 const string16& contents) { 198 DVLOG(1) << __FUNCTION__; 199 200 HMODULE metro_module = base::win::GetMetroModule(); 201 DCHECK(metro_module); 202 203 if (metro_module) { 204 base::win::MetroNotification notification = 205 reinterpret_cast<base::win::MetroNotification>( 206 ::GetProcAddress(metro_module, "DisplayNotification")); 207 DCHECK(notification); 208 notification("", "", title.c_str(), contents.c_str(), L"", 209 base::IntToString(id_).c_str(), NULL, NULL); 210 } 211 } 212 213 void StatusIconMetro::UpdatePlatformContextMenu(ui::MenuModel* menu) { 214 DVLOG(1) << __FUNCTION__ 215 << " This functionality is not supported in Windows 8 metro"; 216 } 217 218