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 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 71 void StatusIconWin::HandleBalloonClickEvent() { 72 if (HasObservers()) 73 DispatchBalloonClickEvent(); 74 } 75 76 void 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 95 void 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 107 void StatusIconWin::SetToolTip(const base::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 base::string16& title, 120 const base::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 void StatusIconWin::ForceVisible() { 148 tray_->UpdateIconVisibilityInBackground(this); 149 } 150 151 //////////////////////////////////////////////////////////////////////////////// 152 // StatusIconWin, private: 153 154 void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) { 155 // |menu_model_| is about to be destroyed. Destroy the menu (which closes it) 156 // so that it doesn't attempt to continue using |menu_model_|. 157 menu_runner_.reset(); 158 DCHECK(menu); 159 menu_model_ = menu; 160 } 161 162 void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) { 163 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 164 memset(icon_data, 0, sizeof(NOTIFYICONDATA)); 165 icon_data->cbSize = sizeof(NOTIFYICONDATA); 166 } else { 167 memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE); 168 icon_data->cbSize = NOTIFYICONDATA_V3_SIZE; 169 } 170 171 icon_data->hWnd = window_; 172 icon_data->uID = icon_id_; 173 } 174