[STOBJECT][MMSYS][POWERCFG] Store the "Show x icon in the taskbar" setting for the...
[reactos.git] / dll / cpl / mmsys / volume.c
1 /*
2 * PROJECT: ReactOS Multimedia Control Panel
3 * FILE: dll/cpl/mmsys/volume.c
4 * PURPOSE: ReactOS Multimedia Control Panel
5 * PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
6 * Johannes Anderwald <janderwald@reactos.com>
7 * Dmitry Chapyshev <dmitry@reactos.org>
8 */
9
10 #include "mmsys.h"
11
12 #include <shellapi.h>
13
14
15 typedef struct _IMGINFO
16 {
17 HBITMAP hBitmap;
18 INT cxSource;
19 INT cySource;
20 } IMGINFO, *PIMGINFO;
21
22
23 typedef struct _GLOBAL_DATA
24 {
25 HMIXER hMixer;
26 HICON hIconMuted;
27 HICON hIconUnMuted;
28 HICON hIconNoHW;
29
30 LONG muteVal;
31 DWORD muteControlID;
32
33 DWORD volumeControlID;
34 DWORD volumeChannels;
35 DWORD volumeMinimum;
36 DWORD volumeMaximum;
37 DWORD volumeStep;
38
39 DWORD maxVolume;
40 PMIXERCONTROLDETAILS_UNSIGNED volumeInitValues;
41 PMIXERCONTROLDETAILS_UNSIGNED volumeCurrentValues;
42
43 } GLOBAL_DATA, *PGLOBAL_DATA;
44
45
46 static VOID
47 InitImageInfo(PIMGINFO ImgInfo)
48 {
49 BITMAP bitmap;
50
51 ZeroMemory(ImgInfo, sizeof(*ImgInfo));
52
53 ImgInfo->hBitmap = LoadImage(hApplet,
54 MAKEINTRESOURCE(IDB_SPEAKIMG),
55 IMAGE_BITMAP,
56 0,
57 0,
58 LR_DEFAULTCOLOR);
59
60 if (ImgInfo->hBitmap != NULL)
61 {
62 GetObject(ImgInfo->hBitmap, sizeof(BITMAP), &bitmap);
63
64 ImgInfo->cxSource = bitmap.bmWidth;
65 ImgInfo->cySource = bitmap.bmHeight;
66 }
67 }
68
69
70 VOID
71 GetMuteControl(PGLOBAL_DATA pGlobalData)
72 {
73 MIXERLINE mxln;
74 MIXERCONTROL mxc;
75 MIXERLINECONTROLS mxlctrl;
76
77 if (pGlobalData->hMixer == NULL)
78 return;
79
80 mxln.cbStruct = sizeof(MIXERLINE);
81 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
82
83 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
84 != MMSYSERR_NOERROR) return;
85
86 mxlctrl.cbStruct = sizeof(MIXERLINECONTROLS);
87 mxlctrl.dwLineID = mxln.dwLineID;
88 mxlctrl.dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
89 mxlctrl.cControls = 1;
90 mxlctrl.cbmxctrl = sizeof(MIXERCONTROL);
91 mxlctrl.pamxctrl = &mxc;
92
93 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlctrl, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
94 != MMSYSERR_NOERROR) return;
95
96 pGlobalData->muteControlID = mxc.dwControlID;
97 }
98
99
100 VOID
101 GetMuteState(PGLOBAL_DATA pGlobalData)
102 {
103 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
104 MIXERCONTROLDETAILS mxcd;
105
106 if (pGlobalData->hMixer == NULL)
107 return;
108
109 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
110 mxcd.dwControlID = pGlobalData->muteControlID;
111 mxcd.cChannels = 1;
112 mxcd.cMultipleItems = 0;
113 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
114 mxcd.paDetails = &mxcdMute;
115
116 if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
117 != MMSYSERR_NOERROR)
118 return;
119
120 pGlobalData->muteVal = mxcdMute.fValue;
121 }
122
123
124 VOID
125 SwitchMuteState(PGLOBAL_DATA pGlobalData)
126 {
127 MIXERCONTROLDETAILS_BOOLEAN mxcdMute;
128 MIXERCONTROLDETAILS mxcd;
129
130 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
131 mxcd.dwControlID = pGlobalData->muteControlID;
132 mxcd.cChannels = 1;
133 mxcd.cMultipleItems = 0;
134 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN);
135 mxcd.paDetails = &mxcdMute;
136
137 mxcdMute.fValue = !pGlobalData->muteVal;
138 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
139 != MMSYSERR_NOERROR)
140 return;
141
142 pGlobalData->muteVal = mxcdMute.fValue;
143 }
144
145
146 VOID
147 GetVolumeControl(PGLOBAL_DATA pGlobalData)
148 {
149 MIXERLINE mxln;
150 MIXERCONTROL mxc;
151 MIXERLINECONTROLS mxlc;
152
153 if (pGlobalData->hMixer == NULL)
154 return;
155
156 mxln.cbStruct = sizeof(MIXERLINE);
157 mxln.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
158 if (mixerGetLineInfo((HMIXEROBJ)pGlobalData->hMixer, &mxln, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE)
159 != MMSYSERR_NOERROR)
160 return;
161
162 pGlobalData->volumeChannels = mxln.cChannels;
163
164 mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
165 mxlc.dwLineID = mxln.dwLineID;
166 mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
167 mxlc.cControls = 1;
168 mxlc.cbmxctrl = sizeof(MIXERCONTROL);
169 mxlc.pamxctrl = &mxc;
170 if (mixerGetLineControls((HMIXEROBJ)pGlobalData->hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE)
171 != MMSYSERR_NOERROR)
172 return;
173
174 pGlobalData->volumeMinimum = mxc.Bounds.dwMinimum;
175 pGlobalData->volumeMaximum = mxc.Bounds.dwMaximum;
176 pGlobalData->volumeControlID = mxc.dwControlID;
177 pGlobalData->volumeStep = (pGlobalData->volumeMaximum - pGlobalData->volumeMinimum) / (VOLUME_MAX - VOLUME_MIN);
178
179 pGlobalData->volumeInitValues = HeapAlloc(GetProcessHeap(),
180 0,
181 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
182 if (pGlobalData->volumeInitValues == NULL)
183 return;
184
185 pGlobalData->volumeCurrentValues = HeapAlloc(GetProcessHeap(),
186 0,
187 mxln.cChannels * sizeof(MIXERCONTROLDETAILS_UNSIGNED));
188 if (pGlobalData->volumeCurrentValues == NULL)
189 return;
190 }
191
192
193 VOID
194 GetVolumeValue(PGLOBAL_DATA pGlobalData)
195 {
196 MIXERCONTROLDETAILS mxcd;
197 DWORD i;
198
199 if (pGlobalData->hMixer == NULL)
200 return;
201
202 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
203 mxcd.dwControlID = pGlobalData->volumeControlID;
204 mxcd.cChannels = pGlobalData->volumeChannels;
205 mxcd.cMultipleItems = 0;
206 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
207 mxcd.paDetails = pGlobalData->volumeInitValues;
208
209 if (mixerGetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE)
210 != MMSYSERR_NOERROR)
211 return;
212
213 pGlobalData->maxVolume = 0;
214 for (i = 0; i < pGlobalData->volumeChannels; i++)
215 {
216 pGlobalData->volumeCurrentValues[i].dwValue = pGlobalData->volumeInitValues[i].dwValue;
217
218 if (pGlobalData->volumeInitValues[i].dwValue > pGlobalData->maxVolume)
219 pGlobalData->maxVolume = pGlobalData->volumeInitValues[i].dwValue;
220 }
221 }
222
223
224 VOID
225 SetVolumeValue(PGLOBAL_DATA pGlobalData,
226 DWORD dwPosition)
227 {
228 MIXERCONTROLDETAILS mxcd;
229 DWORD dwVolume, i;
230
231 if (pGlobalData->hMixer == NULL)
232 return;
233
234 if (dwPosition == VOLUME_MIN)
235 dwVolume = pGlobalData->volumeMinimum;
236 else if (dwPosition == VOLUME_MAX)
237 dwVolume = pGlobalData->volumeMaximum;
238 else
239 dwVolume = (dwPosition * pGlobalData->volumeStep) + pGlobalData->volumeMinimum;
240
241 for (i = 0; i < pGlobalData->volumeChannels; i++)
242 {
243 if (pGlobalData->volumeInitValues[i].dwValue == pGlobalData->maxVolume)
244 {
245 pGlobalData->volumeCurrentValues[i].dwValue = dwVolume;
246 }
247 else
248 {
249 pGlobalData->volumeCurrentValues[i].dwValue =
250 pGlobalData->volumeInitValues[i].dwValue * dwVolume / pGlobalData-> maxVolume;
251 }
252 }
253
254 mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
255 mxcd.dwControlID = pGlobalData->volumeControlID;
256 mxcd.cChannels = pGlobalData->volumeChannels;
257 mxcd.cMultipleItems = 0;
258 mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
259 mxcd.paDetails = pGlobalData->volumeCurrentValues;
260
261 if (mixerSetControlDetails((HMIXEROBJ)pGlobalData->hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE)
262 != MMSYSERR_NOERROR)
263 return;
264 }
265
266
267 static
268 VOID
269 SetSystrayVolumeIconState(BOOL bEnabled)
270 {
271 HWND hwndTaskBar;
272
273 hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
274 if (hwndTaskBar == NULL)
275 return;
276
277 SendMessageW(hwndTaskBar, WM_USER + 220, 4, bEnabled);
278 }
279
280 static
281 BOOL
282 GetSystrayVolumeIconState(VOID)
283 {
284 HWND hwndTaskBar;
285
286 hwndTaskBar = FindWindowW(L"SystemTray_Main", NULL);
287 if (hwndTaskBar == NULL)
288 {
289 return FALSE;
290 }
291
292 return (BOOL)SendMessageW(hwndTaskBar, WM_USER + 221, 4, 0);
293 }
294
295 VOID
296 InitVolumeControls(HWND hwndDlg, PGLOBAL_DATA pGlobalData)
297 {
298 UINT NumMixers;
299 MIXERCAPS mxc;
300 TCHAR szNoDevices[256];
301
302 LoadString(hApplet, IDS_NO_DEVICES, szNoDevices, _countof(szNoDevices));
303
304 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(VOLUME_MIN, VOLUME_MAX));
305 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETTICFREQ, VOLUME_TICFREQ, 0);
306 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPAGESIZE, 0, VOLUME_PAGESIZE);
307
308 NumMixers = mixerGetNumDevs();
309 if (!NumMixers)
310 {
311 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR), FALSE);
312 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_LOW), FALSE);
313 EnableWindow(GetDlgItem(hwndDlg, IDC_VOLUME_HIGH), FALSE);
314 EnableWindow(GetDlgItem(hwndDlg, IDC_MUTE_CHECKBOX), FALSE);
315 EnableWindow(GetDlgItem(hwndDlg, IDC_ICON_IN_TASKBAR), FALSE);
316 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED_BTN), FALSE);
317 EnableWindow(GetDlgItem(hwndDlg, IDC_SPEAKER_VOL_BTN), FALSE);
318 EnableWindow(GetDlgItem(hwndDlg, IDC_ADVANCED2_BTN), FALSE);
319 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconNoHW);
320 SetDlgItemText(hwndDlg, IDC_DEVICE_NAME, szNoDevices);
321 return;
322 }
323
324 if (mixerOpen(&pGlobalData->hMixer, 0, PtrToUlong(hwndDlg), 0, MIXER_OBJECTF_MIXER | CALLBACK_WINDOW) != MMSYSERR_NOERROR)
325 {
326 MessageBox(hwndDlg, _T("Cannot open mixer"), NULL, MB_OK);
327 return;
328 }
329
330 ZeroMemory(&mxc, sizeof(MIXERCAPS));
331 if (mixerGetDevCaps(PtrToUint(pGlobalData->hMixer), &mxc, sizeof(MIXERCAPS)) != MMSYSERR_NOERROR)
332 {
333 MessageBox(hwndDlg, _T("mixerGetDevCaps failed"), NULL, MB_OK);
334 return;
335 }
336
337 CheckDlgButton(hwndDlg,
338 IDC_ICON_IN_TASKBAR,
339 GetSystrayVolumeIconState() ? BST_CHECKED : BST_UNCHECKED);
340
341 GetMuteControl(pGlobalData);
342 GetMuteState(pGlobalData);
343 if (pGlobalData->muteVal)
344 {
345 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
346 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
347 }
348 else
349 {
350 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
351 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
352 }
353
354 GetVolumeControl(pGlobalData);
355 GetVolumeValue(pGlobalData);
356
357 SendDlgItemMessage(hwndDlg, IDC_DEVICE_NAME, WM_SETTEXT, 0, (LPARAM)mxc.szPname);
358 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
359 }
360
361 VOID
362 SaveData(HWND hwndDlg)
363 {
364 BOOL bShowIcon;
365
366 bShowIcon = (IsDlgButtonChecked(hwndDlg, IDC_ICON_IN_TASKBAR) == BST_CHECKED);
367
368 SetSystrayVolumeIconState(bShowIcon);
369 }
370
371 VOID
372 LaunchSoundControl(HWND hwndDlg)
373 {
374 if ((INT_PTR)ShellExecuteW(NULL, L"open", L"sndvol32.exe", NULL, NULL, SW_SHOWNORMAL) > 32)
375 return;
376 MessageBox(hwndDlg, _T("Cannot run sndvol32.exe"), NULL, MB_OK);
377 }
378
379 /* Volume property page dialog callback */
380 //static INT_PTR CALLBACK
381 INT_PTR CALLBACK
382 VolumeDlgProc(HWND hwndDlg,
383 UINT uMsg,
384 WPARAM wParam,
385 LPARAM lParam)
386 {
387 static IMGINFO ImgInfo;
388 PGLOBAL_DATA pGlobalData;
389 UNREFERENCED_PARAMETER(lParam);
390 UNREFERENCED_PARAMETER(wParam);
391
392 pGlobalData = (PGLOBAL_DATA)GetWindowLongPtr(hwndDlg, DWLP_USER);
393
394 switch(uMsg)
395 {
396 case MM_MIXM_LINE_CHANGE:
397 {
398 GetMuteState(pGlobalData);
399 if (pGlobalData->muteVal)
400 {
401 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_CHECKED, (LPARAM)0);
402 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
403 }
404 else
405 {
406 SendDlgItemMessage(hwndDlg, IDC_MUTE_CHECKBOX, BM_SETCHECK, (WPARAM)BST_UNCHECKED, (LPARAM)0);
407 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
408 }
409 break;
410 }
411 case MM_MIXM_CONTROL_CHANGE:
412 {
413 GetVolumeValue(pGlobalData);
414 SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)(pGlobalData->maxVolume - pGlobalData->volumeMinimum) / pGlobalData->volumeStep);
415 break;
416 }
417 case WM_INITDIALOG:
418 {
419 pGlobalData = (GLOBAL_DATA*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
420 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
421
422 pGlobalData->hIconUnMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_CPLICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
423 pGlobalData->hIconMuted = LoadImage(hApplet, MAKEINTRESOURCE(IDI_MUTED_ICON), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
424 pGlobalData->hIconNoHW = LoadImage(hApplet, MAKEINTRESOURCE(IDI_NO_HW), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
425
426 InitImageInfo(&ImgInfo);
427 InitVolumeControls(hwndDlg, pGlobalData);
428 break;
429 }
430
431 case WM_DRAWITEM:
432 {
433 LPDRAWITEMSTRUCT lpDrawItem;
434 lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
435 if(lpDrawItem->CtlID == IDC_SPEAKIMG)
436 {
437 HDC hdcMem;
438 LONG left;
439
440 /* Position image in centre of dialog */
441 left = (lpDrawItem->rcItem.right - ImgInfo.cxSource) / 2;
442
443 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
444 if (hdcMem != NULL)
445 {
446 SelectObject(hdcMem, ImgInfo.hBitmap);
447 BitBlt(lpDrawItem->hDC,
448 left,
449 lpDrawItem->rcItem.top,
450 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
451 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
452 hdcMem,
453 0,
454 0,
455 SRCCOPY);
456 DeleteDC(hdcMem);
457 }
458 }
459 break;
460 }
461
462 case WM_COMMAND:
463 {
464 switch (LOWORD(wParam))
465 {
466 case IDC_MUTE_CHECKBOX:
467 if (HIWORD(wParam) == BN_CLICKED)
468 {
469 SwitchMuteState(pGlobalData);
470 if (pGlobalData->muteVal)
471 {
472 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconMuted);
473 }
474 else
475 {
476 SendDlgItemMessage(hwndDlg, IDC_MUTE_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)pGlobalData->hIconUnMuted);
477 }
478
479 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
480 }
481 break;
482
483 case IDC_ICON_IN_TASKBAR:
484 if (HIWORD(wParam) == BN_CLICKED)
485 {
486 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
487 }
488 break;
489
490 case IDC_ADVANCED_BTN:
491 LaunchSoundControl(hwndDlg);
492 break;
493
494 case IDC_SPEAKER_VOL_BTN:
495 SpeakerVolume(hwndDlg);
496 break;
497 }
498 break;
499 }
500
501 case WM_HSCROLL:
502 {
503 HWND hVolumeTrackbar = GetDlgItem(hwndDlg, IDC_VOLUME_TRACKBAR);
504 if (hVolumeTrackbar == (HWND)lParam)
505 {
506 switch (LOWORD(wParam))
507 {
508 case TB_THUMBPOSITION:
509 break;
510
511 case TB_ENDTRACK:
512 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, SND_ALIAS_ID | SND_ASYNC);
513 break;
514
515 default:
516 SetVolumeValue(pGlobalData,
517 (DWORD)SendDlgItemMessage(hwndDlg, IDC_VOLUME_TRACKBAR, TBM_GETPOS, 0, 0));
518 break;
519 }
520 }
521 break;
522 }
523
524 case WM_DESTROY:
525 if (pGlobalData)
526 {
527 if (pGlobalData->volumeInitValues)
528 HeapFree(GetProcessHeap(), 0, pGlobalData->volumeInitValues);
529
530 if (pGlobalData->volumeCurrentValues)
531 HeapFree(GetProcessHeap(), 0, pGlobalData->volumeCurrentValues);
532
533 mixerClose(pGlobalData->hMixer);
534 DestroyIcon(pGlobalData->hIconMuted);
535 DestroyIcon(pGlobalData->hIconUnMuted);
536 DestroyIcon(pGlobalData->hIconNoHW);
537 HeapFree(GetProcessHeap(), 0, pGlobalData);
538 }
539 break;
540
541 case WM_NOTIFY:
542 if (((LPNMHDR)lParam)->code == (UINT)PSN_APPLY)
543 {
544 SaveData(hwndDlg);
545 }
546 return TRUE;
547 }
548
549 return FALSE;
550 }