Implement Utility Manager software
[reactos.git] / base / applications / utilman / dialog.c
1 /*
2 * PROJECT: ReactOS Utility Manager (Accessibility)
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Main dialog code file
5 * COPYRIGHT: Copyright 2019 Bișoc George (fraizeraust99 at gmail dot com)
6 * Copyright 2019 Hermes Belusca-Maito
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "precomp.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(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].lpProgram);
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 HMENU hSysMenu;
103
104 /* Save the dialog handle */
105 Globals.hMainDlg = hDlg;
106
107 /* Center the dialog on the screen */
108 GetWindowRect(hDlg, &rc);
109 PosX = (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2;
110 PosY = (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2;
111 SetWindowPos(hDlg, 0, PosX, PosY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
112
113 /* Load the icon resource */
114 Globals.hIcon = LoadImageW(Globals.hInstance,
115 MAKEINTRESOURCEW(IDI_ICON_UTILMAN),
116 IMAGE_ICON,
117 0,
118 0,
119 LR_DEFAULTSIZE);
120
121 /* Set the icon within the dialog's title bar */
122 if (Globals.hIcon)
123 {
124 SendMessageW(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)Globals.hIcon);
125 }
126
127 /* Retrieve the system menu and append the "About" menu item onto it */
128 hSysMenu = GetSystemMenu(hDlg, FALSE);
129 if (hSysMenu != NULL)
130 {
131 if (LoadStringW(Globals.hInstance, IDM_ABOUT, szAboutDlg, _countof(szAboutDlg)))
132 {
133 AppendMenuW(hSysMenu, MF_SEPARATOR, 0, NULL);
134 AppendMenuW(hSysMenu, MF_STRING, IDM_ABOUT, szAboutDlg);
135 }
136 }
137
138 /* Get the dialog items, specifically the dialog list box, the Start and Stop buttons */
139 Globals.hListDlg = GetDlgItem(hDlg, IDC_LISTBOX);
140 Globals.hDlgCtlStart = GetDlgItem(hDlg, IDC_START);
141 Globals.hDlgCtlStop = GetDlgItem(hDlg, IDC_STOP);
142
143 /* Initialize the GUI listbox */
144 InitUtilsList(TRUE);
145
146 /* Set the selection to the first item */
147 Globals.iSelectedIndex = 0;
148
149 /* Refresh the list */
150 ListBoxRefreshContents();
151
152 /* Create a timer, we'll use it to control the state of our items in the listbox */
153 Globals.iTimer = SetTimer(hDlg, 0, 400, NULL);
154
155 return TRUE;
156 }
157
158 /**
159 * @GroupBoxUpdateTitle
160 *
161 * Updates the title of the groupbox.
162 *
163 * @return
164 * Nothing.
165 *
166 */
167 VOID GroupBoxUpdateTitle(VOID)
168 {
169 WCHAR szFormat[MAX_BUFFER];
170
171 /* Format the string with the utility's name and set it to the listbox's title */
172 StringCchPrintfW(szFormat, _countof(szFormat), Globals.szGrpBoxTitle, EntriesList[Globals.iSelectedIndex].szResource);
173 SetWindowTextW(GetDlgItem(Globals.hMainDlg, IDC_GROUPBOX), szFormat);
174 }
175
176 /**
177 * @UpdateUtilityState
178 *
179 * Checks the state of the given accessibility tool.
180 *
181 * @param[in] bUtilState
182 * State condition (boolean TRUE: started / FALSE: stopped).
183 *
184 * @return
185 * Nothing.
186 *
187 */
188 VOID UpdateUtilityState(IN BOOL bUtilState)
189 {
190 Button_Enable(Globals.hDlgCtlStart, !bUtilState);
191 Button_Enable(Globals.hDlgCtlStop, bUtilState);
192
193 /* Update the groupbox's title based on the selected utility item */
194 GroupBoxUpdateTitle();
195 }
196
197 /**
198 * @ListBoxRefreshContents
199 *
200 * Handle the tasks on a periodic cycle. This function handles WM_TIMER message.
201 *
202 * @return
203 * Returns 0 to inform the system that WM_TIMER has been processed.
204 *
205 */
206 INT ListBoxRefreshContents(VOID)
207 {
208 UINT i;
209 INT iItem;
210 BOOL bIsRunning;
211 WCHAR szFormat[MAX_BUFFER];
212
213 /* Disable listbox redraw */
214 SendMessageW(Globals.hListDlg, WM_SETREDRAW, FALSE, 0);
215
216 for (i = 0; i < _countof(EntriesList); ++i)
217 {
218 /* Check the utility's state */
219 bIsRunning = IsProcessRunning(EntriesList[i].lpProgram);
220 if (bIsRunning != EntriesList[i].bState)
221 {
222 /* The utility's state has changed, save it */
223 EntriesList[i].bState = bIsRunning;
224
225 /* Update the corresponding item in the listbox */
226 StringCchPrintfW(szFormat, _countof(szFormat),
227 (bIsRunning ? Globals.szRunning : Globals.szNotRunning),
228 EntriesList[i].szResource);
229
230 SendMessageW(Globals.hListDlg, LB_DELETESTRING, (LPARAM)i, 0);
231 iItem = SendMessageW(Globals.hListDlg, LB_INSERTSTRING, (LPARAM)i, (LPARAM)szFormat);
232 if (iItem != LB_ERR)
233 SendMessageW(Globals.hListDlg, LB_SETITEMDATA, iItem, (LPARAM)&EntriesList[i]);
234 }
235 }
236
237 /* Re-enable listbox redraw */
238 SendMessageW(Globals.hListDlg, WM_SETREDRAW, TRUE, 0);
239
240 /*
241 * Check the previously selected item. This will help us determine what
242 * item has been selected and set its focus selection back. Furthermore, check
243 * the state of each accessibility tool and enable/disable the buttons.
244 */
245 SendMessageW(Globals.hListDlg, LB_SETCURSEL, (WPARAM)Globals.iSelectedIndex, 0);
246 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
247
248 return 0;
249 }
250
251 /**
252 * @DlgProc
253 *
254 * Main dialog application procedure function.
255 *
256 * @param[in] hDlg
257 * The handle object of the dialog.
258 *
259 * @param[in] Msg
260 * Message events (in unsigned int).
261 *
262 * @param[in] wParam
263 * Message parameter (in UINT_PTR).
264 *
265 * @param[in] lParam
266 * Message paramater (in LONG_PTR).
267 *
268 * @return
269 * Returns 0 to inform the system that the procedure has been handled.
270 *
271 */
272 INT_PTR APIENTRY DlgProc(
273 IN HWND hDlg,
274 IN UINT Msg,
275 IN WPARAM wParam,
276 IN LPARAM lParam)
277 {
278 switch (Msg)
279 {
280 case WM_INITDIALOG:
281 DlgInitHandler(hDlg);
282 return TRUE;
283
284 case WM_CLOSE:
285 KillTimer(hDlg, Globals.iTimer);
286 DestroyIcon(Globals.hIcon);
287 EndDialog(hDlg, FALSE);
288 break;
289
290 case WM_COMMAND:
291 {
292 switch (LOWORD(wParam))
293 {
294 case IDC_OK:
295 case IDC_CANCEL:
296 EndDialog(hDlg, FALSE);
297 break;
298
299 case IDC_LISTBOX:
300 {
301 switch (HIWORD(wParam))
302 {
303 case LBN_SELCHANGE:
304 {
305 /* Retrieve the index of the current selected item */
306 INT iIndex = SendMessageW(Globals.hListDlg, LB_GETCURSEL, 0, 0);
307 if ((iIndex == LB_ERR) || (iIndex >= _countof(EntriesList)))
308 break;
309
310 /* Assign the selected index and check the utility's state */
311 Globals.iSelectedIndex = iIndex;
312 UpdateUtilityState(EntriesList[Globals.iSelectedIndex].bState);
313 break;
314 }
315 break;
316 }
317 break;
318 }
319
320 case IDC_START:
321 LaunchProcess(EntriesList[Globals.iSelectedIndex].lpProgram);
322 break;
323
324 case IDC_STOP:
325 CloseProcess(EntriesList[Globals.iSelectedIndex].lpProgram);
326 break;
327
328 default:
329 break;
330 }
331 break;
332 }
333
334 case WM_TIMER:
335 ListBoxRefreshContents();
336 return 0;
337
338 case WM_SYSCOMMAND:
339 {
340 switch (LOWORD(wParam))
341 {
342 case IDM_ABOUT:
343 ShowAboutDlg(hDlg);
344 break;
345 }
346 break;
347 }
348 }
349
350 return 0;
351 }
352
353 /**
354 * @wWinMain
355 *
356 * Application entry point.
357 *
358 * @param[in] hInstance
359 * Application instance.
360 *
361 * @param[in] hPrevInstance
362 * The previous instance of the application (not used).
363 *
364 * @param[in] pCmdLine
365 * Pointer to a command line argument (in wide string -- not used).
366 *
367 * @param[in] nCmdShow
368 * An integer served as a flag to note how the application will be shown (not used).
369 *
370 * @return
371 * Returns 0 to let the function terminating before it enters in the message loop.
372 *
373 */
374 INT WINAPI wWinMain(
375 IN HINSTANCE hInstance,
376 IN HINSTANCE hPrevInstance,
377 IN LPWSTR pCmdLine,
378 IN INT nCmdShow)
379 {
380 HANDLE hMutex;
381 DWORD dwError;
382 INITCOMMONCONTROLSEX iccex;
383
384 UNREFERENCED_PARAMETER(hPrevInstance);
385 UNREFERENCED_PARAMETER(pCmdLine);
386 UNREFERENCED_PARAMETER(nCmdShow);
387
388 /* Create a mutant object for the program. */
389 hMutex = CreateMutexW(NULL, FALSE, L"Utilman");
390 if (hMutex)
391 {
392 /* Check if there's already a mutex for the program */
393 dwError = GetLastError();
394 if (dwError == ERROR_ALREADY_EXISTS)
395 {
396 /*
397 The program's instance is already here. That means
398 the program is running and we should not set a new instance
399 and mutex object.
400 */
401 CloseHandle(hMutex);
402 return 0;
403 }
404 }
405
406 /* Load the common controls for the program */
407 iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
408 iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
409 InitCommonControlsEx(&iccex);
410
411 /* Initialize the globals */
412 ZeroMemory(&Globals, sizeof(Globals));
413 Globals.hInstance = hInstance;
414
415 LoadStringW(Globals.hInstance, IDS_RUNNING,
416 Globals.szRunning, _countof(Globals.szRunning));
417 LoadStringW(Globals.hInstance, IDS_NOTRUNNING,
418 Globals.szNotRunning, _countof(Globals.szNotRunning));
419 LoadStringW(Globals.hInstance, IDS_GROUPBOX_OPTIONS_TITLE,
420 Globals.szGrpBoxTitle, _countof(Globals.szGrpBoxTitle));
421
422 /* Initialize the list of accessibility utilities */
423 InitUtilsList(FALSE);
424
425 /* Create the dialog box of the program */
426 DialogBoxW(hInstance,
427 MAKEINTRESOURCEW(IDD_MAIN_DIALOG),
428 GetDesktopWindow(),
429 DlgProc);
430
431 /* Delete the mutex */
432 if (hMutex)
433 {
434 CloseHandle(hMutex);
435 }
436
437 return 0;
438 }