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 19 StatusIconWin::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 39 StatusIconWin::~StatusIconWin() { 40 // Remove our icon. 41 NOTIFYICONDATA icon_data; 42 InitIconData(&icon_data); 43 Shell_NotifyIcon(NIM_DELETE, &icon_data); 44 } 45 46 void 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 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 views::MenuRunner::HAS_MNEMONICS)); 70 } 71 72 void StatusIconWin::HandleBalloonClickEvent() { 73 if (HasObservers()) 74 DispatchBalloonClickEvent(); 75 } 76 77 void StatusIconWin::ResetIcon() { 78 NOTIFYICONDATA icon_data; 79 InitIconData(&icon_data); 80 // Delete any previously existing icon. 81 Shell_NotifyIcon(NIM_DELETE, &icon_data); 82 InitIconData(&icon_data); 83 icon_data.uFlags = NIF_MESSAGE; 84 icon_data.uCallbackMessage = message_id_; 85 icon_data.hIcon = icon_.Get(); 86 // If we have an image, then set the NIF_ICON flag, which tells 87 // Shell_NotifyIcon() to set the image for the status icon it creates. 88 if (icon_data.hIcon) 89 icon_data.uFlags |= NIF_ICON; 90 // Re-add our icon. 91 BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data); 92 if (!result) 93 LOG(WARNING) << "Unable to re-create status tray icon."; 94 } 95 96 void StatusIconWin::SetImage(const gfx::ImageSkia& image) { 97 // Create the icon. 98 NOTIFYICONDATA icon_data; 99 InitIconData(&icon_data); 100 icon_data.uFlags = NIF_ICON; 101 icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap())); 102 icon_data.hIcon = icon_.Get(); 103 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 104 if (!result) 105 LOG(WARNING) << "Error setting status tray icon image"; 106 } 107 108 void StatusIconWin::SetPressedImage(const gfx::ImageSkia& image) { 109 // Ignore pressed images, since the standard on Windows is to not highlight 110 // pressed status icons. 111 } 112 113 void StatusIconWin::SetToolTip(const base::string16& tool_tip) { 114 // Create the icon. 115 NOTIFYICONDATA icon_data; 116 InitIconData(&icon_data); 117 icon_data.uFlags = NIF_TIP; 118 wcscpy_s(icon_data.szTip, tool_tip.c_str()); 119 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 120 if (!result) 121 LOG(WARNING) << "Unable to set tooltip for status tray icon"; 122 } 123 124 void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon, 125 const base::string16& title, 126 const base::string16& contents) { 127 NOTIFYICONDATA icon_data; 128 InitIconData(&icon_data); 129 icon_data.uFlags = NIF_INFO; 130 icon_data.dwInfoFlags = NIIF_INFO; 131 wcscpy_s(icon_data.szInfoTitle, title.c_str()); 132 wcscpy_s(icon_data.szInfo, contents.c_str()); 133 icon_data.uTimeout = 0; 134 135 base::win::Version win_version = base::win::GetVersion(); 136 if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) { 137 balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap())); 138 if (win_version >= base::win::VERSION_VISTA) { 139 icon_data.hBalloonIcon = balloon_icon_.Get(); 140 icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; 141 } else { 142 icon_data.hIcon = balloon_icon_.Get(); 143 icon_data.uFlags |= NIF_ICON; 144 icon_data.dwInfoFlags = NIIF_USER; 145 } 146 } 147 148 BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data); 149 if (!result) 150 LOG(WARNING) << "Unable to create status tray balloon."; 151 } 152 153 void StatusIconWin::ForceVisible() { 154 tray_->UpdateIconVisibilityInBackground(this); 155 } 156 157 //////////////////////////////////////////////////////////////////////////////// 158 // StatusIconWin, private: 159 160 void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { 161 // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 162 // so that it doesn't attempt to continue using |menu_model_|. 163 menu_runner_.reset(); 164 DCHECK(menu); 165 menu_model_ = menu; 166 } 167 168 void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 169 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 170 memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 171 icon_data->cbSize = sizeof(NOTIFYICONDATA); 172 } else { 173 memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 174 icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 175 } 176 177 icon_data->hWnd = window_; 178 icon_data->uID = icon_id_; 179 } 180