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) 62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) menu_runner_.reset(new views::MenuRunner(menu_model_)); 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 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, 685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu ui::MENU_SOURCE_MOUSE, 695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu views::MenuRunner::HAS_MNEMONICS)); 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid StatusIconWin::HandleBalloonClickEvent() { 73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch if (HasObservers()) 74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch DispatchBalloonClickEvent(); 75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch} 76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::ResetIcon() { 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Delete any previously existing icon. 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Shell_NotifyIcon(NIM_DELETE, &icon_data); 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_MESSAGE; 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uCallbackMessage = message_id_; 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = icon_.Get(); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If we have an image, then set the NIF_ICON flag, which tells 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Shell_NotifyIcon() to set the image for the status icon it creates. 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (icon_data.hIcon) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags |= NIF_ICON; 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Re-add our icon. 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to re-create status tray icon."; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetImage(const gfx::ImageSkia& image) { 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the icon. 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_ICON; 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap())); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = icon_.Get(); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Error setting status tray icon image"; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetPressedImage(const gfx::ImageSkia& image) { 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Ignore pressed images, since the standard on Windows is to not highlight 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // pressed status icons. 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusIconWin::SetToolTip(const base::string16& tool_tip) { 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Create the icon. 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_TIP; 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szTip, tool_tip.c_str()); 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to set tooltip for status tray icon"; 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, 125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& title, 126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::string16& contents) { 1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTIFYICONDATA icon_data; 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) InitIconData(&icon_data); 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags = NIF_INFO; 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_INFO; 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szInfoTitle, title.c_str()); 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) wcscpy_s(icon_data.szInfo, contents.c_str()); 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uTimeout = 0; 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::win::Version win_version = base::win::GetVersion(); 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) { 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap())); 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (win_version >= base::win::VERSION_VISTA) { 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hBalloonIcon = balloon_icon_.Get(); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.hIcon = balloon_icon_.Get(); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.uFlags |= NIF_ICON; 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data.dwInfoFlags = NIIF_USER; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!result) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Unable to create status tray balloon."; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid StatusIconWin::ForceVisible() { 1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu tray_->UpdateIconVisibilityInBackground(this); 1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//////////////////////////////////////////////////////////////////////////////// 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, private: 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { 161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch // so that it doesn't attempt to continue using |menu_model_|. 163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch menu_runner_.reset(); 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(menu); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_model_ = menu; 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->cbSize = sizeof(NOTIFYICONDATA); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->hWnd = window_; 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) icon_data->uID = icon_id_; 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 180