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