15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/status_icons/status_icon_win.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h" 95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "chrome/browser/ui/views/status_icons/status_tray_win.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/icon_util.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h" 135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ui/gfx/rect.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_runner.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, public: 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuStatusIconWin::StatusIconWin(StatusTrayWin* tray, 205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu UINT id, 215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu HWND window, 225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu UINT message) 235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu : tray_(tray), 245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu icon_id_(id), 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) window_(window), 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) message_id_(message), 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_model_(NULL) { 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_MESSAGE; 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uCallbackMessage = message_id_; 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This can happen if the explorer process isn't running when we try to 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // create the icon for some reason (for example, at startup). 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to create status tray icon."; 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusIconWin::~StatusIconWin() { 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Remove our icon. 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Shell_NotifyIcon(NIM_DELETE, &icon_data); 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::HandleClickEvent(const gfx::Point& cursor_pos, 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool left_mouse_click) { 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Pass to the observer if appropriate. 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (left_mouse_click && HasObservers()) { 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DispatchClickEvent(); 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!menu_model_) 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Set our window as the foreground window, so the context menu closes when 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // we click away from it. 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!SetForegroundWindow(window_)) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch menu_runner_.reset(new views::MenuRunner(menu_model_, 63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch views::MenuRunner::HAS_MNEMONICS)); 645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu ignore_result(menu_runner_->RunMenuAt(NULL, 655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu NULL, 665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu gfx::Rect(cursor_pos, gfx::Size()), 675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu views::MENU_ANCHOR_TOPLEFT, 68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch ui::MENU_SOURCE_MOUSE)); 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid StatusIconWin::HandleBalloonClickEvent() { 72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (HasObservers()) 73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch DispatchBalloonClickEvent(); 74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::ResetIcon() { 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Delete any previously existing icon. 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Shell_NotifyIcon(NIM_DELETE, &icon_data); 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_MESSAGE; 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uCallbackMessage = message_id_; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = icon_.Get(); 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we have an image, then set the NIF_ICON flag, which tells 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Shell_NotifyIcon() to set the image for the status icon it creates. 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (icon_data.hIcon) 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags |= NIF_ICON; 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Re-add our icon. 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to re-create status tray icon."; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetImage(const gfx::ImageSkia& image) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the icon. 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_ICON; 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap())); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = icon_.Get(); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Error setting status tray icon image"; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusIconWin::SetToolTip(const base::string16& tool_tip) { 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the icon. 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_TIP; 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szTip, tool_tip.c_str()); 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to set tooltip for status tray icon"; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, 119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& title, 120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& contents) { 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_INFO; 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_INFO; 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szInfoTitle, title.c_str()); 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szInfo, contents.c_str()); 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uTimeout = 0; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::Version win_version = base::win::GetVersion(); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) { 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap())); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (win_version >= base::win::VERSION_VISTA) { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hBalloonIcon = balloon_icon_.Get(); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = balloon_icon_.Get(); 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags |= NIF_ICON; 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_USER; 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to create status tray balloon."; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid StatusIconWin::ForceVisible() { 1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu tray_->UpdateIconVisibilityInBackground(this); 1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, private: 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { 155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // so that it doesn't attempt to continue using |menu_model_|. 157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch menu_runner_.reset(); 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(menu); 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_model_ = menu; 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->cbSize = sizeof(NOTIFYICONDATA); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->hWnd = window_; 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->uID = icon_id_; 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 174