status_icon_win.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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/windows_version.h" 9#include "chrome/browser/ui/views/status_icons/status_tray_win.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/gfx/rect.h" 14#include "ui/views/controls/menu/menu_runner.h" 15 16//////////////////////////////////////////////////////////////////////////////// 17// StatusIconWin, public: 18 19StatusIconWin::StatusIconWin(StatusTrayWin* tray, 20 UINT id, 21 HWND window, 22 UINT message) 23 : tray_(tray), 24 icon_id_(id), 25 window_(window), 26 message_id_(message), 27 menu_model_(NULL) { 28 NOTIFYICONDATA icon_data; 29 InitIconData(&icon_data); 30 icon_data.uFlags = NIF_MESSAGE; 31 icon_data.uCallbackMessage = message_id_; 32 BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 33 // This can happen if the explorer process isn't running when we try to 34 // create the icon for some reason (for example, at startup). 35 if (!result) 36 LOG(WARNING) << "Unable to create status tray icon."; 37} 38 39StatusIconWin::~StatusIconWin() { 40 // Remove our icon. 41 NOTIFYICONDATA icon_data; 42 InitIconData(&icon_data); 43 Shell_NotifyIcon(NIM_DELETE, &icon_data); 44} 45 46void StatusIconWin::HandleClickEvent(const gfx::Point& cursor_pos, 47 bool left_mouse_click) { 48 // Pass to the observer if appropriate. 49 if (left_mouse_click && HasObservers()) { 50 DispatchClickEvent(); 51 return; 52 } 53 54 if (!menu_model_) 55 return; 56 57 // Set our window as the foreground window, so the context menu closes when 58 // we click away from it. 59 if (!SetForegroundWindow(window_)) 60 return; 61 62 menu_runner_.reset(new views::MenuRunner(menu_model_, 63 views::MenuRunner::HAS_MNEMONICS)); 64 ignore_result(menu_runner_->RunMenuAt(NULL, 65 NULL, 66 gfx::Rect(cursor_pos, gfx::Size()), 67 views::MENU_ANCHOR_TOPLEFT, 68 ui::MENU_SOURCE_MOUSE)); 69} 70 71void StatusIconWin::HandleBalloonClickEvent() { 72 if (HasObservers()) 73 DispatchBalloonClickEvent(); 74} 75 76void StatusIconWin::ResetIcon() { 77 NOTIFYICONDATA icon_data; 78 InitIconData(&icon_data); 79 // Delete any previously existing icon. 80 Shell_NotifyIcon(NIM_DELETE, &icon_data); 81 InitIconData(&icon_data); 82 icon_data.uFlags = NIF_MESSAGE; 83 icon_data.uCallbackMessage = message_id_; 84 icon_data.hIcon = icon_.Get(); 85 // If we have an image, then set the NIF_ICON flag, which tells 86 // Shell_NotifyIcon() to set the image for the status icon it creates. 87 if (icon_data.hIcon) 88 icon_data.uFlags |= NIF_ICON; 89 // Re-add our icon. 90 BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 91 if (!result) 92 LOG(WARNING) << "Unable to re-create status tray icon."; 93} 94 95void StatusIconWin::SetImage(const gfx::ImageSkia& image) { 96 // Create the icon. 97 NOTIFYICONDATA icon_data; 98 InitIconData(&icon_data); 99 icon_data.uFlags = NIF_ICON; 100 icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap())); 101 icon_data.hIcon = icon_.Get(); 102 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 103 if (!result) 104 LOG(WARNING) << "Error setting status tray icon image"; 105} 106 107void StatusIconWin::SetPressedImage(const gfx::ImageSkia& image) { 108 // Ignore pressed images, since the standard on Windows is to not highlight 109 // pressed status icons. 110} 111 112void StatusIconWin::SetToolTip(const base::string16& tool_tip) { 113 // Create the icon. 114 NOTIFYICONDATA icon_data; 115 InitIconData(&icon_data); 116 icon_data.uFlags = NIF_TIP; 117 wcscpy_s(icon_data.szTip, tool_tip.c_str()); 118 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 119 if (!result) 120 LOG(WARNING) << "Unable to set tooltip for status tray icon"; 121} 122 123void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, 124 const base::string16& title, 125 const base::string16& contents) { 126 NOTIFYICONDATA icon_data; 127 InitIconData(&icon_data); 128 icon_data.uFlags = NIF_INFO; 129 icon_data.dwInfoFlags = NIIF_INFO; 130 wcscpy_s(icon_data.szInfoTitle, title.c_str()); 131 wcscpy_s(icon_data.szInfo, contents.c_str()); 132 icon_data.uTimeout = 0; 133 134 base::win::Version win_version = base::win::GetVersion(); 135 if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) { 136 balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap())); 137 if (win_version >= base::win::VERSION_VISTA) { 138 icon_data.hBalloonIcon = balloon_icon_.Get(); 139 icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; 140 } else { 141 icon_data.hIcon = balloon_icon_.Get(); 142 icon_data.uFlags |= NIF_ICON; 143 icon_data.dwInfoFlags = NIIF_USER; 144 } 145 } 146 147 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 148 if (!result) 149 LOG(WARNING) << "Unable to create status tray balloon."; 150} 151 152void StatusIconWin::ForceVisible() { 153 tray_->UpdateIconVisibilityInBackground(this); 154} 155 156//////////////////////////////////////////////////////////////////////////////// 157// StatusIconWin, private: 158 159void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { 160 // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 161 // so that it doesn't attempt to continue using |menu_model_|. 162 menu_runner_.reset(); 163 DCHECK(menu); 164 menu_model_ = menu; 165} 166 167void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 168 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 169 memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 170 icon_data->cbSize = sizeof(NOTIFYICONDATA); 171 } else { 172 memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 173 icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 174 } 175 176 icon_data->hWnd = window_; 177 icon_data->uID = icon_id_; 178} 179