1// Copyright (c) 2011 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/sys_string_conversions.h"
8#include "third_party/skia/include/core/SkBitmap.h"
9#include "ui/gfx/icon_util.h"
10#include "ui/gfx/point.h"
11#include "views/controls/menu/menu_2.h"
12
13StatusIconWin::StatusIconWin(UINT id, HWND window, UINT message)
14    : icon_id_(id),
15      window_(window),
16      message_id_(message) {
17  NOTIFYICONDATA icon_data;
18  InitIconData(&icon_data);
19  icon_data.uFlags = NIF_MESSAGE;
20  icon_data.uCallbackMessage = message_id_;
21  BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
22  // This can happen if the explorer process isn't running when we try to
23  // create the icon for some reason (for example, at startup).
24  if (!result)
25    LOG(WARNING) << "Unable to create status tray icon.";
26}
27
28StatusIconWin::~StatusIconWin() {
29  // Remove our icon.
30  NOTIFYICONDATA icon_data;
31  InitIconData(&icon_data);
32  Shell_NotifyIcon(NIM_DELETE, &icon_data);
33}
34
35void StatusIconWin::SetImage(const SkBitmap& image) {
36  // Create the icon.
37  NOTIFYICONDATA icon_data;
38  InitIconData(&icon_data);
39  icon_data.uFlags = NIF_ICON;
40  icon_.Set(IconUtil::CreateHICONFromSkBitmap(image));
41  icon_data.hIcon = icon_.Get();
42  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
43  if (!result)
44    LOG(WARNING) << "Error setting status tray icon image";
45}
46
47void StatusIconWin::ResetIcon() {
48  NOTIFYICONDATA icon_data;
49  InitIconData(&icon_data);
50  // Delete any previously existing icon.
51  Shell_NotifyIcon(NIM_DELETE, &icon_data);
52  InitIconData(&icon_data);
53  icon_data.uFlags = NIF_MESSAGE;
54  icon_data.uCallbackMessage = message_id_;
55  icon_data.hIcon = icon_.Get();
56  // If we have an image, then set the NIF_ICON flag, which tells
57  // Shell_NotifyIcon() to set the image for the status icon it creates.
58  if (icon_data.hIcon)
59    icon_data.uFlags |= NIF_ICON;
60  // Re-add our icon.
61  BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
62  if (!result)
63    LOG(WARNING) << "Unable to re-create status tray icon.";
64}
65
66void StatusIconWin::SetPressedImage(const SkBitmap& image) {
67  // Ignore pressed images, since the standard on Windows is to not highlight
68  // pressed status icons.
69}
70
71void StatusIconWin::SetToolTip(const string16& tool_tip) {
72  // Create the icon.
73  NOTIFYICONDATA icon_data;
74  InitIconData(&icon_data);
75  icon_data.uFlags = NIF_TIP;
76  wcscpy_s(icon_data.szTip, tool_tip.c_str());
77  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
78  if (!result)
79    LOG(WARNING) << "Unable to set tooltip for status tray icon";
80}
81
82void StatusIconWin::DisplayBalloon(const string16& title,
83                                   const string16& contents) {
84  NOTIFYICONDATA icon_data;
85  InitIconData(&icon_data);
86  icon_data.uFlags = NIF_INFO;
87  icon_data.dwInfoFlags = NIIF_INFO;
88  wcscpy_s(icon_data.szInfoTitle, title.c_str());
89  wcscpy_s(icon_data.szInfo, contents.c_str());
90  icon_data.uTimeout = 0;
91  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
92  if (!result)
93    LOG(WARNING) << "Unable to create status tray balloon.";
94}
95
96void StatusIconWin::UpdatePlatformContextMenu(ui::MenuModel* menu) {
97  // If no items are passed, blow away our context menu.
98  if (!menu) {
99    context_menu_.reset();
100    return;
101  }
102
103  // Create context menu with the new contents.
104  context_menu_.reset(new views::Menu2(menu));
105}
106
107void StatusIconWin::HandleClickEvent(int x, int y, bool left_mouse_click) {
108  // Pass to the observer if appropriate.
109  if (left_mouse_click && HasObservers()) {
110    DispatchClickEvent();
111    return;
112  }
113
114  // Event not sent to the observer, so display the context menu if one exists.
115  if (context_menu_.get()) {
116    // Set our window as the foreground window, so the context menu closes when
117    // we click away from it.
118    SetForegroundWindow(window_);
119    context_menu_->RunContextMenuAt(gfx::Point(x, y));
120  }
121}
122
123void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) {
124  icon_data->cbSize = sizeof(NOTIFYICONDATA);
125  icon_data->hWnd = window_;
126  icon_data->uID = icon_id_;
127}
128