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