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