15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/views/status_icons/status_icon_win.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h"
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "chrome/browser/ui/views/status_icons/status_tray_win.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/icon_util.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h"
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "ui/gfx/rect.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/menu/menu_runner.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, public:
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuStatusIconWin::StatusIconWin(StatusTrayWin* tray,
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                             UINT id,
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                             HWND window,
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                             UINT message)
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    : tray_(tray),
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      icon_id_(id),
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      window_(window),
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      message_id_(message),
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      menu_model_(NULL) {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_MESSAGE;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uCallbackMessage = message_id_;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This can happen if the explorer process isn't running when we try to
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // create the icon for some reason (for example, at startup).
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to create status tray icon.";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)StatusIconWin::~StatusIconWin() {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Remove our icon.
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell_NotifyIcon(NIM_DELETE, &icon_data);
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::HandleClickEvent(const gfx::Point& cursor_pos,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     bool left_mouse_click) {
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pass to the observer if appropriate.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (left_mouse_click && HasObservers()) {
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DispatchClickEvent();
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!menu_model_)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Set our window as the foreground window, so the context menu closes when
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we click away from it.
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!SetForegroundWindow(window_))
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  menu_runner_.reset(new views::MenuRunner(menu_model_));
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  ignore_result(menu_runner_->RunMenuAt(NULL,
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                        NULL,
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                        gfx::Rect(cursor_pos, gfx::Size()),
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                        views::MENU_ANCHOR_TOPLEFT,
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                        ui::MENU_SOURCE_MOUSE,
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu                                        views::MenuRunner::HAS_MNEMONICS));
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid StatusIconWin::HandleBalloonClickEvent() {
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (HasObservers())
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DispatchBalloonClickEvent();
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::ResetIcon() {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete any previously existing icon.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell_NotifyIcon(NIM_DELETE, &icon_data);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_MESSAGE;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uCallbackMessage = message_id_;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.hIcon = icon_.Get();
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we have an image, then set the NIF_ICON flag, which tells
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shell_NotifyIcon() to set the image for the status icon it creates.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icon_data.hIcon)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data.uFlags |= NIF_ICON;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Re-add our icon.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to re-create status tray icon.";
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetImage(const gfx::ImageSkia& image) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the icon.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_ICON;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap()));
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.hIcon = icon_.Get();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Error setting status tray icon image";
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetPressedImage(const gfx::ImageSkia& image) {
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ignore pressed images, since the standard on Windows is to not highlight
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // pressed status icons.
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusIconWin::SetToolTip(const base::string16& tool_tip) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the icon.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_TIP;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szTip, tool_tip.c_str());
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to set tooltip for status tray icon";
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon,
125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const base::string16& title,
126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const base::string16& contents) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_INFO;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.dwInfoFlags = NIIF_INFO;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szInfoTitle, title.c_str());
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szInfo, contents.c_str());
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uTimeout = 0;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::Version win_version = base::win::GetVersion();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (win_version >= base::win::VERSION_VISTA) {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.hBalloonIcon = balloon_icon_.Get();
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.hIcon = balloon_icon_.Get();
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.uFlags |= NIF_ICON;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.dwInfoFlags = NIIF_USER;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to create status tray balloon.";
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid StatusIconWin::ForceVisible() {
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  tray_->UpdateIconVisibilityInBackground(this);
1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, private:
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) {
161eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // |menu_model_| is about to be destroyed. Destroy the menu (which closes it)
162eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // so that it doesn't attempt to continue using |menu_model_|.
163eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  menu_runner_.reset();
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(menu);
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu_model_ = menu;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memset(icon_data, 0, sizeof(NOTIFYICONDATA));
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data->cbSize = sizeof(NOTIFYICONDATA);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data->cbSize = NOTIFYICONDATA_V3_SIZE;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data->hWnd = window_;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data->uID = icon_id_;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
180