Fix Debug building on Windows
[platform/framework/web/crosswalk-tizen.git] / atom / browser / ui / win / notify_icon_host.cc
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
5 #include "atom/browser/ui/win/notify_icon_host.h"
6
7 #include <commctrl.h>
8 #include <winuser.h>
9
10 #include "atom/browser/ui/win/notify_icon.h"
11 #include "base/bind.h"
12 #include "base/stl_util.h"
13 #include "base/threading/non_thread_safe.h"
14 #include "base/threading/thread.h"
15 #include "base/win/win_util.h"
16 #include "base/win/wrapped_window_proc.h"
17 #include "ui/events/event_constants.h"
18 #include "ui/events/win/system_event_state_lookup.h"
19 #include "ui/gfx/win/hwnd_util.h"
20
21 namespace atom {
22
23 namespace {
24
25 const UINT kNotifyIconMessage = WM_APP + 1;
26
27 // |kBaseIconId| is 2 to avoid conflicts with plugins that hard-code id 1.
28 const UINT kBaseIconId = 2;
29
30 const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
31
32 bool IsWinPressed() {
33   return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
34          ((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
35 }
36
37 int GetKeyboardModifers() {
38   int modifiers = ui::EF_NONE;
39   if (ui::win::IsShiftPressed())
40     modifiers |= ui::EF_SHIFT_DOWN;
41   if (ui::win::IsCtrlPressed())
42     modifiers |= ui::EF_CONTROL_DOWN;
43   if (ui::win::IsAltPressed())
44     modifiers |= ui::EF_ALT_DOWN;
45   if (IsWinPressed())
46     modifiers |= ui::EF_COMMAND_DOWN;
47   return modifiers;
48 }
49
50 }  // namespace
51
52 NotifyIconHost::NotifyIconHost()
53     : next_icon_id_(1),
54       atom_(0),
55       instance_(NULL),
56       window_(NULL) {
57   // Register our window class
58   WNDCLASSEX window_class;
59   base::win::InitializeWindowClass(
60       kNotifyIconHostWindowClass,
61       &base::win::WrappedWindowProc<NotifyIconHost::WndProcStatic>,
62       0, 0, 0, NULL, NULL, NULL, NULL, NULL,
63       &window_class);
64   instance_ = window_class.hInstance;
65   atom_ = RegisterClassEx(&window_class);
66   CHECK(atom_);
67
68   // If the taskbar is re-created after we start up, we have to rebuild all of
69   // our icons.
70   taskbar_created_message_ = RegisterWindowMessage(TEXT("TaskbarCreated"));
71
72   // Create an offscreen window for handling messages for the status icons. We
73   // create a hidden WS_POPUP window instead of an HWND_MESSAGE window, because
74   // only top-level windows such as popups can receive broadcast messages like
75   // "TaskbarCreated".
76   window_ = CreateWindow(MAKEINTATOM(atom_),
77                          0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0);
78   gfx::CheckWindowCreated(window_);
79   gfx::SetWindowUserData(window_, this);
80 }
81
82 NotifyIconHost::~NotifyIconHost() {
83   if (window_)
84     DestroyWindow(window_);
85
86   if (atom_)
87     UnregisterClass(MAKEINTATOM(atom_), instance_);
88
89   for (NotifyIcon* ptr : notify_icons_)
90     delete ptr;
91 }
92
93 NotifyIcon* NotifyIconHost::CreateNotifyIcon() {
94   NotifyIcon* notify_icon =
95       new NotifyIcon(this, NextIconId(), window_, kNotifyIconMessage);
96   notify_icons_.push_back(notify_icon);
97   return notify_icon;
98 }
99
100 void NotifyIconHost::Remove(NotifyIcon* icon) {
101   NotifyIcons::iterator i(
102       std::find(notify_icons_.begin(), notify_icons_.end(), icon));
103
104   if (i == notify_icons_.end()) {
105     NOTREACHED();
106     return;
107   }
108
109   notify_icons_.erase(i);
110 }
111
112 LRESULT CALLBACK NotifyIconHost::WndProcStatic(HWND hwnd,
113                                               UINT message,
114                                               WPARAM wparam,
115                                               LPARAM lparam) {
116   NotifyIconHost* msg_wnd = reinterpret_cast<NotifyIconHost*>(
117       GetWindowLongPtr(hwnd, GWLP_USERDATA));
118   if (msg_wnd)
119     return msg_wnd->WndProc(hwnd, message, wparam, lparam);
120   else
121     return ::DefWindowProc(hwnd, message, wparam, lparam);
122 }
123
124 LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd,
125                                         UINT message,
126                                         WPARAM wparam,
127                                         LPARAM lparam) {
128   if (message == taskbar_created_message_) {
129     // We need to reset all of our icons because the taskbar went away.
130     for (NotifyIcons::const_iterator i(notify_icons_.begin());
131          i != notify_icons_.end(); ++i) {
132       NotifyIcon* win_icon = static_cast<NotifyIcon*>(*i);
133       win_icon->ResetIcon();
134     }
135     return TRUE;
136   } else if (message == kNotifyIconMessage) {
137     NotifyIcon* win_icon = NULL;
138
139     // Find the selected status icon.
140     for (NotifyIcons::const_iterator i(notify_icons_.begin());
141          i != notify_icons_.end(); ++i) {
142       NotifyIcon* current_win_icon = static_cast<NotifyIcon*>(*i);
143       if (current_win_icon->icon_id() == wparam) {
144         win_icon = current_win_icon;
145         break;
146       }
147     }
148
149     // It is possible for this procedure to be called with an obsolete icon
150     // id.  In that case we should just return early before handling any
151     // actions.
152     if (!win_icon)
153       return TRUE;
154
155     switch (lparam) {
156       case TB_CHECKBUTTON:
157         win_icon->NotifyBalloonShow();
158         return TRUE;
159
160       case TB_INDETERMINATE:
161         win_icon->NotifyBalloonClicked();
162         return TRUE;
163
164       case TB_HIDEBUTTON:
165         win_icon->NotifyBalloonClosed();
166         return TRUE;
167
168       case WM_LBUTTONDOWN:
169       case WM_RBUTTONDOWN:
170       case WM_LBUTTONDBLCLK:
171       case WM_RBUTTONDBLCLK:
172       case WM_CONTEXTMENU:
173         // Walk our icons, find which one was clicked on, and invoke its
174         // HandleClickEvent() method.
175         win_icon->HandleClickEvent(
176             GetKeyboardModifers(),
177             (lparam == WM_LBUTTONDOWN || lparam == WM_LBUTTONDBLCLK),
178             (lparam == WM_LBUTTONDBLCLK || lparam == WM_RBUTTONDBLCLK));
179         return TRUE;
180     }
181   }
182   return ::DefWindowProc(hwnd, message, wparam, lparam);
183 }
184
185 UINT NotifyIconHost::NextIconId() {
186   UINT icon_id = next_icon_id_++;
187   return kBaseIconId + icon_id;
188 }
189
190 }  // namespace atom