[STOBJECT] Implement the hotplug and the power icons in the system tray
[reactos.git] / dll / shellext / stobject / csystray.cpp
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/stobject/csystray.cpp
5 * PURPOSE: Systray shell service object implementation
6 * PROGRAMMERS: David Quintana <gigaherz@gmail.com>
7 * Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
8 */
9
10 #include "precomp.h"
11
12 WINE_DEFAULT_DEBUG_CHANNEL(stobject);
13
14 SysTrayIconHandlers_t g_IconHandlers [] = {
15 { Volume_Init, Volume_Shutdown, Volume_Update, Volume_Message },
16 { Hotplug_Init, Hotplug_Shutdown, Hotplug_Update, Hotplug_Message },
17 { Power_Init, Power_Shutdown, Power_Update, Power_Message }
18 };
19 const int g_NumIcons = _countof(g_IconHandlers);
20
21 CSysTray::CSysTray() {}
22 CSysTray::~CSysTray() {}
23
24 HRESULT CSysTray::InitNetShell()
25 {
26 HRESULT hr = CoCreateInstance(CLSID_ConnectionTray, 0, 1u, IID_PPV_ARG(IOleCommandTarget, &pctNetShell));
27 if (FAILED(hr))
28 return hr;
29
30 return pctNetShell->Exec(&CGID_ShellServiceObject,
31 OLECMDID_NEW,
32 OLECMDEXECOPT_DODEFAULT, NULL, NULL);
33 }
34
35 HRESULT CSysTray::ShutdownNetShell()
36 {
37 if (!pctNetShell)
38 return S_FALSE;
39 HRESULT hr = pctNetShell->Exec(&CGID_ShellServiceObject,
40 OLECMDID_SAVE,
41 OLECMDEXECOPT_DODEFAULT, NULL, NULL);
42 pctNetShell.Release();
43 return hr;
44 }
45
46 HRESULT CSysTray::InitIcons()
47 {
48 TRACE("Initializing Notification icons...\n");
49 for (int i = 0; i < g_NumIcons; i++)
50 {
51 HRESULT hr = g_IconHandlers[i].pfnInit(this);
52 if (FAILED(hr))
53 return hr;
54 }
55
56 return InitNetShell();
57 }
58
59 HRESULT CSysTray::ShutdownIcons()
60 {
61 TRACE("Shutting down Notification icons...\n");
62 for (int i = 0; i < g_NumIcons; i++)
63 {
64 HRESULT hr = g_IconHandlers[i].pfnShutdown(this);
65 if (FAILED(hr))
66 return hr;
67 }
68
69 return ShutdownNetShell();
70 }
71
72 HRESULT CSysTray::UpdateIcons()
73 {
74 TRACE("Updating Notification icons...\n");
75 for (int i = 0; i < g_NumIcons; i++)
76 {
77 HRESULT hr = g_IconHandlers[i].pfnUpdate(this);
78 if (FAILED(hr))
79 return hr;
80 }
81
82 return S_OK;
83 }
84
85 HRESULT CSysTray::ProcessIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
86 {
87 for (int i = 0; i < g_NumIcons; i++)
88 {
89 HRESULT hr = g_IconHandlers[i].pfnMessage(this, uMsg, wParam, lParam, lResult);
90 if (FAILED(hr))
91 return hr;
92
93 if (hr == S_OK)
94 return hr;
95 }
96
97 // Not handled by anyone, so return accordingly.
98 return S_FALSE;
99 }
100
101 /*++
102 * @name NotifyIcon
103 *
104 * Basically a Shell_NotifyIcon wrapper.
105 * Based on the parameters provided, it changes the current state of the notification icon.
106 *
107 * @param code
108 * Determines whether to add, delete or modify the notification icon (represented by uId).
109 * @param uId
110 * Represents the particular notification icon.
111 * @param hIcon
112 * A handle to an icon for the notification object.
113 * @param szTip
114 * A string for the tooltip of the notification.
115 * @param dwstate
116 * Determines whether to show or hide the notification icon.
117 *
118 * @return The error code.
119 *
120 *--*/
121 HRESULT CSysTray::NotifyIcon(INT code, UINT uId, HICON hIcon, LPCWSTR szTip, DWORD dwstate)
122 {
123 NOTIFYICONDATA nim = { 0 };
124
125 TRACE("NotifyIcon code=%d, uId=%d, hIcon=%p, szTip=%S\n", code, uId, hIcon, szTip);
126
127 nim.cbSize = sizeof(NOTIFYICONDATA);
128 nim.uFlags = NIF_MESSAGE | NIF_ICON | NIF_STATE | NIF_TIP;
129 nim.hIcon = hIcon;
130 nim.uID = uId;
131 nim.uCallbackMessage = uId;
132 nim.dwState = dwstate;
133 nim.dwStateMask = NIS_HIDDEN;
134 nim.hWnd = m_hWnd;
135 nim.uVersion = NOTIFYICON_VERSION;
136 if (szTip)
137 StringCchCopy(nim.szTip, _countof(nim.szTip), szTip);
138 else
139 nim.szTip[0] = 0;
140 BOOL ret = Shell_NotifyIcon(code, &nim);
141 return ret ? S_OK : E_FAIL;
142 }
143
144 DWORD WINAPI CSysTray::s_SysTrayThreadProc(PVOID param)
145 {
146 CSysTray * st = (CSysTray*) param;
147 return st->SysTrayThreadProc();
148 }
149
150 HRESULT CSysTray::SysTrayMessageLoop()
151 {
152 BOOL ret;
153 MSG msg;
154
155 while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
156 {
157 if (ret < 0)
158 break;
159
160 TranslateMessage(&msg);
161 DispatchMessage(&msg);
162 }
163
164 return S_OK;
165 }
166
167 HRESULT CSysTray::SysTrayThreadProc()
168 {
169 WCHAR strFileName[MAX_PATH];
170 GetModuleFileNameW(g_hInstance, strFileName, MAX_PATH);
171 HMODULE hLib = LoadLibraryW(strFileName);
172
173 CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED);
174
175 Create(NULL);
176
177 HRESULT ret = SysTrayMessageLoop();
178
179 CoUninitialize();
180
181 FreeLibraryAndExitThread(hLib, ret);
182 }
183
184 HRESULT CSysTray::CreateSysTrayThread()
185 {
186 TRACE("CSysTray Init TODO: Initialize tray icon handlers.\n");
187
188 HANDLE hThread = CreateThread(NULL, 0, s_SysTrayThreadProc, this, 0, NULL);
189
190 CloseHandle(hThread);
191
192 return S_OK;
193 }
194
195 HRESULT CSysTray::DestroySysTrayWindow()
196 {
197 DestroyWindow();
198 hwndSysTray = NULL;
199 return S_OK;
200 }
201
202 // *** IOleCommandTarget methods ***
203 HRESULT STDMETHODCALLTYPE CSysTray::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
204 {
205 UNIMPLEMENTED;
206 return S_OK;
207 }
208
209 HRESULT STDMETHODCALLTYPE CSysTray::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
210 {
211 if (!IsEqualGUID(*pguidCmdGroup, CGID_ShellServiceObject))
212 return E_FAIL;
213
214 switch (nCmdID)
215 {
216 case OLECMDID_NEW: // init
217 return CreateSysTrayThread();
218 case OLECMDID_SAVE: // shutdown
219 return DestroySysTrayWindow();
220 }
221 return S_OK;
222 }
223
224 BOOL CSysTray::ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult, DWORD dwMsgMapID)
225 {
226 HRESULT hr;
227
228 if (hWnd != m_hWnd)
229 return FALSE;
230
231 switch (uMsg)
232 {
233 case WM_NCCREATE:
234 case WM_NCDESTROY:
235 return FALSE;
236
237 case WM_CREATE:
238 InitIcons();
239 SetTimer(1, 2000, NULL);
240 return TRUE;
241
242 case WM_TIMER:
243 UpdateIcons();
244 return TRUE;
245 case WM_DESTROY:
246 KillTimer(1);
247 ShutdownIcons();
248 return TRUE;
249 }
250
251 TRACE("SysTray message received %u (%08p %08p)\n", uMsg, wParam, lParam);
252
253 hr = ProcessIconMessage(uMsg, wParam, lParam, lResult);
254 if (FAILED(hr))
255 return FALSE;
256
257 return (hr == S_OK);
258 }