Revert "[SHELL32] SHChangeNotify: Use tree for CDirectoryList (#6784)" (#6800)
[reactos.git] / base / applications / utilman / umandlg / umandlg.c
1 /*
2 * PROJECT: ReactOS Utility Manager Resources DLL (UManDlg.dll)
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Main DLL code file
5 * COPYRIGHT: Copyright 2019-2020 George Bișoc (george.bisoc@reactos.org)
6 * Copyright 2019 Hermes Belusca-Maito
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "umandlg.h"
12
13 /* GLOBALS ********************************************************************/
14
15 UTILMAN_GLOBALS Globals;
16
17 /* DECLARATIONS ***************************************************************/
18
19 UTILMAN_STATE EntriesList[] =
20 {
21 {L"magnify.exe", IDS_MAGNIFIER, L"", FALSE},
22 {L"osk.exe", IDS_OSK, L"", FALSE}
23 };
24
25 /* FUNCTIONS ******************************************************************/
26
27 /**
28 * @InitUtilsList
29 *
30 * Initializes the list of accessibility utilities.
31 *
32 * @param[in] bInitGui
33 * Whether we are initializing the UI list (TRUE) or the internal array (FALSE).
34 *
35 * @return
36 * Nothing.
37 */
38 VOID InitUtilsList(IN BOOL bInitGui)
39 {
40 UINT i;
41
42 if (!bInitGui)
43 {
44 // TODO: Load the list dynamically from the registry key
45 // hklm\software\microsoft\windows nt\currentversion\accessibility
46
47 /* Initialize the resource utility strings only once */
48 for (i = 0; i < _countof(EntriesList); ++i)
49 {
50 LoadStringW(Globals.hInstance, EntriesList[i].uNameId,
51 EntriesList[i].szResource, _countof(EntriesList[i].szResource));
52
53 EntriesList[i].bState = FALSE;
54 }
55 }
56 else
57 {
58 INT iItem;
59 BOOL bIsRunning;
60 WCHAR szFormat[MAX_BUFFER];
61
62 /* Reset the listbox */
63 SendMessageW(Globals.hListDlg, LB_RESETCONTENT, 0, 0);
64
65 /* Add the utilities in the listbox */
66 for (i = 0; i < _countof(EntriesList); ++i)
67 {
68 bIsRunning = IsProcessRunning(EntriesList[i].lpszProgram);
69 EntriesList[i].bState = bIsRunning;
70
71 /* Load the string and append the utility's name to the format */
72 StringCchPrintfW(szFormat, _countof(szFormat),
73 (bIsRunning ? Globals.szRunning : Globals.szNotRunning),
74 EntriesList[i].szResource);
75
76 /* Add the item in the listbox */
77 iItem = (INT)SendMessageW(Globals.hListDlg, LB_ADDSTRING, 0, (LPARAM)szFormat);
78 if (iItem != LB_ERR)
79 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]);
80 }
81 }
82 }
83
84 /**
85 * @DlgInitHandler
86 *
87 * Function which processes several operations for WM_INITDIALOG.
88 *
89 * @param[in] hDlg
90 * The handle object of the dialog.
91 *
92 * @return
93 * TRUE to inform the system that WM_INITDIALOG has been processed and
94 * that it should set the keyboard focus to the control.
95 *
96 */
97 BOOL DlgInitHandler(IN HWND hDlg)
98 {
99 INT PosX, PosY;
100 RECT rc;
101 WCHAR szAboutDlg[MAX_BUFFER];
102 WCHAR szAppPath[MAX_BUFFER];
103 HMENU hSysMenu;
104
105 /* Save the dialog handle */
106 Globals.hMainDlg = hDlg;
107
108 /* Center the dialog on the screen */
109 GetWindowRect(hDlg, &rc);
110 PosX = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
111 PosY = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
112 SetWindowPos(hDlg, 0, PosX, PosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
113
114 /* Extract the icon resource from the executable process */
115 GetModuleFileNameW(NULL, szAppPath, _countof(szAppPath));
116 Globals.hIcon = ExtractIconW(Globals.hInstance, szAppPath, 0);
117
118 /* Set the icon within the dialog's title bar */
119 if (Globals.hIcon)
120 {
121 SendMessageW(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)Globals.hIcon);
122 }
123
124 /* Retrieve the system menu and append the "About" menu item onto it */
125 hSysMenu = GetSystemMenu(hDlg, FALSE);
126 if (hSysMenu != NULL)
127 {
128 if (LoadStringW(Globals.hInstance, IDM_ABOUT, szAboutDlg, _countof(szAboutDlg)))
129 {
130 AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL);
131 AppendMenuW(hSysMenu, MF_STRING, IDM_ABOUT, szAboutDlg);
132 }
133 }
134
135 /* Get the dialog items, specifically the dialog list box, the Start and Stop buttons */
136 Globals.hListDlg = GetDlgItem(hDlg, IDC_LISTBOX);
137 Globals.hDlgCtlStart = GetDlgItem(hDlg, IDC_START);
138 Globals.hDlgCtlStop = GetDlgItem(hDlg, IDC_STOP);
139
140 /* Initialize the GUI listbox */
141 InitUtilsList(TRUE);
142
143 /* Set the selection to the first item */
144 Globals.iSelectedIndex = 0;
145
146 /* Refresh the list */
147 ListBoxRefreshContents();
148
149 /* Create a timer, we'll use it to control the state of our items in the listbox */
150 Globals.iTimer = SetTimer(hDlg, 0, 400, NULL);
151
152 return TRUE;
153 }
154
155 /**
156 * @ShowAboutDlg
157 *
158 * Displays the Shell "About" dialog box.
159 *
160 * @param[in] hDlgParent
161 * A handle to the parent dialog window.
162 *
163 * @return
164 * Nothing.
165 *
166 */
167 VOID ShowAboutDlg(IN HWND hDlgParent)
168 {
169 WCHAR szApp[MAX_BUFFER];
170 WCHAR szAuthors[MAX_BUFFER];
171
172 LoadStringW(Globals.hInstance, IDS_APP_NAME, szApp, _countof(szApp));
173 LoadStringW(Globals.hInstance, IDS_AUTHORS, szAuthors, _countof(szAuthors));
174
175 ShellAboutW(hDlgParent, szApp, szAuthors, Globals.hIcon);
176 }
177
178 /**
179 * @GroupBoxUpdateTitle
180 *
181 * Updates the title of the groupbox.
182 *
183 * @return
184 * Nothing.
185 *
186 */
187 VOID GroupBoxUpdateTitle(VOID)
188 {
189 WCHAR szFormat[MAX_BUFFER];
190
191 /* Format the string with the utility's name and set it to the listbox's title */
192 StringCchPrintfW(szFormat, _countof(szFormat), Globals.szGrpBoxTitle, EntriesList[Globals.iSelectedIndex].szResource);
193 SetWindowTextW(GetDlgItem(Globals.hMainDlg, IDC_GROUPBOX), szFormat);
194 }
195
196 /**
197 * @UpdateUtilityState
198 *
199 * Checks the state of the given accessibility tool.
200 *
201 * @param[in] bUtilState
202 * State condition (boolean TRUE: started / FALSE: stopped).
203 *
204 * @return
205 * Nothing.
206 *
207 */
208 VOID UpdateUtilityState(IN BOOL bUtilState)
209 {
210 Button_Enable(Globals.hDlgCtlStart, !bUtilState);
211 Button_Enable(Globals.hDlgCtlStop, bUtilState);
212
213 /* Update the groupbox's title based on the selected utility item */
214 GroupBoxUpdateTitle();
215 }
216
217 /**
218 * @ListBoxRefreshContents
219 *
220 * Handle the tasks on a periodic cycle. This function handles WM_TIMER message.
221 *
222 * @return
223 * Returns 0 to inform the system that WM_TIMER has been processed.
224 *
225 */
226 INT ListBoxRefreshContents(VOID)
227 {
228 UINT i;
229 INT iItem;
230 BOOL bIsRunning;
231 WCHAR szFormat[MAX_BUFFER];
232
233 /* Disable listbox redraw */
234 SendMessageW(Globals.hListDlg, WM_SETREDRAW, FALSE, 0);
235
236 for (i = 0; i < _countof(EntriesList); ++i)
237 {
238 /* Check the utility's state */
239 bIsRunning = IsProcessRunning(EntriesList[i].lpszProgram);
240 if (bIsRunning != EntriesList[i].bState)
241 {
242 /* The utility's state has changed, save it */
243 EntriesList[i].bState = bIsRunning;
244
245 /* Update the corresponding item in the listbox */
246 StringCchPrintfW(szFormat, _countof(szFormat),
247 (bIsRunning ? Globals.szRunning : Globals.szNotRunning),
248 EntriesList[i].szResource);
249
250 SendMessageW(Globals.hListDlg, LB_DELETESTRING, (LPARAM)i, 0);
251 iItem = SendMessageW(Globals.hListDlg, LB_INSERTSTRING, (LPARAM)i, (LPARAM)szFormat);
252 if (iItem != LB_ERR)
253 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]);
254 }
255 }
256
257 /* Re-enable listbox redraw */
258 SendMessageW(Globals.hListDlg, WM_SETREDRAW, TRUE, 0);
259
260 /*
261 * Check the previously selected item. This will help us determine what
262 * item has been selected and set its focus selection back. Furthermore, check
263 * the state of each accessibility tool and enable/disable the buttons.
264 */
265 SendMessageW(Globals.hListDlg, LB_SETCURSEL, (WPARAM)Globals.iSelectedIndex, 0);
266 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
267
268 return 0;
269 }
270
271 /**
272 * @DlgProc
273 *
274 * Main dialog application procedure function.
275 *
276 * @param[in] hDlg
277 * The handle object of the dialog.
278 *
279 * @param[in] Msg
280 * Message events (in unsigned int).
281 *
282 * @param[in] wParam
283 * Message parameter (in UINT_PTR).
284 *
285 * @param[in] lParam
286 * Message parameter (in LONG_PTR).
287 *
288 * @return
289 * Returns 0 to inform the system that the procedure has been handled.
290 *
291 */
292 INT_PTR APIENTRY DlgProc(
293 IN HWND hDlg,
294 IN UINT Msg,
295 IN WPARAM wParam,
296 IN LPARAM lParam)
297 {
298 switch (Msg)
299 {
300 case WM_INITDIALOG:
301 DlgInitHandler(hDlg);
302 return TRUE;
303
304 case WM_CLOSE:
305 KillTimer(hDlg, Globals.iTimer);
306 DestroyIcon(Globals.hIcon);
307 EndDialog(hDlg, FALSE);
308 break;
309
310 case WM_COMMAND:
311 {
312 switch (LOWORD(wParam))
313 {
314 case IDC_OK:
315 case IDC_CANCEL:
316 EndDialog(hDlg, FALSE);
317 break;
318
319 case IDC_LISTBOX:
320 {
321 switch (HIWORD(wParam))
322 {
323 case LBN_SELCHANGE:
324 {
325 /* Retrieve the index of the current selected item */
326 INT iIndex = SendMessageW(Globals.hListDlg, LB_GETCURSEL, 0, 0);
327 if ((iIndex == LB_ERR) || (iIndex >= _countof(EntriesList)))
328 break;
329
330 /* Assign the selected index and check the utility's state */
331 Globals.iSelectedIndex = iIndex;
332 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
333 break;
334 }
335 break;
336 }
337 break;
338 }
339
340 case IDC_START:
341 LaunchProcess(EntriesList[Globals.iSelectedIndex].lpszProgram);
342 break;
343
344 case IDC_STOP:
345 CloseProcess(EntriesList[Globals.iSelectedIndex].lpszProgram);
346 break;
347
348 default:
349 break;
350 }
351 break;
352 }
353
354 case WM_TIMER:
355 ListBoxRefreshContents();
356 return 0;
357
358 case WM_SYSCOMMAND:
359 {
360 switch (LOWORD(wParam))
361 {
362 case IDM_ABOUT:
363 ShowAboutDlg(hDlg);
364 break;
365 }
366 break;
367 }
368 }
369
370 return 0;
371 }
372
373 /**
374 * @UManStartDlg
375 *
376 * Executes the dialog initialization mechanism and starts Utility Manager.
377 * The function is exported for use by the main process.
378 *
379 * @return
380 * Returns TRUE when the operation has succeeded, FALSE otherwise.
381 *
382 */
383 BOOL WINAPI UManStartDlg(VOID)
384 {
385 HANDLE hMutex;
386 DWORD dwError;
387 INITCOMMONCONTROLSEX iccex;
388
389 /* Create a mutant object for the program. */
390 hMutex = CreateMutexW(NULL, FALSE, L"Utilman");
391 if (hMutex)
392 {
393 /* Check if there's already a mutex for the program */
394 dwError = GetLastError();
395 if (dwError == ERROR_ALREADY_EXISTS)
396 {
397 /*
398 The program's instance is already here. That means
399 the program is running and we should not set a new instance
400 and mutex object.
401 */
402 CloseHandle(hMutex);
403 return FALSE;
404 }
405 }
406
407 /* Load the common controls for the program */
408 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
409 iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
410 InitCommonControlsEx(&iccex);
411
412 LoadStringW(Globals.hInstance, IDS_RUNNING,
413 Globals.szRunning, _countof(Globals.szRunning));
414 LoadStringW(Globals.hInstance, IDS_NOTRUNNING,
415 Globals.szNotRunning, _countof(Globals.szNotRunning));
416 LoadStringW(Globals.hInstance, IDS_GROUPBOX_OPTIONS_TITLE,
417 Globals.szGrpBoxTitle, _countof(Globals.szGrpBoxTitle));
418
419 /* Initialize the list of accessibility utilities */
420 InitUtilsList(FALSE);
421
422 /* Create the dialog box of the program */
423 DialogBoxW(Globals.hInstance,
424 MAKEINTRESOURCEW(IDD_MAIN_DIALOG),
425 GetDesktopWindow(),
426 DlgProc);
427
428 /* Delete the mutex */
429 if (hMutex)
430 {
431 CloseHandle(hMutex);
432 }
433
434 return TRUE;
435 }
436
437 /**
438 * @DllMain
439 *
440 * Core routine of the Utility Manager's library.
441 *
442 * @param[in] hDllInstance
443 * The entry point instance of the library.
444 *
445 * @param[in] fdwReason
446 * The reason argument to indicate the motive DllMain
447 * is being called.
448 *
449 * @param[in] lpvReserved
450 * Reserved.
451 *
452 * @return
453 * Returns TRUE when main call initialization has succeeded, FALSE
454 * otherwise.
455 *
456 */
457 BOOL WINAPI DllMain(IN HINSTANCE hDllInstance,
458 IN DWORD fdwReason,
459 IN LPVOID lpvReserved)
460 {
461 switch (fdwReason)
462 {
463 case DLL_PROCESS_ATTACH:
464 {
465 /* We don't care for DLL_THREAD_ATTACH and DLL_THREAD_DETACH notifications */
466 DisableThreadLibraryCalls(hDllInstance);
467
468 /* Initialize the globals */
469 ZeroMemory(&Globals, sizeof(Globals));
470 Globals.hInstance = hDllInstance;
471 break;
472 }
473
474 case DLL_PROCESS_DETACH:
475 break;
476 }
477
478 return TRUE;
479 }