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)
62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  menu_runner_.reset(new views::MenuRunner(menu_model_,
63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                           views::MenuRunner::HAS_MNEMONICS));
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,
68116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                        ui::MENU_SOURCE_MOUSE));
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
71eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid StatusIconWin::HandleBalloonClickEvent() {
72eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (HasObservers())
73eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DispatchBalloonClickEvent();
74eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::ResetIcon() {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Delete any previously existing icon.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Shell_NotifyIcon(NIM_DELETE, &icon_data);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_MESSAGE;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uCallbackMessage = message_id_;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.hIcon = icon_.Get();
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If we have an image, then set the NIF_ICON flag, which tells
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Shell_NotifyIcon() to set the image for the status icon it creates.
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (icon_data.hIcon)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data.uFlags |= NIF_ICON;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Re-add our icon.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to re-create status tray icon.";
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::SetImage(const gfx::ImageSkia& image) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the icon.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_ICON;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_.Set(IconUtil::CreateHICONFromSkBitmap(*image.bitmap()));
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.hIcon = icon_.Get();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Error setting status tray icon image";
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void StatusIconWin::SetToolTip(const base::string16& tool_tip) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the icon.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_TIP;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szTip, tool_tip.c_str());
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to set tooltip for status tray icon";
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::DisplayBalloon(const gfx::ImageSkia& icon,
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const base::string16& title,
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                                   const base::string16& contents) {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  NOTIFYICONDATA icon_data;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  InitIconData(&icon_data);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uFlags = NIF_INFO;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.dwInfoFlags = NIIF_INFO;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szInfoTitle, title.c_str());
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  wcscpy_s(icon_data.szInfo, contents.c_str());
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data.uTimeout = 0;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::Version win_version = base::win::GetVersion();
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!icon.isNull() && win_version != base::win::VERSION_PRE_XP) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    balloon_icon_.Set(IconUtil::CreateHICONFromSkBitmap(*icon.bitmap()));
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (win_version >= base::win::VERSION_VISTA) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.hBalloonIcon = balloon_icon_.Get();
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.hIcon = balloon_icon_.Get();
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.uFlags |= NIF_ICON;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      icon_data.dwInfoFlags = NIIF_USER;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!result)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(WARNING) << "Unable to create status tray balloon.";
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid StatusIconWin::ForceVisible() {
1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  tray_->UpdateIconVisibilityInBackground(this);
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)////////////////////////////////////////////////////////////////////////////////
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// StatusIconWin, private:
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void StatusIconWin::UpdatePlatformContextMenu(StatusIconMenuModel* menu) {
155eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // |menu_model_| is about to be destroyed. Destroy the menu (which closes it)
156eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // so that it doesn't attempt to continue using |menu_model_|.
157eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  menu_runner_.reset();
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(menu);
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  menu_model_ = menu;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void StatusIconWin::InitIconData(NOTIFYICONDATA* icon_data) {
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memset(icon_data, 0, sizeof(NOTIFYICONDATA));
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data->cbSize = sizeof(NOTIFYICONDATA);
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memset(icon_data, 0, NOTIFYICONDATA_V3_SIZE);
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_data->cbSize = NOTIFYICONDATA_V3_SIZE;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data->hWnd = window_;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  icon_data->uID = icon_id_;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
174