xref: /reactos/dll/cpl/mmsys/sounds.c (revision 84344399)
1 /*
2  * PROJECT:         ReactOS Multimedia Control Panel
3  * FILE:            dll/cpl/mmsys/sounds.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  *                  Victor Martinez Calvo <victor.martinez@reactos.org>
9  *                  Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
10  */
11 
12 #include "mmsys.h"
13 
14 #include <commdlg.h>
15 #include <windowsx.h>
16 
17 #include <debug.h>
18 
19 #define ID_SOUND_TEST_TIMER 1
20 
21 typedef struct _LABEL_MAP
22 {
23     PWSTR szName;
24     PWSTR szDesc;
25     PWSTR szIcon;
26     struct _APP_MAP *AppMap;
27     struct _LABEL_MAP *Next;
28 } LABEL_MAP, *PLABEL_MAP;
29 
30 typedef struct _APP_MAP
31 {
32     WCHAR szName[MAX_PATH];
33     WCHAR szDesc[MAX_PATH];
34     WCHAR szIcon[MAX_PATH];
35 
36     struct _APP_MAP *Next;
37     PLABEL_MAP LabelMap;
38 } APP_MAP, *PAPP_MAP;
39 
40 typedef struct _LABEL_CONTEXT
41 {
42     PLABEL_MAP LabelMap;
43     PAPP_MAP AppMap;
44     WCHAR szValue[MAX_PATH];
45     struct _LABEL_CONTEXT *Next;
46 } LABEL_CONTEXT, *PLABEL_CONTEXT;
47 
48 typedef struct _SOUND_SCHEME_CONTEXT
49 {
50     WCHAR szName[MAX_PATH];
51     WCHAR szDesc[MAX_PATH];
52     PLABEL_CONTEXT LabelContext;
53 } SOUND_SCHEME_CONTEXT, *PSOUND_SCHEME_CONTEXT;
54 
55 typedef struct _GLOBAL_DATA
56 {
57     WCHAR szDefault[MAX_PATH];
58     HIMAGELIST hSoundsImageList;
59     PLABEL_MAP pLabelMap;
60     PAPP_MAP pAppMap;
61     UINT NumWavOut;
62     HICON hPlayIcon;
63     HICON hStopIcon;
64 } GLOBAL_DATA, *PGLOBAL_DATA;
65 
66 
67 /* A filter string is a list separated by NULL and ends with double NULLs. */
68 LPWSTR MakeFilter(LPWSTR psz)
69 {
70     PWCHAR pch;
71 
72     ASSERT(psz[0] != UNICODE_NULL &&
73            psz[wcslen(psz) - 1] == L'|');
74     for (pch = psz; *pch != UNICODE_NULL; pch++)
75     {
76         /* replace vertical bar with NULL */
77         if (*pch == L'|')
78         {
79             *pch = UNICODE_NULL;
80         }
81     }
82     return psz;
83 }
84 
85 PLABEL_MAP FindLabel(PGLOBAL_DATA pGlobalData, PAPP_MAP pAppMap, PCWSTR szName)
86 {
87     PLABEL_MAP pMap = pGlobalData->pLabelMap;
88 
89     while (pMap)
90     {
91         ASSERT(pMap);
92         ASSERT(pMap->szName);
93         if (!wcscmp(pMap->szName, szName))
94             return pMap;
95 
96         pMap = pMap->Next;
97     }
98 
99     pMap = pAppMap->LabelMap;
100 
101     while (pMap)
102     {
103         ASSERT(pMap);
104         ASSERT(pMap->szName);
105         if (!wcscmp(pMap->szName, szName))
106             return pMap;
107 
108         pMap = pMap->Next;
109     }
110 
111     pMap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LABEL_MAP));
112     if (!pMap)
113         return NULL;
114 
115     pMap->szName = pMap->szDesc = _wcsdup(szName);
116     if (!pMap->szName)
117     {
118         HeapFree(GetProcessHeap(), 0, pMap);
119         return NULL;
120     }
121 
122     pMap->AppMap = pAppMap;
123     pMap->Next = pGlobalData->pLabelMap;
124     pGlobalData->pLabelMap = pMap;
125 
126     return pMap;
127 }
128 
129 
130 VOID RemoveLabel(PGLOBAL_DATA pGlobalData, PLABEL_MAP pMap)
131 {
132     PLABEL_MAP pCurMap = pGlobalData->pLabelMap;
133 
134     if (pCurMap == pMap)
135     {
136         pGlobalData->pLabelMap = pGlobalData->pLabelMap->Next;
137         return;
138     }
139 
140     while (pCurMap)
141     {
142         if (pCurMap->Next == pMap)
143         {
144             pCurMap->Next = pCurMap->Next->Next;
145             return;
146         }
147         pCurMap = pCurMap->Next;
148     }
149 }
150 
151 static
152 VOID
153 FreeLabelMap(PGLOBAL_DATA pGlobalData)
154 {
155     PLABEL_MAP pCurMap;
156 
157     while (pGlobalData->pLabelMap)
158     {
159         pCurMap = pGlobalData->pLabelMap->Next;
160 
161         /* Prevent double freeing (for "FindLabel") */
162         if (pGlobalData->pLabelMap->szName != pGlobalData->pLabelMap->szDesc)
163         {
164             free(pGlobalData->pLabelMap->szName);
165         }
166 
167         free(pGlobalData->pLabelMap->szDesc);
168         free(pGlobalData->pLabelMap->szIcon);
169 
170         HeapFree(GetProcessHeap(), 0, pGlobalData->pLabelMap);
171         pGlobalData->pLabelMap = pCurMap;
172     }
173 }
174 
175 PAPP_MAP FindApp(PGLOBAL_DATA pGlobalData, PCWSTR szName)
176 {
177     PAPP_MAP pMap = pGlobalData->pAppMap;
178 
179     while (pMap)
180     {
181         if (!wcscmp(pMap->szName, szName))
182             return pMap;
183 
184         pMap = pMap->Next;
185 
186     }
187     return NULL;
188 }
189 
190 static
191 VOID
192 FreeAppMap(PGLOBAL_DATA pGlobalData)
193 {
194     PAPP_MAP pCurMap;
195 
196     while (pGlobalData->pAppMap)
197     {
198         pCurMap = pGlobalData->pAppMap->Next;
199         HeapFree(GetProcessHeap(), 0, pGlobalData->pAppMap);
200         pGlobalData->pAppMap = pCurMap;
201     }
202 }
203 
204 PLABEL_CONTEXT FindLabelContext(PGLOBAL_DATA pGlobalData, PSOUND_SCHEME_CONTEXT pSoundScheme, PCWSTR AppName, PCWSTR LabelName)
205 {
206     PLABEL_CONTEXT pLabelContext;
207 
208     pLabelContext = pSoundScheme->LabelContext;
209 
210     while (pLabelContext)
211     {
212         ASSERT(pLabelContext->AppMap);
213         ASSERT(pLabelContext->LabelMap);
214 
215         if (!_wcsicmp(pLabelContext->AppMap->szName, AppName) && !_wcsicmp(pLabelContext->LabelMap->szName, LabelName))
216         {
217             return pLabelContext;
218         }
219         pLabelContext = pLabelContext->Next;
220     }
221 
222     pLabelContext = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LABEL_CONTEXT));
223     if (!pLabelContext)
224         return NULL;
225 
226     pLabelContext->AppMap = FindApp(pGlobalData, AppName);
227     pLabelContext->LabelMap = FindLabel(pGlobalData, pLabelContext->AppMap, LabelName);
228     ASSERT(pLabelContext->AppMap);
229     ASSERT(pLabelContext->LabelMap);
230     pLabelContext->szValue[0] = UNICODE_NULL;
231     pLabelContext->Next = pSoundScheme->LabelContext;
232     pSoundScheme->LabelContext = pLabelContext;
233 
234     return pLabelContext;
235 }
236 
237 
238 BOOL
239 LoadEventLabel(PGLOBAL_DATA pGlobalData, HKEY hKey, PCWSTR szSubKey)
240 {
241     HKEY hSubKey;
242     DWORD cbValue;
243     WCHAR szDesc[MAX_PATH];
244     WCHAR szData[MAX_PATH];
245     PLABEL_MAP pMap;
246 
247     if (RegOpenKeyExW(hKey,
248                       szSubKey,
249                       0,
250                       KEY_READ,
251                       &hSubKey) != ERROR_SUCCESS)
252     {
253         return FALSE;
254     }
255 
256     cbValue = sizeof(szDesc);
257     if (RegQueryValueExW(hSubKey,
258                          NULL,
259                          NULL,
260                          NULL,
261                          (LPBYTE)szDesc,
262                          &cbValue) != ERROR_SUCCESS)
263     {
264         RegCloseKey(hSubKey);
265         return FALSE;
266     }
267 
268     cbValue = sizeof(szData);
269     if (RegQueryValueExW(hSubKey,
270                          L"DispFileName",
271                          NULL,
272                          NULL,
273                          (LPBYTE)szData,
274                          &cbValue) != ERROR_SUCCESS)
275     {
276         RegCloseKey(hSubKey);
277         return FALSE;
278     }
279 
280     pMap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LABEL_MAP));
281     if (!pMap)
282     {
283         return FALSE;
284     }
285 
286     pMap->szName = _wcsdup(szSubKey);
287     pMap->szDesc = _wcsdup(szDesc);
288     pMap->szIcon = _wcsdup(szData);
289 
290     if (pGlobalData->pLabelMap)
291     {
292         pMap->Next = pGlobalData->pLabelMap;
293         pGlobalData->pLabelMap = pMap;
294     }
295     else
296     {
297         pGlobalData->pLabelMap = pMap;
298         pGlobalData->pLabelMap->Next = NULL;
299     }
300     return TRUE;
301 }
302 
303 
304 BOOL
305 LoadEventLabels(PGLOBAL_DATA pGlobalData)
306 {
307     HKEY hSubKey;
308     DWORD dwCurKey;
309     WCHAR szName[MAX_PATH];
310     DWORD dwName;
311     DWORD dwResult;
312     DWORD dwCount;
313     if (RegOpenKeyExW(HKEY_CURRENT_USER,
314                       L"AppEvents\\EventLabels",
315                       0,
316                       KEY_READ,
317                       &hSubKey) != ERROR_SUCCESS)
318     {
319         return FALSE;
320     }
321 
322     dwCurKey = 0;
323     dwCount = 0;
324     do
325     {
326         dwName = _countof(szName);
327         dwResult = RegEnumKeyExW(hSubKey,
328                                  dwCurKey,
329                                  szName,
330                                  &dwName,
331                                  NULL,
332                                  NULL,
333                                  NULL,
334                                  NULL);
335 
336         if (dwResult == ERROR_SUCCESS)
337         {
338             if (LoadEventLabel(pGlobalData, hSubKey, szName))
339             {
340                 dwCount++;
341             }
342         }
343         dwCurKey++;
344 
345     } while (dwResult == ERROR_SUCCESS);
346 
347     RegCloseKey(hSubKey);
348     return (dwCount != 0);
349 }
350 
351 
352 BOOL
353 AddSoundProfile(HWND hwndDlg, HKEY hKey, PCWSTR szSubKey, BOOL SetDefault)
354 {
355     HKEY hSubKey;
356     WCHAR szValue[MAX_PATH];
357     DWORD cbValue, dwResult;
358     LRESULT lResult;
359     PSOUND_SCHEME_CONTEXT pScheme;
360 
361     if (RegOpenKeyExW(hKey,
362                       szSubKey,
363                       0,
364                       KEY_READ,
365                       &hSubKey) != ERROR_SUCCESS)
366     {
367         return FALSE;
368     }
369 
370     cbValue = sizeof(szValue);
371     dwResult = RegQueryValueExW(hSubKey,
372                                 NULL,
373                                 NULL,
374                                 NULL,
375                                 (LPBYTE)szValue,
376                                 &cbValue);
377     RegCloseKey(hSubKey);
378 
379     if (dwResult != ERROR_SUCCESS)
380         return FALSE;
381 
382     /* Try to add the new profile */
383     lResult = ComboBox_AddString(GetDlgItem(hwndDlg, IDC_SOUND_SCHEME), szValue);
384     if (lResult == CB_ERR)
385         return FALSE;
386 
387     /* Allocate the profile scheme buffer */
388     pScheme = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SOUND_SCHEME_CONTEXT));
389     if (pScheme == NULL)
390     {
391         /* We failed to allocate the buffer, no need to keep a dangling string in the combobox */
392         ComboBox_DeleteString(GetDlgItem(hwndDlg, IDC_SOUND_SCHEME), lResult);
393         return FALSE;
394     }
395 
396     StringCchCopyW(pScheme->szDesc, _countof(pScheme->szDesc), szValue);
397     StringCchCopyW(pScheme->szName, _countof(pScheme->szName), szSubKey);
398 
399     /* Associate the value with the item in the combobox */
400     ComboBox_SetItemData(GetDlgItem(hwndDlg, IDC_SOUND_SCHEME), lResult, pScheme);
401 
402     /* Optionally, select the profile */
403     if (SetDefault)
404     {
405         ComboBox_SetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_SCHEME), lResult);
406     }
407 
408     return TRUE;
409 }
410 
411 
412 DWORD
413 EnumerateSoundProfiles(PGLOBAL_DATA pGlobalData, HWND hwndDlg, HKEY hKey)
414 {
415     HKEY hSubKey;
416     DWORD dwName, dwCurKey, dwResult, dwNumSchemes;
417     DWORD cbDefault;
418     WCHAR szName[MAX_PATH];
419 
420     cbDefault = sizeof(pGlobalData->szDefault);
421     if (RegQueryValueExW(hKey,
422                          NULL,
423                          NULL,
424                          NULL,
425                          (LPBYTE)pGlobalData->szDefault,
426                          &cbDefault) != ERROR_SUCCESS)
427     {
428         return FALSE;
429     }
430 
431     if (RegOpenKeyExW(hKey,
432                       L"Names",
433                       0,
434                       KEY_READ,
435                       &hSubKey) != ERROR_SUCCESS)
436     {
437         return FALSE;
438     }
439 
440     dwNumSchemes = 0;
441     dwCurKey = 0;
442     do
443     {
444         dwName = _countof(szName);
445         dwResult = RegEnumKeyExW(hSubKey,
446                                  dwCurKey,
447                                  szName,
448                                  &dwName,
449                                  NULL,
450                                  NULL,
451                                  NULL,
452                                  NULL);
453 
454         if (dwResult == ERROR_SUCCESS)
455         {
456             if (AddSoundProfile(hwndDlg, hSubKey, szName, (!_wcsicmp(szName, pGlobalData->szDefault))))
457             {
458                 dwNumSchemes++;
459             }
460         }
461 
462         dwCurKey++;
463     } while (dwResult == ERROR_SUCCESS);
464 
465     RegCloseKey(hSubKey);
466     return dwNumSchemes;
467 }
468 
469 
470 PSOUND_SCHEME_CONTEXT FindSoundProfile(HWND hwndDlg, PCWSTR szName)
471 {
472     LRESULT lCount, lIndex, lResult;
473     PSOUND_SCHEME_CONTEXT pScheme;
474     HWND hwndComboBox;
475 
476     hwndComboBox = GetDlgItem(hwndDlg, IDC_SOUND_SCHEME);
477     lCount = ComboBox_GetCount(hwndComboBox);
478     if (lCount == CB_ERR)
479     {
480         return NULL;
481     }
482 
483     for (lIndex = 0; lIndex < lCount; lIndex++)
484     {
485         lResult = ComboBox_GetItemData(hwndComboBox, lIndex);
486         if (lResult == CB_ERR)
487         {
488             continue;
489         }
490 
491         pScheme = (PSOUND_SCHEME_CONTEXT)lResult;
492         if (!_wcsicmp(pScheme->szName, szName))
493         {
494             return pScheme;
495         }
496     }
497     return NULL;
498 }
499 
500 static
501 VOID
502 FreeSoundProfiles(HWND hwndDlg)
503 {
504     LRESULT lCount, lIndex, lResult;
505     PSOUND_SCHEME_CONTEXT pScheme;
506     PLABEL_CONTEXT pLabelContext;
507     HWND hwndComboBox;
508 
509     hwndComboBox = GetDlgItem(hwndDlg, IDC_SOUND_SCHEME);
510     lCount = ComboBox_GetCount(hwndComboBox);
511     if (lCount == CB_ERR)
512         return;
513 
514     for (lIndex = 0; lIndex < lCount; lIndex++)
515     {
516         lResult = ComboBox_GetItemData(hwndComboBox, lIndex);
517         if (lResult == CB_ERR)
518         {
519             continue;
520         }
521 
522         pScheme = (PSOUND_SCHEME_CONTEXT)lResult;
523 
524         while (pScheme->LabelContext)
525         {
526             pLabelContext = pScheme->LabelContext->Next;
527             HeapFree(GetProcessHeap(), 0, pScheme->LabelContext);
528             pScheme->LabelContext = pLabelContext;
529         }
530 
531         HeapFree(GetProcessHeap(), 0, pScheme);
532     }
533 }
534 
535 BOOL
536 ImportSoundLabel(PGLOBAL_DATA pGlobalData, HWND hwndDlg, HKEY hKey, PCWSTR szProfile, PCWSTR szLabelName, PCWSTR szAppName, PAPP_MAP AppMap, PLABEL_MAP LabelMap)
537 {
538     HKEY hSubKey;
539     WCHAR szValue[MAX_PATH];
540     WCHAR szBuffer[MAX_PATH];
541     DWORD cbValue, cchLength;
542     PSOUND_SCHEME_CONTEXT pScheme;
543     PLABEL_CONTEXT pLabelContext;
544     BOOL bCurrentProfile, bActiveProfile;
545 
546     bCurrentProfile = !_wcsicmp(szProfile, L".Current");
547     bActiveProfile = !_wcsicmp(szProfile, pGlobalData->szDefault);
548 
549     if (RegOpenKeyExW(hKey,
550                       szProfile,
551                       0,
552                       KEY_READ,
553                       &hSubKey) != ERROR_SUCCESS)
554     {
555         return FALSE;
556     }
557 
558     cbValue = sizeof(szValue);
559     if (RegQueryValueExW(hSubKey,
560                          NULL,
561                          NULL,
562                          NULL,
563                          (LPBYTE)szValue,
564                          &cbValue) != ERROR_SUCCESS)
565     {
566         return FALSE;
567     }
568 
569     if (bCurrentProfile)
570         pScheme = FindSoundProfile(hwndDlg, pGlobalData->szDefault);
571     else
572         pScheme = FindSoundProfile(hwndDlg, szProfile);
573 
574     if (!pScheme)
575     {
576         return FALSE;
577     }
578     pLabelContext = FindLabelContext(pGlobalData, pScheme, AppMap->szName, LabelMap->szName);
579 
580     cchLength = ExpandEnvironmentStringsW(szValue, szBuffer, _countof(szBuffer));
581     if (cchLength == 0 || cchLength > _countof(szBuffer))
582     {
583         /* fixme */
584         return FALSE;
585     }
586 
587     if (bCurrentProfile)
588         StringCchCopyW(pLabelContext->szValue, _countof(pLabelContext->szValue), szBuffer);
589     else if (!bActiveProfile)
590         StringCchCopyW(pLabelContext->szValue, _countof(pLabelContext->szValue), szBuffer);
591 
592     return TRUE;
593 }
594 
595 
596 DWORD
597 ImportSoundEntry(PGLOBAL_DATA pGlobalData, HWND hwndDlg, HKEY hKey, PCWSTR szLabelName, PCWSTR szAppName, PAPP_MAP pAppMap)
598 {
599     HKEY hSubKey;
600     DWORD dwNumProfiles;
601     DWORD dwCurKey;
602     DWORD dwResult;
603     DWORD dwProfile;
604     WCHAR szProfile[MAX_PATH];
605     PLABEL_MAP pLabel;
606 
607     if (RegOpenKeyExW(hKey,
608                       szLabelName,
609                       0,
610                       KEY_READ,
611                       &hSubKey) != ERROR_SUCCESS)
612     {
613         return FALSE;
614     }
615     pLabel = FindLabel(pGlobalData, pAppMap, szLabelName);
616 
617     ASSERT(pLabel);
618     RemoveLabel(pGlobalData, pLabel);
619 
620     pLabel->AppMap = pAppMap;
621     pLabel->Next = pAppMap->LabelMap;
622     pAppMap->LabelMap = pLabel;
623 
624     dwNumProfiles = 0;
625     dwCurKey = 0;
626     do
627     {
628         dwProfile = _countof(szProfile);
629         dwResult = RegEnumKeyExW(hSubKey,
630                                  dwCurKey,
631                                  szProfile,
632                                  &dwProfile,
633                                  NULL,
634                                  NULL,
635                                  NULL,
636                                  NULL);
637 
638         if (dwResult == ERROR_SUCCESS)
639         {
640             if (ImportSoundLabel(pGlobalData, hwndDlg, hSubKey, szProfile, szLabelName, szAppName, pAppMap, pLabel))
641             {
642                 dwNumProfiles++;
643             }
644         }
645 
646         dwCurKey++;
647     } while (dwResult == ERROR_SUCCESS);
648 
649     RegCloseKey(hSubKey);
650 
651     return dwNumProfiles;
652 }
653 
654 
655 DWORD
656 ImportAppProfile(PGLOBAL_DATA pGlobalData, HWND hwndDlg, HKEY hKey, PCWSTR szAppName)
657 {
658     HKEY hSubKey;
659     WCHAR szDefault[MAX_PATH];
660     DWORD cbValue;
661     DWORD dwCurKey;
662     DWORD dwResult;
663     DWORD dwNumEntry;
664     DWORD dwName;
665     WCHAR szName[MAX_PATH];
666     WCHAR szIcon[MAX_PATH];
667     PAPP_MAP AppMap;
668 
669     AppMap = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(APP_MAP));
670     if (!AppMap)
671         return 0;
672 
673     if (RegOpenKeyExW(hKey,
674                       szAppName,
675                       0,
676                       KEY_READ,
677                       &hSubKey) != ERROR_SUCCESS)
678     {
679         HeapFree(GetProcessHeap(), 0, AppMap);
680         return 0;
681     }
682 
683     cbValue = sizeof(szDefault);
684     if (RegQueryValueExW(hSubKey,
685                          NULL,
686                          NULL,
687                          NULL,
688                          (LPBYTE)szDefault,
689                          &cbValue) != ERROR_SUCCESS)
690     {
691         RegCloseKey(hSubKey);
692         HeapFree(GetProcessHeap(), 0, AppMap);
693         return 0;
694     }
695 
696     cbValue = sizeof(szIcon);
697     if (RegQueryValueExW(hSubKey,
698                          L"DispFileName",
699                          NULL,
700                          NULL,
701                          (LPBYTE)szIcon,
702                          &cbValue) != ERROR_SUCCESS)
703     {
704         szIcon[0] = UNICODE_NULL;
705     }
706 
707     /* initialize app map */
708     StringCchCopyW(AppMap->szName, _countof(AppMap->szName), szAppName);
709     StringCchCopyW(AppMap->szDesc, _countof(AppMap->szDesc), szDefault);
710     StringCchCopyW(AppMap->szIcon, _countof(AppMap->szIcon), szIcon);
711 
712     AppMap->Next = pGlobalData->pAppMap;
713     pGlobalData->pAppMap = AppMap;
714 
715 
716     dwCurKey = 0;
717     dwNumEntry = 0;
718     do
719     {
720         dwName = _countof(szName);
721         dwResult = RegEnumKeyExW(hSubKey,
722                                  dwCurKey,
723                                  szName,
724                                  &dwName,
725                                  NULL,
726                                  NULL,
727                                  NULL,
728                                  NULL);
729         if (dwResult == ERROR_SUCCESS)
730         {
731             if (ImportSoundEntry(pGlobalData, hwndDlg, hSubKey, szName, szAppName, AppMap))
732             {
733                 dwNumEntry++;
734             }
735         }
736         dwCurKey++;
737     } while (dwResult == ERROR_SUCCESS);
738 
739     RegCloseKey(hSubKey);
740     return dwNumEntry;
741 }
742 
743 
744 BOOL
745 ImportSoundProfiles(PGLOBAL_DATA pGlobalData, HWND hwndDlg, HKEY hKey)
746 {
747     DWORD dwCurKey;
748     DWORD dwResult;
749     DWORD dwNumApps;
750     WCHAR szName[MAX_PATH];
751     HKEY hSubKey;
752 
753     if (RegOpenKeyExW(hKey,
754                       L"Apps",
755                       0,
756                       KEY_READ,
757                       &hSubKey) != ERROR_SUCCESS)
758     {
759         return FALSE;
760     }
761 
762     dwNumApps = 0;
763     dwCurKey = 0;
764     do
765     {
766         dwResult = RegEnumKeyW(hSubKey,
767                                dwCurKey,
768                                szName,
769                                _countof(szName));
770 
771         if (dwResult == ERROR_SUCCESS)
772         {
773             if (ImportAppProfile(pGlobalData, hwndDlg, hSubKey, szName))
774             {
775                 dwNumApps++;
776             }
777         }
778         dwCurKey++;
779     } while (dwResult == ERROR_SUCCESS);
780 
781     RegCloseKey(hSubKey);
782 
783     return (dwNumApps != 0);
784 }
785 
786 
787 BOOL
788 LoadSoundProfiles(PGLOBAL_DATA pGlobalData, HWND hwndDlg)
789 {
790     HKEY hSubKey;
791     DWORD dwNumSchemes;
792 
793     if (RegOpenKeyExW(HKEY_CURRENT_USER,
794                       L"AppEvents\\Schemes",
795                       0,
796                       KEY_READ,
797                       &hSubKey) != ERROR_SUCCESS)
798     {
799         return FALSE;
800     }
801 
802     dwNumSchemes = EnumerateSoundProfiles(pGlobalData, hwndDlg, hSubKey);
803 
804 
805     if (dwNumSchemes)
806     {
807         ImportSoundProfiles(pGlobalData, hwndDlg, hSubKey);
808     }
809 
810     RegCloseKey(hSubKey);
811     return FALSE;
812 }
813 
814 
815 BOOL
816 LoadSoundFiles(HWND hwndDlg)
817 {
818     WCHAR szList[256];
819     WCHAR szPath[MAX_PATH];
820     PWSTR ptr;
821     WIN32_FIND_DATAW FileData;
822     HANDLE hFile;
823     LRESULT lResult;
824     UINT length;
825 
826     /* Add no sound listview item */
827     if (LoadStringW(hApplet, IDS_NO_SOUND, szList, _countof(szList)))
828     {
829         szList[_countof(szList) - 1] = UNICODE_NULL;
830         ComboBox_AddString(GetDlgItem(hwndDlg, IDC_SOUND_LIST), szList);
831     }
832 
833     /* Load sound files */
834     length = GetWindowsDirectoryW(szPath, _countof(szPath));
835     if (length == 0 || length >= _countof(szPath) - CONST_STR_LEN(L"\\media\\*"))
836     {
837         return FALSE;
838     }
839 
840     //PathCchAppend(szPath, _countof(szPath), L"media\\*");
841     StringCchCatW(szPath, _countof(szPath), L"\\media\\*");
842 
843     hFile = FindFirstFileW(szPath, &FileData);
844     if (hFile == INVALID_HANDLE_VALUE)
845     {
846         return FALSE;
847     }
848 
849     do
850     {
851         if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
852             continue;
853 
854         ptr = wcsrchr(FileData.cFileName, L'\\');
855         if (ptr)
856         {
857             ptr++;
858         }
859         else
860         {
861             ptr = FileData.cFileName;
862         }
863         lResult = SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_ADDSTRING, 0, (LPARAM)ptr);
864         if (lResult != CB_ERR)
865         {
866             StringCchCopyW(szPath + (length + CONST_STR_LEN(L"\\media\\")),
867                            _countof(szPath) - (length + CONST_STR_LEN(L"\\media\\")),
868                            FileData.cFileName);
869             SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_SETITEMDATA, (WPARAM)lResult, (LPARAM)_wcsdup(szPath));
870         }
871     } while (FindNextFileW(hFile, &FileData) != 0);
872 
873     FindClose(hFile);
874     return TRUE;
875 }
876 
877 static
878 VOID
879 FreeSoundFiles(HWND hwndDlg)
880 {
881     LRESULT lCount, lIndex, lResult;
882     PWSTR pSoundPath;
883     HWND hwndComboBox;
884 
885     hwndComboBox = GetDlgItem(hwndDlg, IDC_SOUND_LIST);
886     lCount = ComboBox_GetCount(hwndComboBox);
887     if (lCount == CB_ERR)
888         return;
889 
890     for (lIndex = 0; lIndex < lCount; lIndex++)
891     {
892         lResult = ComboBox_GetItemData(hwndComboBox, lIndex);
893         if (lResult == CB_ERR)
894         {
895             continue;
896         }
897 
898         pSoundPath = (PWSTR)lResult;
899         free(pSoundPath);
900     }
901 }
902 
903 static
904 LRESULT
905 FindSoundFileInList(HWND hwndDlg, LPCWSTR pSoundPath)
906 {
907     LRESULT lCount, lIndex, lResult;
908     LPWSTR pszPath;
909     HWND hwndComboBox;
910 
911     hwndComboBox = GetDlgItem(hwndDlg, IDC_SOUND_LIST);
912     lCount = ComboBox_GetCount(hwndComboBox);
913     if (lCount == CB_ERR)
914         return CB_ERR;
915 
916     for (lIndex = 0; lIndex < lCount; lIndex++)
917     {
918         lResult = ComboBox_GetItemData(hwndComboBox, lIndex);
919         if (lResult == CB_ERR || lResult == 0)
920             continue;
921 
922         pszPath = (LPWSTR)lResult;
923         if (_wcsicmp(pszPath, pSoundPath) == 0)
924             return lIndex;
925     }
926 
927     return CB_ERR;
928 }
929 
930 BOOL
931 ShowSoundScheme(PGLOBAL_DATA pGlobalData, HWND hwndDlg)
932 {
933     LRESULT lIndex;
934     PSOUND_SCHEME_CONTEXT pScheme;
935     PAPP_MAP pAppMap;
936     PLABEL_MAP pLabelMap;
937     PLABEL_CONTEXT pLabelContext;
938     HWND hDlgCtrl, hList;
939     TVINSERTSTRUCTW tvItem;
940     HTREEITEM hTreeItem;
941 
942     hDlgCtrl = GetDlgItem(hwndDlg, IDC_SOUND_SCHEME);
943     hList = GetDlgItem(hwndDlg, IDC_SCHEME_LIST);
944 
945     if (pGlobalData->hSoundsImageList != NULL)
946     {
947         TreeView_SetImageList(hList, pGlobalData->hSoundsImageList, TVSIL_NORMAL);
948     }
949 
950     lIndex = SendMessageW(hDlgCtrl, CB_GETCURSEL, 0, 0);
951     if (lIndex == CB_ERR)
952     {
953         return FALSE;
954     }
955 
956     lIndex = SendMessageW(hDlgCtrl, CB_GETITEMDATA, (WPARAM)lIndex, 0);
957     if (lIndex == CB_ERR)
958     {
959         return FALSE;
960     }
961     pScheme = (PSOUND_SCHEME_CONTEXT)lIndex;
962 
963     StringCchCopyW(pGlobalData->szDefault, _countof(pGlobalData->szDefault), pScheme->szName);
964 
965     pAppMap = pGlobalData->pAppMap;
966     while (pAppMap)
967     {
968         ZeroMemory(&tvItem, sizeof(tvItem));
969         tvItem.hParent = TVI_ROOT;
970         tvItem.hInsertAfter = TVI_FIRST;
971 
972         tvItem.item.mask = TVIF_STATE | TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
973         tvItem.item.state = TVIS_EXPANDED;
974         tvItem.item.stateMask = TVIS_EXPANDED;
975         tvItem.item.pszText = pAppMap->szDesc;
976         tvItem.item.iImage = IMAGE_SOUND_SECTION;
977         tvItem.item.iSelectedImage = IMAGE_SOUND_SECTION;
978         tvItem.item.lParam = (LPARAM)NULL;
979 
980         hTreeItem = TreeView_InsertItem(hList, &tvItem);
981 
982         pLabelMap = pAppMap->LabelMap;
983         while (pLabelMap)
984         {
985             pLabelContext = FindLabelContext(pGlobalData, pScheme, pAppMap->szName, pLabelMap->szName);
986 
987             ZeroMemory(&tvItem, sizeof(tvItem));
988             tvItem.hParent = hTreeItem;
989             tvItem.hInsertAfter = TVI_SORT;
990 
991             tvItem.item.mask = TVIF_STATE | TVIF_TEXT | TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
992             tvItem.item.state = TVIS_EXPANDED;
993             tvItem.item.stateMask = TVIS_EXPANDED;
994             tvItem.item.pszText = pLabelMap->szDesc;
995             if (pLabelContext->szValue && wcslen(pLabelContext->szValue) > 0)
996             {
997                 tvItem.item.iImage = IMAGE_SOUND_ASSIGNED;
998                 tvItem.item.iSelectedImage = IMAGE_SOUND_ASSIGNED;
999             }
1000             else
1001             {
1002                 tvItem.item.iImage = IMAGE_SOUND_NONE;
1003                 tvItem.item.iSelectedImage = IMAGE_SOUND_NONE;
1004             }
1005             tvItem.item.lParam = (LPARAM)FindLabelContext(pGlobalData, pScheme, pAppMap->szName, pLabelMap->szName);
1006 
1007             TreeView_InsertItem(hList, &tvItem);
1008 
1009             pLabelMap = pLabelMap->Next;
1010         }
1011         pAppMap = pAppMap->Next;
1012     }
1013     return TRUE;
1014 }
1015 
1016 
1017 BOOL
1018 ApplyChanges(HWND hwndDlg)
1019 {
1020     HKEY hKey, hSubKey;
1021     DWORD dwType;
1022     LRESULT lIndex;
1023     PSOUND_SCHEME_CONTEXT pScheme;
1024     HWND hDlgCtrl;
1025     PLABEL_CONTEXT pLabelContext;
1026     WCHAR Buffer[100];
1027 
1028     hDlgCtrl = GetDlgItem(hwndDlg, IDC_SOUND_SCHEME);
1029 
1030     lIndex = SendMessageW(hDlgCtrl, CB_GETCURSEL, 0, 0);
1031     if (lIndex == CB_ERR)
1032     {
1033         return FALSE;
1034     }
1035 
1036     lIndex = SendMessageW(hDlgCtrl, CB_GETITEMDATA, (WPARAM)lIndex, 0);
1037     if (lIndex == CB_ERR)
1038     {
1039         return FALSE;
1040     }
1041     pScheme = (PSOUND_SCHEME_CONTEXT)lIndex;
1042 
1043     if (RegOpenKeyExW(HKEY_CURRENT_USER,
1044                       L"AppEvents\\Schemes",
1045                       0,
1046                       KEY_WRITE,
1047                       &hKey) != ERROR_SUCCESS)
1048     {
1049         return FALSE;
1050     }
1051 
1052     RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pScheme->szName, (wcslen(pScheme->szName) + 1) * sizeof(WCHAR));
1053     RegCloseKey(hKey);
1054 
1055     if (RegOpenKeyExW(HKEY_CURRENT_USER,
1056                       L"AppEvents\\Schemes\\Apps",
1057                       0,
1058                       KEY_WRITE,
1059                       &hKey) != ERROR_SUCCESS)
1060     {
1061         return FALSE;
1062     }
1063 
1064     pLabelContext = pScheme->LabelContext;
1065 
1066     while (pLabelContext)
1067     {
1068         StringCchPrintfW(Buffer, _countof(Buffer), L"%s\\%s\\.Current", pLabelContext->AppMap->szName, pLabelContext->LabelMap->szName);
1069 
1070         if (RegOpenKeyExW(hKey, Buffer, 0, KEY_WRITE, &hSubKey) == ERROR_SUCCESS)
1071         {
1072             dwType = (wcschr(pLabelContext->szValue, L'%') ? REG_EXPAND_SZ : REG_SZ);
1073             RegSetValueExW(hSubKey, NULL, 0, dwType, (LPBYTE)pLabelContext->szValue, (wcslen(pLabelContext->szValue) + 1) * sizeof(WCHAR));
1074             RegCloseKey(hSubKey);
1075         }
1076 
1077         pLabelContext = pLabelContext->Next;
1078     }
1079     RegCloseKey(hKey);
1080 
1081     SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, (LONG_PTR)PSNRET_NOERROR);
1082     return TRUE;
1083 }
1084 
1085 
1086 HIMAGELIST
1087 InitImageList(UINT StartResource,
1088               UINT EndResource,
1089               UINT Width,
1090               UINT Height,
1091               ULONG type)
1092 {
1093     HANDLE hImage;
1094     HIMAGELIST himl;
1095     UINT i;
1096     INT ret;
1097 
1098     /* Create the toolbar icon image list */
1099     himl = ImageList_Create(Width,
1100                             Height,
1101                             ILC_MASK | ILC_COLOR32,
1102                             EndResource - StartResource,
1103                             0);
1104     if (himl == NULL)
1105         return NULL;
1106 
1107     ret = 0;
1108     for (i = StartResource; i <= EndResource && ret != -1; i++)
1109     {
1110         hImage = LoadImageW(hApplet,
1111                             MAKEINTRESOURCEW(i),
1112                             type,
1113                             Width,
1114                             Height,
1115                             LR_LOADTRANSPARENT);
1116         if (hImage == NULL)
1117         {
1118             ret = -1;
1119             break;
1120         }
1121 
1122         if (type == IMAGE_BITMAP)
1123         {
1124             ret = ImageList_AddMasked(himl,
1125                                       hImage,
1126                                       RGB(255, 0, 128));
1127         }
1128         else if (type == IMAGE_ICON)
1129         {
1130             ret = ImageList_AddIcon(himl,
1131                                     hImage);
1132         }
1133 
1134         DeleteObject(hImage);
1135     }
1136 
1137     if (ret == -1)
1138     {
1139         ImageList_Destroy(himl);
1140         himl = NULL;
1141     }
1142 
1143     return himl;
1144 }
1145 
1146 
1147 /**
1148  * @brief
1149  * Get the duration of a waveform audio file.
1150  *
1151  * @param[in]  pFileName
1152  * The file name or full path of the file.
1153  *
1154  * @return
1155  * The duration of the sound, in milliseconds.
1156  * Returns 0 in case of failure.
1157  **/
1158 static
1159 DWORD
1160 GetSoundDuration(LPCWSTR pFileName)
1161 {
1162     MCI_OPEN_PARMSW openParms;
1163     MCI_GENERIC_PARMS closeParms;
1164     MCI_SET_PARMS setParms;
1165     MCI_STATUS_PARMS statusParms;
1166     MCIERROR mciError;
1167 
1168     ZeroMemory(&openParms, sizeof(openParms));
1169     openParms.lpstrDeviceType = L"waveaudio";
1170     openParms.lpstrElementName = pFileName;
1171 
1172     mciError = mciSendCommandW(0,
1173                                MCI_OPEN,
1174                                MCI_OPEN_TYPE | MCI_OPEN_ELEMENT | MCI_WAIT,
1175                                (DWORD_PTR)&openParms);
1176     if (mciError != 0)
1177         return 0;
1178 
1179     ZeroMemory(&setParms, sizeof(setParms));
1180     setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
1181 
1182     mciError = mciSendCommandW(openParms.wDeviceID,
1183                                MCI_SET,
1184                                MCI_SET_TIME_FORMAT | MCI_WAIT,
1185                                (DWORD_PTR)&setParms);
1186     if (mciError == 0)
1187     {
1188         ZeroMemory(&statusParms, sizeof(statusParms));
1189         statusParms.dwItem = MCI_STATUS_LENGTH;
1190 
1191         mciError = mciSendCommandW(openParms.wDeviceID,
1192                                    MCI_STATUS,
1193                                    MCI_STATUS_ITEM | MCI_WAIT,
1194                                    (DWORD_PTR)&statusParms);
1195     }
1196 
1197     closeParms.dwCallback = 0;
1198     mciSendCommandW(openParms.wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)&closeParms);
1199 
1200     if (mciError != 0)
1201         return 0;
1202 
1203     return statusParms.dwReturn;
1204 }
1205 
1206 static
1207 BOOL
1208 StartSoundTest(HWND hwndDlg, LPCWSTR pszSound)
1209 {
1210     DWORD dwDuration;
1211 
1212     dwDuration = GetSoundDuration(pszSound);
1213     if (dwDuration == 0)
1214         return FALSE;
1215 
1216     if (PlaySoundW(pszSound, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT))
1217     {
1218         SetTimer(hwndDlg, ID_SOUND_TEST_TIMER, dwDuration, NULL);
1219         return TRUE;
1220     }
1221 
1222     return FALSE;
1223 }
1224 
1225 static
1226 BOOL
1227 StopSoundTest(HWND hwndDlg)
1228 {
1229     /* Check if the sound is playing */
1230     if (KillTimer(hwndDlg, ID_SOUND_TEST_TIMER))
1231     {
1232         /* Stop the sound */
1233         PlaySoundW(NULL, NULL, SND_ASYNC);
1234         return TRUE;
1235     }
1236 
1237     return FALSE;
1238 }
1239 
1240 /* Sounds property page dialog callback */
1241 INT_PTR
1242 CALLBACK
1243 SoundsDlgProc(HWND hwndDlg,
1244               UINT uMsg,
1245               WPARAM wParam,
1246               LPARAM lParam)
1247 {
1248     PGLOBAL_DATA pGlobalData;
1249 
1250     OPENFILENAMEW ofn;
1251     WCHAR filename[MAX_PATH];
1252     WCHAR szFilter[256], szTitle[256];
1253     LPWSTR pFileName;
1254     LRESULT lResult;
1255 
1256     pGlobalData = (PGLOBAL_DATA)GetWindowLongPtrW(hwndDlg, DWLP_USER);
1257 
1258     switch (uMsg)
1259     {
1260         case WM_INITDIALOG:
1261         {
1262             pGlobalData = (PGLOBAL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GLOBAL_DATA));
1263             SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pGlobalData);
1264 
1265             pGlobalData->NumWavOut = waveOutGetNumDevs();
1266 
1267             pGlobalData->hPlayIcon = LoadImageW(hApplet,
1268                                                 MAKEINTRESOURCEW(IDI_PLAY_ICON),
1269                                                 IMAGE_ICON,
1270                                                 32,
1271                                                 32,
1272                                                 LR_DEFAULTCOLOR);
1273             pGlobalData->hStopIcon = LoadImageW(hApplet,
1274                                                 MAKEINTRESOURCEW(IDI_STOP_ICON),
1275                                                 IMAGE_ICON,
1276                                                 32,
1277                                                 32,
1278                                                 LR_DEFAULTCOLOR);
1279             SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1280                                 IMAGE_ICON, (LPARAM)pGlobalData->hPlayIcon);
1281 
1282             pGlobalData->hSoundsImageList = InitImageList(IDI_SOUND_SECTION,
1283                                                           IDI_SOUND_ASSIGNED,
1284                                                           GetSystemMetrics(SM_CXSMICON),
1285                                                           GetSystemMetrics(SM_CXSMICON),
1286                                                           IMAGE_ICON);
1287 
1288             LoadEventLabels(pGlobalData);
1289             LoadSoundProfiles(pGlobalData, hwndDlg);
1290             LoadSoundFiles(hwndDlg);
1291             ShowSoundScheme(pGlobalData, hwndDlg);
1292 
1293             if (wParam == (WPARAM)GetDlgItem(hwndDlg, IDC_SOUND_SCHEME))
1294                 return TRUE;
1295             SetFocus(GetDlgItem(hwndDlg, IDC_SOUND_SCHEME));
1296             return FALSE;
1297         }
1298         case WM_COMMAND:
1299         {
1300             switch (LOWORD(wParam))
1301             {
1302                 case IDC_BROWSE_SOUND:
1303                 {
1304                     ZeroMemory(&ofn, sizeof(ofn));
1305                     ofn.lStructSize = sizeof(ofn);
1306                     ofn.hwndOwner = hwndDlg;
1307                     ofn.lpstrFile = filename;
1308                     ofn.lpstrFile[0] = UNICODE_NULL;
1309                     ofn.nMaxFile = _countof(filename);
1310                     LoadStringW(hApplet, IDS_WAVE_FILES_FILTER, szFilter, _countof(szFilter));
1311                     ofn.lpstrFilter = MakeFilter(szFilter);
1312                     ofn.nFilterIndex = 0;
1313                     LoadStringW(hApplet, IDS_BROWSE_FOR_SOUND, szTitle, _countof(szTitle));
1314                     ofn.lpstrTitle = szTitle;
1315                     ofn.lpstrInitialDir = L"%SystemRoot%\\Media";
1316                     ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
1317 
1318                     if (GetOpenFileNameW(&ofn))
1319                     {
1320                         // search if list already contains that sound
1321                         lResult = FindSoundFileInList(hwndDlg, filename);
1322                         if (lResult != CB_ERR)
1323                         {
1324                             SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_SETCURSEL, (WPARAM)lResult, 0);
1325                             break;
1326                         }
1327 
1328                         // extract file name
1329                         pFileName = wcsrchr(filename, L'\\');
1330                         ASSERT(pFileName != NULL);
1331                         pFileName++;
1332 
1333                         // add to list
1334                         lResult = SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_ADDSTRING, 0, (LPARAM)pFileName);
1335                         if (lResult != CB_ERR)
1336                         {
1337                             // add path and select item
1338                             SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_SETITEMDATA, (WPARAM)lResult, (LPARAM)_wcsdup(filename));
1339                             SendDlgItemMessageW(hwndDlg, IDC_SOUND_LIST, CB_SETCURSEL, (WPARAM)lResult, 0);
1340                         }
1341                     }
1342                     break;
1343                 }
1344                 case IDC_PLAY_SOUND:
1345                 {
1346                     LRESULT lIndex;
1347 
1348                     if (StopSoundTest(hwndDlg))
1349                     {
1350                         SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1351                                             IMAGE_ICON, (LPARAM)pGlobalData->hPlayIcon);
1352                         break;
1353                     }
1354 
1355                     lIndex = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_LIST));
1356                     if (lIndex == CB_ERR)
1357                     {
1358                         break;
1359                     }
1360 
1361                     lIndex = ComboBox_GetItemData(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex);
1362                     if (lIndex != CB_ERR)
1363                     {
1364                         if (StartSoundTest(hwndDlg, (LPWSTR)lIndex))
1365                         {
1366                             SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1367                                                 IMAGE_ICON, (LPARAM)pGlobalData->hStopIcon);
1368                         }
1369                     }
1370                     break;
1371                 }
1372                 case IDC_SOUND_SCHEME:
1373                 {
1374                     if (HIWORD(wParam) == CBN_SELENDOK)
1375                     {
1376                         TreeView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_SCHEME_LIST));
1377                         ShowSoundScheme(pGlobalData, hwndDlg);
1378                         EnableWindow(GetDlgItem(hwndDlg, IDC_SOUND_LIST), FALSE);
1379                         EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT_SOUND), FALSE);
1380                         EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), FALSE);
1381                         EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE_SOUND), FALSE);
1382                         PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1383                     }
1384                     break;
1385                 }
1386                 case IDC_SOUND_LIST:
1387                 {
1388                     if (HIWORD(wParam) == CBN_SELENDOK)
1389                     {
1390                         PLABEL_CONTEXT pLabelContext;
1391                         HTREEITEM hItem;
1392                         TVITEMW item;
1393                         LRESULT lIndex;
1394 
1395                         hItem = TreeView_GetSelection(GetDlgItem(hwndDlg, IDC_SCHEME_LIST));
1396                         if (hItem == NULL)
1397                         {
1398                             break;
1399                         }
1400 
1401                         lIndex = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_LIST));
1402                         if (lIndex == CB_ERR)
1403                         {
1404                             break;
1405                         }
1406 
1407                         ZeroMemory(&item, sizeof(item));
1408                         item.mask = TVIF_PARAM;
1409                         item.hItem = hItem;
1410                         if (TreeView_GetItem(GetDlgItem(hwndDlg, IDC_SCHEME_LIST), &item))
1411                         {
1412                             LRESULT lResult;
1413                             pLabelContext = (PLABEL_CONTEXT)item.lParam;
1414 
1415                             lResult = ComboBox_GetItemData(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex);
1416                             if (lResult == CB_ERR || lResult == 0)
1417                             {
1418                                 if (lIndex != pLabelContext->szValue[0])
1419                                 {
1420                                     /* Update the tree view item image */
1421                                     item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1422                                     item.iImage = IMAGE_SOUND_NONE;
1423                                     item.iSelectedImage = IMAGE_SOUND_NONE;
1424                                     TreeView_SetItem(GetDlgItem(hwndDlg, IDC_SCHEME_LIST), &item);
1425 
1426                                     PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1427 
1428                                     EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), FALSE);
1429                                 }
1430 
1431                                 pLabelContext->szValue[0] = UNICODE_NULL;
1432 
1433                                 break;
1434                             }
1435 
1436                             if (_wcsicmp(pLabelContext->szValue, (PWSTR)lResult) || (lIndex != pLabelContext->szValue[0]))
1437                             {
1438                                 /* Update the tree view item image */
1439                                 item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1440                                 item.iImage = IMAGE_SOUND_ASSIGNED;
1441                                 item.iSelectedImage = IMAGE_SOUND_ASSIGNED;
1442                                 TreeView_SetItem(GetDlgItem(hwndDlg, IDC_SCHEME_LIST), &item);
1443 
1444                                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
1445 
1446                                 ///
1447                                 /// Should store in current member
1448                                 ///
1449                                 StringCchCopyW(pLabelContext->szValue, _countof(pLabelContext->szValue), (PWSTR)lResult);
1450                             }
1451 
1452                             if (wcslen((PWSTR)lResult) && lIndex != 0 && pGlobalData->NumWavOut != 0)
1453                             {
1454                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), TRUE);
1455                             }
1456                             else
1457                             {
1458                                 EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), FALSE);
1459                             }
1460                         }
1461                     }
1462                     break;
1463                 }
1464             }
1465             break;
1466         }
1467         case WM_DESTROY:
1468         {
1469             StopSoundTest(hwndDlg);
1470             FreeSoundFiles(hwndDlg);
1471             FreeSoundProfiles(hwndDlg);
1472             FreeAppMap(pGlobalData);
1473             FreeLabelMap(pGlobalData);
1474             if (pGlobalData->hSoundsImageList)
1475                 ImageList_Destroy(pGlobalData->hSoundsImageList);
1476 
1477             if (pGlobalData->hStopIcon)
1478                 DestroyIcon(pGlobalData->hStopIcon);
1479 
1480             if (pGlobalData->hPlayIcon)
1481                 DestroyIcon(pGlobalData->hPlayIcon);
1482 
1483             HeapFree(GetProcessHeap(), 0, pGlobalData);
1484             break;
1485         }
1486         case WM_NOTIFY:
1487         {
1488             PLABEL_CONTEXT pLabelContext;
1489             PWSTR ptr;
1490 
1491             LPNMHDR lpnm = (LPNMHDR)lParam;
1492 
1493             switch (lpnm->code)
1494             {
1495                 case PSN_APPLY:
1496                 {
1497                     ApplyChanges(hwndDlg);
1498                     break;
1499                 }
1500                 case PSN_KILLACTIVE:
1501                 {
1502                     if (StopSoundTest(hwndDlg))
1503                     {
1504                         SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1505                                             IMAGE_ICON, (LPARAM)pGlobalData->hPlayIcon);
1506                     }
1507 
1508                     break;
1509                 }
1510                 case TVN_SELCHANGED:
1511                 {
1512                     LPNMTREEVIEWW nm = (LPNMTREEVIEWW)lParam;
1513                     LRESULT lCount, lIndex, lResult;
1514 
1515                     if (StopSoundTest(hwndDlg))
1516                     {
1517                         SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1518                                             IMAGE_ICON, (LPARAM)pGlobalData->hPlayIcon);
1519                     }
1520 
1521                     pLabelContext = (PLABEL_CONTEXT)nm->itemNew.lParam;
1522                     if (pLabelContext == NULL)
1523                     {
1524                         EnableWindow(GetDlgItem(hwndDlg, IDC_SOUND_LIST), FALSE);
1525                         EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT_SOUND), FALSE);
1526                         EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE_SOUND), FALSE);
1527                         EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), FALSE);
1528                         return FALSE;
1529                     }
1530 
1531                     EnableWindow(GetDlgItem(hwndDlg, IDC_SOUND_LIST), TRUE);
1532                     EnableWindow(GetDlgItem(hwndDlg, IDC_TEXT_SOUND), TRUE);
1533                     EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE_SOUND), TRUE);
1534 
1535                     if (wcslen(pLabelContext->szValue) == 0)
1536                     {
1537                         lIndex = ComboBox_SetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_LIST), 0);
1538                         EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), FALSE);
1539                         break;
1540                     }
1541 
1542                     if (pGlobalData->NumWavOut != 0)
1543                         EnableWindow(GetDlgItem(hwndDlg, IDC_PLAY_SOUND), TRUE);
1544 
1545                     lCount = ComboBox_GetCount(GetDlgItem(hwndDlg, IDC_SOUND_LIST));
1546                     for (lIndex = 0; lIndex < lCount; lIndex++)
1547                     {
1548                         lResult = ComboBox_GetItemData(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex);
1549                         if (lResult == CB_ERR || lResult == 0)
1550                             continue;
1551 
1552                         if (!wcscmp((PWSTR)lResult, pLabelContext->szValue))
1553                         {
1554                             ComboBox_SetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex);
1555                             return FALSE;
1556                         }
1557                     }
1558 
1559                     ptr = wcsrchr(pLabelContext->szValue, L'\\');
1560                     if (ptr)
1561                     {
1562                         ptr++;
1563                     }
1564                     else
1565                     {
1566                         ptr = pLabelContext->szValue;
1567                     }
1568 
1569                     lIndex = ComboBox_AddString(GetDlgItem(hwndDlg, IDC_SOUND_LIST), ptr);
1570                     if (lIndex != CB_ERR)
1571                     {
1572                         ComboBox_SetItemData(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex, _wcsdup(pLabelContext->szValue));
1573                         ComboBox_SetCurSel(GetDlgItem(hwndDlg, IDC_SOUND_LIST), lIndex);
1574                     }
1575                     break;
1576                 }
1577             }
1578             break;
1579         }
1580         case WM_TIMER:
1581         {
1582             if (wParam == ID_SOUND_TEST_TIMER)
1583             {
1584                 KillTimer(hwndDlg, ID_SOUND_TEST_TIMER);
1585                 SendDlgItemMessageW(hwndDlg, IDC_PLAY_SOUND, BM_SETIMAGE,
1586                                     IMAGE_ICON, (LPARAM)pGlobalData->hPlayIcon);
1587             }
1588 
1589             break;
1590         }
1591     }
1592 
1593     return FALSE;
1594 }
1595