[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / dll / shellext / stobject / power.cpp
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/shellext/stobject/power.cpp
5 * PURPOSE: Power notification icon handler
6 * PROGRAMMERS: Eric Kohl <eric.kohl@reactos.org>
7 Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
8 * David Quintana <gigaherz@gmail.com>
9 */
10
11 #include "precomp.h"
12
13 #include <devguid.h>
14 #include <winioctl.h>
15 #include <powrprof.h>
16 #include <windows.h>
17 #include <batclass.h>
18
19 int br_icons[5] = { IDI_BATTCAP0, IDI_BATTCAP1, IDI_BATTCAP2, IDI_BATTCAP3, IDI_BATTCAP4 }; // battery mode icons.
20 int bc_icons[5] = { IDI_BATTCHA0, IDI_BATTCHA1, IDI_BATTCHA2, IDI_BATTCHA3, IDI_BATTCHA4 }; // charging mode icons.
21
22 typedef struct _PWRSCHEMECONTEXT
23 {
24 HMENU hPopup;
25 UINT uiFirst;
26 UINT uiLast;
27 } PWRSCHEMECONTEXT, *PPWRSCHEMECONTEXT;
28
29 CString g_strTooltip;
30 static HICON g_hIconBattery = NULL;
31
32
33 /*++
34 * @name Quantize
35 *
36 * This function quantizes the mentioned quantity to nearest level.
37 *
38 * @param p
39 * Should be a quantity in percentage.
40 *
41 * @return Nearest quantized level, can be directly used as array index based on context.
42 *
43 @remarks This function uses centred/symmetric logic for quantization.
44 For the case of lvl = 4, You will get following integer levels if given (p) value falls in between the range partitions:
45 0 <= p < 12.5 : returns 0; (corresponding to 0% centre)
46 12.5 <= p < 37.5 : returns 1; (corresponding to 25% centre)
47 37.5 <= p < 62.5 : returns 2; (corresponding to 50% centre)
48 62.5 <= p < 87.5 : returns 3; (corresponding to 75% centre)
49 87.5 <= p <= 100 : returns 4; (corresponding to 100% centre)
50 *--*/
51 static UINT Quantize(BYTE p)
52 {
53 if (p <= 12)
54 return 0;
55 else if (p > 12 && p <= 37)
56 return 1;
57 else if (p > 37 && p <= 62)
58 return 2;
59 else if (p > 62 && p <= 87)
60 return 3;
61 else
62 return 4;
63 }
64
65 /*++
66 * @name DynamicLoadIcon
67 *
68 * Returns the respective icon as per the current battery capacity.
69 * It also does the work of setting global parameters of battery capacity and tooltips.
70 *
71 * @param hinst
72 * A handle to a instance of the module.
73 *
74 * @return The handle to respective battery icon.
75 *
76 *--*/
77 static HICON DynamicLoadIcon(HINSTANCE hinst)
78 {
79 SYSTEM_POWER_STATUS PowerStatus;
80 HICON hBatIcon;
81 UINT index = -1;
82
83 if (!GetSystemPowerStatus(&PowerStatus) ||
84 PowerStatus.ACLineStatus == AC_LINE_UNKNOWN ||
85 PowerStatus.BatteryFlag == BATTERY_FLAG_UNKNOWN)
86 {
87 hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_BATTCAP_ERR));
88 g_strTooltip.LoadStringW(IDS_PWR_UNKNOWN_REMAINING);
89 return hBatIcon;
90 }
91
92 if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
93 ((PowerStatus.BatteryFlag & BATTERY_FLAG_CHARGING) == BATTERY_FLAG_CHARGING))
94 {
95 index = Quantize(PowerStatus.BatteryLifePercent);
96 hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(bc_icons[index]));
97 g_strTooltip.Format(IDS_PWR_CHARGING, PowerStatus.BatteryLifePercent);
98 }
99 else if (((PowerStatus.BatteryFlag & BATTERY_FLAG_NO_BATTERY) == 0) &&
100 ((PowerStatus.BatteryFlag & BATTERY_FLAG_CHARGING) == 0))
101 {
102 index = Quantize(PowerStatus.BatteryLifePercent);
103 hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(br_icons[index]));
104 g_strTooltip.Format(IDS_PWR_PERCENT_REMAINING, PowerStatus.BatteryLifePercent);
105 }
106 else
107 {
108 hBatIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_POWER_AC));
109 g_strTooltip.LoadStringW(IDS_PWR_AC);
110 }
111
112 return hBatIcon;
113 }
114
115 HRESULT STDMETHODCALLTYPE Power_Init(_In_ CSysTray * pSysTray)
116 {
117 TRACE("Power_Init\n");
118 g_hIconBattery = DynamicLoadIcon(g_hInstance);
119
120 return pSysTray->NotifyIcon(NIM_ADD, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
121 }
122
123 HRESULT STDMETHODCALLTYPE Power_Update(_In_ CSysTray * pSysTray)
124 {
125 TRACE("Power_Update\n");
126 g_hIconBattery = DynamicLoadIcon(g_hInstance);
127
128 return pSysTray->NotifyIcon(NIM_MODIFY, ID_ICON_POWER, g_hIconBattery, g_strTooltip);
129 }
130
131 HRESULT STDMETHODCALLTYPE Power_Shutdown(_In_ CSysTray * pSysTray)
132 {
133 TRACE("Power_Shutdown\n");
134
135 return pSysTray->NotifyIcon(NIM_DELETE, ID_ICON_POWER, NULL, NULL);
136 }
137
138 static void _RunPower()
139 {
140 ShellExecuteW(NULL, NULL, L"powercfg.cpl", NULL, NULL, SW_SHOWNORMAL);
141 }
142
143 static void _ShowContextMenu(CSysTray * pSysTray)
144 {
145 CString strOpen((LPCSTR)IDS_PWR_PROPERTIES);
146 HMENU hPopup = CreatePopupMenu();
147 AppendMenuW(hPopup, MF_STRING, IDS_PWR_PROPERTIES, strOpen);
148 SetMenuDefaultItem(hPopup, IDS_PWR_PROPERTIES, FALSE);
149
150 SetForegroundWindow(pSysTray->GetHWnd());
151 DWORD flags = TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN;
152 POINT pt;
153 GetCursorPos(&pt);
154
155 DWORD id = TrackPopupMenuEx(hPopup, flags,
156 pt.x, pt.y,
157 pSysTray->GetHWnd(), NULL);
158
159 switch (id)
160 {
161 case IDS_PWR_PROPERTIES:
162 _RunPower();
163 break;
164 }
165 DestroyMenu(hPopup);
166 }
167
168 static
169 BOOLEAN
170 CALLBACK
171 PowerSchemesEnumProc(
172 UINT uiIndex,
173 DWORD dwName,
174 LPWSTR sName,
175 DWORD dwDesc,
176 LPWSTR sDesc,
177 PPOWER_POLICY pp,
178 LPARAM lParam)
179 {
180 PPWRSCHEMECONTEXT PowerSchemeContext = (PPWRSCHEMECONTEXT)lParam;
181
182 if (AppendMenuW(PowerSchemeContext->hPopup, MF_STRING, uiIndex + 1, sName))
183 {
184 if (PowerSchemeContext->uiFirst == 0)
185 PowerSchemeContext->uiFirst = uiIndex + 1;
186
187 PowerSchemeContext->uiLast = uiIndex + 1;
188 }
189
190 return TRUE;
191 }
192
193 static
194 VOID
195 ShowPowerSchemesPopupMenu(
196 CSysTray *pSysTray)
197 {
198 PWRSCHEMECONTEXT PowerSchemeContext = {NULL, 0, 0};
199 UINT uiActiveScheme;
200 DWORD id;
201 POINT pt;
202 PowerSchemeContext.hPopup = CreatePopupMenu();
203 EnumPwrSchemes(PowerSchemesEnumProc, (LPARAM)&PowerSchemeContext);
204
205 if (GetActivePwrScheme(&uiActiveScheme))
206 {
207 CheckMenuRadioItem(PowerSchemeContext.hPopup,
208 PowerSchemeContext.uiFirst,
209 PowerSchemeContext.uiLast,
210 uiActiveScheme + 1,
211 MF_BYCOMMAND);
212 }
213
214 SetForegroundWindow(pSysTray->GetHWnd());
215 GetCursorPos(&pt);
216
217 id = TrackPopupMenuEx(PowerSchemeContext.hPopup,
218 TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTALIGN | TPM_BOTTOMALIGN,
219 pt.x,
220 pt.y,
221 pSysTray->GetHWnd(),
222 NULL);
223
224 DestroyMenu(PowerSchemeContext.hPopup);
225
226 if (id != 0)
227 SetActivePwrScheme(id - 1, NULL, NULL);
228 }
229
230 HRESULT STDMETHODCALLTYPE Power_Message(_In_ CSysTray * pSysTray, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &lResult)
231 {
232 TRACE("Power_Message uMsg=%d, wParam=%x, lParam=%x\n", uMsg, wParam, lParam);
233
234 switch (uMsg)
235 {
236 case WM_USER + 220:
237 TRACE("Power_Message: WM_USER+220\n");
238 if (wParam == POWER_SERVICE_FLAG)
239 {
240 if (lParam)
241 {
242 pSysTray->EnableService(POWER_SERVICE_FLAG, TRUE);
243 return Power_Init(pSysTray);
244 }
245 else
246 {
247 pSysTray->EnableService(POWER_SERVICE_FLAG, FALSE);
248 return Power_Shutdown(pSysTray);
249 }
250 }
251 return S_FALSE;
252
253 case WM_USER + 221:
254 TRACE("Power_Message: WM_USER+221\n");
255 if (wParam == POWER_SERVICE_FLAG)
256 {
257 lResult = (LRESULT)pSysTray->IsServiceEnabled(POWER_SERVICE_FLAG);
258 return S_OK;
259 }
260 return S_FALSE;
261
262 case WM_TIMER:
263 if (wParam == POWER_TIMER_ID)
264 {
265 KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
266 ShowPowerSchemesPopupMenu(pSysTray);
267 }
268 break;
269
270 case ID_ICON_POWER:
271 Power_Update(pSysTray);
272
273 switch (lParam)
274 {
275 case WM_LBUTTONDOWN:
276 SetTimer(pSysTray->GetHWnd(), POWER_TIMER_ID, GetDoubleClickTime(), NULL);
277 break;
278
279 case WM_LBUTTONUP:
280 break;
281
282 case WM_LBUTTONDBLCLK:
283 KillTimer(pSysTray->GetHWnd(), POWER_TIMER_ID);
284 _RunPower();
285 break;
286
287 case WM_RBUTTONDOWN:
288 break;
289
290 case WM_RBUTTONUP:
291 _ShowContextMenu(pSysTray);
292 break;
293
294 case WM_RBUTTONDBLCLK:
295 break;
296
297 case WM_MOUSEMOVE:
298 break;
299 }
300 return S_OK;
301
302 default:
303 TRACE("Power_Message received for unknown ID %d, ignoring.\n");
304 return S_FALSE;
305 }
306
307 return S_FALSE;
308 }