notify_icon.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright (c) 2014 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/ui/win/notify_icon.h"
  5. #include "atom/browser/ui/win/notify_icon_host.h"
  6. #include "base/strings/string_number_conversions.h"
  7. #include "base/strings/utf_string_conversions.h"
  8. #include "base/win/windows_version.h"
  9. #include "third_party/skia/include/core/SkBitmap.h"
  10. #include "ui/display/screen.h"
  11. #include "ui/display/win/screen_win.h"
  12. #include "ui/gfx/geometry/point.h"
  13. #include "ui/gfx/geometry/rect.h"
  14. #include "ui/gfx/image/image.h"
  15. #include "ui/views/controls/menu/menu_runner.h"
  16. #include "ui/views/widget/widget.h"
  17. namespace atom {
  18. NotifyIcon::NotifyIcon(NotifyIconHost* host, UINT id, HWND window, UINT message)
  19. : host_(host),
  20. icon_id_(id),
  21. window_(window),
  22. message_id_(message),
  23. weak_factory_(this) {
  24. NOTIFYICONDATA icon_data;
  25. InitIconData(&icon_data);
  26. icon_data.uFlags |= NIF_MESSAGE;
  27. icon_data.uCallbackMessage = message_id_;
  28. BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
  29. // This can happen if the explorer process isn't running when we try to
  30. // create the icon for some reason (for example, at startup).
  31. if (!result)
  32. LOG(WARNING) << "Unable to create status tray icon.";
  33. }
  34. NotifyIcon::~NotifyIcon() {
  35. // Remove our icon.
  36. host_->Remove(this);
  37. NOTIFYICONDATA icon_data;
  38. InitIconData(&icon_data);
  39. Shell_NotifyIcon(NIM_DELETE, &icon_data);
  40. }
  41. void NotifyIcon::HandleClickEvent(int modifiers,
  42. bool left_mouse_click,
  43. bool double_button_click) {
  44. gfx::Rect bounds = GetBounds();
  45. if (left_mouse_click) {
  46. if (double_button_click) // double left click
  47. NotifyDoubleClicked(bounds, modifiers);
  48. else // single left click
  49. NotifyClicked(bounds,
  50. display::Screen::GetScreen()->GetCursorScreenPoint(),
  51. modifiers);
  52. return;
  53. } else if (!double_button_click) { // single right click
  54. if (menu_model_)
  55. PopUpContextMenu(gfx::Point(), menu_model_);
  56. else
  57. NotifyRightClicked(bounds, modifiers);
  58. }
  59. }
  60. void NotifyIcon::ResetIcon() {
  61. NOTIFYICONDATA icon_data;
  62. InitIconData(&icon_data);
  63. // Delete any previously existing icon.
  64. Shell_NotifyIcon(NIM_DELETE, &icon_data);
  65. InitIconData(&icon_data);
  66. icon_data.uFlags |= NIF_MESSAGE;
  67. icon_data.uCallbackMessage = message_id_;
  68. icon_data.hIcon = icon_.get();
  69. // If we have an image, then set the NIF_ICON flag, which tells
  70. // Shell_NotifyIcon() to set the image for the status icon it creates.
  71. if (icon_data.hIcon)
  72. icon_data.uFlags |= NIF_ICON;
  73. // Re-add our icon.
  74. BOOL result = Shell_NotifyIcon(NIM_ADD, &icon_data);
  75. if (!result)
  76. LOG(WARNING) << "Unable to re-create status tray icon.";
  77. }
  78. void NotifyIcon::SetImage(HICON image) {
  79. icon_ = base::win::ScopedHICON(CopyIcon(image));
  80. // Create the icon.
  81. NOTIFYICONDATA icon_data;
  82. InitIconData(&icon_data);
  83. icon_data.uFlags |= NIF_ICON;
  84. icon_data.hIcon = image;
  85. BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
  86. if (!result)
  87. LOG(WARNING) << "Error setting status tray icon image";
  88. }
  89. void NotifyIcon::SetPressedImage(HICON image) {
  90. // Ignore pressed images, since the standard on Windows is to not highlight
  91. // pressed status icons.
  92. }
  93. void NotifyIcon::SetToolTip(const std::string& tool_tip) {
  94. // Create the icon.
  95. NOTIFYICONDATA icon_data;
  96. InitIconData(&icon_data);
  97. icon_data.uFlags |= NIF_TIP;
  98. wcsncpy_s(icon_data.szTip, base::UTF8ToUTF16(tool_tip).c_str(), _TRUNCATE);
  99. BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
  100. if (!result)
  101. LOG(WARNING) << "Unable to set tooltip for status tray icon";
  102. }
  103. void NotifyIcon::DisplayBalloon(HICON icon,
  104. const base::string16& title,
  105. const base::string16& contents) {
  106. NOTIFYICONDATA icon_data;
  107. InitIconData(&icon_data);
  108. icon_data.uFlags |= NIF_INFO;
  109. icon_data.dwInfoFlags = NIIF_INFO;
  110. wcsncpy_s(icon_data.szInfoTitle, title.c_str(), _TRUNCATE);
  111. wcsncpy_s(icon_data.szInfo, contents.c_str(), _TRUNCATE);
  112. icon_data.uTimeout = 0;
  113. icon_data.hBalloonIcon = icon;
  114. icon_data.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
  115. BOOL result = Shell_NotifyIcon(NIM_MODIFY, &icon_data);
  116. if (!result)
  117. LOG(WARNING) << "Unable to create status tray balloon.";
  118. }
  119. void NotifyIcon::PopUpContextMenu(const gfx::Point& pos,
  120. AtomMenuModel* menu_model) {
  121. // Returns if context menu isn't set.
  122. if (menu_model == nullptr && menu_model_ == nullptr)
  123. return;
  124. // Set our window as the foreground window, so the context menu closes when
  125. // we click away from it.
  126. if (!SetForegroundWindow(window_))
  127. return;
  128. // Cancel current menu if there is one.
  129. if (menu_runner_ && menu_runner_->IsRunning())
  130. menu_runner_->Cancel();
  131. // Show menu at mouse's position by default.
  132. gfx::Rect rect(pos, gfx::Size());
  133. if (pos.IsOrigin())
  134. rect.set_origin(display::Screen::GetScreen()->GetCursorScreenPoint());
  135. // Create a widget for the menu, otherwise we get no keyboard events, which
  136. // is required for accessibility.
  137. widget_.reset(new views::Widget());
  138. views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
  139. params.ownership =
  140. views::Widget::InitParams::Ownership::WIDGET_OWNS_NATIVE_WIDGET;
  141. params.bounds = gfx::Rect(0, 0, 0, 0);
  142. params.force_software_compositing = true;
  143. widget_->Init(params);
  144. widget_->Show();
  145. widget_->Activate();
  146. menu_runner_.reset(new views::MenuRunner(
  147. menu_model != nullptr ? menu_model : menu_model_,
  148. views::MenuRunner::CONTEXT_MENU | views::MenuRunner::HAS_MNEMONICS,
  149. base::Bind(&NotifyIcon::OnContextMenuClosed,
  150. weak_factory_.GetWeakPtr())));
  151. menu_runner_->RunMenuAt(widget_.get(), NULL, rect,
  152. views::MenuAnchorPosition::kTopLeft,
  153. ui::MENU_SOURCE_MOUSE);
  154. }
  155. void NotifyIcon::SetContextMenu(AtomMenuModel* menu_model) {
  156. menu_model_ = menu_model;
  157. }
  158. gfx::Rect NotifyIcon::GetBounds() {
  159. NOTIFYICONIDENTIFIER icon_id;
  160. memset(&icon_id, 0, sizeof(NOTIFYICONIDENTIFIER));
  161. icon_id.uID = icon_id_;
  162. icon_id.hWnd = window_;
  163. icon_id.cbSize = sizeof(NOTIFYICONIDENTIFIER);
  164. RECT rect = {0};
  165. Shell_NotifyIconGetRect(&icon_id, &rect);
  166. return display::win::ScreenWin::ScreenToDIPRect(window_, gfx::Rect(rect));
  167. }
  168. void NotifyIcon::InitIconData(NOTIFYICONDATA* icon_data) {
  169. memset(icon_data, 0, sizeof(NOTIFYICONDATA));
  170. icon_data->cbSize = sizeof(NOTIFYICONDATA);
  171. icon_data->hWnd = window_;
  172. icon_data->uID = icon_id_;
  173. }
  174. void NotifyIcon::OnContextMenuClosed() {
  175. widget_->Close();
  176. }
  177. } // namespace atom