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