xref: /reactos/base/applications/sndvol32/dialog.c (revision 48cc7814)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS Sound Volume Control
4  * FILE:        base/applications/sndvol32/dialog.c
5  * PROGRAMMERS: Johannes Anderwald
6  */
7 
8 #include "sndvol32.h"
9 
10 
11 VOID
12 ConvertRect(LPRECT lpRect, UINT xBaseUnit, UINT yBaseUnit)
13 {
14     lpRect->left = MulDiv(lpRect->left, xBaseUnit, 4);
15     lpRect->right = MulDiv(lpRect->right, xBaseUnit, 4);
16     lpRect->top = MulDiv(lpRect->top, yBaseUnit, 8);
17     lpRect->bottom = MulDiv(lpRect->bottom, yBaseUnit, 8);
18 }
19 
20 LPVOID
21 LoadDialogResource(
22     IN HMODULE hModule,
23     IN LPCWSTR ResourceName,
24     OUT LPDWORD ResourceLength)
25 {
26     HRSRC hSrc;
27     HGLOBAL hRes;
28     PVOID Result;
29 
30     /* find resource */
31     hSrc = FindResourceW(hModule, ResourceName, (LPCWSTR)RT_DIALOG);
32 
33     if (!hSrc)
34     {
35         /* failed to find resource */
36         return NULL;
37     }
38 
39     /* now load the resource */
40     hRes = LoadResource(hAppInstance, hSrc);
41     if (!hRes)
42     {
43         /* failed to load resource */
44         return NULL;
45     }
46 
47     /* now lock the resource */
48     Result = LockResource(hRes);
49 
50     if (!Result)
51     {
52         /* failed to lock resource */
53         return NULL;
54     }
55 
56     if (ResourceLength)
57     {
58         /* store output length */
59         *ResourceLength = SizeofResource(hAppInstance, hSrc);
60     }
61 
62     /* done */
63     return Result;
64 }
65 
66 LPWORD
67 AddDialogControl(
68     IN HWND hwndDialog,
69     IN HWND * OutWnd,
70     IN LPRECT DialogOffset,
71     IN PDLGITEMTEMPLATE DialogItem,
72     IN DWORD DialogIdMultiplier,
73     IN HFONT hFont,
74     UINT xBaseUnit,
75     UINT yBaseUnit)
76 {
77     RECT rect;
78     LPWORD Offset;
79     LPWSTR ClassName, WindowName = NULL;
80     HWND hwnd;
81     DWORD wID;
82     INT nSteps, i;
83 
84     /* initialize client rectangle */
85     rect.left = DialogItem->x;
86     rect.top = DialogItem->y;
87     rect.right = DialogItem->x + DialogItem->cx;
88     rect.bottom = DialogItem->y + DialogItem->cy;
89 
90     /* Convert Dialog units to pixes */
91     ConvertRect(&rect, xBaseUnit, yBaseUnit);
92 
93     rect.left += DialogOffset->left;
94     rect.right += DialogOffset->left;
95     rect.top += DialogOffset->top;
96     rect.bottom += DialogOffset->top;
97 
98     /* move offset after dialog item */
99     Offset = (LPWORD)(DialogItem + 1);
100 
101     if (*Offset == 0xFFFF)
102     {
103         /* class is encoded as type */
104         Offset++;
105 
106         /* get control type */
107         switch(*Offset)
108         {
109             case 0x80:
110                 ClassName = L"button";
111                 WindowName = (LPWSTR)(Offset + 1);
112                 break ;
113             case 0x82:
114                 ClassName = L"static";
115                 WindowName = (LPWSTR)(Offset + 1);
116                 break;
117             default:
118                /* FIXME */
119                assert(0);
120                ClassName = NULL;
121         }
122     }
123     else
124     {
125         /* class name is encoded as string */
126         ClassName = (LPWSTR)Offset;
127 
128         /* move offset to the end of class string */
129         Offset += wcslen(ClassName);
130 
131         /* get window name */
132         WindowName = (LPWSTR)(Offset + 1);
133     }
134 
135     /* move offset past class type/string */
136     Offset++;
137 
138     if (DialogItem->id == MAXWORD)
139     {
140         /* id is not important */
141         wID = DialogItem->id;
142     }
143     else
144     {
145         /* calculate id */
146         wID = DialogItem->id * (DialogIdMultiplier + 1);
147 
148     }
149 
150     /* now create the window */
151     hwnd = CreateWindowExW(DialogItem->dwExtendedStyle,
152                            ClassName,
153                            WindowName,
154                            DialogItem->style,
155                            rect.left,
156                            rect.top,
157                            rect.right - rect.left,
158                            rect.bottom - rect.top,
159                            hwndDialog,
160                            (HMENU)(wID),
161                            hAppInstance,
162                            NULL);
163 
164     /* sanity check */
165     assert(hwnd);
166 
167     /* store window */
168     *OutWnd = hwnd;
169 
170     /* check if this the track bar */
171     if (!wcsicmp(ClassName, L"msctls_trackbar32"))
172     {
173         if (DialogItem->style & TBS_VERT)
174         {
175             /* Vertical trackbar: Volume */
176 
177             /* set up range */
178             SendMessage(hwnd, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0, VOLUME_STEPS));
179 
180             /* set up page size */
181             SendMessage(hwnd, TBM_SETPAGESIZE, 0, (LPARAM)VOLUME_PAGE_SIZE);
182 
183             /* set position */
184             SendMessage(hwnd, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)0);
185 
186             /* Calculate and set ticks */
187             nSteps = (VOLUME_STEPS / (VOLUME_TICKS + 1));
188             if (VOLUME_STEPS % (VOLUME_TICKS + 1) != 0)
189                 nSteps++;
190             for (i = nSteps; i < VOLUME_STEPS; i += nSteps)
191                 SendMessage(hwnd, TBM_SETTIC, 0, (LPARAM)i);
192         }
193         else
194         {
195             /* Horizontal trackbar: Balance */
196 
197             /* set up range */
198             SendMessage(hwnd, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0, BALANCE_STEPS));
199 
200             /* set up page size */
201             SendMessage(hwnd, TBM_SETPAGESIZE, 0, (LPARAM)BALANCE_PAGE_SIZE);
202 
203             /* set position */
204             SendMessage(hwnd, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)BALANCE_STEPS / 2);
205 
206             /* Calculate and set ticks */
207             nSteps = (BALANCE_STEPS / (BALANCE_TICKS + 1));
208             if (BALANCE_STEPS % (BALANCE_TICKS + 1) != 0)
209                 nSteps++;
210             for (i = nSteps; i < BALANCE_STEPS; i += nSteps)
211                 SendMessage(hwnd, TBM_SETTIC, 0, (LPARAM)i);
212         }
213     }
214     else if (!wcsicmp(ClassName, L"static") || !wcsicmp(ClassName, L"button"))
215     {
216         /* set font */
217         SendMessageW(hwnd, WM_SETFONT, (WPARAM)hFont, TRUE);
218     }
219 
220     //ShowWindow(hwnd, SW_SHOWNORMAL);
221 
222     if (WindowName != NULL)
223     {
224         /* move offset past window name */
225         Offset += wcslen(WindowName) + 1;
226     }
227 
228     /* check if there is additional data */
229     if (*Offset == 0)
230     {
231         /* no additional data */
232         Offset++;
233     }
234     else
235     {
236         /* FIXME: Determine whether this should be "Offset += 1 + *Offset" to explicitly skip the data count too. */
237         /* skip past additional data */
238         Offset += *Offset;
239     }
240 
241     /* make sure next template is word-aligned */
242     Offset = (LPWORD)(((ULONG_PTR)Offset + 3) & ~3);
243 
244     /* done */
245     return Offset;
246 }
247 
248 VOID
249 LoadDialogControls(
250     IN PMIXER_WINDOW MixerWindow,
251     LPRECT DialogOffset,
252     WORD ItemCount,
253     PDLGITEMTEMPLATE DialogItem,
254     DWORD DialogIdMultiplier,
255     UINT xBaseUnit,
256     UINT yBaseUnit)
257 {
258     LPWORD Offset;
259     WORD Index;
260 
261     /* sanity check */
262     assert(ItemCount);
263 
264     if (MixerWindow->Window)
265         MixerWindow->Window = (HWND*)HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MixerWindow->Window, (MixerWindow->WindowCount + ItemCount) * sizeof(HWND));
266     else
267         MixerWindow->Window = (HWND*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ItemCount * sizeof(HWND));
268     if (!MixerWindow->Window)
269     {
270         /* no memory */
271         return;
272     }
273 
274     /* enumerate now all controls */
275     for (Index = 0; Index < ItemCount; Index++)
276     {
277         /* add controls */
278         Offset = AddDialogControl(MixerWindow->hWnd, &MixerWindow->Window[MixerWindow->WindowCount], DialogOffset, DialogItem, DialogIdMultiplier, MixerWindow->hFont, xBaseUnit, yBaseUnit);
279 
280         /* sanity check */
281         assert(Offset);
282 
283         /* move dialog item to new offset */
284         DialogItem =(PDLGITEMTEMPLATE)Offset;
285 
286         /* increment window count */
287         MixerWindow->WindowCount++;
288     }
289 }
290 
291 VOID
292 LoadDialog(
293     IN HMODULE hModule,
294     IN PMIXER_WINDOW MixerWindow,
295     IN LPCWSTR DialogResId,
296     IN DWORD Index)
297 {
298     LPDLGTEMPLATE DlgTemplate;
299     PDLGITEMTEMPLATE DlgItem;
300     RECT dialogRect;
301     LPWORD Offset;
302     WORD FontSize;
303     WCHAR FontName[100];
304     WORD Length;
305     int width;
306 
307     DWORD units = GetDialogBaseUnits();
308     UINT xBaseUnit = LOWORD(units);
309     UINT yBaseUnit = HIWORD(units);
310 
311     /* first load the dialog resource */
312     DlgTemplate = (LPDLGTEMPLATE)LoadDialogResource(hModule, DialogResId, NULL);
313     if (!DlgTemplate)
314     {
315         /* failed to load resource */
316         return;
317     }
318 
319     /* Now walk past the dialog header */
320     Offset = (LPWORD)(DlgTemplate + 1);
321 
322     /* FIXME: support menu */
323     assert(*Offset == 0);
324     Offset++;
325 
326     /* FIXME: support classes */
327     assert(*Offset == 0);
328     Offset++;
329 
330     /* FIXME: support titles */
331     assert(*Offset == 0);
332     Offset++;
333 
334     /* get font size */
335     FontSize = *Offset;
336     Offset++;
337 
338     /* calculate font length */
339     Length = wcslen((LPWSTR)Offset) + 1;
340     assert(Length < (sizeof(FontName) / sizeof(WCHAR)));
341 
342     /* copy font */
343     wcscpy(FontName, (LPWSTR)Offset);
344 
345     if (DlgTemplate->style & DS_SETFONT)
346     {
347         HDC hDC;
348 
349         hDC = GetDC(0);
350 
351         if (!MixerWindow->hFont)
352         {
353             int pixels = MulDiv(FontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
354             MixerWindow->hFont = CreateFontW(-pixels, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, FontName);
355         }
356 
357         if (MixerWindow->hFont)
358         {
359             SIZE charSize;
360             HFONT hOldFont;
361 
362             hOldFont = SelectObject(hDC, MixerWindow->hFont);
363             charSize.cx = GdiGetCharDimensions(hDC, NULL, &charSize.cy);
364             if (charSize.cx)
365             {
366                 xBaseUnit = charSize.cx;
367                 yBaseUnit = charSize.cy;
368             }
369             SelectObject(hDC, hOldFont);
370         }
371     }
372 
373 //    assert(MixerWindow->hFont);
374 
375     /* move offset after font name */
376     Offset += Length;
377 
378     /* offset is now at first dialog item control */
379     DlgItem = (PDLGITEMTEMPLATE)Offset;
380 
381     dialogRect.left = 0;
382     dialogRect.right = DlgTemplate->cx;
383     dialogRect.top = 0;
384     dialogRect.bottom = DlgTemplate->cy;
385 
386     ConvertRect(&dialogRect, xBaseUnit, yBaseUnit);
387 
388     width = dialogRect.right - dialogRect.left;
389 
390     dialogRect.left += MixerWindow->rect.right;
391     dialogRect.right += MixerWindow->rect.right;
392     dialogRect.top += MixerWindow->rect.top;
393     dialogRect.bottom += MixerWindow->rect.top;
394 
395     MixerWindow->rect.right += width;
396     if ((dialogRect.bottom - dialogRect.top) > (MixerWindow->rect.bottom - MixerWindow->rect.top))
397         MixerWindow->rect.bottom = MixerWindow->rect.top + dialogRect.bottom - dialogRect.top;
398 
399     /* now add the controls */
400     LoadDialogControls(MixerWindow, &dialogRect, DlgTemplate->cdit, DlgItem, Index, xBaseUnit, yBaseUnit);
401 }
402 
403 BOOL
404 CALLBACK
405 EnumConnectionsCallback(
406     PSND_MIXER Mixer,
407     DWORD LineID,
408     LPMIXERLINE Line,
409     PVOID Context)
410 {
411     WCHAR LineName[MIXER_LONG_NAME_CHARS];
412     DWORD Flags;
413     DWORD wID;
414     UINT ControlCount = 0, Index;
415     LPMIXERCONTROL Control = NULL;
416     HWND hDlgCtrl;
417     PPREFERENCES_CONTEXT PrefContext = (PPREFERENCES_CONTEXT)Context;
418 
419     if (Line->cControls != 0)
420     {
421       /* get line name */
422       if (SndMixerGetLineName(PrefContext->MixerWindow->Mixer, PrefContext->SelectedLine, LineName, MIXER_LONG_NAME_CHARS, TRUE) == -1)
423       {
424           /* failed to get line name */
425           LineName[0] = L'\0';
426       }
427 
428       /* check if line is found in registry settings */
429       if (ReadLineConfig(PrefContext->DeviceName,
430                          LineName,
431                          Line->szName,
432                          &Flags))
433       {
434           /* is it selected */
435           if (Flags != 0x4)
436           {
437               int dlgId = (PrefContext->MixerWindow->Mode == SMALL_MODE) ? IDD_SMALL_MASTER : IDD_NORMAL_MASTER;
438 
439               /* load dialog resource */
440               LoadDialog(hAppInstance, PrefContext->MixerWindow, MAKEINTRESOURCE(dlgId), PrefContext->Count);
441 
442               /* get id */
443               wID = (PrefContext->Count + 1) * IDC_LINE_NAME;
444 
445               /* set line name */
446               SetDlgItemTextW(PrefContext->MixerWindow->hWnd, wID, Line->szName);
447 
448               /* query controls */
449               if (SndMixerQueryControls(Mixer, &ControlCount, Line, &Control) != FALSE)
450               {
451                   /* now go through all controls and update their states */
452                   for(Index = 0; Index < Line->cControls; Index++)
453                   {
454                       if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_SWITCH)
455                       {
456                           MIXERCONTROLDETAILS_BOOLEAN Details;
457 
458                           /* get volume control details */
459                           if (SndMixerGetVolumeControlDetails(Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_BOOLEAN), (LPVOID)&Details) != -1)
460                           {
461                               /* update dialog control */
462                               wID = (PrefContext->Count + 1) * IDC_LINE_SWITCH;
463 
464                               /* get dialog control */
465                               hDlgCtrl = GetDlgItem(PrefContext->MixerWindow->hWnd, wID);
466 
467                               if (hDlgCtrl != NULL)
468                               {
469                                   /* check state */
470                                   if (SendMessageW(hDlgCtrl, BM_GETCHECK, 0, 0) != Details.fValue)
471                                   {
472                                       /* update control state */
473                                       SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)Details.fValue, 0);
474                                   }
475                               }
476                           }
477                       }
478                       else if ((Control[Index].dwControlType & MIXERCONTROL_CT_CLASS_MASK) == MIXERCONTROL_CT_CLASS_FADER)
479                       {
480                           MIXERCONTROLDETAILS_UNSIGNED Details;
481 
482                           /* get volume control details */
483                           if (SndMixerGetVolumeControlDetails(Mixer, Control[Index].dwControlID, sizeof(MIXERCONTROLDETAILS_UNSIGNED), (LPVOID)&Details) != -1)
484                           {
485                               /* update dialog control */
486                               DWORD Position;
487                               DWORD Step = 0x10000 / VOLUME_STEPS;
488 
489                               /* FIXME: give me granularity */
490                               Position = VOLUME_STEPS - (Details.dwValue / Step);
491 
492                               /* FIXME support left - right slider */
493                               wID = (PrefContext->Count + 1) * IDC_LINE_SLIDER_VERT;
494 
495                               /* get dialog control */
496                               hDlgCtrl = GetDlgItem(PrefContext->MixerWindow->hWnd, wID);
497 
498                               if (hDlgCtrl != NULL)
499                               {
500                                   /* check state */
501                                   LRESULT OldPosition = SendMessageW(hDlgCtrl, TBM_GETPOS, 0, 0);
502                                   if (OldPosition != Position)
503                                   {
504                                       /* update control state */
505                                       SendMessageW(hDlgCtrl, TBM_SETPOS, (WPARAM)TRUE, Position + Index);
506                                   }
507                               }
508                           }
509                       }
510                   }
511 
512                   /* free controls */
513                   HeapFree(GetProcessHeap(), 0, Control);
514               }
515 
516               /* increment dialog count */
517               PrefContext->Count++;
518           }
519       }
520     }
521     return TRUE;
522 }
523 
524 VOID
525 LoadDialogCtrls(
526     PPREFERENCES_CONTEXT PrefContext)
527 {
528     HWND hDlgCtrl;
529     RECT statusRect;
530 
531     /* set dialog count to zero */
532     PrefContext->Count = 0;
533 
534     SetRectEmpty(&PrefContext->MixerWindow->rect);
535 
536     /* enumerate controls */
537     SndMixerEnumConnections(PrefContext->MixerWindow->Mixer, PrefContext->SelectedLine, EnumConnectionsCallback, (PVOID)PrefContext);
538 
539     if (PrefContext->MixerWindow->hStatusBar)
540     {
541         GetWindowRect(PrefContext->MixerWindow->hStatusBar, &statusRect);
542         PrefContext->MixerWindow->rect.bottom += (statusRect.bottom - statusRect.top);
543     }
544 
545     /* now move the window */
546     AdjustWindowRect(&PrefContext->MixerWindow->rect, WS_DLGFRAME | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE, TRUE);
547     SetWindowPos(PrefContext->MixerWindow->hWnd, HWND_TOP, PrefContext->MixerWindow->rect.left, PrefContext->MixerWindow->rect.top, PrefContext->MixerWindow->rect.right - PrefContext->MixerWindow->rect.left, PrefContext->MixerWindow->rect.bottom - PrefContext->MixerWindow->rect.top, SWP_NOMOVE | SWP_NOZORDER);
548 
549     /* get last line separator */
550     hDlgCtrl = GetDlgItem(PrefContext->MixerWindow->hWnd, IDC_LINE_SEP * PrefContext->Count);
551 
552     if (hDlgCtrl != NULL)
553     {
554         /* hide last separator */
555         ShowWindow(hDlgCtrl, SW_HIDE);
556     }
557 }
558 
559 VOID
560 UpdateDialogLineSwitchControl(
561     PPREFERENCES_CONTEXT PrefContext,
562     LPMIXERLINE Line,
563     LONG fValue)
564 {
565     DWORD Index;
566     DWORD wID;
567     HWND hDlgCtrl;
568     WCHAR LineName[MIXER_LONG_NAME_CHARS];
569 
570     /* find the index of this line */
571     for(Index = 0; Index < PrefContext->Count; Index++)
572     {
573         /* get id */
574         wID = (Index + 1) * IDC_LINE_NAME;
575 
576         if (GetDlgItemText(PrefContext->MixerWindow->hWnd, wID, LineName, MIXER_LONG_NAME_CHARS) == 0)
577         {
578             /* failed to retrieve id */
579             continue;
580         }
581 
582         /* check if the line name matches */
583         if (!wcsicmp(LineName, Line->szName))
584         {
585             /* found matching line name */
586             wID = (Index + 1) * IDC_LINE_SWITCH;
587 
588             /* get dialog control */
589             hDlgCtrl = GetDlgItem(PrefContext->MixerWindow->hWnd, wID);
590 
591             if (hDlgCtrl != NULL)
592             {
593                 /* check state */
594                 if (SendMessageW(hDlgCtrl, BM_GETCHECK, 0, 0) != fValue)
595                 {
596                     /* update control state */
597                     SendMessageW(hDlgCtrl, BM_SETCHECK, (WPARAM)fValue, 0);
598                 }
599             }
600             break;
601         }
602     }
603 }
604 
605 VOID
606 UpdateDialogLineSliderControl(
607     PPREFERENCES_CONTEXT PrefContext,
608     LPMIXERLINE Line,
609     DWORD dwControlID,
610     DWORD dwDialogID,
611     DWORD Position)
612 {
613     DWORD Index;
614     DWORD wID;
615     HWND hDlgCtrl;
616     WCHAR LineName[MIXER_LONG_NAME_CHARS];
617 
618     /* find the index of this line */
619     for(Index = 0; Index < PrefContext->Count; Index++)
620     {
621         /* get id */
622         wID = (Index + 1) * IDC_LINE_NAME;
623 
624         if (GetDlgItemText(PrefContext->MixerWindow->hWnd, wID, LineName, MIXER_LONG_NAME_CHARS) == 0)
625         {
626             /* failed to retrieve id */
627             continue;
628         }
629 
630         /* check if the line name matches */
631         if (!wcsicmp(LineName, Line->szName))
632         {
633             /* found matching line name */
634             wID = (Index + 1) * dwDialogID;
635 
636             /* get dialog control */
637             hDlgCtrl = GetDlgItem(PrefContext->MixerWindow->hWnd, wID);
638 
639             if (hDlgCtrl != NULL)
640             {
641                 /* check state */
642                 LRESULT OldPosition = SendMessageW(hDlgCtrl, TBM_GETPOS, 0, 0);
643                 if (OldPosition != Position)
644                 {
645                     /* update control state */
646                     SendMessageW(hDlgCtrl, TBM_SETPOS, (WPARAM)TRUE, Position + Index);
647                 }
648             }
649             break;
650         }
651     }
652 }
653